├── .gitignore ├── README.md ├── assets ├── detail.png ├── list.png ├── notification.png └── video.gif ├── build.gradle ├── demo ├── .gitignore ├── build.gradle ├── debug.keystore ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── nshmura │ │ └── strictmodenotifier │ │ └── demo │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── nshmura │ │ │ └── strictmodenotifier │ │ │ └── demo │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── nshmura │ └── strictmodenotifier │ └── demo │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library-common ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── nshmura │ └── strictmodenotifier │ ├── CustomAction.java │ ├── IgnoreAction.java │ ├── NotifierConfig.java │ ├── StrictModeViolation.java │ └── ViolationType.java ├── library-no-op ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── nshmura │ └── strictmodenotifier │ └── StrictModeNotifier.java ├── library ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── nshmura │ │ └── strictmodenotifier │ │ ├── LogWatchService.java │ │ ├── ReportActivityUtils.java │ │ ├── ReportAdapter.java │ │ ├── StrictModeLog.java │ │ ├── StrictModeNotifier.java │ │ ├── StrictModeNotifierInternals.java │ │ ├── StrictModeNotifierSingleThreadFactory.java │ │ ├── StrictModeReportActivity.java │ │ ├── StrictModeReportDetailActivity.java │ │ ├── StringModeConfig.java │ │ ├── ViolationStore.java │ │ ├── ViolationTypeInfo.java │ │ └── detector │ │ ├── ClassInstanceLimitDetector.java │ │ ├── CleartextNetworkDetector.java │ │ ├── CustomSlowCallDetector.java │ │ ├── Detector.java │ │ ├── FileUriExposureDetector.java │ │ ├── LeakedClosableObjectsDetector.java │ │ ├── NetworkDetector.java │ │ └── ResourceMismatchDetector.java │ └── res │ ├── drawable-hdpi │ ├── strictmode_notifier_ic_launcher.png │ └── strictmode_notifier_ic_notification.png │ ├── drawable-mdpi │ ├── strictmode_notifier_ic_launcher.png │ └── strictmode_notifier_ic_notification.png │ ├── drawable-xhdpi │ ├── strictmode_notifier_ic_launcher.png │ └── strictmode_notifier_ic_notification.png │ ├── drawable-xxhdpi │ ├── strictmode_notifier_ic_launcher.png │ └── strictmode_notifier_ic_notification.png │ ├── drawable-xxxhdpi │ ├── strictmode_notifier_ic_launcher.png │ └── strictmode_notifier_ic_notification.png │ ├── layout │ ├── strictmode_notifier_activity_report.xml │ ├── strictmode_notifier_activity_report_detail.xml │ └── strictmode_notifier_row.xml │ ├── menu │ └── strictmode_notifier_menu.xml │ ├── values-v14 │ └── strictmode_notifier_styles.xml │ ├── values-v21 │ └── strictmode_notifier_styles.xml │ └── values │ ├── strictmode_notifer_strings.xml │ ├── strictmode_notifier_colors.xml │ └── strictmode_notifier_styles.xml ├── settings.gradle └── testapp ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── nshmura │ └── strictmodenotifier │ └── testapp │ └── ApplicationTest.java ├── main ├── AndroidManifest.xml ├── java │ └── com │ │ └── nshmura │ │ └── strictmodenotifier │ │ └── testapp │ │ ├── ClassInstanceLimitActivity.java │ │ ├── CustomLogWatchService.java │ │ ├── LeakedBroadcastReceiver.java │ │ ├── LeakedClosableObjectsActivity.java │ │ ├── LeakedSQLiteOpenHelper.java │ │ ├── MainActivity.java │ │ └── MainApplication.java └── res │ ├── layout │ ├── activity_main.xml │ └── row_actions.xml │ ├── menu │ └── main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── test └── java └── com └── nshmura └── strictmodenotificator └── ExampleUnitTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | 4 | # Files for the Dalvik VM 5 | *.dex 6 | 7 | # Java class files 8 | *.class 9 | 10 | # Generated files 11 | bin/ 12 | gen/ 13 | obj/ 14 | apk/ 15 | target/ 16 | build/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | .idea 21 | *.iml 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | 26 | # Proguard folder generated by Eclipse 27 | proguard/ 28 | 29 | # Crashlytics 30 | res/values/com_crashlytics_export_strings.xml 31 | assets/crashlytics-build.properties 32 | 33 | # docs 34 | docs/ 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # strictmode-notifier 2 | [![Android Weekly](http://img.shields.io/badge/Android%20Weekly-%23200-2CB3E5.svg?style=flat)](http://androidweekly.net/issues/issue-200) 3 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-strictmode--notifier-green.svg?style=true)](https://android-arsenal.com/details/1/3405) 4 | 5 | An Android library that improves the StrictMode reporting. 6 | 7 | - *Head-up Notification* of StrictMode violations. 8 | - *Ignoring Specific Violations* 9 | - *Custom Actions* that called when StrictMode violations is happened. 10 | - *Violation History Viewer* that automatically installed.
11 | 12 | ## Screenshots 13 | 14 | 15 | 16 | 17 | ## Sample App 18 | [strictmode-notifier/testapp](https://github.com/nshmura/strictmode-notifier/tree/master/testapp) 19 | 20 |
21 | 22 | ## About StrictMode 23 | - [StrictMode for Runtime Analysis on Android](https://medium.com/google-developers/strictmode-for-runtime-analysis-on-android-f8d0a2c5667e#.elffd4gi1) 24 | - [StrictMode for enforcing best practices at runtime](https://www.youtube.com/watch?v=BxTfwT7mkB4) 25 | 26 | ## Getting started 27 | 28 | In your `build.gradle`: 29 | 30 | ```gradle 31 | repositories { 32 | jcenter() 33 | } 34 | 35 | dependencies { 36 | debugCompile 'com.nshmura:strictmode-notifier:0.9.3' 37 | releaseCompile 'com.nshmura:strictmode-notifier-no-op:0.9.3' 38 | } 39 | ``` 40 | 41 | In your `Application` class: 42 | 43 | ```java 44 | public class ExampleApplication extends Application { 45 | 46 | @Override public void onCreate() { 47 | super.onCreate(); 48 | 49 | if (BuildConfig.DEBUG) { 50 | //setup this library 51 | StrictModeNotifier.install(this); 52 | 53 | //setup StrictMode. 54 | // 55 | // penaltyLog() should be called for strictmode-notifier 56 | // 57 | new Handler().post(new Runnable() { 58 | @Override public void run() { 59 | StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder() 60 | .detectAll() 61 | .permitDiskReads() 62 | .permitDiskWrites() 63 | .penaltyLog() // Must! 64 | .build(); 65 | StrictMode.setThreadPolicy(threadPolicy); 66 | 67 | StrictMode.VmPolicy vmPolicy = new StrictMode.VmPolicy.Builder() 68 | .detectAll() 69 | .penaltyLog() // Must! 70 | .build(); 71 | StrictMode.setVmPolicy(vmPolicy); 72 | } 73 | }); 74 | } 75 | } 76 | } 77 | ``` 78 | 79 | ## How does it work? 80 | 1. `strictmode-notifier` starts `logcat` command in backgound thread, and infinitely reads the log from `logcat`. 81 | 2. If StrictMode violation is happened, error logs is outputed. 82 | 3. `strictmode-notifier` reads that log via `logcat`, and shows a notification of the violation. 83 | 84 | ## Customizing 85 | 86 | ### How to ignore specific violations 87 | 88 | ```java 89 | StrictModeNotifier 90 | .install(context) 91 | .setIgnoreAction(new IgnoreAction() { 92 | @Override public boolean ignore(StrictModeViolation violation) { 93 | // ex) ignore LEAKED_CLOSABLE_OBJECTS that contains android.foo.bar in stacktrace. 94 | return violation.violationType == ViolationType.LEAKED_CLOSABLE_OBJECTS 95 | && violation.getStacktraceText().contains("android.foo.bar"); 96 | } 97 | }); 98 | ``` 99 | 100 | ### How to add custom actions 101 | 102 | ```java 103 | StrictModeNotifier 104 | .install(context) 105 | .addCustomAction(new CustomAction() { 106 | @Override public void onViolation(StrictModeViolation violation) { 107 | //ex) Send messages into Slack 108 | } 109 | }); 110 | ``` 111 | 112 | ### How to disable Headup Notification 113 | 114 | ```java 115 | StrictModeNotifier 116 | .install(context) 117 | .setHeadupEnabled(false); 118 | ``` 119 | 120 | ### How to enable debug mode of strictmode-notifier 121 | 122 | ```java 123 | StrictModeNotifier 124 | .install(context) 125 | .setDebugMode(true); 126 | ``` 127 | 128 | ## Todo 129 | Parsing following violations 130 | - ActivityLeaks 131 | - LeakedRegistrationObjects 132 | - LeakedSqlLiteObjects 133 | 134 | ## Thanks 135 | Inspired by [square/leakcanary](https://github.com/square/leakcanary) 136 | 137 | ## License 138 | ``` 139 | Copyright (C) 2016 nshmura 140 | 141 | Licensed under the Apache License, Version 2.0 (the "License"); 142 | you may not use this file except in compliance with the License. 143 | You may obtain a copy of the License at 144 | 145 | http://www.apache.org/licenses/LICENSE-2.0 146 | 147 | Unless required by applicable law or agreed to in writing, software 148 | distributed under the License is distributed on an "AS IS" BASIS, 149 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 150 | See the License for the specific language governing permissions and 151 | limitations under the License. 152 | ``` 153 | -------------------------------------------------------------------------------- /assets/detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshmura/strictmode-notifier/c2f91aab6462ecbe0c5ca14f858040e99dea0f01/assets/detail.png -------------------------------------------------------------------------------- /assets/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshmura/strictmode-notifier/c2f91aab6462ecbe0c5ca14f858040e99dea0f01/assets/list.png -------------------------------------------------------------------------------- /assets/notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshmura/strictmode-notifier/c2f91aab6462ecbe0c5ca14f858040e99dea0f01/assets/notification.png -------------------------------------------------------------------------------- /assets/video.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshmura/strictmode-notifier/c2f91aab6462ecbe0c5ca14f858040e99dea0f01/assets/video.gif -------------------------------------------------------------------------------- /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:2.1.2' 9 | classpath 'com.novoda:bintray-release:0.3.4' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } 25 | 26 | ext { 27 | minSdkVersion = 9 28 | compileSdkVersion = 23 29 | targetSdkVersion = compileSdkVersion 30 | buildToolsVersion = '23.0.2' 31 | javaVersion = JavaVersion.VERSION_1_7 32 | supportLibraryVersion = '23.2.1' 33 | versionCode = 4 34 | versionName = "0.9.3" 35 | 36 | POM_DEVELOPER_ID = "nshmura" 37 | POM_GROUP = "com.nshmura" 38 | POM_VERSION_NAME = versionName 39 | POM_URL = "https://github.com/nshmura/strictmode-notifier" 40 | 41 | POM_ARTIFACT_ID = "strictmode-notifier" 42 | POM_ARTIFACT_NAME = "strictmode-notifier" 43 | POM_DESCRIPTION = "An Android library that report notification of StrictMode violation." 44 | 45 | POM_ARTIFACT_ID_NO_OP = "strictmode-notifier-no-op" 46 | POM_ARTIFACT_NAME_NO_OP = "strictmode-notifier-no-op" 47 | POM_DESCRIPTION_NO_OP = "No op strictmode-notifier" 48 | } -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | 7 | defaultConfig { 8 | applicationId "com.nshmura.strictmodenotifier.demo" 9 | minSdkVersion 11 10 | targetSdkVersion rootProject.ext.targetSdkVersion 11 | versionCode rootProject.ext.versionCode 12 | versionName rootProject.ext.versionName 13 | } 14 | 15 | signingConfigs { 16 | product { 17 | storeFile file("debug.keystore") 18 | storePassword "android" 19 | keyAlias "androiddebugkey" 20 | keyPassword "android" 21 | } 22 | } 23 | 24 | buildTypes { 25 | release { 26 | signingConfig signingConfigs.product 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | } 32 | 33 | dependencies { 34 | compile fileTree(dir: 'libs', include: ['*.jar']) 35 | testCompile 'junit:junit:4.12' 36 | compile "com.android.support:appcompat-v7:$rootProject.ext.supportLibraryVersion" 37 | 38 | debugCompile project(":library") 39 | releaseCompile project(":library-no-op") 40 | //debugCompile "com.nshmura:strictmode-notifier:$rootProject.ext.versionName" 41 | //releaseCompile "com.nshmura:strictmode-notifier-no-op:$rootProject.ext.versionName" 42 | } 43 | -------------------------------------------------------------------------------- /demo/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nshmura/strictmode-notifier/c2f91aab6462ecbe0c5ca14f858040e99dea0f01/demo/debug.keystore -------------------------------------------------------------------------------- /demo/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 /Users/a13088/Library/Android/sdk/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 | -------------------------------------------------------------------------------- /demo/src/androidTest/java/com/nshmura/strictmodenotifier/demo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.nshmura.strictmodenotifier.demo; 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 | } -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /demo/src/main/java/com/nshmura/strictmodenotifier/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.nshmura.strictmodenotifier.demo; 2 | 3 | import android.os.Bundle; 4 | import android.os.StrictMode; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | 10 | @Override protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main); 13 | 14 | //noinspection ConstantConditions 15 | findViewById(R.id.fire_error).setOnClickListener(new View.OnClickListener() { 16 | @Override public void onClick(View v) { 17 | StrictMode.noteSlowCall("fireSlowCall"); 18 | } 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demo/src/main/java/com/nshmura/strictmodenotifier/demo/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.nshmura.strictmodenotifier.demo; 2 | 3 | import android.app.Application; 4 | import android.os.Handler; 5 | import android.os.StrictMode; 6 | import com.nshmura.strictmodenotifier.CustomAction; 7 | import com.nshmura.strictmodenotifier.IgnoreAction; 8 | import com.nshmura.strictmodenotifier.StrictModeNotifier; 9 | import com.nshmura.strictmodenotifier.StrictModeViolation; 10 | import com.nshmura.strictmodenotifier.ViolationType; 11 | 12 | public class MainApplication extends Application { 13 | 14 | @Override public void onCreate() { 15 | super.onCreate(); 16 | 17 | if (BuildConfig.DEBUG) { 18 | StrictModeNotifier 19 | .install(this) 20 | .setDebugMode(true) 21 | .setHeadupEnabled(true) 22 | .setIgnoreAction(new IgnoreAction() { 23 | @Override public boolean ignore(StrictModeViolation violation) { 24 | return violation.violationType == ViolationType.LEAKED_CLOSABLE_OBJECTS 25 | && violation.getStacktraceText().contains("android.foo.barr"); 26 | } 27 | }) 28 | .addCustomAction(new CustomAction() { 29 | @Override public void onViolation(StrictModeViolation violation) { 30 | //ex) Send messages into Slack 31 | } 32 | }); 33 | 34 | //https://code.google.com/p/android/issues/detail?id=35298 35 | new Handler().post(new Runnable() { 36 | @Override public void run() { 37 | StrictMode.setThreadPolicy( 38 | new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); 39 | } 40 | }); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 |