Skip to content

Commit e6cb95a

Browse files
committed
Fix crash when requesting permissions after changing activities
1 parent 6ccfe1e commit e6cb95a

File tree

2 files changed

+43
-24
lines changed

2 files changed

+43
-24
lines changed

firebase-appdistribution/test-app/src/main/java/com/googletest/firebase/appdistribution/testapp/NotificationFeedbackTrigger.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.google.firebase.ktx.Firebase
2424
import com.googletest.firebase.appdistribution.testapp.NotificationFeedbackTrigger.SCREENSHOT_FILE_NAME
2525
import com.googletest.firebase.appdistribution.testapp.NotificationFeedbackTrigger.takeScreenshot
2626
import java.io.IOException
27+
import java.util.*
2728

2829
@SuppressLint("StaticFieldLeak") // Reference to Activity is set to null in onActivityDestroyed
2930
object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
@@ -35,7 +36,9 @@ object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
3536
private var isEnabled = false
3637
private var hasRequestedPermission = false
3738
private var currentActivity: Activity? = null
38-
private var requestPermissionLauncher: ActivityResultLauncher<String>? = null
39+
// TODO(lkellogg): this is getting too complex - simplify it by, for example, only enabling this
40+
// trigger on a per-activity basis instead of app-wide
41+
private var requestPermissionLaunchers: WeakHashMap<Activity, ActivityResultLauncher<String>?> = WeakHashMap()
3942

4043
fun initialize(application: Application) {
4144
// Create the NotificationChannel, but only on API 26+ because
@@ -108,7 +111,7 @@ object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
108111
}
109112

110113
private fun requestPermission(activity: Activity) {
111-
var launcher = requestPermissionLauncher
114+
var launcher = requestPermissionLaunchers[activity]
112115
if (launcher == null) {
113116
Log.i(TAG, "Not requesting permission, because of inability to register for result.")
114117
} else {
@@ -155,14 +158,14 @@ object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
155158
override fun onActivityDestroyed(activity: Activity) {
156159
if (activity == currentActivity) {
157160
Log.d(TAG, "clearing current activity")
158-
requestPermissionLauncher = null
159161
currentActivity = null
160162
}
163+
requestPermissionLaunchers[activity] = null
161164
}
162165

163166
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
164167
if (activity is ActivityResultCaller && !hasRequestedPermission) {
165-
requestPermissionLauncher =
168+
requestPermissionLaunchers[activity] =
166169
activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) {
167170
isGranted: Boolean ->
168171
if (!isEnabled) {

firebase-appdistribution/test-app/src/main/java/com/googletest/firebase/appdistribution/testapp/ScreenshotDetectionFeedbackTrigger.kt

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ private constructor(private val infoTextResourceId: Int, handler: Handler) :
3131
ContentObserver(handler), Application.ActivityLifecycleCallbacks {
3232

3333
private val seenImages = HashSet<Uri>()
34-
private var requestPermissionLauncher: ActivityResultLauncher<String>? = null
34+
// TODO(lkellogg): this is getting too complex - simplify it by, for example, only enabling this
35+
// trigger on a per-activity basis instead of app-wide
36+
private var requestPermissionLaunchers: WeakHashMap<Activity, ActivityResultLauncher<String>?> = WeakHashMap()
3537
private var currentActivity: Activity? = null
3638
private var currentUri: Uri? = null
3739
private var isEnabled = false
@@ -46,7 +48,7 @@ private constructor(private val infoTextResourceId: Int, handler: Handler) :
4648

4749
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
4850
if (activity is ActivityResultCaller) {
49-
requestPermissionLauncher =
51+
requestPermissionLaunchers[activity] =
5052
activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) {
5153
isGranted: Boolean ->
5254
if (!isEnabled) {
@@ -62,10 +64,17 @@ private constructor(private val infoTextResourceId: Int, handler: Handler) :
6264
}
6365
}
6466
} else {
65-
Log.w(TAG, "Not listening for screenshots because this activity can't register for permission request results: $activity")
67+
Log.w(
68+
TAG,
69+
"Not listening for screenshots because this activity can't register for permission request results: $activity"
70+
)
6671
}
6772
}
6873

74+
override fun onActivityDestroyed(activity: Activity) {
75+
requestPermissionLaunchers[activity] = null
76+
}
77+
6978
override fun onActivityResumed(activity: Activity) {
7079
currentActivity = activity
7180
if (isEnabled) {
@@ -85,7 +94,10 @@ private constructor(private val infoTextResourceId: Int, handler: Handler) :
8594
if (isPermissionGranted(activity)) {
8695
maybeStartFeedbackForScreenshot(activity, uri)
8796
} else if (hasRequestedPermission) {
88-
Log.i(TAG, "We've already request permission. Not requesting again for the life of the activity.")
97+
Log.i(
98+
TAG,
99+
"We've already request permission. Not requesting again for the life of the activity."
100+
)
89101
} else {
90102
// Set an in memory flag so we don't ask them again right away
91103
hasRequestedPermission = true
@@ -95,22 +107,27 @@ private constructor(private val infoTextResourceId: Int, handler: Handler) :
95107
}
96108

97109
private fun requestReadPermission(activity: Activity, uri: Uri) {
98-
if (activity.shouldShowRequestPermissionRationale(permissionToRequest)) {
99-
Log.i(TAG, "Showing customer rationale for requesting permission.")
100-
AlertDialog.Builder(activity)
101-
.setMessage(
102-
"Taking a screenshot of the app can initiate feedback to the developer. To enable this feature, allow the app access to device storage."
103-
)
104-
.setPositiveButton("OK") { _, _ ->
105-
Log.i(TAG, "Launching request for permission.")
106-
currentUri = uri
107-
requestPermissionLauncher!!.launch(permissionToRequest)
108-
}
109-
.show()
110+
var launcher = requestPermissionLaunchers[activity]
111+
if (launcher == null) {
112+
Log.i(TAG, "Not requesting permission, because of inability to register for result.")
110113
} else {
111-
Log.i(TAG, "Launching request for permission without rationale.")
112-
currentUri = uri
113-
requestPermissionLauncher!!.launch(permissionToRequest)
114+
if (activity.shouldShowRequestPermissionRationale(permissionToRequest)) {
115+
Log.i(TAG, "Showing customer rationale for requesting permission.")
116+
AlertDialog.Builder(activity)
117+
.setMessage(
118+
"Taking a screenshot of the app can initiate feedback to the developer. To enable this feature, allow the app access to device storage."
119+
)
120+
.setPositiveButton("OK") { _, _ ->
121+
Log.i(TAG, "Launching request for permission.")
122+
currentUri = uri
123+
launcher.launch(permissionToRequest)
124+
}
125+
.show()
126+
} else {
127+
Log.i(TAG, "Launching request for permission without rationale.")
128+
currentUri = uri
129+
launcher.launch(permissionToRequest)
130+
}
114131
}
115132
}
116133

@@ -161,7 +178,6 @@ private constructor(private val infoTextResourceId: Int, handler: Handler) :
161178
}
162179

163180
// Other lifecycle methods
164-
override fun onActivityDestroyed(activity: Activity) {}
165181
override fun onActivityStarted(activity: Activity) {}
166182
override fun onActivityStopped(activity: Activity) {}
167183
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}

0 commit comments

Comments
 (0)