Skip to content

Commit 558223e

Browse files
authored
Merge pull request #4 from haroldadmin/auto-init
Add support for automatic initialization using a content provider
2 parents 01db91f + 2218868 commit 558223e

File tree

8 files changed

+125
-33
lines changed

8 files changed

+125
-33
lines changed

README.md

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,29 @@
22

33
WhatTheStack is a library to make your debugging experience on Android better.
44

5-
WhatTheStack shows you a pretty error screen when your Android App crashes, instead of a boring old dialog saying "Unfortunately, \<your-app\> has crashed".
5+
It shows you a pretty error screen when your Android App crashes, instead of a boring old dialog saying "Unfortunately, \<your-app\> has crashed".
66

77
## Setup
88

9-
All that is needed to initialize this library is to use the `init()` method in the `onCreate()` callback of your custom Application class.
9+
Follow the [Installation Instructions](#installation) to set it up.
1010

11-
```kotlin
12-
class MyApp : Application() {
13-
override fun onCreate() {
14-
super.onCreate()
15-
if (BuildConfig.DEBUG) {
16-
WhatTheStack(this).init()
17-
}
18-
}
19-
}
20-
```
21-
22-
And when an error is thrown in your application, you shall be greeted with a screen similar to this:
11+
Now when an uncaught exception is thrown in your application, you shall be greeted with a screen similar to this:
2312

2413
<img src="media/screenshot.jpeg" width="360px" height="640px"/>
2514

15+
### Disabling automatic initialization
16+
17+
`WhatTheStack` initializes automatically when your application starts. It accomplishes this using a `ContentProvider`.
18+
19+
If you want to disable automatic initialization, you should disable the initialization content provider of this library by adding the following lines to your application's `AndroidManifest.xml` file:
20+
21+
```xml
22+
<provider
23+
android:name="com.haroldadmin.whatthestack.WhatTheStackInitProvider"
24+
android:authorities="${applicationId}.WhatTheStackInitProvider"
25+
tools:node="remove" />
26+
```
27+
2628
## Under the hood
2729

2830
This library works by setting a default `UncaughtExceptionHandler` on your app, and running a service to receive notifications about these exceptions.
@@ -38,20 +40,21 @@ Add Jitpack repository in your root `build.gradle` file:
3840
```groovy
3941
allprojects {
4042
repositories {
41-
...
4243
maven { url 'https://jitpack.io' }
43-
}
44+
}
4445
}
4546
```
4647

4748
And then add the dependency to your app:
4849

4950
```groovy
5051
dependencies {
51-
implementation 'com.github.haroldadmin:WhatTheStack:(latest-version)'
52+
debugImplementation 'com.github.haroldadmin:WhatTheStack:(latest-version)'
5253
}
5354
```
5455

56+
It is not recommended to use WhatTheStack in anything other than debug builds of your app. Only use `debugImplementation` when adding this dependency.
57+
5558
[![Release](https://jitpack.io/v/haroldadmin/WhatTheStack.svg)](https://jitpack.io/#haroldadmin/WhatTheStack)
5659

5760
## Contributions

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ android {
4545
dependencies {
4646
implementation fileTree(dir: 'libs', include: ['*.jar'])
4747

48-
implementation project(path: ":what-the-stack")
48+
debugImplementation project(path: ":what-the-stack")
4949

5050
implementation libs.kotlinStdLib
5151
implementation libs.appCompat

app/src/main/AndroidManifest.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
package="com.haroldadmin.crashyapp">
44

55
<application
6-
android:name=".CrashyApp"
76
android:allowBackup="true"
87
android:icon="@mipmap/ic_launcher"
98
android:label="@string/app_name"

app/src/main/java/com/haroldadmin/crashyapp/CrashyApp.kt

Lines changed: 0 additions & 12 deletions
This file was deleted.

app/src/main/java/com/haroldadmin/crashyapp/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ class MainActivity : AppCompatActivity() {
1616
}
1717
}
1818

19-
private class BecauseICanException : Exception("This exception is purely thrown because it can be thrown")
19+
private class BecauseICanException : Exception("This exception is thrown purely because it can be thrown")

what-the-stack/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="com.haroldadmin.whatthestack">
33
<application>
4+
<provider
5+
android:name=".WhatTheStackInitProvider"
6+
android:authorities="${applicationId}.WhatTheStackInitProvider"
7+
android:exported="false"
8+
android:enabled="true"/>
49
<service
510
android:name=".WhatTheStackService"
611
android:process=":what_the_stack_process" />

what-the-stack/src/main/java/com/haroldadmin/whatthestack/WhatTheStack.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,30 @@ import android.content.Intent
55
import android.os.Messenger
66

77
/**
8+
* **DO NOT USE**
89
* A class to allow initialization of WhatTheStack service.
910
*
11+
* WhatTheStack initializes automatically using a content provider. You do not need to initialize
12+
* it explicitly using this class.
13+
*
1014
* @param applicationContext The context used to start the service to catch uncaught exceptions
1115
*/
16+
@Deprecated(
17+
"WhatTheStack initializes automatically at application startup. Do not explicitly initialize it",
18+
level = DeprecationLevel.ERROR
19+
)
1220
class WhatTheStack(private val applicationContext: Context) {
1321

22+
@Suppress("unused")
23+
fun init() {
24+
WhatTheStackInitializer.init(applicationContext)
25+
}
26+
}
27+
28+
internal object WhatTheStackInitializer {
29+
30+
private var isInitialized: Boolean = false
31+
1432
private val connection = WhatTheStackConnection(
1533
onConnected = { binder ->
1634
val messenger = Messenger(binder)
@@ -19,7 +37,9 @@ class WhatTheStack(private val applicationContext: Context) {
1937
}
2038
)
2139

22-
fun init() {
40+
fun init(applicationContext: Context) {
41+
if (isInitialized) { return }
42+
isInitialized = true
2343
val intent = Intent(applicationContext, WhatTheStackService::class.java)
2444
applicationContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)
2545
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.haroldadmin.whatthestack
2+
3+
import android.content.ContentProvider
4+
import android.content.ContentValues
5+
import android.content.Context
6+
import android.content.pm.ProviderInfo
7+
import android.database.Cursor
8+
import android.net.Uri
9+
import android.util.Log
10+
11+
private const val TAG = "WhatTheStackInitializer"
12+
13+
/**
14+
* A content provider to initialize WhatTheStack automatically at application startup. It does not
15+
* provide the ability to query underlying data.
16+
*/
17+
class WhatTheStackInitProvider : ContentProvider() {
18+
19+
override fun onCreate(): Boolean {
20+
val applicationContext = context ?: return false
21+
WhatTheStackInitializer.init(applicationContext)
22+
Log.d(TAG, "Initialized")
23+
return true
24+
}
25+
26+
override fun attachInfo(context: Context?, info: ProviderInfo?) {
27+
super.attachInfo(context, info)
28+
checkProperAuthority(info)
29+
}
30+
31+
override fun insert(uri: Uri, values: ContentValues?): Uri? {
32+
throw UnsupportedOperationException("This content provider can only be used for initializing WhatTheStack")
33+
}
34+
35+
override fun query(
36+
uri: Uri,
37+
projection: Array<out String>?,
38+
selection: String?,
39+
selectionArgs: Array<out String>?,
40+
sortOrder: String?
41+
): Cursor? {
42+
throw UnsupportedOperationException("This content provider can only be used for initializing WhatTheStack")
43+
}
44+
45+
override fun update(
46+
uri: Uri,
47+
values: ContentValues?,
48+
selection: String?,
49+
selectionArgs: Array<out String>?
50+
): Int {
51+
throw UnsupportedOperationException("This content provider can only be used for initializing WhatTheStack")
52+
}
53+
54+
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
55+
throw UnsupportedOperationException("This content provider can only be used for initializing WhatTheStack")
56+
}
57+
58+
override fun getType(uri: Uri): String? {
59+
throw UnsupportedOperationException("This content provider can only be used for initializing WhatTheStack")
60+
}
61+
62+
/**
63+
* Checks to see if the application using this library has declared an applicationId in its
64+
* build.gradle file. If not, it crashes the app with a proper error message.
65+
*
66+
* Performing this check at startup is important because if a user's device has multiple apps
67+
* installed with WhatTheStack, and they do not declare their application IDs, then the content
68+
* providers in those applications will fallback to the same name. Since the authority is used
69+
* to identify the content provider, it **must** be unique.
70+
*/
71+
private fun checkProperAuthority(info: ProviderInfo?) {
72+
val contentProviderName = WhatTheStackInitProvider::class.java.name
73+
require(info?.authority != contentProviderName) {
74+
"Please provide an applicationId inside your application's build.gradle file"
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)