Skip to content

Commit 6ccfe1e

Browse files
authored
Change notification importance/priority for notification trigger (#4169)
1 parent cde0818 commit 6ccfe1e

File tree

1 file changed

+181
-178
lines changed

1 file changed

+181
-178
lines changed

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

Lines changed: 181 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -27,197 +27,200 @@ import java.io.IOException
2727

2828
@SuppressLint("StaticFieldLeak") // Reference to Activity is set to null in onActivityDestroyed
2929
object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
30-
private const val TAG: String = "NotificationFeedbackTrigger"
31-
private const val FEEBACK_NOTIFICATION_CHANNEL_ID = "InAppFeedbackNotification"
32-
private const val FEEDBACK_NOTIFICATION_ID = 1
33-
const val SCREENSHOT_FILE_NAME =
34-
"com.googletest.firebase.appdistribution.testapp.screenshot.png"
35-
36-
private var isEnabled = false
37-
private var hasRequestedPermission = false
38-
private var currentActivity: Activity? = null
39-
private var requestPermissionLauncher: ActivityResultLauncher<String>? = null
40-
41-
fun initialize(application: Application) {
42-
// Create the NotificationChannel, but only on API 26+ because
43-
// the NotificationChannel class is new and not in the support library
44-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
45-
val channel = NotificationChannel(
46-
FEEBACK_NOTIFICATION_CHANNEL_ID,
47-
application.getString(R.string.feedback_notification_channel_name),
48-
NotificationManager.IMPORTANCE_HIGH
49-
)
50-
channel.description =
51-
application.getString(R.string.feedback_notification_channel_description)
52-
application.getSystemService(NotificationManager::class.java)
53-
.createNotificationChannel(channel)
54-
}
55-
application.registerActivityLifecycleCallbacks(this)
30+
private const val TAG: String = "NotificationFeedbackTrigger"
31+
private const val FEEBACK_NOTIFICATION_CHANNEL_ID = "InAppFeedbackNotification"
32+
private const val FEEDBACK_NOTIFICATION_ID = 1
33+
const val SCREENSHOT_FILE_NAME = "com.googletest.firebase.appdistribution.testapp.screenshot.png"
34+
35+
private var isEnabled = false
36+
private var hasRequestedPermission = false
37+
private var currentActivity: Activity? = null
38+
private var requestPermissionLauncher: ActivityResultLauncher<String>? = null
39+
40+
fun initialize(application: Application) {
41+
// Create the NotificationChannel, but only on API 26+ because
42+
// the NotificationChannel class is new and not in the support library
43+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
44+
val channel =
45+
NotificationChannel(
46+
FEEBACK_NOTIFICATION_CHANNEL_ID,
47+
application.getString(R.string.feedback_notification_channel_name),
48+
NotificationManager.IMPORTANCE_LOW
49+
)
50+
channel.description =
51+
application.getString(R.string.feedback_notification_channel_description)
52+
application
53+
.getSystemService(NotificationManager::class.java)
54+
.createNotificationChannel(channel)
5655
}
57-
58-
fun enable(currentActivity: Activity? = null) {
59-
this.currentActivity = currentActivity
60-
isEnabled = true
61-
if (currentActivity != null) {
62-
showNotification(currentActivity)
63-
}
56+
application.registerActivityLifecycleCallbacks(this)
57+
}
58+
59+
fun enable(currentActivity: Activity? = null) {
60+
this.currentActivity = currentActivity
61+
isEnabled = true
62+
if (currentActivity != null) {
63+
showNotification(currentActivity)
6464
}
65-
66-
fun disable() {
67-
isEnabled = false
68-
val activity = currentActivity
69-
currentActivity = null
70-
if (activity != null) {
71-
cancelNotification(activity)
72-
}
65+
}
66+
67+
fun disable() {
68+
isEnabled = false
69+
val activity = currentActivity
70+
currentActivity = null
71+
if (activity != null) {
72+
cancelNotification(activity)
7373
}
74-
75-
private fun showNotification(activity: Activity) {
76-
if (ContextCompat.checkSelfPermission(activity, POST_NOTIFICATIONS) == PERMISSION_GRANTED) {
77-
val intent = Intent(activity, TakeScreenshotAndTriggerFeedbackActivity::class.java)
78-
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
79-
val pendingIntent = PendingIntent.getActivity(
80-
activity, /* requestCode= */ 0, intent, PendingIntent.FLAG_IMMUTABLE
81-
)
82-
val builder = NotificationCompat.Builder(activity, FEEBACK_NOTIFICATION_CHANNEL_ID)
83-
.setSmallIcon(R.mipmap.ic_launcher)
84-
.setContentTitle(activity.getText(R.string.feedback_notification_title))
85-
.setContentText(activity.getText(R.string.feedback_notification_text))
86-
.setPriority(NotificationCompat.PRIORITY_HIGH)
87-
.setContentIntent(pendingIntent)
88-
val notificationManager = NotificationManagerCompat.from(activity)
89-
Log.i(TAG, "Showing notification")
90-
notificationManager.notify(FEEDBACK_NOTIFICATION_ID, builder.build())
91-
} else {
92-
// no permission to post notifications - try to get it
93-
if (hasRequestedPermission) {
94-
Log.i(
95-
TAG,
96-
"We've already request permission. Not requesting again for the life of the activity."
97-
)
98-
} else {
99-
requestPermission(activity)
100-
}
101-
}
74+
}
75+
76+
private fun showNotification(activity: Activity) {
77+
if (ContextCompat.checkSelfPermission(activity, POST_NOTIFICATIONS) == PERMISSION_GRANTED) {
78+
val intent = Intent(activity, TakeScreenshotAndTriggerFeedbackActivity::class.java)
79+
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
80+
val pendingIntent =
81+
PendingIntent.getActivity(
82+
activity,
83+
/* requestCode= */ 0,
84+
intent,
85+
PendingIntent.FLAG_IMMUTABLE
86+
)
87+
val builder =
88+
NotificationCompat.Builder(activity, FEEBACK_NOTIFICATION_CHANNEL_ID)
89+
.setSmallIcon(R.mipmap.ic_launcher)
90+
.setContentTitle(activity.getText(R.string.feedback_notification_title))
91+
.setContentText(activity.getText(R.string.feedback_notification_text))
92+
.setPriority(NotificationCompat.PRIORITY_LOW)
93+
.setContentIntent(pendingIntent)
94+
val notificationManager = NotificationManagerCompat.from(activity)
95+
Log.i(TAG, "Showing notification")
96+
notificationManager.notify(FEEDBACK_NOTIFICATION_ID, builder.build())
97+
} else {
98+
// no permission to post notifications - try to get it
99+
if (hasRequestedPermission) {
100+
Log.i(
101+
TAG,
102+
"We've already request permission. Not requesting again for the life of the activity."
103+
)
104+
} else {
105+
requestPermission(activity)
106+
}
102107
}
103-
104-
private fun requestPermission(activity: Activity) {
105-
var launcher = requestPermissionLauncher
106-
if (launcher == null) {
107-
Log.i(TAG, "Not requesting permission, because of inability to register for result.")
108-
} else {
109-
if (activity.shouldShowRequestPermissionRationale(POST_NOTIFICATIONS)) {
110-
Log.i(TAG, "Showing customer rationale for requesting permission.")
111-
AlertDialog.Builder(activity)
112-
.setMessage(
113-
"Using a notification to initiate feedback to the developer. "
114-
+ "To enable this feature, allow the app to post notifications."
115-
)
116-
.setPositiveButton("OK") { _, _ ->
117-
Log.i(TAG, "Launching request for permission.")
118-
launcher.launch(POST_NOTIFICATIONS)
119-
}
120-
.show()
121-
} else {
122-
Log.i(TAG, "Launching request for permission without rationale.")
123-
launcher.launch(POST_NOTIFICATIONS)
124-
}
125-
hasRequestedPermission = true
126-
}
127-
}
128-
129-
private fun cancelNotification(context: Context) {
130-
val notificationManager = NotificationManagerCompat.from(context)
131-
Log.i(TAG, "Cancelling notification")
132-
notificationManager.cancel(FEEDBACK_NOTIFICATION_ID)
108+
}
109+
110+
private fun requestPermission(activity: Activity) {
111+
var launcher = requestPermissionLauncher
112+
if (launcher == null) {
113+
Log.i(TAG, "Not requesting permission, because of inability to register for result.")
114+
} else {
115+
if (activity.shouldShowRequestPermissionRationale(POST_NOTIFICATIONS)) {
116+
Log.i(TAG, "Showing customer rationale for requesting permission.")
117+
AlertDialog.Builder(activity)
118+
.setMessage(
119+
"Using a notification to initiate feedback to the developer. " +
120+
"To enable this feature, allow the app to post notifications."
121+
)
122+
.setPositiveButton("OK") { _, _ ->
123+
Log.i(TAG, "Launching request for permission.")
124+
launcher.launch(POST_NOTIFICATIONS)
125+
}
126+
.show()
127+
} else {
128+
Log.i(TAG, "Launching request for permission without rationale.")
129+
launcher.launch(POST_NOTIFICATIONS)
130+
}
131+
hasRequestedPermission = true
133132
}
134-
135-
override fun onActivityResumed(activity: Activity) {
136-
if (isEnabled) {
137-
if (activity !is TakeScreenshotAndTriggerFeedbackActivity) {
138-
Log.d(TAG, "setting current activity")
139-
currentActivity = activity
140-
}
141-
showNotification(activity)
142-
}
133+
}
134+
135+
private fun cancelNotification(context: Context) {
136+
val notificationManager = NotificationManagerCompat.from(context)
137+
Log.i(TAG, "Cancelling notification")
138+
notificationManager.cancel(FEEDBACK_NOTIFICATION_ID)
139+
}
140+
141+
override fun onActivityResumed(activity: Activity) {
142+
if (isEnabled) {
143+
if (activity !is TakeScreenshotAndTriggerFeedbackActivity) {
144+
Log.d(TAG, "setting current activity")
145+
currentActivity = activity
146+
showNotification(activity)
147+
}
143148
}
149+
}
144150

145-
override fun onActivityPaused(activity: Activity) {
146-
requestPermissionLauncher = null
147-
cancelNotification(activity)
148-
}
151+
override fun onActivityPaused(activity: Activity) {
152+
cancelNotification(activity)
153+
}
149154

150-
override fun onActivityDestroyed(activity: Activity) {
151-
Log.d(TAG, "clearing current activity")
152-
currentActivity = null
155+
override fun onActivityDestroyed(activity: Activity) {
156+
if (activity == currentActivity) {
157+
Log.d(TAG, "clearing current activity")
158+
requestPermissionLauncher = null
159+
currentActivity = null
153160
}
154-
155-
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
156-
if (activity is ActivityResultCaller && !hasRequestedPermission) {
157-
requestPermissionLauncher =
158-
activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
159-
if (!isEnabled) {
160-
Log.w(
161-
TAG,
162-
"Trigger disabled after permission check. Abandoning notification."
163-
)
164-
} else if (isGranted) {
165-
showNotification(activity)
166-
} else {
167-
Log.i(TAG, "Permission not granted")
168-
// TODO: Ideally we would show a message indicating the impact of not
169-
// enabling the permission, but there's no way to know if they've
170-
// permanently denied the permission, and we don't want to show them a
171-
// message after each time we try to post a notification.
172-
}
173-
}
174-
} else {
175-
Log.w(
176-
TAG,
177-
"Not showing notification because this activity can't register for permission request results: $activity"
178-
)
161+
}
162+
163+
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
164+
if (activity is ActivityResultCaller && !hasRequestedPermission) {
165+
requestPermissionLauncher =
166+
activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) {
167+
isGranted: Boolean ->
168+
if (!isEnabled) {
169+
Log.w(TAG, "Trigger disabled after permission check. Abandoning notification.")
170+
} else if (isGranted) {
171+
showNotification(activity)
172+
} else {
173+
Log.i(TAG, "Permission not granted")
174+
// TODO: Ideally we would show a message indicating the impact of not
175+
// enabling the permission, but there's no way to know if they've
176+
// permanently denied the permission, and we don't want to show them a
177+
// message after each time we try to post a notification.
178+
}
179179
}
180+
} else {
181+
Log.w(
182+
TAG,
183+
"Not showing notification because this activity can't register for permission request results: $activity"
184+
)
180185
}
181-
182-
// Other lifecycle methods
183-
override fun onActivityStarted(activity: Activity) {}
184-
override fun onActivityStopped(activity: Activity) {}
185-
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
186-
187-
fun takeScreenshot() {
188-
val activity = currentActivity
189-
if (activity != null) {
190-
val view = activity.window.decorView.rootView
191-
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.RGB_565)
192-
val canvas = Canvas(bitmap)
193-
view.draw(canvas)
194-
try {
195-
activity.openFileOutput(SCREENSHOT_FILE_NAME, Context.MODE_PRIVATE)
196-
.use { outputStream ->
197-
bitmap.compress(
198-
Bitmap.CompressFormat.PNG, /* quality = */ 100, outputStream
199-
)
200-
}
201-
Log.i(TAG, "Wrote screenshot to $SCREENSHOT_FILE_NAME")
202-
} catch (e: IOException) {
203-
Log.e(TAG, "Can't write $SCREENSHOT_FILE_NAME", e)
204-
}
205-
} else {
206-
Log.e(TAG, "Can't take screenshot because current activity is unknown")
207-
return
186+
}
187+
188+
// Other lifecycle methods
189+
override fun onActivityStarted(activity: Activity) {}
190+
override fun onActivityStopped(activity: Activity) {}
191+
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
192+
193+
fun takeScreenshot() {
194+
val activity = currentActivity
195+
if (activity != null) {
196+
val view = activity.window.decorView.rootView
197+
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.RGB_565)
198+
val canvas = Canvas(bitmap)
199+
view.draw(canvas)
200+
try {
201+
activity.openFileOutput(SCREENSHOT_FILE_NAME, Context.MODE_PRIVATE).use { outputStream ->
202+
bitmap.compress(Bitmap.CompressFormat.PNG, /* quality = */ 100, outputStream)
208203
}
204+
Log.i(TAG, "Wrote screenshot to $SCREENSHOT_FILE_NAME")
205+
} catch (e: IOException) {
206+
Log.e(TAG, "Can't write $SCREENSHOT_FILE_NAME", e)
207+
}
208+
} else {
209+
Log.e(TAG, "Can't take screenshot because current activity is unknown")
210+
return
209211
}
212+
}
210213
}
211214

212215
class TakeScreenshotAndTriggerFeedbackActivity : AppCompatActivity() {
213-
override fun onCreate(savedInstanceState: Bundle?) {
214-
super.onCreate(savedInstanceState)
215-
takeScreenshot()
216-
}
217-
218-
override fun onResume() {
219-
super.onResume()
220-
val screenshotUri = Uri.fromFile(getFileStreamPath(SCREENSHOT_FILE_NAME))
221-
Firebase.appDistribution.startFeedback(R.string.terms_and_conditions, screenshotUri)
222-
}
223-
}
216+
override fun onCreate(savedInstanceState: Bundle?) {
217+
super.onCreate(savedInstanceState)
218+
takeScreenshot()
219+
}
220+
221+
override fun onResume() {
222+
super.onResume()
223+
val screenshotUri = Uri.fromFile(getFileStreamPath(SCREENSHOT_FILE_NAME))
224+
Firebase.appDistribution.startFeedback(R.string.terms_and_conditions, screenshotUri)
225+
}
226+
}

0 commit comments

Comments
 (0)