@@ -13,8 +13,8 @@ import android.os.Build
13
13
import android.os.Bundle
14
14
import android.util.Log
15
15
import androidx.activity.result.ActivityResultCaller
16
- import androidx.activity.result.ActivityResultLauncher
17
16
import androidx.activity.result.contract.ActivityResultContracts
17
+ import androidx.annotation.RequiresApi
18
18
import androidx.core.app.NotificationCompat
19
19
import androidx.core.app.NotificationManagerCompat
20
20
import androidx.core.content.ContextCompat
@@ -33,8 +33,16 @@ object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
33
33
34
34
private var isEnabled = false
35
35
private var hasRequestedPermission = false
36
- private var currentActivity: Activity ? = null
37
-
36
+ private var currentActivity: Activity ? = null // Activity to be used for screenshot
37
+
38
+ /* *
39
+ * Initialize the notification trigger for this application.
40
+ *
41
+ * This should be called during [Application.onCreate].
42
+ * [enable] should then be called when you want to actually show the notification.
43
+ *
44
+ * @param application the [Application] object
45
+ */
38
46
fun initialize (application : Application ) {
39
47
// Create the NotificationChannel, but only on API 26+ because
40
48
// the NotificationChannel class is new and not in the support library
@@ -43,7 +51,7 @@ object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
43
51
NotificationChannel (
44
52
FEEBACK_NOTIFICATION_CHANNEL_ID ,
45
53
application.getString(R .string.feedback_notification_channel_name),
46
- NotificationManager .IMPORTANCE_LOW
54
+ NotificationManager .IMPORTANCE_HIGH
47
55
)
48
56
channel.description =
49
57
application.getString(R .string.feedback_notification_channel_description)
@@ -54,15 +62,27 @@ object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
54
62
application.registerActivityLifecycleCallbacks(this )
55
63
}
56
64
57
- fun <T > registerPermissionLauncher (activity : T ): ActivityResultLauncher <String >? where
58
- T : Activity ,
59
- T : ActivityResultCaller {
65
+ /* *
66
+ * Requests permission to show notifications for this application.
67
+ *
68
+ * This must be called during [Activity.onCreate].
69
+ * [enable] should then be called when you want to actually show the notification.
70
+ *
71
+ * @param activity the [Activity] object
72
+ */
73
+ @RequiresApi(Build .VERSION_CODES .TIRAMISU )
74
+ fun <T > requestPermission (activity : T ) where T : Activity, T : ActivityResultCaller {
75
+ if (ContextCompat .checkSelfPermission(activity, POST_NOTIFICATIONS ) == PERMISSION_GRANTED ) {
76
+ Log .i(TAG , " Already has permission." )
77
+ return
78
+ }
79
+
60
80
if (hasRequestedPermission) {
61
81
Log .i(TAG , " Already request permission; Not trying again." )
62
- return null
82
+ return
63
83
}
64
84
65
- return activity.registerForActivityResult(ActivityResultContracts .RequestPermission ()) {
85
+ val launcher = activity.registerForActivityResult(ActivityResultContracts .RequestPermission ()) {
66
86
isGranted: Boolean ->
67
87
if (! isEnabled) {
68
88
Log .w(TAG , " Trigger disabled after permission check. Abandoning notification." )
@@ -76,81 +96,71 @@ object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
76
96
// message after each time we try to post a notification.
77
97
}
78
98
}
99
+
100
+ if (activity.shouldShowRequestPermissionRationale(POST_NOTIFICATIONS )) {
101
+ Log .i(TAG , " Showing customer rationale for requesting permission." )
102
+ AlertDialog .Builder (activity)
103
+ .setMessage(
104
+ " Using a notification to initiate feedback to the developer. " +
105
+ " To enable this feature, allow the app to post notifications."
106
+ )
107
+ .setPositiveButton(" OK" ) { _, _ ->
108
+ Log .i(TAG , " Launching request for permission." )
109
+ launcher.launch(POST_NOTIFICATIONS )
110
+ }
111
+ .show()
112
+ } else {
113
+ Log .i(TAG , " Launching request for permission without rationale." )
114
+ launcher.launch(POST_NOTIFICATIONS )
115
+ }
116
+ hasRequestedPermission = true
79
117
}
80
118
81
- fun enable (activity : Activity ? = null, launcher : ActivityResultLauncher <String >? = null) {
119
+ /* *
120
+ * Show notifications.
121
+ *
122
+ * This could be called during [Activity.onCreate].
123
+ *
124
+ * @param activity the [Activity] object
125
+ */
126
+ fun enable (activity : Activity ) {
82
127
currentActivity = activity
83
128
isEnabled = true
84
- if (activity != null ) {
85
- if (hasPermission(activity)) {
86
- showNotification(activity)
87
- } else {
88
- if (launcher != null ) {
89
- requestPermission(activity, launcher)
90
- } else {
91
- Log .i(TAG , " Not requesting permission, because of no launcher was provided." )
92
- }
93
- }
94
- }
129
+ showNotification(activity)
95
130
}
96
131
132
+ /* * Hide notifications. */
97
133
fun disable () {
98
- isEnabled = false
99
134
val activity = currentActivity
100
- currentActivity = null
101
135
if (activity != null ) {
102
136
cancelNotification(activity)
103
137
}
138
+ isEnabled = false
139
+ currentActivity = null
104
140
}
105
141
106
- private fun showNotification (activity : Activity ) {
107
- val intent = Intent (activity , TakeScreenshotAndTriggerFeedbackActivity ::class .java)
142
+ private fun showNotification (context : Context ) {
143
+ val intent = Intent (context , TakeScreenshotAndTriggerFeedbackActivity ::class .java)
108
144
intent.addFlags(Intent .FLAG_ACTIVITY_NO_HISTORY )
109
145
val pendingIntent =
110
146
PendingIntent .getActivity(
111
- activity ,
147
+ context ,
112
148
/* requestCode = */ 0 ,
113
149
intent,
114
150
PendingIntent .FLAG_IMMUTABLE
115
151
)
116
152
val builder =
117
- NotificationCompat .Builder (activity , FEEBACK_NOTIFICATION_CHANNEL_ID )
153
+ NotificationCompat .Builder (context , FEEBACK_NOTIFICATION_CHANNEL_ID )
118
154
.setSmallIcon(R .mipmap.ic_launcher)
119
- .setContentTitle(activity .getText(R .string.feedback_notification_title))
120
- .setContentText(activity .getText(R .string.feedback_notification_text))
121
- .setPriority(NotificationCompat .PRIORITY_LOW )
155
+ .setContentTitle(context .getText(R .string.feedback_notification_title))
156
+ .setContentText(context .getText(R .string.feedback_notification_text))
157
+ .setPriority(NotificationCompat .PRIORITY_HIGH )
122
158
.setContentIntent(pendingIntent)
123
- val notificationManager = NotificationManagerCompat .from(activity )
159
+ val notificationManager = NotificationManagerCompat .from(context )
124
160
Log .i(TAG , " Showing notification" )
125
161
notificationManager.notify(FEEDBACK_NOTIFICATION_ID , builder.build())
126
162
}
127
163
128
- private fun hasPermission (activity : Activity ) =
129
- ContextCompat .checkSelfPermission(activity, POST_NOTIFICATIONS ) == PERMISSION_GRANTED
130
-
131
- private fun requestPermission (activity : Activity , launcher : ActivityResultLauncher <String >) {
132
- if (hasRequestedPermission) {
133
- Log .i(TAG , " Already request permission; Not trying again." )
134
- return
135
- }
136
- if (activity.shouldShowRequestPermissionRationale(POST_NOTIFICATIONS )) {
137
- Log .i(TAG , " Showing customer rationale for requesting permission." )
138
- AlertDialog .Builder (activity)
139
- .setMessage(
140
- " Using a notification to initiate feedback to the developer. " +
141
- " To enable this feature, allow the app to post notifications."
142
- )
143
- .setPositiveButton(" OK" ) { _, _ ->
144
- Log .i(TAG , " Launching request for permission." )
145
- launcher.launch(POST_NOTIFICATIONS )
146
- }
147
- .show()
148
- } else {
149
- Log .i(TAG , " Launching request for permission without rationale." )
150
- launcher.launch(POST_NOTIFICATIONS )
151
- }
152
- hasRequestedPermission = true
153
- }
154
164
155
165
private fun cancelNotification (context : Context ) {
156
166
val notificationManager = NotificationManagerCompat .from(context)
@@ -163,14 +173,10 @@ object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
163
173
if (activity !is TakeScreenshotAndTriggerFeedbackActivity ) {
164
174
Log .d(TAG , " setting current activity" )
165
175
currentActivity = activity
166
- showNotification(activity)
167
176
}
168
177
}
169
178
}
170
179
171
- override fun onActivityPaused (activity : Activity ) {
172
- cancelNotification(activity)
173
- }
174
180
175
181
override fun onActivityDestroyed (activity : Activity ) {
176
182
if (activity == currentActivity) {
@@ -182,28 +188,28 @@ object NotificationFeedbackTrigger : Application.ActivityLifecycleCallbacks {
182
188
// Other lifecycle methods
183
189
override fun onActivityCreated (activity : Activity , savedInstanceState : Bundle ? ) {}
184
190
override fun onActivityStarted (activity : Activity ) {}
191
+ override fun onActivityPaused (activity : Activity ) {}
185
192
override fun onActivityStopped (activity : Activity ) {}
186
193
override fun onActivitySaveInstanceState (activity : Activity , outState : Bundle ) {}
187
194
188
195
fun takeScreenshot () {
189
196
val activity = currentActivity
190
- if (activity != null ) {
191
- val view = activity.window.decorView.rootView
192
- val bitmap = Bitmap .createBitmap(view.width, view.height, Bitmap .Config .RGB_565 )
193
- val canvas = Canvas (bitmap)
194
- view.draw(canvas)
195
- try {
196
- activity.openFileOutput(SCREENSHOT_FILE_NAME , Context .MODE_PRIVATE ).use { outputStream ->
197
- bitmap.compress(Bitmap .CompressFormat .PNG , /* quality = */ 100 , outputStream)
198
- }
199
- Log .i(TAG , " Wrote screenshot to $SCREENSHOT_FILE_NAME " )
200
- } catch (e: IOException ) {
201
- Log .e(TAG , " Can't write $SCREENSHOT_FILE_NAME " , e)
202
- }
203
- } else {
197
+ if (activity == null ) {
204
198
Log .e(TAG , " Can't take screenshot because current activity is unknown" )
205
199
return
206
200
}
201
+ val view = activity.window.decorView.rootView
202
+ val bitmap = Bitmap .createBitmap(view.width, view.height, Bitmap .Config .RGB_565 )
203
+ val canvas = Canvas (bitmap)
204
+ view.draw(canvas)
205
+ try {
206
+ activity.openFileOutput(SCREENSHOT_FILE_NAME , Context .MODE_PRIVATE ).use { outputStream ->
207
+ bitmap.compress(Bitmap .CompressFormat .PNG , /* quality = */ 100 , outputStream)
208
+ }
209
+ Log .i(TAG , " Wrote screenshot to $SCREENSHOT_FILE_NAME " )
210
+ } catch (e: IOException ) {
211
+ Log .e(TAG , " Can't write $SCREENSHOT_FILE_NAME " , e)
212
+ }
207
213
}
208
214
}
209
215
0 commit comments