diff --git a/app/src/main/java/com/balsikandar/crashreporter/sample/MainActivity.java b/app/src/main/java/com/balsikandar/crashreporter/sample/MainActivity.java
index 0ba97ad..41a2ab3 100644
--- a/app/src/main/java/com/balsikandar/crashreporter/sample/MainActivity.java
+++ b/app/src/main/java/com/balsikandar/crashreporter/sample/MainActivity.java
@@ -1,16 +1,19 @@
package com.balsikandar.crashreporter.sample;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import com.balsikandar.crashreporter.CrashReporter;
+import com.balsikandar.crashreporter.ui.CrashReporterActivity;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
Context context;
+ Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -62,11 +65,20 @@ public void run() {
context.getResources();
} catch (Exception e) {
//log caught Exception
- CrashReporter.logException(e, MainActivity.class.getSimpleName());
+ CrashReporter.logException(e);
}
}
}).start();
+ mContext = this;
+ findViewById(R.id.crashLogActivity).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(mContext, CrashReporterActivity.class);
+ startActivity(intent);
+ }
+ });
+
}
}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 4b55d7f..4de0308 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -46,38 +46,15 @@
android:textColor="@color/black"
android:textSize="14sp" />
-
+
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a6ea6e7..2d33664 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -4,7 +4,5 @@
IndexOutOfBound
ClassCast Exeption
ArrayStoreException
- /Android/data/your-app-package-name/files/crashReports
- Crash this app by clicking these buttons and check your logs at below location
Add your own crash and check if it gets logged
diff --git a/crashreporter/build.gradle b/crashreporter/build.gradle
index 40fb288..dead20a 100644
--- a/crashreporter/build.gradle
+++ b/crashreporter/build.gradle
@@ -26,7 +26,9 @@ dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
+ compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:appcompat-v7:25.3.1'
+ compile 'com.android.support:design:25.3.1'
testCompile 'junit:junit:4.12'
}
diff --git a/crashreporter/src/main/AndroidManifest.xml b/crashreporter/src/main/AndroidManifest.xml
index d0171fe..10a6348 100644
--- a/crashreporter/src/main/AndroidManifest.xml
+++ b/crashreporter/src/main/AndroidManifest.xml
@@ -1,16 +1,29 @@
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporter.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporter.java
index 8301060..f0b42d4 100644
--- a/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporter.java
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/CrashReporter.java
@@ -1,30 +1,43 @@
package com.balsikandar.crashreporter;
import android.content.Context;
+import android.content.Intent;
-import com.balsikandar.crashreporter.custom.CrashReporterNotInitializedException;
-import com.balsikandar.crashreporter.handler.CrashReporterExceptionHandler;
+import com.balsikandar.crashreporter.ui.CrashReporterActivity;
+import com.balsikandar.crashreporter.utils.CrashReporterNotInitializedException;
+import com.balsikandar.crashreporter.utils.CrashReporterExceptionHandler;
import com.balsikandar.crashreporter.utils.CrashUtil;
public class CrashReporter {
private static Context applicationContext;
+ private static String crashReportPath;
+
+ private static boolean isNotificationEnabled = true;
+
private CrashReporter() {
// This class in not publicly instantiable
}
public static void initialize(Context context) {
applicationContext = context;
- setUpExceptionHandler(null);
+ setUpExceptionHandler();
if (BuildConfig.DEBUG) {
CrashUtil.logD("CrashReporter", "your crash report files will be saved in \"android/data/your-app-pkg/crashReports/\" path");
}
}
- public static void initialize(Context context, String crashReportPath) {
+ public static void initialize(Context context, String crashReportSavePath) {
applicationContext = context;
- setUpExceptionHandler(crashReportPath);
+ crashReportPath = crashReportSavePath;
+ setUpExceptionHandler();
+ }
+
+ private static void setUpExceptionHandler() {
+ if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof CrashReporterExceptionHandler)) {
+ Thread.setDefaultUncaughtExceptionHandler(new CrashReporterExceptionHandler());
+ }
}
public static Context getContext() {
@@ -38,32 +51,25 @@ public static Context getContext() {
return applicationContext;
}
- private static void setUpExceptionHandler(String crashReportPath) {
- if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof CrashReporterExceptionHandler)) {
- Thread.setDefaultUncaughtExceptionHandler(new CrashReporterExceptionHandler(
- crashReportPath));
- }
- }
-
- //LOG Exception APIs
- public static void logException(Exception exception) {
- CrashUtil.logException(null/*pass null for path*/, exception, null/*pass null for tag*/);
+ public static String getCrashReportPath() {
+ return crashReportPath;
}
- public static void logException(Exception exception, String tag) {
- CrashUtil.logException(null/*pass null for path*/, exception, tag);
+ public static boolean isNotificationEnabled() {
+ return isNotificationEnabled;
}
- public static void logException(String exceptionMsg) {
- CrashUtil.logException(null/*pass null for path*/, exceptionMsg);
+ //LOG Exception APIs
+ public static void logException(Exception exception) {
+ CrashUtil.logException(exception);
}
- public static void logException(String exceptionSavePath, Exception exception) {
- CrashUtil.logException(exceptionSavePath, exception, null/*pass null for tag*/);
+ public static Intent getLaunchIntent() {
+ return new Intent(applicationContext, CrashReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
- public static void logException(String exceptionSavePath, String exceptionMsg) {
- CrashUtil.logException(exceptionSavePath, exceptionMsg);
+ public static void disableNotification() {
+ isNotificationEnabled = false;
}
}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/CrashLogAdapter.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/CrashLogAdapter.java
new file mode 100644
index 0000000..afb27bf
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/CrashLogAdapter.java
@@ -0,0 +1,80 @@
+package com.balsikandar.crashreporter.adapter;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.balsikandar.crashreporter.R;
+import com.balsikandar.crashreporter.ui.LogMessageActivity;
+import com.balsikandar.crashreporter.utils.CrashUtil;
+import com.balsikandar.crashreporter.utils.FileUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+
+/**
+ * Created by bali on 10/08/17.
+ */
+
+public class CrashLogAdapter extends RecyclerView.Adapter {
+
+ private Context context;
+ private ArrayList crashFileList;
+
+ public CrashLogAdapter(Context context, ArrayList 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 allCrashLogs) {
+ crashFileList = allCrashLogs;
+ notifyDataSetChanged();
+ }
+
+
+ private class CrashLogViewHolder extends RecyclerView.ViewHolder {
+ private TextView textViewMsg, messageLogTime;
+
+ CrashLogViewHolder(View itemView) {
+ super(itemView);
+ messageLogTime = (TextView) itemView.findViewById(R.id.messageLogTime);
+ textViewMsg = (TextView) 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);
+ }
+ });
+ }
+ }
+}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/MainPagerAdapter.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/MainPagerAdapter.java
new file mode 100644
index 0000000..6f190d6
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/adapter/MainPagerAdapter.java
@@ -0,0 +1,50 @@
+package com.balsikandar.crashreporter.adapter;
+
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.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();
+ }
+}
\ No newline at end of file
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/handler/CrashReporterExceptionHandler.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/handler/CrashReporterExceptionHandler.java
deleted file mode 100644
index 1a8a7a0..0000000
--- a/crashreporter/src/main/java/com/balsikandar/crashreporter/handler/CrashReporterExceptionHandler.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.balsikandar.crashreporter.handler;
-
-import com.balsikandar.crashreporter.utils.CrashUtil;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.Writer;
-
-
-public class CrashReporterExceptionHandler implements Thread.UncaughtExceptionHandler {
-
- private Thread.UncaughtExceptionHandler exceptionHandler;
-
- private String crashReportPath;
-
- public CrashReporterExceptionHandler(String crashReportPath) {
- this.exceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
- this.crashReportPath = crashReportPath;
- }
-
- @Override
- public void uncaughtException(Thread t, Throwable e) {
-
- final Writer result = new StringWriter();
- final PrintWriter printWriter = new PrintWriter(result);
-
- e.printStackTrace(printWriter);
- String crashLog = result.toString();
- printWriter.close();
-
-
- CrashUtil.saveCrashReport(crashReportPath, crashLog);
-
- exceptionHandler.uncaughtException(t, e);
- }
-
-}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashLogFragment.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashLogFragment.java
new file mode 100644
index 0000000..2525c64
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashLogFragment.java
@@ -0,0 +1,87 @@
+package com.balsikandar.crashreporter.ui;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.balsikandar.crashreporter.CrashReporter;
+import com.balsikandar.crashreporter.R;
+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;
+
+/**
+ * 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 getAllCrashes() {
+ String crashDirPath;
+ if (TextUtils.isEmpty(CrashReporter.getCrashReportPath())){
+ crashDirPath = CrashUtil.getDefaultPath();
+ } else{
+ crashDirPath = CrashReporter.getCrashReportPath();
+ }
+ ArrayList listOfFiles = new ArrayList<>(Arrays.asList(new File(crashDirPath).listFiles()));
+ for (Iterator iterator = listOfFiles.iterator(); iterator.hasNext(); ) {
+ if (iterator.next().getName().contains(Constants.EXCEPTION_SUFFIX)) {
+ iterator.remove();
+ }
+ }
+ Collections.sort(listOfFiles, Collections.reverseOrder());
+ return listOfFiles;
+ }
+
+}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashReporterActivity.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashReporterActivity.java
new file mode 100644
index 0000000..c0a41b7
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/CrashReporterActivity.java
@@ -0,0 +1,95 @@
+package com.balsikandar.crashreporter.ui;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.Bundle;
+import android.support.design.widget.TabLayout;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.balsikandar.crashreporter.R;
+import com.balsikandar.crashreporter.adapter.MainPagerAdapter;
+import com.balsikandar.crashreporter.utils.Constants;
+import com.balsikandar.crashreporter.utils.FileUtils;
+import com.balsikandar.crashreporter.utils.SimplePageChangeListener;
+
+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);
+ setContentView(R.layout.crash_reporter_activity);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar.setTitle(getString(R.string.crash_reporter));
+ toolbar.setSubtitle(getApplicationName());
+ setSupportActionBar(toolbar);
+
+ ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
+ if (viewPager != null) {
+ setupViewPager(viewPager);
+ }
+
+ TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
+ tabLayout.setupWithViewPager(viewPager);
+ }
+ //endregion
+
+ private void clearCrashLog() {
+ if (FileUtils.deleteFiles(null)) {
+ mainPagerAdapter.clearLogs();
+ }
+ }
+
+ 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);
+ }
+
+}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/ExceptionLogFragment.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/ExceptionLogFragment.java
new file mode 100644
index 0000000..db561d1
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/ExceptionLogFragment.java
@@ -0,0 +1,86 @@
+package com.balsikandar.crashreporter.ui;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.balsikandar.crashreporter.CrashReporter;
+import com.balsikandar.crashreporter.R;
+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;
+
+/**
+ * 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 getAllExceptions() {
+ String crashDirPath;
+ if (TextUtils.isEmpty(CrashReporter.getCrashReportPath())){
+ crashDirPath = CrashUtil.getDefaultPath();
+ } else{
+ crashDirPath = CrashReporter.getCrashReportPath();
+ }
+ ArrayList listOfFiles = new ArrayList<>(Arrays.asList(new File(crashDirPath).listFiles()));
+ for (Iterator iterator = listOfFiles.iterator(); iterator.hasNext(); ) {
+ if (iterator.next().getName().contains(Constants.CRASH_SUFFIX)) {
+ iterator.remove();
+ }
+ }
+ Collections.sort(listOfFiles, Collections.reverseOrder());
+ return listOfFiles;
+ }
+
+}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/LogMessageActivity.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/LogMessageActivity.java
new file mode 100644
index 0000000..3deb6e0
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/ui/LogMessageActivity.java
@@ -0,0 +1,86 @@
+package com.balsikandar.crashreporter.ui;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+import com.balsikandar.crashreporter.R;
+import com.balsikandar.crashreporter.utils.AppUtils;
+import com.balsikandar.crashreporter.utils.FileUtils;
+
+import java.io.File;
+
+public class LogMessageActivity extends AppCompatActivity {
+
+ private TextView appInfo;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_log_message);
+ appInfo = (TextView) 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 = (TextView) findViewById(R.id.logMessage);
+ textView.setText(crashLog);
+ }
+
+ Toolbar myToolbar = (Toolbar) 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) {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("*/*");
+ intent.putExtra(Intent.EXTRA_TEXT, appInfo.getText().toString());
+ intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filePath)));
+ startActivity(Intent.createChooser(intent, "Share via"));
+ }
+
+}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/AppUtils.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/AppUtils.java
new file mode 100644
index 0000000..e95f82d
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/AppUtils.java
@@ -0,0 +1,120 @@
+package com.balsikandar.crashreporter.utils;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+
+import java.util.HashMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+/**
+ * Created by bali on 12/08/17.
+ */
+
+public class AppUtils {
+ private static String getCurrentLauncherApp(Context context) {
+ String str = "";
+ PackageManager localPackageManager = context.getPackageManager();
+ Intent intent = new Intent("android.intent.action.MAIN");
+ intent.addCategory("android.intent.category.HOME");
+ try {
+ ResolveInfo resolveInfo = localPackageManager.resolveActivity(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (resolveInfo != null && resolveInfo.activityInfo != null) {
+ str = resolveInfo.activityInfo.packageName;
+ }
+ } catch (Exception e) {
+ CrashUtil.logE("AppUtils", "Exception : " + e.getMessage());
+ }
+ return str;
+ }
+
+ private static String getUserIdentity(Context context) {
+ AccountManager manager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
+ Account[] list = manager.getAccounts();
+ String emailId = null;
+ for (Account account : list) {
+ if (account.type.equalsIgnoreCase("com.google")) {
+ emailId = account.name;
+ break;
+ }
+ }
+ if (emailId != null) {
+ return emailId;
+ }
+ return "";
+ }
+
+ public static String getDeviceDetails(Context context) {
+
+ return "Device Information\n"
+ + "\nDEVICE.ID : " + getDeviceId(context)
+ + "\nUSER.ID : " + getUserIdentity(context)
+ + "\nAPP.VERSION : " + getAppVersion(context)
+ + "\nLAUNCHER.APP : " + getCurrentLauncherApp(context)
+ + "\nTIMEZONE : " + timeZone()
+ + "\nVERSION.RELEASE : " + Build.VERSION.RELEASE
+ + "\nVERSION.INCREMENTAL : " + Build.VERSION.INCREMENTAL
+ + "\nVERSION.SDK.NUMBER : " + Build.VERSION.SDK_INT
+ + "\nBOARD : " + Build.BOARD
+ + "\nBOOTLOADER : " + Build.BOOTLOADER
+ + "\nBRAND : " + Build.BRAND
+ + "\nCPU_ABI : " + Build.CPU_ABI
+ + "\nCPU_ABI2 : " + Build.CPU_ABI2
+ + "\nDISPLAY : " + Build.DISPLAY
+ + "\nFINGERPRINT : " + Build.FINGERPRINT
+ + "\nHARDWARE : " + Build.HARDWARE
+ + "\nHOST : " + Build.HOST
+ + "\nID : " + Build.ID
+ + "\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;
+ }
+
+ private static String timeZone() {
+ TimeZone tz = TimeZone.getDefault();
+ return tz.getID();
+ }
+
+ private static String getDeviceId(Context context) {
+ String androidDeviceId = getAndroidDeviceId(context);
+ if (androidDeviceId == null)
+ androidDeviceId = UUID.randomUUID().toString();
+ return androidDeviceId;
+
+ }
+
+ private static String getAndroidDeviceId(Context context) {
+ final String INVALID_ANDROID_ID = "9774d56d682e549c";
+ final String androidId = android.provider.Settings.Secure.getString(
+ context.getContentResolver(),
+ android.provider.Settings.Secure.ANDROID_ID);
+ if (androidId == null
+ || androidId.toLowerCase().equals(INVALID_ANDROID_ID)) {
+ return null;
+ }
+ return androidId;
+ }
+
+ private static int getAppVersion(Context context) {
+ try {
+ PackageInfo packageInfo = context.getPackageManager()
+ .getPackageInfo(context.getPackageName(), 0);
+ return packageInfo.versionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Could not get package name: " + e);
+ }
+ }
+}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/Constants.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/Constants.java
new file mode 100644
index 0000000..ec878e5
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/Constants.java
@@ -0,0 +1,14 @@
+package com.balsikandar.crashreporter.utils;
+
+/**
+ * Created by bali on 15/08/17.
+ */
+
+public class Constants {
+ public static final String EXCEPTION_SUFFIX = "_exception";
+ public static final String CRASH_SUFFIX = "_crash";
+ public static final String FILE_EXTENSION = ".txt";
+ public static final String CRASH_REPORT_DIR = "crashReports";
+ public static final int NOTIFICATION_ID = 1;
+ public static final String LANDING = "landing";
+}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/custom/CrashReporterException.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterException.java
similarity index 97%
rename from crashreporter/src/main/java/com/balsikandar/crashreporter/custom/CrashReporterException.java
rename to crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterException.java
index 3133d13..ede6f5f 100644
--- a/crashreporter/src/main/java/com/balsikandar/crashreporter/custom/CrashReporterException.java
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterException.java
@@ -1,4 +1,4 @@
-package com.balsikandar.crashreporter.custom;
+package com.balsikandar.crashreporter.utils;
/**
* Created by bali on 02/08/17.
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterExceptionHandler.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterExceptionHandler.java
new file mode 100644
index 0000000..19a0af9
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterExceptionHandler.java
@@ -0,0 +1,28 @@
+package com.balsikandar.crashreporter.utils;
+
+import com.balsikandar.crashreporter.CrashReporter;
+import com.balsikandar.crashreporter.utils.CrashUtil;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.List;
+
+import static java.lang.String.format;
+
+
+public class CrashReporterExceptionHandler implements Thread.UncaughtExceptionHandler {
+
+ private Thread.UncaughtExceptionHandler exceptionHandler;
+
+ public CrashReporterExceptionHandler() {
+ this.exceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+ }
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable throwable) {
+
+ CrashUtil.saveCrashReport(exceptionHandler, thread, throwable);
+ }
+}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/custom/CrashReporterNotInitializedException.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterNotInitializedException.java
similarity index 96%
rename from crashreporter/src/main/java/com/balsikandar/crashreporter/custom/CrashReporterNotInitializedException.java
rename to crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterNotInitializedException.java
index 3dfc3b1..5b6d35a 100644
--- a/crashreporter/src/main/java/com/balsikandar/crashreporter/custom/CrashReporterNotInitializedException.java
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashReporterNotInitializedException.java
@@ -1,4 +1,4 @@
-package com.balsikandar.crashreporter.custom;
+package com.balsikandar.crashreporter.utils;
/**
* Created by bali on 02/08/17.
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashUtil.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashUtil.java
index cfcc5f7..c414621 100644
--- a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashUtil.java
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/CrashUtil.java
@@ -1,14 +1,22 @@
package com.balsikandar.crashreporter.utils;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;
import com.balsikandar.crashreporter.BuildConfig;
import com.balsikandar.crashreporter.CrashReporter;
+import com.balsikandar.crashreporter.R;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
@@ -16,79 +24,135 @@
import java.util.Date;
import java.util.Locale;
+import static android.content.Context.NOTIFICATION_SERVICE;
+
public class CrashUtil {
+ private static final String TAG = CrashUtil.class.getSimpleName();
+
private CrashUtil() {
//this class is not publicly instantiable
}
-
private static String getCrashLogTime() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
return dateFormat.format(new Date());
}
- public static void saveCrashReport(String crashReportPath, String crashLog) {
- String crashTime = getCrashLogTime() + "_crash";
- String filename = crashTime + ".txt";
-
- saveInFile(crashReportPath, crashLog, filename);
- }
-
- public static void logException(String crashReportPath, String crashLog) {
- String crashTime = getCrashLogTime() + "_exception";
- String filename = crashTime + ".txt";
+ public static void saveCrashReport(final Thread.UncaughtExceptionHandler exceptionHandler,
+ final Thread thread, final Throwable throwable) {
- saveInFile(crashReportPath, crashLog, filename);
- }
-
- public static void logException(String crashReportPath, Exception exception, String tag) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
- String crashTime = !TextUtils.isEmpty(tag) ? tag + "_" + getCrashLogTime() : getCrashLogTime();
- String filename = crashTime + "_exception" + ".txt";
+ String crashReportPath = CrashReporter.getCrashReportPath();
+ String filename = getCrashLogTime() + Constants.CRASH_SUFFIX + Constants.FILE_EXTENSION;
+ writeToFile(crashReportPath, filename, getStackTrace(throwable));
- final Writer result = new StringWriter();
- final PrintWriter printWriter = new PrintWriter(result);
+ showNotification(throwable.getLocalizedMessage(), true);
- exception.printStackTrace(printWriter);
- String crashLog = result.toString();
- printWriter.close();
+ } catch (Exception e) {
+ logE(TAG, e.getMessage());
+ logE(TAG, "you may haven't given storage permission");
+ e.printStackTrace();
+ } finally {
+ exceptionHandler.uncaughtException(thread, throwable);
+ }
+ }
+ }).start();
- saveInFile(crashReportPath, crashLog, filename);
}
- private static void saveInFile(final String crashReportPath, final String crashLog, final String filename) {
+ public static void logException(final Exception exception) {
+
new Thread(new Runnable() {
@Override
public void run() {
try {
- String crashLogPath = crashReportPath;
- //if user sends null path
- if (TextUtils.isEmpty(crashLogPath)) {
- crashLogPath = getDefaultPath();
- }
+ String crashReportPath = CrashReporter.getCrashReportPath();
+ final String filename = getCrashLogTime() + Constants.EXCEPTION_SUFFIX + Constants.FILE_EXTENSION;
+ writeToFile(crashReportPath, filename, getStackTrace(exception));
- BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(
- crashLogPath + "/" + filename));
- bufferedWriter.write(crashLog);
- bufferedWriter.flush();
- bufferedWriter.close();
+ showNotification(exception.getLocalizedMessage(), false);
- logD("CrashUtil", "crash report saved in : " + crashLogPath);
} catch (Exception e) {
- logE("CrashUtil", e.getMessage());
- logE("CrashUtil", "you may haven't given storage permission");
+ logE(TAG, e.getMessage());
+ logE(TAG, "you may haven't given storage permission");
e.printStackTrace();
}
}
}).start();
+ }
+ private static void writeToFile(String crashReportPath, String filename, String crashLog)
+ throws IOException {
+ //if user sends null path
+ if (TextUtils.isEmpty(crashReportPath)) {
+ crashReportPath = getDefaultPath();
+ }
+
+ File crashDir = new File(crashReportPath);
+ if (!crashDir.isDirectory()) {
+ crashReportPath = getDefaultPath();
+ }
+
+ BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(
+ crashReportPath + File.separator + filename));
+ bufferedWriter.write(crashLog);
+ bufferedWriter.flush();
+ bufferedWriter.close();
+
+ logD(TAG, "crash report saved in : " + crashReportPath);
+ }
+
+ private static void showNotification(String localisedMsg, boolean isCrash) {
+
+ if (CrashReporter.isNotificationEnabled()) {
+ Context context = CrashReporter.getContext();
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+ builder.setSmallIcon(R.drawable.ic_warning_black_24dp);
+
+ Intent intent = CrashReporter.getLaunchIntent();
+ intent.putExtra(Constants.LANDING, isCrash);
+ intent.setAction(Long.toString(System.currentTimeMillis()));
+
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ builder.setContentIntent(pendingIntent);
+
+ builder.setContentTitle(context.getString(R.string.view_crash_report));
+
+ if (TextUtils.isEmpty(localisedMsg)) {
+ builder.setContentText(context.getString(R.string.check_your_message_here));
+ } else {
+ builder.setContentText(localisedMsg);
+ }
+
+ builder.setAutoCancel(true);
+ builder.setColor(ContextCompat.getColor(context, R.color.colorAccent_CrashReporter));
+
+ NotificationManager notificationManager = (NotificationManager) context.
+ getSystemService(NOTIFICATION_SERVICE);
+ notificationManager.notify(Constants.NOTIFICATION_ID, builder.build());
+ }
+ }
+
+ private static String getStackTrace(Throwable e) {
+ final Writer result = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(result);
+
+ e.printStackTrace(printWriter);
+ String crashLog = result.toString();
+ printWriter.close();
+ return crashLog;
}
public static String getDefaultPath() {
String defaultPath = CrashReporter.getContext().getExternalFilesDir(null).getAbsolutePath()
- + File.separator + "crashReports";
+ + File.separator + Constants.CRASH_REPORT_DIR;
File file = new File(defaultPath);
file.mkdirs();
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/FileUtils.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/FileUtils.java
new file mode 100644
index 0000000..516106a
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/FileUtils.java
@@ -0,0 +1,129 @@
+package com.balsikandar.crashreporter.utils;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.webkit.MimeTypeMap;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * Created by bali on 10/08/17.
+ */
+
+public class FileUtils {
+
+ public static final String TAG = FileUtils.class.getSimpleName();
+
+ private FileUtils() {
+ //this class is not publicly instantiable
+ }
+
+ public static boolean delete(String absPath) {
+ if (TextUtils.isEmpty(absPath)) {
+ return false;
+ }
+
+ File file = new File(absPath);
+ return delete(file);
+ }
+
+ public static boolean delete(File file) {
+ if (!exists(file)) {
+ return true;
+ }
+
+ if (file.isFile()) {
+ return file.delete();
+ }
+
+ boolean result = true;
+ File files[] = file.listFiles();
+ if (files == null) return false;
+ for (int index = 0; index < files.length; index++) {
+ result |= delete(files[index]);
+ }
+ result |= file.delete();
+
+ return result;
+ }
+
+ public static boolean exists(File file) {
+ return file != null && file.exists();
+ }
+
+ public static String cleanPath(String absPath) {
+ if (TextUtils.isEmpty(absPath)) {
+ return absPath;
+ }
+ try {
+ File file = new File(absPath);
+ absPath = file.getCanonicalPath();
+ } catch (Exception e) {
+
+ }
+ return absPath;
+ }
+
+ public final static String getParent(File file) {
+ return file == null ? null : file.getParent();
+ }
+
+ public final static String getParent(String absPath) {
+ if (TextUtils.isEmpty(absPath)) {
+ return null;
+ }
+ absPath = cleanPath(absPath);
+ File file = new File(absPath);
+ return getParent(file);
+ }
+
+ public static boolean deleteFiles(String directoryPath) {
+ String directoryToDelete;
+ if (!TextUtils.isEmpty(directoryPath)) {
+ directoryToDelete = directoryPath;
+ } else {
+ directoryToDelete = CrashUtil.getDefaultPath();
+ }
+
+ return delete(directoryToDelete);
+ }
+
+ public static String readFirstLineFromFile(File file) {
+ String line = "";
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ line = reader.readLine();
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return line;
+ }
+
+ public static String readFromFile(File file) {
+ StringBuilder crash = new StringBuilder();
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ crash.append(line);
+ crash.append('\n');
+ }
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return crash.toString();
+ }
+}
diff --git a/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/SimplePageChangeListener.java b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/SimplePageChangeListener.java
new file mode 100644
index 0000000..76cea0a
--- /dev/null
+++ b/crashreporter/src/main/java/com/balsikandar/crashreporter/utils/SimplePageChangeListener.java
@@ -0,0 +1,16 @@
+package com.balsikandar.crashreporter.utils;
+
+import android.support.v4.view.ViewPager;
+
+/**
+ * Created by bali on 11/08/17.
+ */
+
+public abstract class SimplePageChangeListener implements ViewPager.OnPageChangeListener {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
+ @Override
+ public abstract void onPageSelected(int position);
+ @Override
+ public void onPageScrollStateChanged(int state) {}
+}
diff --git a/crashreporter/src/main/res/drawable/ic_menu_delete_white_24dp.xml b/crashreporter/src/main/res/drawable/ic_menu_delete_white_24dp.xml
new file mode 100644
index 0000000..92e2ade
--- /dev/null
+++ b/crashreporter/src/main/res/drawable/ic_menu_delete_white_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/crashreporter/src/main/res/drawable/ic_menu_share_white_24dp.xml b/crashreporter/src/main/res/drawable/ic_menu_share_white_24dp.xml
new file mode 100644
index 0000000..1d53e22
--- /dev/null
+++ b/crashreporter/src/main/res/drawable/ic_menu_share_white_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/crashreporter/src/main/res/drawable/ic_search_white_24dp.xml b/crashreporter/src/main/res/drawable/ic_search_white_24dp.xml
new file mode 100644
index 0000000..c927d3d
--- /dev/null
+++ b/crashreporter/src/main/res/drawable/ic_search_white_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/crashreporter/src/main/res/drawable/ic_warning_black_24dp.xml b/crashreporter/src/main/res/drawable/ic_warning_black_24dp.xml
new file mode 100644
index 0000000..7c69ee8
--- /dev/null
+++ b/crashreporter/src/main/res/drawable/ic_warning_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/crashreporter/src/main/res/layout/activity_log_message.xml b/crashreporter/src/main/res/layout/activity_log_message.xml
new file mode 100644
index 0000000..0eb74be
--- /dev/null
+++ b/crashreporter/src/main/res/layout/activity_log_message.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/crashreporter/src/main/res/layout/crash_log.xml b/crashreporter/src/main/res/layout/crash_log.xml
new file mode 100644
index 0000000..636f606
--- /dev/null
+++ b/crashreporter/src/main/res/layout/crash_log.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/crashreporter/src/main/res/layout/crash_reporter_activity.xml b/crashreporter/src/main/res/layout/crash_reporter_activity.xml
new file mode 100644
index 0000000..f2f61c9
--- /dev/null
+++ b/crashreporter/src/main/res/layout/crash_reporter_activity.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/crashreporter/src/main/res/layout/custom_item.xml b/crashreporter/src/main/res/layout/custom_item.xml
new file mode 100644
index 0000000..28928e7
--- /dev/null
+++ b/crashreporter/src/main/res/layout/custom_item.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/crashreporter/src/main/res/layout/exception_log.xml b/crashreporter/src/main/res/layout/exception_log.xml
new file mode 100644
index 0000000..41bab16
--- /dev/null
+++ b/crashreporter/src/main/res/layout/exception_log.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/crashreporter/src/main/res/menu/crash_detail_menu.xml b/crashreporter/src/main/res/menu/crash_detail_menu.xml
new file mode 100644
index 0000000..977da47
--- /dev/null
+++ b/crashreporter/src/main/res/menu/crash_detail_menu.xml
@@ -0,0 +1,15 @@
+
+
\ No newline at end of file
diff --git a/crashreporter/src/main/res/menu/log_main_menu.xml b/crashreporter/src/main/res/menu/log_main_menu.xml
new file mode 100644
index 0000000..5a0b306
--- /dev/null
+++ b/crashreporter/src/main/res/menu/log_main_menu.xml
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/crashreporter/src/main/res/values/colors.xml b/crashreporter/src/main/res/values/colors.xml
new file mode 100644
index 0000000..451218a
--- /dev/null
+++ b/crashreporter/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #ff4081
+ #f50057
+ #cf1162
+ #000000
+
\ No newline at end of file
diff --git a/crashreporter/src/main/res/values/strings.xml b/crashreporter/src/main/res/values/strings.xml
index 87b376d..667a121 100644
--- a/crashreporter/src/main/res/values/strings.xml
+++ b/crashreporter/src/main/res/values/strings.xml
@@ -1,3 +1,10 @@
- CrashReporter
+ CrashReporter
+ Crashes
+ Exceptions
+ View Crash Report
+ Check your crashes and exceptions here.
+ Are you sure to delete all the crash logs
+ CANCEL
+ OK
diff --git a/crashreporter/src/main/res/values/styles.xml b/crashreporter/src/main/res/values/styles.xml
new file mode 100644
index 0000000..18c3058
--- /dev/null
+++ b/crashreporter/src/main/res/values/styles.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file