Migrate crash reporter UI to Jetpack Compose
This commit is contained in:
@@ -1,24 +1,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.mm20.launcher2.crashreporter">
|
||||
<application
|
||||
android:supportsRtl="true">
|
||||
|
||||
<application>
|
||||
<provider
|
||||
android:name="com.balsikandar.crashreporter.CrashReporterInitProvider"
|
||||
android:authorities="${applicationId}.CrashReporterInitProvider"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.balsikandar.crashreporter.ui.CrashReporterActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:excludeFromRecents="true"
|
||||
android:taskAffinity="com.balsikandar.android.task"
|
||||
android:theme="@style/CrashReporter.Theme" />
|
||||
|
||||
<activity
|
||||
android:name="com.balsikandar.crashreporter.ui.LogMessageActivity"
|
||||
android:parentActivityName="com.balsikandar.crashreporter.ui.CrashReporterActivity"
|
||||
android:theme="@style/CrashReporter.Theme" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -3,7 +3,6 @@ package com.balsikandar.crashreporter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.balsikandar.crashreporter.ui.CrashReporterActivity;
|
||||
import com.balsikandar.crashreporter.utils.CrashReporterNotInitializedException;
|
||||
import com.balsikandar.crashreporter.utils.CrashReporterExceptionHandler;
|
||||
import com.balsikandar.crashreporter.utils.CrashUtil;
|
||||
@@ -61,10 +60,6 @@ public class CrashReporter {
|
||||
CrashUtil.logException(exception);
|
||||
}
|
||||
|
||||
public static Intent getLaunchIntent() {
|
||||
return new Intent(applicationContext, CrashReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
|
||||
public static void disableNotification() {
|
||||
isNotificationEnabled = false;
|
||||
}
|
||||
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package com.balsikandar.crashreporter.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.balsikandar.crashreporter.ui.LogMessageActivity;
|
||||
import com.balsikandar.crashreporter.utils.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.mm20.launcher2.crashreporter.R;
|
||||
|
||||
/**
|
||||
* Created by bali on 10/08/17.
|
||||
*/
|
||||
|
||||
public class CrashLogAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private ArrayList<File> crashFileList;
|
||||
|
||||
public CrashLogAdapter(Context context, ArrayList<File> allCrashLogs) {
|
||||
this.context = context;
|
||||
crashFileList = allCrashLogs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.custom_item, null);
|
||||
return new CrashLogViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
((CrashLogViewHolder) holder).setUpViewHolder(context, crashFileList.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return crashFileList.size();
|
||||
}
|
||||
|
||||
|
||||
public void updateList(ArrayList<File> allCrashLogs) {
|
||||
crashFileList = allCrashLogs;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
private class CrashLogViewHolder extends RecyclerView.ViewHolder {
|
||||
private TextView textViewMsg, messageLogTime;
|
||||
|
||||
CrashLogViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
messageLogTime = itemView.findViewById(R.id.messageLogTime);
|
||||
textViewMsg = itemView.findViewById(R.id.textViewMsg);
|
||||
}
|
||||
|
||||
void setUpViewHolder(final Context context, final File file) {
|
||||
final String filePath = file.getAbsolutePath();
|
||||
messageLogTime.setText(file.getName().replaceAll("[a-zA-Z_.]", ""));
|
||||
textViewMsg.setText(FileUtils.readFirstLineFromFile(new File(filePath)));
|
||||
|
||||
textViewMsg.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(context, LogMessageActivity.class);
|
||||
intent.putExtra("LogMessage", filePath);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
package com.balsikandar.crashreporter.adapter;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
|
||||
import com.balsikandar.crashreporter.ui.CrashLogFragment;
|
||||
import com.balsikandar.crashreporter.ui.ExceptionLogFragment;
|
||||
|
||||
/**
|
||||
* Created by bali on 11/08/17.
|
||||
*/
|
||||
|
||||
public class MainPagerAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private CrashLogFragment crashLogFragment;
|
||||
private ExceptionLogFragment exceptionLogFragment;
|
||||
private String[] titles;
|
||||
|
||||
public MainPagerAdapter(FragmentManager fm, String[] titles) {
|
||||
super(fm);
|
||||
this.titles = titles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
if (position == 0) {
|
||||
return crashLogFragment = new CrashLogFragment();
|
||||
} else if (position == 1) {
|
||||
return exceptionLogFragment = new ExceptionLogFragment();
|
||||
} else {
|
||||
return new CrashLogFragment();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return titles[position];
|
||||
}
|
||||
|
||||
public void clearLogs() {
|
||||
crashLogFragment.clearLog();
|
||||
exceptionLogFragment.clearLog();
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package com.balsikandar.crashreporter.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.balsikandar.crashreporter.CrashReporter;
|
||||
import com.balsikandar.crashreporter.adapter.CrashLogAdapter;
|
||||
import com.balsikandar.crashreporter.utils.Constants;
|
||||
import com.balsikandar.crashreporter.utils.CrashUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
import de.mm20.launcher2.crashreporter.R;
|
||||
|
||||
/**
|
||||
* Created by bali on 11/08/17.
|
||||
*/
|
||||
|
||||
public class CrashLogFragment extends Fragment {
|
||||
|
||||
private CrashLogAdapter logAdapter;
|
||||
|
||||
private RecyclerView crashRecyclerView;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.crash_log, container, false);
|
||||
crashRecyclerView = (RecyclerView) view.findViewById(R.id.crashRecyclerView);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
loadAdapter(getActivity(), crashRecyclerView);
|
||||
}
|
||||
|
||||
private void loadAdapter(Context context, RecyclerView crashRecyclerView) {
|
||||
|
||||
logAdapter = new CrashLogAdapter(context, getAllCrashes());
|
||||
crashRecyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
|
||||
crashRecyclerView.setAdapter(logAdapter);
|
||||
}
|
||||
|
||||
public void clearLog() {
|
||||
if (logAdapter != null) {
|
||||
logAdapter.updateList(getAllCrashes());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ArrayList<File> getAllCrashes() {
|
||||
String directoryPath;
|
||||
String crashReportPath = CrashReporter.getCrashReportPath();
|
||||
|
||||
if (TextUtils.isEmpty(crashReportPath)) {
|
||||
directoryPath = CrashUtil.getDefaultPath();
|
||||
} else {
|
||||
directoryPath = crashReportPath;
|
||||
}
|
||||
File directory = new File(directoryPath);
|
||||
if (!directory.exists() || !directory.isDirectory()) {
|
||||
throw new RuntimeException("The path provided doesn't exists : " + directoryPath);
|
||||
}
|
||||
ArrayList<File> listOfFiles = new ArrayList<>(Arrays.asList(directory.listFiles()));
|
||||
for (Iterator<File> iterator = listOfFiles.iterator(); iterator.hasNext(); ) {
|
||||
if (iterator.next().getName().contains(Constants.EXCEPTION_SUFFIX)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
Collections.sort(listOfFiles, Collections.reverseOrder());
|
||||
return listOfFiles;
|
||||
}
|
||||
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
package com.balsikandar.crashreporter.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.balsikandar.crashreporter.CrashReporter;
|
||||
import com.balsikandar.crashreporter.adapter.MainPagerAdapter;
|
||||
import com.balsikandar.crashreporter.utils.Constants;
|
||||
import com.balsikandar.crashreporter.utils.CrashUtil;
|
||||
import com.balsikandar.crashreporter.utils.FileUtils;
|
||||
import com.balsikandar.crashreporter.utils.SimplePageChangeListener;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import de.mm20.launcher2.crashreporter.R;
|
||||
|
||||
public class CrashReporterActivity extends AppCompatActivity {
|
||||
|
||||
private MainPagerAdapter mainPagerAdapter;
|
||||
private int selectedTabPosition = 0;
|
||||
|
||||
//region activity callbacks
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.log_main_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.delete_crash_logs) {
|
||||
clearCrashLog();
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getTheme().applyStyle(R.style.DefaultColors, true);
|
||||
setContentView(R.layout.crash_reporter_activity);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar.setTitle(getString(R.string.crash_reporter));
|
||||
toolbar.setSubtitle(getApplicationName());
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ViewPager viewPager = findViewById(R.id.viewpager);
|
||||
if (viewPager != null) {
|
||||
setupViewPager(viewPager);
|
||||
}
|
||||
|
||||
TabLayout tabLayout = findViewById(R.id.tabs);
|
||||
tabLayout.setupWithViewPager(viewPager);
|
||||
}
|
||||
//endregion
|
||||
|
||||
private void clearCrashLog() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String crashReportPath = TextUtils.isEmpty(CrashReporter.getCrashReportPath()) ?
|
||||
CrashUtil.getDefaultPath() : CrashReporter.getCrashReportPath();
|
||||
|
||||
File[] logs = new File(crashReportPath).listFiles();
|
||||
for (File file : logs) {
|
||||
FileUtils.delete(file);
|
||||
}
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mainPagerAdapter.clearLogs();
|
||||
}
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void setupViewPager(ViewPager viewPager) {
|
||||
String[] titles = {getString(R.string.crashes), getString(R.string.exceptions)};
|
||||
mainPagerAdapter = new MainPagerAdapter(getSupportFragmentManager(), titles);
|
||||
viewPager.setAdapter(mainPagerAdapter);
|
||||
|
||||
viewPager.addOnPageChangeListener(new SimplePageChangeListener() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
selectedTabPosition = position;
|
||||
}
|
||||
});
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (intent != null && !intent.getBooleanExtra(Constants.LANDING, false)) {
|
||||
selectedTabPosition = 1;
|
||||
}
|
||||
viewPager.setCurrentItem(selectedTabPosition);
|
||||
}
|
||||
|
||||
private String getApplicationName() {
|
||||
ApplicationInfo applicationInfo = getApplicationInfo();
|
||||
int stringId = applicationInfo.labelRes;
|
||||
return stringId == 0 ? applicationInfo.nonLocalizedLabel.toString() : getString(stringId);
|
||||
}
|
||||
|
||||
}
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
package com.balsikandar.crashreporter.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.balsikandar.crashreporter.CrashReporter;
|
||||
import com.balsikandar.crashreporter.adapter.CrashLogAdapter;
|
||||
import com.balsikandar.crashreporter.utils.Constants;
|
||||
import com.balsikandar.crashreporter.utils.CrashUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
import de.mm20.launcher2.crashreporter.R;
|
||||
|
||||
/**
|
||||
* Created by bali on 11/08/17.
|
||||
*/
|
||||
|
||||
public class ExceptionLogFragment extends Fragment {
|
||||
|
||||
private CrashLogAdapter logAdapter;
|
||||
|
||||
private RecyclerView exceptionRecyclerView;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.exception_log, container, false);
|
||||
exceptionRecyclerView = (RecyclerView) view.findViewById(R.id.exceptionRecyclerView);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
loadAdapter(getActivity(), exceptionRecyclerView);
|
||||
}
|
||||
|
||||
private void loadAdapter(Context context, RecyclerView exceptionRecyclerView) {
|
||||
|
||||
logAdapter = new CrashLogAdapter(context, getAllExceptions());
|
||||
exceptionRecyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
|
||||
exceptionRecyclerView.setAdapter(logAdapter);
|
||||
}
|
||||
|
||||
public void clearLog() {
|
||||
if (logAdapter != null) {
|
||||
logAdapter.updateList(getAllExceptions());
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<File> getAllExceptions() {
|
||||
String directoryPath;
|
||||
String crashReportPath = CrashReporter.getCrashReportPath();
|
||||
|
||||
if (TextUtils.isEmpty(crashReportPath)){
|
||||
directoryPath = CrashUtil.getDefaultPath();
|
||||
} else{
|
||||
directoryPath = crashReportPath;
|
||||
}
|
||||
|
||||
File directory = new File(directoryPath);
|
||||
if (!directory.exists() || !directory.isDirectory()){
|
||||
throw new RuntimeException("The path provided doesn't exists : " + directoryPath);
|
||||
}
|
||||
|
||||
ArrayList<File> listOfFiles = new ArrayList<>(Arrays.asList(directory.listFiles()));
|
||||
for (Iterator<File> iterator = listOfFiles.iterator(); iterator.hasNext(); ) {
|
||||
if (iterator.next().getName().contains(Constants.CRASH_SUFFIX)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
Collections.sort(listOfFiles, Collections.reverseOrder());
|
||||
return listOfFiles;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package com.balsikandar.crashreporter.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import com.balsikandar.crashreporter.utils.AppUtils;
|
||||
import com.balsikandar.crashreporter.utils.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import de.mm20.launcher2.crashreporter.R;
|
||||
|
||||
public class LogMessageActivity extends AppCompatActivity {
|
||||
|
||||
private TextView appInfo;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_log_message);
|
||||
appInfo = findViewById(R.id.appInfo);
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (intent != null) {
|
||||
String dirPath = intent.getStringExtra("LogMessage");
|
||||
File file = new File(dirPath);
|
||||
String crashLog = FileUtils.readFromFile(file);
|
||||
TextView textView = findViewById(R.id.logMessage);
|
||||
textView.setText(crashLog);
|
||||
}
|
||||
|
||||
Toolbar myToolbar = findViewById(R.id.toolbar);
|
||||
myToolbar.setTitle(getString(R.string.crash_reporter));
|
||||
setSupportActionBar(myToolbar);
|
||||
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
getAppInfo();
|
||||
}
|
||||
|
||||
private void getAppInfo() {
|
||||
appInfo.setText(AppUtils.getDeviceDetails(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.crash_detail_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
Intent intent = getIntent();
|
||||
String filePath = null;
|
||||
if (intent != null) {
|
||||
filePath = intent.getStringExtra("LogMessage");
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.delete_log) {
|
||||
if (FileUtils.delete(filePath)) {
|
||||
finish();
|
||||
}
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.share_crash_log) {
|
||||
shareCrashReport(filePath);
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void shareCrashReport(String filePath) {
|
||||
Uri uri = FileProvider.getUriForFile(this,
|
||||
this.getApplicationContext().getPackageName() + ".fileprovider",
|
||||
new File(filePath));
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("*/*");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, appInfo.getText().toString());
|
||||
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
startActivity(Intent.createChooser(intent, "Share via"));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.balsikandar.crashreporter.utils;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
@@ -11,8 +8,6 @@ import android.content.pm.ResolveInfo;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -40,9 +35,8 @@ public class AppUtils {
|
||||
|
||||
public static String getDeviceDetails(Context context) {
|
||||
|
||||
return "Device Information\n"
|
||||
+ "\nDEVICE.ID : " + getDeviceId(context)
|
||||
+ "\nAPP.VERSION : " + getAppVersion(context)
|
||||
return "APP.VERSION : " + getAppVersion(context)
|
||||
+ "\nAPP.VERSIONCODE : " + getAppVersionCode(context)
|
||||
+ "\nLAUNCHER.APP : " + getCurrentLauncherApp(context)
|
||||
+ "\nTIMEZONE : " + timeZone()
|
||||
+ "\nVERSION.RELEASE : " + Build.VERSION.RELEASE
|
||||
@@ -61,12 +55,9 @@ public class AppUtils {
|
||||
+ "\nMANUFACTURER : " + Build.MANUFACTURER
|
||||
+ "\nMODEL : " + Build.MODEL
|
||||
+ "\nPRODUCT : " + Build.PRODUCT
|
||||
+ "\nSERIAL : " + Build.SERIAL
|
||||
+ "\nTAGS : " + Build.TAGS
|
||||
+ "\nTIME : " + Build.TIME
|
||||
+ "\nTYPE : " + Build.TYPE
|
||||
+ "\nUNKNOWN : " + Build.UNKNOWN
|
||||
+ "\nUSER : " + Build.USER;
|
||||
+ "\nTYPE : " + Build.TYPE;
|
||||
}
|
||||
|
||||
private static String timeZone() {
|
||||
@@ -94,7 +85,7 @@ public class AppUtils {
|
||||
return androidId;
|
||||
}
|
||||
|
||||
private static int getAppVersion(Context context) {
|
||||
private static int getAppVersionCode(Context context) {
|
||||
try {
|
||||
PackageInfo packageInfo = context.getPackageManager()
|
||||
.getPackageInfo(context.getPackageName(), 0);
|
||||
@@ -103,4 +94,14 @@ public class AppUtils {
|
||||
throw new RuntimeException("Could not get package name: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getAppVersion(Context context) {
|
||||
try {
|
||||
PackageInfo packageInfo = context.getPackageManager()
|
||||
.getPackageInfo(context.getPackageName(), 0);
|
||||
return packageInfo.versionName;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RuntimeException("Could not get package name: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,28 +3,30 @@ package com.balsikandar.crashreporter.utils;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.balsikandar.crashreporter.CrashReporter;
|
||||
import de.mm20.launcher2.crashreporter.R;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import de.mm20.launcher2.crashreporter.R;
|
||||
|
||||
import static android.content.Context.NOTIFICATION_SERVICE;
|
||||
import static com.balsikandar.crashreporter.utils.Constants.CHANNEL_NOTIFICATION_ID;
|
||||
|
||||
@@ -47,7 +49,13 @@ public class CrashUtil {
|
||||
String filename = getCrashLogTime() + Constants.CRASH_SUFFIX + Constants.FILE_EXTENSION;
|
||||
writeToFile(crashReportPath, filename, getStackTrace(throwable));
|
||||
|
||||
showNotification(throwable.getLocalizedMessage(), true);
|
||||
//if (crashReportPath.isEmpty()) crashReportPath = getDefaultPath();
|
||||
|
||||
try {
|
||||
showNotification(throwable.getLocalizedMessage(), filename);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void logException(final Exception exception) {
|
||||
@@ -91,7 +99,7 @@ public class CrashUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static void showNotification(String localisedMsg, boolean isCrash) {
|
||||
private static void showNotification(String localisedMsg, String fileName) throws UnsupportedEncodingException {
|
||||
|
||||
if (CrashReporter.isNotificationEnabled()) {
|
||||
Context context = CrashReporter.getContext();
|
||||
@@ -101,8 +109,11 @@ public class CrashUtil {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_NOTIFICATION_ID);
|
||||
builder.setSmallIcon(R.drawable.ic_warning_black_24dp);
|
||||
|
||||
Intent intent = CrashReporter.getLaunchIntent();
|
||||
intent.putExtra(Constants.LANDING, isCrash);
|
||||
String filePath = new File(getDefaultPath(), fileName).getAbsolutePath();
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(new ComponentName(context.getPackageName(), "de.mm20.launcher2.ui.settings.SettingsActivity"));
|
||||
intent.putExtra("de.mm20.launcher2.settings.ROUTE", "settings/debug/crashreporter/report?fileName=" + URLEncoder.encode(filePath, "utf8"));
|
||||
intent.setAction(Long.toString(System.currentTimeMillis()));
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package de.mm20.launcher2.crashreporter
|
||||
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import android.icu.util.TimeZone
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class CrashReport(
|
||||
val type: CrashReportType,
|
||||
val time: Date,
|
||||
val summary: String,
|
||||
val stacktrace: String?,
|
||||
val filePath: String
|
||||
) {
|
||||
companion object {
|
||||
suspend fun fromFile(file: File, loadStackTrace: Boolean): CrashReport {
|
||||
val df = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
|
||||
val time = df.parse(file.name.replace("[a-zA-Z_.]", ""))
|
||||
val content = if (loadStackTrace) {
|
||||
withContext(Dispatchers.IO) {
|
||||
file.inputStream().bufferedReader().use {
|
||||
it.readText()
|
||||
}
|
||||
}
|
||||
} else null
|
||||
val summary = content?.substringBefore("\n")
|
||||
?: withContext(Dispatchers.IO) {
|
||||
file.inputStream().bufferedReader().use {
|
||||
it.readLine()
|
||||
}
|
||||
}
|
||||
return CrashReport(
|
||||
type = if (file.name.endsWith("_crash.txt")) CrashReportType.Crash else CrashReportType.Exception,
|
||||
time = time,
|
||||
summary = summary,
|
||||
stacktrace = content,
|
||||
filePath = file.absolutePath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class CrashReportType {
|
||||
Exception,
|
||||
Crash
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
package de.mm20.launcher2.crashreporter
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.balsikandar.crashreporter.CrashReporter
|
||||
import com.balsikandar.crashreporter.utils.AppUtils
|
||||
import com.balsikandar.crashreporter.utils.CrashUtil
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
object CrashReporter {
|
||||
fun logException(e: Exception) {
|
||||
@@ -12,7 +19,23 @@ object CrashReporter {
|
||||
Log.e("MM20", Log.getStackTraceString(e))
|
||||
}
|
||||
|
||||
fun getLaunchIntent(): Intent {
|
||||
return com.balsikandar.crashreporter.CrashReporter.getLaunchIntent()
|
||||
suspend fun getCrashReports(): List<CrashReport> {
|
||||
val files = withContext(Dispatchers.IO) {
|
||||
val now = System.currentTimeMillis()
|
||||
val path = CrashReporter.getCrashReportPath()?.takeIf { it.isEmpty() } ?: CrashUtil.getDefaultPath()
|
||||
File(path).listFiles { f ->
|
||||
f.lastModified() > now - 7 * 24 * 60 * 60 * 1000L
|
||||
}?.sortedByDescending { it.lastModified() }
|
||||
}
|
||||
return files?.map { CrashReport.fromFile(it, false) } ?: emptyList()
|
||||
}
|
||||
|
||||
suspend fun getCrashReport(filePath: String): CrashReport {
|
||||
val path = CrashReporter.getCrashReportPath()?.takeIf { it.isEmpty() } ?: CrashUtil.getDefaultPath()
|
||||
return CrashReport.fromFile(File(filePath), true)
|
||||
}
|
||||
|
||||
fun getDeviceInformation(context: Context): String {
|
||||
return AppUtils.getDeviceDetails(context)
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:context="com.balsikandar.crashreporter.ui.LogMessageActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/logMessage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:textColor="?colorAccent" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/appInfo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="10dp"
|
||||
android:textColor="?android:textColorPrimary" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -1,4 +0,0 @@
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/crashRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.balsikandar.crashreporter.ui.CrashReporterActivity">
|
||||
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/viewpager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@+id/appbar"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/messageLogTime"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:padding="5dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewMsg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@+id/messageLogTime"
|
||||
android:maxLines="4"
|
||||
android:orientation="vertical"
|
||||
android:padding="5dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@+id/textViewMsg"
|
||||
android:layout_marginTop="3dp"
|
||||
android:background="#dcdada" />
|
||||
</RelativeLayout>
|
||||
@@ -1,4 +0,0 @@
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/exceptionRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
Reference in New Issue
Block a user