├── .gitignore
├── AndroidCrash.iml
├── README.md
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── githang
│ │ └── androidcrash
│ │ └── app
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── githang
│ │ └── androidcrash
│ │ └── app
│ │ ├── AppManager.java
│ │ ├── MainActivity.java
│ │ └── MyApplication.java
│ └── res
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ └── ic_launcher.png
│ ├── layout
│ └── activity_main.xml
│ └── values
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle-app.setting
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── .gitignore
├── build.gradle
├── library.iml
├── libs
│ ├── activation.jar
│ ├── additionnal.jar
│ └── mail.jar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── githang
│ │ └── androidcrash
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── githang
│ │ └── androidcrash
│ │ ├── AndroidCrash.java
│ │ ├── log
│ │ ├── CrashCatcher.java
│ │ ├── CrashListener.java
│ │ └── LogWriter.java
│ │ ├── reporter
│ │ ├── AbstractCrashHandler.java
│ │ ├── httpreporter
│ │ │ ├── CrashHttpReporter.java
│ │ │ └── SimpleMultipartEntity.java
│ │ └── mailreporter
│ │ │ ├── CrashEmailReporter.java
│ │ │ └── LogMail.java
│ │ └── util
│ │ └── AssertUtil.java
│ └── res
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ └── ic_launcher.png
│ └── values
│ └── strings.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | *.iml
3 | .idea/
4 | .DS_Store
5 |
6 | # Built application files
7 | *.apk
8 | *.ap_
9 |
10 | # Files for the Dalvik VM
11 | *.dex
12 |
13 | # Java class files
14 | *.class
15 |
16 | # Generated files
17 | bin/
18 | gen/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
--------------------------------------------------------------------------------
/AndroidCrash.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PLEASE NOTE, THIS PROJECT IS NO LONGER BEING MAINTAINED
2 | ---
3 | ***
4 |
5 | android-crash
6 | =============
7 |
8 | android crash 是我写的一个Android程序崩溃信息处理框架。通过它,可以在程序崩溃时收集崩溃信息并用你指定的方式发送出来。
9 |
10 | 在本框架中,我实现了邮件及HTTP POST请求的发送方式。如果要采用其他方式,可以继承AbstractCrashReportHandler类并实现其抽象方法。
11 |
12 | 使用本框架的方法只需要两个步骤。
13 | 1、添加依赖:
14 | 在repository中添加jcenter。
15 | ```groovy
16 | repository {
17 | jcenter() // or mavenCentral()
18 | }
19 | ```
20 | 在dependencies中添加如下依赖。
21 | ```groovy
22 |
23 | compile 'com.githang:android-crash:1.0'
24 | ```
25 |
26 | 2、写一个类,继承自Application,并在AndroidManifest.xml中指定。然后在onCreate方法中添加如下代码:
27 |
28 | 使用E-mail方式,需要添加activation.jar, additionnal.jar, mail.jar 这三个jar包,可以从本项目的libs文件夹中获取。使用HTTP方式则不需要其他依赖库。
29 |
30 | ```java
31 | @Override
32 | public void onCreate() {
33 | super.onCreate();
34 |
35 | initHttpReporter();
36 | }
37 |
38 | /**
39 | * 使用EMAIL发送日志
40 | */
41 | private void initEmailReporter() {
42 | CrashEmailReporter reporter = new CrashEmailReporter(this);
43 | reporter.setReceiver("你的接收邮箱");
44 | reporter.setSender("irain_log@163.com");
45 | reporter.setSendPassword("xxxxxxxx");
46 | reporter.setSMTPHost("smtp.163.com");
47 | reporter.setPort("465");
48 | AndroidCrash.getInstance().setCrashReporter(reporter).init(this);
49 | }
50 |
51 | /**
52 | * 使用HTTP发送日志
53 | */
54 | private void initHttpReporter() {
55 | CrashHttpReporter reporter = new CrashHttpReporter(this) {
56 | /**
57 | * 重写此方法,可以弹出自定义的崩溃提示对话框,而不使用系统的崩溃处理。
58 | * @param thread
59 | * @param ex
60 | */
61 | @Override
62 | public void closeApp(Thread thread, Throwable ex) {
63 | final Activity activity = AppManager.currentActivity();
64 | Toast.makeText(activity, "发生异常,正在退出", Toast.LENGTH_SHORT).show();
65 | // 自定义弹出对话框
66 | new AlertDialog.Builder(activity).
67 | setMessage("程序发生异常,现在退出").
68 | setPositiveButton("确定", new DialogInterface.OnClickListener() {
69 | @Override
70 | public void onClick(DialogInterface dialog, int which) {
71 | AppManager.AppExit(activity);
72 | }
73 | }).create().show();
74 | Log.d("MyApplication", "thead:" + Thread.currentThread().getName());
75 | }
76 | };
77 | reporter.setUrl("http://crashreport.jd-app.com/your_receiver").setFileParam("fileName")
78 | .setToParam("to").setTo("你的接收邮箱")
79 | .setTitleParam("subject").setBodyParam("message");
80 | reporter.setCallback(new CrashHttpReporter.HttpReportCallback() {
81 | @Override
82 | public boolean isSuccess(int i, String s) {
83 | return s.endsWith("ok");
84 | }
85 | });
86 | AndroidCrash.getInstance().setCrashReporter(reporter).init(this);
87 | }
88 |
89 | ```
90 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 21
5 | buildToolsVersion "21.1.0"
6 |
7 | defaultConfig {
8 | applicationId "com.githang.androidcrash.app"
9 | minSdkVersion 8
10 | targetSdkVersion 21
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | lintOptions {
21 | abortOnError false
22 | }
23 | }
24 |
25 | dependencies {
26 | compile fileTree(dir: 'libs', include: ['*.jar'])
27 | compile project(':library')
28 | }
29 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\android-sdk-windows/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/githang/androidcrash/app/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.githang.androidcrash.app;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/githang/androidcrash/app/AppManager.java:
--------------------------------------------------------------------------------
1 | package com.githang.androidcrash.app;
2 |
3 | import android.app.Activity;
4 | import android.app.ActivityManager;
5 | import android.content.Context;
6 |
7 | import java.util.Stack;
8 |
9 | /**
10 | * 应用程序Activity管理类:用于Activity管理和应用程序退出
11 | *
12 | * @author liux (http://my.oschina.net/liux)
13 | * @version 1.0
14 | * @created 2012-3-21
15 | */
16 | public class AppManager {
17 | private static Stack activityStack = new Stack();
18 |
19 | /**
20 | * 添加Activity到堆栈
21 | */
22 | public static void addActivity(Activity activity) {
23 | activityStack.push(activity);
24 | }
25 |
26 | /**
27 | * 获取当前Activity(堆栈中最后一个压入的)
28 | */
29 | public static Activity currentActivity() {
30 | return activityStack.lastElement();
31 | }
32 |
33 | /**
34 | * 结束当前Activity(堆栈中最后一个压入的)
35 | */
36 | public static void finishCurrentActivity() {
37 | Activity activity = activityStack.pop();
38 | activity.finish();
39 | }
40 |
41 | /**
42 | * 结束指定的Activity
43 | */
44 | public static void finishActivity(Activity activity) {
45 | if (activity != null) {
46 | activityStack.remove(activity);
47 | if(!activity.isFinishing()) {
48 | activity.finish();
49 | }
50 | }
51 | }
52 |
53 | /**
54 | * 结束指定类名的Activity
55 | */
56 | public static void finishActivity(Class> cls) {
57 | for (Activity activity : activityStack) {
58 | if (activity.getClass().equals(cls)) {
59 | finishActivity(activity);
60 | }
61 | }
62 | }
63 |
64 | /**
65 | * 结束所有Activity
66 | */
67 | public static void finishAllActivity() {
68 | for (Activity activity : activityStack) {
69 | if (activity != null) {
70 | activity.finish();
71 | }
72 | }
73 | activityStack.clear();
74 | }
75 |
76 | /**
77 | * 退出应用程序
78 | */
79 | public static void AppExit(Context context) {
80 | try {
81 | finishAllActivity();
82 | ActivityManager manager = (ActivityManager) context
83 | .getSystemService(Context.ACTIVITY_SERVICE);
84 | manager.killBackgroundProcesses(context.getPackageName());
85 | System.exit(0);
86 | } catch (Exception e) {
87 | e.printStackTrace();
88 | }
89 | }
90 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/githang/androidcrash/app/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.githang.androidcrash.app;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.view.View;
6 |
7 | /**
8 | * User: Geek_Soledad(msdx.android@qq.com)
9 | * Date: 2014-11-03
10 | * Time: 23:25
11 | * FIXME
12 | */
13 | public class MainActivity extends Activity {
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | this.setContentView(R.layout.activity_main);
19 | findViewById(R.id.bt_crash).setOnClickListener(new View.OnClickListener() {
20 | @Override
21 | public void onClick(View v) {
22 | throw new NullPointerException("Test");
23 | }
24 | });
25 | AppManager.addActivity(this);
26 | }
27 |
28 | @Override
29 | protected void onDestroy() {
30 | super.onDestroy();
31 | AppManager.finishActivity(this);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/githang/androidcrash/app/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.githang.androidcrash.app;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.app.Application;
6 | import android.content.DialogInterface;
7 | import android.util.Log;
8 | import android.widget.Toast;
9 |
10 | import com.githang.androidcrash.AndroidCrash;
11 | import com.githang.androidcrash.reporter.httpreporter.CrashHttpReporter;
12 | import com.githang.androidcrash.reporter.mailreporter.CrashEmailReporter;
13 |
14 | /**
15 | * User: Geek_Soledad(msdx.android@qq.com)
16 | * Date: 2014-11-03
17 | * Time: 23:26
18 | * FIXME
19 | */
20 | public class MyApplication extends Application{
21 |
22 | @Override
23 | public void onCreate() {
24 | super.onCreate();
25 |
26 | initHttpReporter();
27 | }
28 |
29 | /**
30 | * 使用EMAIL发送日志
31 | */
32 | private void initEmailReporter() {
33 | CrashEmailReporter reporter = new CrashEmailReporter(this);
34 | reporter.setReceiver("你的接收邮箱");
35 | reporter.setSender("你的发送邮箱");
36 | reporter.setSendPassword("xxxxxxxx");
37 | reporter.setSMTPHost("smtp.163.com");
38 | reporter.setPort("465");
39 | AndroidCrash.getInstance().setCrashReporter(reporter).init(this);
40 | }
41 |
42 | /**
43 | * 使用HTTP发送日志
44 | */
45 | private void initHttpReporter() {
46 | CrashHttpReporter reporter = new CrashHttpReporter(this) {
47 | /**
48 | * 重写此方法,可以弹出自定义的崩溃提示对话框,而不使用系统的崩溃处理。
49 | * @param thread
50 | * @param ex
51 | */
52 | @Override
53 | public void closeApp(Thread thread, Throwable ex) {
54 | final Activity activity = AppManager.currentActivity();
55 | Toast.makeText(activity, "发生异常,正在退出", Toast.LENGTH_SHORT).show();
56 | // 自定义弹出对话框
57 | new AlertDialog.Builder(activity).
58 | setMessage("程序发生异常,现在退出").
59 | setPositiveButton("确定", new DialogInterface.OnClickListener() {
60 | @Override
61 | public void onClick(DialogInterface dialog, int which) {
62 | AppManager.AppExit(activity);
63 | }
64 | }).create().show();
65 | Log.d("MyApplication", "thead:" + Thread.currentThread().getName());
66 | }
67 | };
68 | reporter.setUrl("接收你请求的API").setFileParam("fileName")
69 | .setToParam("to").setTo("你的接收邮箱")
70 | .setTitleParam("subject").setBodyParam("message");
71 | reporter.setCallback(new CrashHttpReporter.HttpReportCallback() {
72 | @Override
73 | public boolean isSuccess(int i, String s) {
74 | return s.endsWith("ok");
75 | }
76 | });
77 | AndroidCrash.getInstance().setCrashReporter(reporter).init(this);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidCrash
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.0.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/gradle-app.setting:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 22 13:00:30 CST 2014
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | group = 'com.githang'
2 | version= 1.0
3 |
4 | buildscript {
5 | repositories {
6 | jcenter()
7 | }
8 | dependencies {
9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
10 | }
11 | }
12 |
13 | apply plugin: 'com.android.library'
14 | apply plugin: 'com.jfrog.bintray'
15 | apply plugin: 'maven-publish'
16 |
17 | android {
18 | compileSdkVersion 21
19 | buildToolsVersion "21.1.0"
20 |
21 | defaultConfig {
22 | minSdkVersion 8
23 | targetSdkVersion 21
24 | }
25 | buildTypes {
26 | release {
27 | minifyEnabled false
28 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
29 | }
30 | }
31 | lintOptions {
32 | abortOnError false
33 | }
34 |
35 | }
36 |
37 | dependencies {
38 | compile fileTree(dir: 'libs', include: ['*.jar'])
39 | }
40 |
41 |
42 | task androidJavadocs(type: Javadoc) {
43 | source = android.sourceSets.main.java
44 | }
45 |
46 | task androidJavadocsJar(type: Jar) {
47 | classifier = 'javadoc'
48 | from androidJavadocs.destinationDir
49 | }
50 |
51 | task coreLibJar(type: Jar) {
52 | from fileTree(dir: './build/intermediates/classes/release')
53 | .exclude ("com/githang/androidcrash/R.class",
54 | "com/githang/androidcrash/R\$*.class",
55 | "com/githang/androidcrash/BuildConfig.class"
56 | )
57 | }
58 |
59 | task androidSourcesJar(type: Jar) {
60 | classifier = 'sources'
61 | from android.sourceSets.main.java.srcDirs
62 | }
63 |
64 | artifacts {
65 | archives androidSourcesJar
66 | }
67 |
68 | def pomConfig = {
69 | licenses {
70 | license {
71 | name "The Apache Software License, Version 2.0"
72 | url "http://www.apache.org/licenses/LICENSE-2.0.txt"
73 | distribution "repo"
74 | }
75 | }
76 | developers {
77 | developer {
78 | id "msdx"
79 | name "Geek_Soledad"
80 | email "msdx.android@qq.com"
81 | }
82 | }
83 | }
84 |
85 | publishing {
86 | publications {
87 | mavenJava(MavenPublication) {
88 | artifactId 'android-crash'
89 | artifact androidSourcesJar
90 | artifact androidJavadocsJar
91 | artifact coreLibJar
92 | pom.withXml {
93 | def root = asNode()
94 | root.appendNode('description', 'An android crash handler.')
95 | root.children().last() + pomConfig
96 | }
97 | }
98 | }
99 | }
100 |
101 | bintray {
102 | user = bintrayUser
103 | key = bintrayKey
104 |
105 | publications = ['mavenJava'] //When uploading Maven-based publication files
106 | // - AND/OR -
107 | filesSpec { //When uploading any arbitrary files ('filesSpec' is a standard Gradle CopySpec)
108 | from 'arbitrary-files'
109 | into 'standalone_files/level1'
110 | rename '(.+)\\.(.+)', '$1-suffix.$2'
111 | }
112 | dryRun = false //Whether to run this as dry-run, without deploying
113 | publish = true //If version should be auto published after an upload
114 | pkg {
115 | repo = 'maven'
116 | // userOrg = 'myorg' //An optional organization name when the repo belongs to one of the user's orgs
117 | name = 'android-crash'
118 | desc = 'what a fantastic package indeed!'
119 | websiteUrl = 'https://github.com/msdx/android-crash'
120 | issueTrackerUrl = 'https://github.com/msdx/android-crash/issues'
121 | vcsUrl = 'https://github.com/msdx/android-crash.git'
122 | licenses = ['Apache-2.0']
123 | // labels = ['gear', 'gore', 'gorilla']
124 | publicDownloadNumbers = true
125 | //attributes= ['a': ['ay1', 'ay2'], 'b': ['bee'], c: 'cee'] //Optional package-level attributes
126 | //Optional version descriptor
127 | version {
128 | name = project.version //Bintray logical version name
129 | vcsTag = project.version
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/library/library.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/library/libs/activation.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/library/libs/activation.jar
--------------------------------------------------------------------------------
/library/libs/additionnal.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/library/libs/additionnal.jar
--------------------------------------------------------------------------------
/library/libs/mail.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/library/libs/mail.jar
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:/android-sdk-windows/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/library/src/androidTest/java/com/githang/androidcrash/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.githang.androidcrash;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/AndroidCrash.java:
--------------------------------------------------------------------------------
1 | package com.githang.androidcrash;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import com.githang.androidcrash.log.CrashCatcher;
7 | import com.githang.androidcrash.reporter.AbstractCrashHandler;
8 |
9 | import java.io.File;
10 |
11 | /**
12 | * User: Geek_Soledad(msdx.android@qq.com)
13 | * Date: 2014-11-03
14 | * Time: 21:37
15 | * FIXME
16 | */
17 | public class AndroidCrash {
18 | private static final AndroidCrash instance = new AndroidCrash();
19 |
20 | private AbstractCrashHandler mReporter;
21 |
22 | private String mLogName;
23 |
24 | private AndroidCrash(){}
25 |
26 | public static AndroidCrash getInstance() {
27 | return instance;
28 | }
29 |
30 | /**
31 | * 设置报告处理。
32 | * @param reporter
33 | * @return
34 | */
35 | public AndroidCrash setCrashReporter(AbstractCrashHandler reporter) {
36 | mReporter = reporter;
37 | return this;
38 | }
39 |
40 | /**
41 | * 设置日志文件名。
42 | * @param name
43 | * @return
44 | */
45 | public AndroidCrash setLogFileName(String name) {
46 | mLogName = name;
47 | return this;
48 | }
49 |
50 | public void init(Context mContext) {
51 | if (mLogName == null) {
52 | mLogName = "AndroidCrash.log";
53 | }
54 | File logFile = getLogFile(mContext, mLogName);
55 | CrashCatcher.getInstance().init(logFile, mReporter);
56 | Thread.setDefaultUncaughtExceptionHandler(CrashCatcher.getInstance());
57 | Log.d("AndroidCrash", "init success: " + Thread.getDefaultUncaughtExceptionHandler().getClass());
58 | }
59 |
60 | protected static final File getLogFile(Context context, String name) {
61 | return new File(context.getFilesDir(), name);
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/log/CrashCatcher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * @(#)CrashHandler.java Project: crash
3 | * Date:2014-5-26
4 | *
5 | * Copyright (c) 2014 CFuture09, Institute of Software,
6 | * Guangdong Ocean University, Zhanjiang, GuangDong, China.
7 | * All rights reserved.
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 | package com.githang.androidcrash.log;
22 |
23 | import android.os.Looper;
24 | import android.util.Log;
25 |
26 | import com.githang.androidcrash.util.AssertUtil;
27 |
28 | import java.io.File;
29 | import java.lang.Thread.UncaughtExceptionHandler;
30 |
31 | /**
32 | * 崩溃处理者。
33 | *
34 | * @author Geek_Soledad
39 | */
40 | public class CrashCatcher implements UncaughtExceptionHandler {
41 | private static final String LOG_TAG = CrashCatcher.class.getSimpleName();
42 |
43 | private static final CrashCatcher sHandler = new CrashCatcher();
44 |
45 | private CrashListener mListener;
46 | private File mLogFile;
47 |
48 | public static CrashCatcher getInstance() {
49 | return sHandler;
50 | }
51 |
52 | @Override
53 | public void uncaughtException(final Thread thread, final Throwable ex) {
54 | try {
55 | LogWriter.writeLog(mLogFile, "CrashHandler", ex.getMessage(), ex);
56 | }catch (Exception e) {
57 | Log.w(LOG_TAG, e);
58 | }
59 |
60 | mListener.sendFile(mLogFile);
61 | new Thread(new Runnable() {
62 | @Override
63 | public void run() {
64 | Looper.prepare();
65 | try {
66 | mListener.closeApp(thread, ex);
67 | }catch (Exception e) {
68 | e.printStackTrace();
69 | }
70 | Looper.loop();
71 | }
72 | }).start();
73 | }
74 |
75 | /**
76 | * 初始化日志文件及CrashListener对象
77 | *
78 | * @param logFile
79 | * 保存日志的文件
80 | * @param listener
81 | * 回调接口
82 | */
83 | public void init(File logFile, CrashListener listener) {
84 | AssertUtil.assertNotNull("logFile", logFile);
85 | AssertUtil.assertNotNull("crashListener", listener);
86 | mLogFile = logFile;
87 | mListener = listener;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/log/CrashListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * @(#)CrashListener.java Project: crash
3 | * Date:2014-5-27
4 | *
5 | * Copyright (c) 2014 CFuture09, Institute of Software,
6 | * Guangdong Ocean University, Zhanjiang, GuangDong, China.
7 | * All rights reserved.
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 | package com.githang.androidcrash.log;
22 |
23 | import java.io.File;
24 |
25 | /**
26 | * 崩溃监听。
27 | *
28 | * @author Geek_Soledad
33 | */
34 | public interface CrashListener {
35 | /**
36 | * 发送日志文件。
37 | * @param file
38 | */
39 | public void sendFile(File file);
40 |
41 | /**
42 | * 退出APP。
43 | * @param thread
44 | * @param ex
45 | */
46 | public void closeApp(Thread thread, Throwable ex);
47 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/log/LogWriter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * @(#)LogUtil.java Project: crash
3 | * Date:2014-5-27
4 | *
5 | * Copyright (c) 2014 CFuture09, Institute of Software,
6 | * Guangdong Ocean University, Zhanjiang, GuangDong, China.
7 | * All rights reserved.
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 | package com.githang.androidcrash.log;
22 |
23 | import java.io.BufferedWriter;
24 | import java.io.Closeable;
25 | import java.io.File;
26 | import java.io.FileWriter;
27 | import java.io.IOException;
28 | import java.io.PrintWriter;
29 | import java.text.SimpleDateFormat;
30 | import java.util.Calendar;
31 | import java.util.Locale;
32 |
33 | /**
34 | * @author Geek_Soledad
39 | */
40 | public class LogWriter {
41 | private static final SimpleDateFormat timeFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS",
42 | Locale.getDefault());
43 |
44 | /**
45 | * 将日志写入文件。
46 | *
47 | * @param tag
48 | * @param message
49 | * @param tr
50 | */
51 | public static synchronized void writeLog(File logFile, String tag, String message, Throwable tr) {
52 | logFile.getParentFile().mkdirs();
53 | if (!logFile.exists()) {
54 | try {
55 | logFile.createNewFile();
56 | } catch (IOException e) {
57 | e.printStackTrace();
58 | }
59 | }
60 | String time = timeFormat.format(Calendar.getInstance().getTime());
61 | synchronized (logFile) {
62 | FileWriter fileWriter = null;
63 | BufferedWriter bufdWriter = null;
64 | PrintWriter printWriter = null;
65 | try {
66 | fileWriter = new FileWriter(logFile, true);
67 | bufdWriter = new BufferedWriter(fileWriter);
68 | printWriter = new PrintWriter(fileWriter);
69 | bufdWriter.append(time).append(" ").append("E").append('/').append(tag).append(" ")
70 | .append(message).append('\n');
71 | bufdWriter.flush();
72 | tr.printStackTrace(printWriter);
73 | printWriter.flush();
74 | fileWriter.flush();
75 | } catch (IOException e) {
76 | closeQuietly(fileWriter);
77 | closeQuietly(bufdWriter);
78 | closeQuietly(printWriter);
79 | }
80 | }
81 | }
82 |
83 | public static void closeQuietly(Closeable closeable) {
84 | if (closeable != null) {
85 | try {
86 | closeable.close();
87 | } catch (IOException ioe) {
88 | // ignore
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/reporter/AbstractCrashHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * @(#)AbstractReportHandler.java Project: crash
3 | * Date:2014-5-27
4 | *
5 | * Copyright (c) 2014 CFuture09, Institute of Software,
6 | * Guangdong Ocean University, Zhanjiang, GuangDong, China.
7 | * All rights reserved.
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 | package com.githang.androidcrash.reporter;
22 |
23 | import android.content.Context;
24 | import android.content.pm.ApplicationInfo;
25 | import android.content.pm.PackageInfo;
26 | import android.content.pm.PackageManager;
27 | import android.os.Build;
28 |
29 | import com.githang.androidcrash.log.CrashListener;
30 |
31 | import java.io.File;
32 | import java.util.concurrent.ExecutorService;
33 | import java.util.concurrent.Executors;
34 | import java.util.concurrent.Future;
35 | import java.util.concurrent.TimeUnit;
36 |
37 | /**
38 | * 抽象的日志报告类
39 | *
40 | * @author Geek_Soledad
45 | */
46 | public abstract class AbstractCrashHandler implements CrashListener {
47 | /**
48 | * 系统默认异常处理。
49 | */
50 | private static final Thread.UncaughtExceptionHandler sDefaultHandler = Thread
51 | .getDefaultUncaughtExceptionHandler();
52 |
53 | private Context mContext;
54 |
55 | private ExecutorService mSingleExecutor = Executors.newSingleThreadExecutor();
56 | protected Future mFuture;
57 | private int TIMEOUT = 5;
58 |
59 | public AbstractCrashHandler(Context context) {
60 | mContext = context;
61 | }
62 |
63 | /**
64 | * 发送报告
65 | *
66 | * @param title
67 | * 报告标题
68 | * @param body
69 | * 报告正文,为设备信息及安装包的版本信息
70 | * @param file
71 | * 崩溃日志
72 | */
73 | protected abstract void sendReport(String title, String body, File file);
74 |
75 | @Override
76 | public void sendFile(final File file) {
77 | if(mFuture != null && !mFuture.isDone()){
78 | mFuture.cancel(false);
79 | }
80 | mFuture = mSingleExecutor.submit(new Runnable() {
81 | @Override
82 | public void run() {
83 | sendReport(buildTitle(mContext), buildBody(mContext), file);
84 | }
85 | });
86 | }
87 |
88 | /**
89 | * 构建标题
90 | *
91 | * @param context
92 | * @return
93 | */
94 | public String buildTitle(Context context) {
95 | return "Crash Log: "
96 | + context.getPackageManager().getApplicationLabel(context.getApplicationInfo());
97 | }
98 |
99 | /**
100 | * 构建正文
101 | *
102 | * @param context
103 | * @return
104 | */
105 | public String buildBody(Context context) {
106 | StringBuilder sb = new StringBuilder();
107 |
108 | sb.append("APPLICATION INFORMATION").append('\n');
109 | PackageManager pm = context.getPackageManager();
110 | ApplicationInfo ai = context.getApplicationInfo();
111 | sb.append("Application : ").append(pm.getApplicationLabel(ai)).append('\n');
112 |
113 | try {
114 | PackageInfo pi = pm.getPackageInfo(ai.packageName, 0);
115 | sb.append("Version Code: ").append(pi.versionCode).append('\n');
116 | sb.append("Version Name: ").append(pi.versionName).append('\n');
117 | } catch (PackageManager.NameNotFoundException e) {
118 | e.printStackTrace();
119 | }
120 |
121 | sb.append('\n').append("DEVICE INFORMATION").append('\n');
122 | sb.append("Board: ").append(Build.BOARD).append('\n');
123 | sb.append("BOOTLOADER: ").append(Build.BOOTLOADER).append('\n');
124 | sb.append("BRAND: ").append(Build.BRAND).append('\n');
125 | sb.append("CPU_ABI: ").append(Build.CPU_ABI).append('\n');
126 | sb.append("CPU_ABI2: ").append(Build.CPU_ABI2).append('\n');
127 | sb.append("DEVICE: ").append(Build.DEVICE).append('\n');
128 | sb.append("DISPLAY: ").append(Build.DISPLAY).append('\n');
129 | sb.append("FINGERPRINT: ").append(Build.FINGERPRINT).append('\n');
130 | sb.append("HARDWARE: ").append(Build.HARDWARE).append('\n');
131 | sb.append("HOST: ").append(Build.HOST).append('\n');
132 | sb.append("ID: ").append(Build.ID).append('\n');
133 | sb.append("MANUFACTURER: ").append(Build.MANUFACTURER).append('\n');
134 | sb.append("PRODUCT: ").append(Build.PRODUCT).append('\n');
135 | sb.append("TAGS: ").append(Build.TAGS).append('\n');
136 | sb.append("TYPE: ").append(Build.TYPE).append('\n');
137 | sb.append("USER: ").append(Build.USER).append('\n');
138 |
139 | return sb.toString();
140 | }
141 |
142 | @Override
143 | public void closeApp(Thread thread, Throwable ex) {
144 | try {
145 | mFuture.get(TIMEOUT, TimeUnit.SECONDS);
146 | } catch (Exception e) {
147 | e.printStackTrace();
148 | }
149 | sDefaultHandler.uncaughtException(thread, ex);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/reporter/httpreporter/CrashHttpReporter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * @(#)CrashHttpReport.java Project: CrashHandler
3 | * Date: 2014-7-1
4 | *
5 | * Copyright (c) 2014 CFuture09, Institute of Software,
6 | * Guangdong Ocean University, Zhanjiang, GuangDong, China.
7 | * All rights reserved.
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 | package com.githang.androidcrash.reporter.httpreporter;
22 |
23 | import android.content.Context;
24 | import android.util.Log;
25 |
26 | import com.githang.androidcrash.reporter.AbstractCrashHandler;
27 |
28 | import org.apache.http.HttpResponse;
29 | import org.apache.http.client.HttpClient;
30 | import org.apache.http.client.methods.HttpPost;
31 | import org.apache.http.impl.client.DefaultHttpClient;
32 | import org.apache.http.util.EntityUtils;
33 |
34 | import java.io.File;
35 | import java.util.Map;
36 |
37 | /**
38 | * HTTP的post请求方式发送。
39 | *
40 | * @author Geek_Soledad
45 | */
46 | public class CrashHttpReporter extends AbstractCrashHandler {
47 | HttpClient httpclient = new DefaultHttpClient();
48 | private String url;
49 | private Map otherParams;
50 | private String titleParam;
51 | private String bodyParam;
52 | private String fileParam;
53 | private String to;
54 | private String toParam;
55 | private HttpReportCallback callback;
56 |
57 | public CrashHttpReporter(Context context) {
58 | super(context);
59 | // httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
60 | // HttpVersion.HTTP_1_1);
61 | }
62 |
63 | @Override
64 | protected void sendReport(String title, String body, File file) {
65 | SimpleMultipartEntity entity = new SimpleMultipartEntity();
66 | entity.addPart(titleParam, title);
67 | entity.addPart(bodyParam, body);
68 | entity.addPart(toParam, to);
69 | if (otherParams != null) {
70 | for (Map.Entry param : otherParams.entrySet()) {
71 | entity.addPart(param.getKey(), param.getValue());
72 | }
73 | }
74 | entity.addPart(fileParam, file, true);
75 |
76 | try {
77 | HttpPost req = new HttpPost(url);
78 | req.setEntity(entity);
79 | HttpResponse resp = httpclient.execute(req);
80 | int statusCode = resp.getStatusLine().getStatusCode();
81 | String responseString = EntityUtils.toString(resp.getEntity());
82 | if (callback != null) {
83 | if (callback.isSuccess(statusCode, responseString)) {
84 | deleteLog(file);
85 | }
86 | } else if(statusCode == 200){
87 | deleteLog(file);
88 | }
89 | } catch (Exception e) {
90 | e.printStackTrace();
91 | }
92 | }
93 |
94 | private void deleteLog(File file) {
95 | Log.d("CrashHttpReporter", "delete: " + file.getName());
96 | file.delete();
97 | }
98 |
99 | public String getUrl() {
100 | return url;
101 | }
102 |
103 | /**
104 | * 发送请求的地址。
105 | *
106 | * @param url
107 | */
108 | public CrashHttpReporter setUrl(String url) {
109 | this.url = url;
110 | return this;
111 | }
112 |
113 | public String getTitleParam() {
114 | return titleParam;
115 | }
116 |
117 | /**
118 | * 标题的参数名
119 | *
120 | * @param titleParam
121 | */
122 | public CrashHttpReporter setTitleParam(String titleParam) {
123 | this.titleParam = titleParam;
124 | return this;
125 | }
126 |
127 | public String getBodyParam() {
128 | return bodyParam;
129 | }
130 |
131 | /**
132 | * 内容的参数名
133 | *
134 | * @param bodyParam
135 | */
136 | public CrashHttpReporter setBodyParam(String bodyParam) {
137 | this.bodyParam = bodyParam;
138 | return this;
139 | }
140 |
141 | public String getFileParam() {
142 | return fileParam;
143 | }
144 |
145 | /**
146 | * 文件的参数名
147 | *
148 | * @param fileParam
149 | */
150 | public CrashHttpReporter setFileParam(String fileParam) {
151 | this.fileParam = fileParam;
152 | return this;
153 | }
154 |
155 | public Map getOtherParams() {
156 | return otherParams;
157 | }
158 |
159 | /**
160 | * 其他自定义的参数对(可不设置)。
161 | *
162 | * @param otherParams
163 | */
164 | public void setOtherParams(Map otherParams) {
165 | this.otherParams = otherParams;
166 | }
167 |
168 | public String getTo() {
169 | return to;
170 | }
171 |
172 | /**
173 | * 收件人
174 | *
175 | * @param to
176 | */
177 | public CrashHttpReporter setTo(String to) {
178 | this.to = to;
179 | return this;
180 | }
181 |
182 | public HttpReportCallback getCallback() {
183 | return callback;
184 | }
185 |
186 | /**
187 | * 设置发送请求之后的回调接口。
188 | *
189 | * @param callback
190 | */
191 | public CrashHttpReporter setCallback(HttpReportCallback callback) {
192 | this.callback = callback;
193 | return this;
194 | }
195 |
196 | public String getToParam() {
197 | return toParam;
198 | }
199 |
200 | /**
201 | * 收件人参数名。
202 | *
203 | * @param toParam
204 | */
205 | public CrashHttpReporter setToParam(String toParam) {
206 | this.toParam = toParam;
207 | return this;
208 | }
209 |
210 | /**
211 | * 发送请求之后的回调接口。
212 | *
213 | * @author Geek_Soledad
218 | */
219 | public static interface HttpReportCallback {
220 | /**
221 | * 判断是否发送成功。它在发送日志的方法中被调用,如果成功,则日志文件会被删除。
222 | *
223 | * @param status
224 | * 状态码
225 | * @param content
226 | * 返回的内容。
227 | * @return
228 | */
229 | public boolean isSuccess(int status, String content);
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/reporter/httpreporter/SimpleMultipartEntity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * @(#)MultipartEntity.java Project:androidkit
3 | * Date:2013-5-5
4 | *
5 | * Copyright (c) 2013 Geek_Soledad.
6 | * All rights reserved.
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | */
20 |
21 | package com.githang.androidcrash.reporter.httpreporter;
22 |
23 | import java.io.ByteArrayInputStream;
24 | import java.io.ByteArrayOutputStream;
25 | import java.io.File;
26 | import java.io.FileInputStream;
27 | import java.io.FileNotFoundException;
28 | import java.io.IOException;
29 | import java.io.InputStream;
30 | import java.io.OutputStream;
31 | import java.util.Random;
32 |
33 | import org.apache.http.Header;
34 | import org.apache.http.HttpEntity;
35 | import org.apache.http.message.BasicHeader;
36 |
37 | import android.util.Log;
38 |
39 | /**
40 | * 以下代码参考自文章:http://blog.rafaelsanches.com/2011/01/29/upload
41 | * -using-multipart-post-using-httpclient-in-android/
42 | * Multipart/form coded HTTP entity consisting of multiple body parts.
43 | *
44 | */
45 | public class SimpleMultipartEntity implements HttpEntity {
46 |
47 | /**
48 | * ASCII的字符池,用于生成分界线。
49 | */
50 | private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
51 | .toCharArray();
52 |
53 | private ByteArrayOutputStream out;
54 | /**
55 | * 是否设置了开头的分界线
56 | */
57 | boolean isSetFirst = false;
58 | /**
59 | * 是否设置了最后的分界线
60 | */
61 | boolean isSetLast = false;
62 |
63 | /**
64 | * 分界线
65 | */
66 | private String boundary;
67 |
68 | public SimpleMultipartEntity() {
69 | out = new ByteArrayOutputStream();
70 | boundary = generateBoundary();
71 | }
72 |
73 | /**
74 | * 生成分界线
75 | *
76 | * @return 返回生成的分界线
77 | */
78 | protected String generateBoundary() {
79 | StringBuilder buffer = new StringBuilder();
80 | Random rand = new Random();
81 | int count = rand.nextInt(11) + 30; // a random size from 30 to 40
82 | for (int i = 0; i < count; i++) {
83 | buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
84 | }
85 | return buffer.toString();
86 | }
87 |
88 | public void writeFirstBoundaryIfNeeds() {
89 | if (!isSetFirst) {
90 | try {
91 | out.write(("--" + boundary + "\r\n").getBytes());
92 | } catch (final IOException e) {
93 | Log.w(e.getMessage(), e);
94 | }
95 | }
96 | isSetFirst = true;
97 | }
98 |
99 | public void writeLastBoundaryIfNeeds() {
100 | if (!isSetLast) {
101 | try {
102 | out.write(("\r\n--" + boundary + "--\r\n").getBytes());
103 | } catch (final IOException e) {
104 | Log.w(e.getMessage(), e);
105 | }
106 | isSetLast = true;
107 | }
108 | }
109 |
110 | public void addPart(final String key, final String value) {
111 | writeFirstBoundaryIfNeeds();
112 | try {
113 | out.write(("Content-Disposition: form-data; name=\"" + key + "\"\r\n\r\n").getBytes());
114 | out.write(value.getBytes());
115 | out.write(("\r\n--" + boundary + "\r\n").getBytes());
116 | } catch (final IOException e) {
117 | e.printStackTrace();
118 | }
119 | }
120 |
121 | public void addPart(final String key, final String fileName, final InputStream fin,
122 | final boolean isLast) {
123 | addPart(key, fileName, fin, "application/octet-stream", isLast);
124 | }
125 |
126 | public void addPart(final String key, final String fileName, final InputStream fin,
127 | String type, final boolean isLast) {
128 | writeFirstBoundaryIfNeeds();
129 | try {
130 | type = "Content-Type: " + type + "\r\n";
131 | out.write(("Content-Disposition: form-data; name=\"" + key + "\"; filename=\""
132 | + fileName + "\"\r\n").getBytes());
133 | out.write(type.getBytes());
134 | out.write("Content-Transfer-Encoding: binary\r\n\r\n".getBytes());
135 |
136 | final byte[] tmp = new byte[4096];
137 | int l = 0;
138 | while ((l = fin.read(tmp)) != -1) {
139 | out.write(tmp, 0, l);
140 | }
141 | if (!isLast)
142 | out.write(("\r\n--" + boundary + "\r\n").getBytes());
143 | out.flush();
144 | } catch (final IOException e) {
145 | e.printStackTrace();
146 | } finally {
147 | try {
148 | fin.close();
149 | } catch (final IOException e) {
150 | e.printStackTrace();
151 | }
152 | }
153 | }
154 |
155 | public void addPart(final String key, final File value, final boolean isLast) {
156 | try {
157 | addPart(key, value.getName(), new FileInputStream(value), isLast);
158 | } catch (final FileNotFoundException e) {
159 | e.printStackTrace();
160 | }
161 | }
162 |
163 | @Override
164 | public long getContentLength() {
165 | writeLastBoundaryIfNeeds();
166 | return out.toByteArray().length;
167 | }
168 |
169 | @Override
170 | public Header getContentType() {
171 | return new BasicHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
172 | }
173 |
174 | @Override
175 | public boolean isChunked() {
176 | return false;
177 | }
178 |
179 | @Override
180 | public boolean isRepeatable() {
181 | return false;
182 | }
183 |
184 | @Override
185 | public boolean isStreaming() {
186 | return false;
187 | }
188 |
189 | @Override
190 | public void writeTo(final OutputStream outstream) throws IOException {
191 | outstream.write(out.toByteArray());
192 | }
193 |
194 | @Override
195 | public Header getContentEncoding() {
196 | return null;
197 | }
198 |
199 | @Override
200 | public void consumeContent() throws IOException, UnsupportedOperationException {
201 | if (isStreaming()) {
202 | throw new UnsupportedOperationException(
203 | "Streaming entity does not implement #consumeContent()");
204 | }
205 | }
206 |
207 | @Override
208 | public InputStream getContent() throws IOException, UnsupportedOperationException {
209 | return new ByteArrayInputStream(out.toByteArray());
210 | }
211 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/reporter/mailreporter/CrashEmailReporter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * @(#)CrashEmailReport.java Project: CrashHandler
3 | * Date: 2014-5-27
4 | *
5 | * Copyright (c) 2014 CFuture09, Institute of Software,
6 | * Guangdong Ocean University, Zhanjiang, GuangDong, China.
7 | * All rights reserved.
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 | package com.githang.androidcrash.reporter.mailreporter;
22 |
23 | import java.io.File;
24 |
25 | import android.content.Context;
26 |
27 | import com.githang.androidcrash.reporter.AbstractCrashHandler;
28 |
29 | /**
30 | * 已经实现的日志报告类,这里通过邮件方式发送日志报告
31 | *
32 | * @author Geek_Soledad
37 | */
38 | public class CrashEmailReporter extends AbstractCrashHandler {
39 | private String mReceiveEmail;
40 | private String mSendEmail;
41 | private String mSendPassword;
42 | private String mHost;
43 | private String mPort;
44 |
45 | public CrashEmailReporter(Context context) {
46 | super(context);
47 | }
48 |
49 | /**
50 | * 设置接收者
51 | *
52 | * @param receiveEmail
53 | */
54 | public void setReceiver(String receiveEmail) {
55 | mReceiveEmail = receiveEmail;
56 | }
57 |
58 | /**
59 | * 设置发送者邮箱
60 | *
61 | * @param email
62 | */
63 | public void setSender(String email) {
64 | mSendEmail = email;
65 | }
66 |
67 | /**
68 | * 设置发送者密码
69 | *
70 | * @param password
71 | */
72 | public void setSendPassword(String password) {
73 | mSendPassword = password;
74 | }
75 |
76 | /**
77 | * 设置SMTP 主机
78 | *
79 | * @param host
80 | */
81 | public void setSMTPHost(String host) {
82 | mHost = host;
83 | }
84 |
85 | /**
86 | * 设置端口
87 | *
88 | * @param port
89 | */
90 | public void setPort(String port) {
91 | mPort = port;
92 | }
93 |
94 | @Override
95 | protected void sendReport(String title, String body, File file) {
96 | LogMail sender = new LogMail().setUser(mSendEmail).setPass(mSendPassword)
97 | .setFrom(mSendEmail).setTo(mReceiveEmail).setHost(mHost).setPort(mPort)
98 | .setSubject(title).setBody(body);
99 | sender.init();
100 | try {
101 | sender.addAttachment(file.getPath(), file.getName());
102 | sender.send();
103 | file.delete();
104 | } catch (Exception e) {
105 | e.printStackTrace();
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/reporter/mailreporter/LogMail.java:
--------------------------------------------------------------------------------
1 | /*
2 | * @(#)Snippet.java Project: CrashHandler
3 | * Date: 2014-5-27
4 | *
5 | * Copyright (c) 2014 CFuture09, Institute of Software,
6 | * Guangdong Ocean University, Zhanjiang, GuangDong, China.
7 | * All rights reserved.
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | */
21 | package com.githang.androidcrash.reporter.mailreporter;
22 |
23 | import java.util.Date;
24 | import java.util.Properties;
25 |
26 | import javax.activation.CommandMap;
27 | import javax.activation.MailcapCommandMap;
28 | import javax.mail.Authenticator;
29 | import javax.mail.BodyPart;
30 | import javax.mail.MessagingException;
31 | import javax.mail.Multipart;
32 | import javax.mail.PasswordAuthentication;
33 | import javax.mail.Session;
34 | import javax.mail.Transport;
35 | import javax.mail.internet.InternetAddress;
36 | import javax.mail.internet.MimeBodyPart;
37 | import javax.mail.internet.MimeMessage;
38 | import javax.mail.internet.MimeMultipart;
39 |
40 | import android.util.Log;
41 |
42 | /**
43 | * 邮件类,用于构造要发送的邮件。
44 | *
45 | * @author Geek_Soledad
50 | */
51 | public class LogMail extends Authenticator {
52 | private String host;
53 | private String port;
54 | private String user;
55 | private String pass;
56 | private String from;
57 | private String to;
58 | private String subject;
59 | private String body;
60 |
61 | private Multipart multipart;
62 | private Properties props;
63 |
64 | public LogMail() {
65 | }
66 |
67 | /**
68 | * @param user
69 | * 邮箱登录名
70 | * @param pass
71 | * 邮箱登录密码
72 | * @param from
73 | * 发件人
74 | * @param to
75 | * 收件人
76 | * @param host
77 | * 主机
78 | * @param port
79 | * SMTP端口号
80 | * @param subject
81 | * 邮件主题
82 | * @param body
83 | * 邮件正文
84 | */
85 | public LogMail(String user, String pass, String from, String to, String host, String port,
86 | String subject, String body) {
87 | this.host = host;
88 | this.port = port;
89 | this.user = user;
90 | this.pass = pass;
91 | this.from = from;
92 | this.to = to;
93 | this.subject = subject;
94 | this.body = body;
95 | }
96 |
97 | public LogMail setHost(String host) {
98 | this.host = host;
99 | return this;
100 | }
101 |
102 | public LogMail setPort(String port) {
103 | this.port = port;
104 | return this;
105 | }
106 |
107 | public LogMail setUser(String user) {
108 | this.user = user;
109 | return this;
110 | }
111 |
112 | public LogMail setPass(String pass) {
113 | this.pass = pass;
114 | return this;
115 | }
116 |
117 | public LogMail setFrom(String from) {
118 | this.from = from;
119 | return this;
120 | }
121 |
122 | public LogMail setTo(String to) {
123 | this.to = to;
124 | return this;
125 | }
126 |
127 | public LogMail setSubject(String subject) {
128 | this.subject = subject;
129 | return this;
130 | }
131 |
132 | public LogMail setBody(String body) {
133 | this.body = body;
134 | return this;
135 | }
136 |
137 | /**
138 | * 初始化。它在设置好用户名、密码、发件人、收件人、主题、正文、主机及端口号之后显示调用。
139 | */
140 | public void init() {
141 | multipart = new MimeMultipart();
142 | // There is something wrong with MailCap, javamail can not find a
143 | // handler for the multipart/mixed part, so this bit needs to be added.
144 | MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
145 | mc.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");
146 | mc.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
147 | mc.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");
148 | mc.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");
149 | mc.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");
150 | CommandMap.setDefaultCommandMap(mc);
151 |
152 | props = new Properties();
153 |
154 | props.put("mail.smtp.host", host);
155 | props.put("mail.smtp.auth", "true");
156 | props.put("mail.smtp.port", port);
157 | props.put("mail.smtp.socketFactory.port", port);
158 | props.put("mail.transport.protocol", "smtp");
159 | props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
160 | props.put("mail.smtp.socketFactory.fallback", "false");
161 | }
162 |
163 | /**
164 | * 发送邮件
165 | *
166 | * @return 是否发送成功
167 | * @throws MessagingException
168 | */
169 | public boolean send() throws MessagingException {
170 | if (!user.equals("") && !pass.equals("") && !to.equals("") && !from.equals("")) {
171 | Session session = Session.getDefaultInstance(props, this);
172 | Log.d("SendUtil", host + "..." + port + ".." + user + "..." + pass);
173 |
174 | MimeMessage msg = new MimeMessage(session);
175 |
176 | msg.setFrom(new InternetAddress(from));
177 |
178 | InternetAddress addressTo = new InternetAddress(to);
179 | msg.setRecipient(MimeMessage.RecipientType.TO, addressTo);
180 |
181 | msg.setSubject(subject);
182 | msg.setSentDate(new Date());
183 |
184 | // setup message body
185 | BodyPart messageBodyPart = new MimeBodyPart();
186 | messageBodyPart.setText(body);
187 |
188 | multipart.addBodyPart(messageBodyPart, 0);
189 |
190 | // Put parts in message
191 | msg.setContent(multipart);
192 |
193 | // send email
194 | Transport.send(msg);
195 |
196 | return true;
197 | } else {
198 | return false;
199 | }
200 | }
201 |
202 | /**
203 | * 添加附件
204 | *
205 | * @param filePath
206 | * 附件路径
207 | * @param fileName
208 | * 附件名称
209 | * @throws Exception
210 | */
211 | public void addAttachment(String filePath, String fileName) throws Exception {
212 | BodyPart messageBodyPart = new MimeBodyPart();
213 | ((MimeBodyPart) messageBodyPart).attachFile(filePath);
214 | multipart.addBodyPart(messageBodyPart);
215 | }
216 |
217 | @Override
218 | public PasswordAuthentication getPasswordAuthentication() {
219 | return new PasswordAuthentication(user, pass);
220 | }
221 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/githang/androidcrash/util/AssertUtil.java:
--------------------------------------------------------------------------------
1 | package com.githang.androidcrash.util;
2 |
3 | /**
4 | * User: Geek_Soledad(msdx.android@qq.com)
5 | * Date: 2014-11-03
6 | * Time: 22:11
7 | * FIXME
8 | */
9 | public class AssertUtil {
10 | /**
11 | * 断言参数非空。
12 | * @param name 参数名
13 | * @param object 参数值
14 | */
15 | public static final void assertNotNull(String name, Object object) {
16 | if(object == null) {
17 | throw new IllegalArgumentException("The argument '" + name + "' is null");
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/library/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/library/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/library/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msdx/android-crash/3773a1b38e4cf1864551cfb6271b87047a1ea3e5/library/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidCrash
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------