Skip to content

Commit 644483e

Browse files
authored
Notification trigger API (#4183)
* Add notification default trigger to in-app feedback * Address Kai's feedback * Update api.txt * Address some feedback * Add InterruptionLevel to notification trigger API (#4205) * Add InterruptionLevel to API * Move interruption and priority levels to InterruptionLevel enum * Mark interruptionLevel as @nonnull * Add @StringRes annotation
1 parent 9df3b20 commit 644483e

23 files changed

+727
-129
lines changed

firebase-appdistribution-api/api.txt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ package com.google.firebase.appdistribution {
1414
}
1515

1616
public interface FirebaseAppDistribution {
17+
method public void cancelFeedbackNotification();
1718
method @NonNull public com.google.android.gms.tasks.Task<com.google.firebase.appdistribution.AppDistributionRelease> checkForNewRelease();
1819
method @NonNull public static com.google.firebase.appdistribution.FirebaseAppDistribution getInstance();
1920
method public boolean isTesterSignedIn();
21+
method public void showFeedbackNotification(@StringRes int, @NonNull com.google.firebase.appdistribution.InterruptionLevel);
22+
method public void showFeedbackNotification(@NonNull CharSequence, @NonNull com.google.firebase.appdistribution.InterruptionLevel);
2023
method @NonNull public com.google.android.gms.tasks.Task<java.lang.Void> signInTester();
2124
method public void signOutTester();
22-
method public void startFeedback(int);
25+
method public void startFeedback(@StringRes int);
2326
method public void startFeedback(@NonNull CharSequence);
24-
method public void startFeedback(@NonNull int, @Nullable android.net.Uri);
27+
method public void startFeedback(@StringRes int, @Nullable android.net.Uri);
2528
method public void startFeedback(@NonNull CharSequence, @Nullable android.net.Uri);
2629
method @NonNull public com.google.firebase.appdistribution.UpdateTask updateApp();
2730
method @NonNull public com.google.firebase.appdistribution.UpdateTask updateIfNewReleaseAvailable();
@@ -46,6 +49,14 @@ package com.google.firebase.appdistribution {
4649
enum_constant public static final com.google.firebase.appdistribution.FirebaseAppDistributionException.Status UPDATE_NOT_AVAILABLE;
4750
}
4851

52+
public enum InterruptionLevel {
53+
enum_constant public static final com.google.firebase.appdistribution.InterruptionLevel DEFAULT;
54+
enum_constant public static final com.google.firebase.appdistribution.InterruptionLevel HIGH;
55+
enum_constant public static final com.google.firebase.appdistribution.InterruptionLevel LOW;
56+
enum_constant public static final com.google.firebase.appdistribution.InterruptionLevel MAX;
57+
enum_constant public static final com.google.firebase.appdistribution.InterruptionLevel MIN;
58+
}
59+
4960
public interface OnProgressListener {
5061
method public void onProgressUpdate(@NonNull com.google.firebase.appdistribution.UpdateProgress);
5162
}

firebase-appdistribution-api/src/main/java/com/google/firebase/appdistribution/FirebaseAppDistribution.java

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414

1515
package com.google.firebase.appdistribution;
1616

17+
import android.app.Activity;
1718
import android.net.Uri;
1819
import androidx.annotation.NonNull;
1920
import androidx.annotation.Nullable;
21+
import androidx.annotation.StringRes;
2022
import com.google.android.gms.tasks.Task;
2123
import com.google.firebase.FirebaseApp;
2224
import com.google.firebase.appdistribution.internal.FirebaseAppDistributionProxy;
@@ -34,6 +36,7 @@
3436
* <p>Call {@link #getInstance()} to get the singleton instance of {@link FirebaseAppDistribution}.
3537
*/
3638
public interface FirebaseAppDistribution {
39+
3740
/**
3841
* Updates the app to the newest release, if one is available.
3942
*
@@ -125,7 +128,7 @@ public interface FirebaseAppDistribution {
125128
* @param infoTextResourceId string resource ID of text to display to the tester before collecting
126129
* feedback data (e.g. Terms and Conditions)
127130
*/
128-
void startFeedback(int infoTextResourceId);
131+
void startFeedback(@StringRes int infoTextResourceId);
129132

130133
/**
131134
* Takes a screenshot, and starts an activity to collect and submit feedback from the tester.
@@ -159,7 +162,7 @@ public interface FirebaseAppDistribution {
159162
* @param screenshot URI to a bitmap containing a screenshot that will be included with the
160163
* report, or null to not include a screenshot
161164
*/
162-
void startFeedback(@NonNull int infoTextResourceId, @Nullable Uri screenshot);
165+
void startFeedback(@StringRes int infoTextResourceId, @Nullable Uri screenshot);
163166

164167
/**
165168
* Starts an activity to collect and submit feedback from the tester, along with the given
@@ -179,6 +182,70 @@ public interface FirebaseAppDistribution {
179182
*/
180183
void startFeedback(@NonNull CharSequence infoText, @Nullable Uri screenshot);
181184

185+
/**
186+
* Displays a notification that, when tapped, will take a screenshot of the current activity, then
187+
* start a new activity to collect and submit feedback from the tester along with the screenshot.
188+
*
189+
* <p>On Android 13 and above, this method requires the <a
190+
* href="https://developer.android.com/develop/ui/views/notifications/notification-permission">runtime
191+
* permission for sending notifications</a>: {@code POST_NOTIFICATIONS}. If your app targets
192+
* Android 13 (API level 33) or above, you should <a
193+
* href="https://developer.android.com/training/permissions/requesting">request the
194+
* permission</a>.
195+
*
196+
* <p>When the notification is tapped:
197+
*
198+
* <ol>
199+
* <li>If the app is open, take a screenshot of the current activity
200+
* <li>If tester is not signed in, presents the tester with a Google Sign-in UI
201+
* <li>Starts a full screen activity for the tester to compose and submit the feedback
202+
* </ol>
203+
*
204+
* @param infoTextResourceId string resource ID of text to display to the tester before collecting
205+
* feedback data (e.g. Terms and Conditions)
206+
* @param interruptionLevel the level of interruption for the feedback notification. On platforms
207+
* below Android 8, this corresponds to a notification channel importance and once set cannot
208+
* be changed except by the user.
209+
*/
210+
void showFeedbackNotification(
211+
@StringRes int infoTextResourceId, @NonNull InterruptionLevel interruptionLevel);
212+
213+
/**
214+
* Displays a notification that, when tapped, will take a screenshot of the current activity, then
215+
* start a new activity to collect and submit feedback from the tester along with the screenshot.
216+
*
217+
* <p>On Android 13 and above, this method requires the <a
218+
* href="https://developer.android.com/develop/ui/views/notifications/notification-permission">runtime
219+
* permission for sending notifications</a>: {@code POST_NOTIFICATIONS}. If your app targets
220+
* Android 13 (API level 33) or above, you should <a
221+
* href="https://developer.android.com/training/permissions/requesting">request the
222+
* permission</a>.
223+
*
224+
* <p>When the notification is tapped:
225+
*
226+
* <ol>
227+
* <li>If the app is open, take a screenshot of the current activity
228+
* <li>If tester is not signed in, presents the tester with a Google Sign-in UI
229+
* <li>Starts a full screen activity for the tester to compose and submit the feedback
230+
* </ol>
231+
*
232+
* @param infoText text to display to the tester before collecting feedback data (e.g. Terms and
233+
* Conditions)
234+
* @param interruptionLevel the level of interruption for the feedback notification. On platforms
235+
* below Android 8, this corresponds to a notification channel importance and once set cannot
236+
* be changed except by the user.
237+
*/
238+
void showFeedbackNotification(
239+
@NonNull CharSequence infoText, @NonNull InterruptionLevel interruptionLevel);
240+
241+
/**
242+
* Hides the notification shown with {@link #showFeedbackNotification}.
243+
*
244+
* <p>This should be called in the {@link Activity#onDestroy} of the activity that showed the
245+
* notification.
246+
*/
247+
void cancelFeedbackNotification();
248+
182249
/** Gets the singleton {@link FirebaseAppDistribution} instance. */
183250
@NonNull
184251
static FirebaseAppDistribution getInstance() {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.appdistribution;
16+
17+
import android.app.NotificationManager;
18+
import androidx.core.app.NotificationCompat;
19+
20+
/** An enum specifying the level of interruption of a notification when it is created. */
21+
public enum InterruptionLevel {
22+
23+
/**
24+
* Minimum interruption level.
25+
*
26+
* <p>Translates to {@link NotificationManager#IMPORTANCE_MIN} on Android O+ and {@link
27+
* NotificationCompat#PRIORITY_MIN} on older platforms.
28+
*/
29+
MIN(NotificationManager.IMPORTANCE_MIN, NotificationCompat.PRIORITY_MIN),
30+
31+
/**
32+
* Low interruption level.
33+
*
34+
* <p>Translates to {@link NotificationManager#IMPORTANCE_LOW} on Android O+ and {@link
35+
* NotificationCompat#PRIORITY_LOW} on older platforms.
36+
*/
37+
LOW(NotificationManager.IMPORTANCE_LOW, NotificationCompat.PRIORITY_LOW),
38+
39+
/**
40+
* Default interruption level.
41+
*
42+
* <p>Translates to {@link NotificationManager#IMPORTANCE_DEFAULT} on Android O+ and {@link
43+
* NotificationCompat#PRIORITY_DEFAULT} on older platforms.
44+
*/
45+
DEFAULT(NotificationManager.IMPORTANCE_DEFAULT, NotificationCompat.PRIORITY_DEFAULT),
46+
47+
/**
48+
* High interruption level.
49+
*
50+
* <p>Translates to {@link NotificationManager#IMPORTANCE_HIGH} on Android O+ and {@link
51+
* NotificationCompat#PRIORITY_HIGH} on older platforms.
52+
*/
53+
HIGH(NotificationManager.IMPORTANCE_HIGH, NotificationCompat.PRIORITY_HIGH),
54+
55+
/**
56+
* Maximum interruption level.
57+
*
58+
* <p>Translates to {@link NotificationManager#IMPORTANCE_HIGH} on Android O+ and {@link
59+
* NotificationCompat#PRIORITY_MAX} on older platforms.
60+
*/
61+
MAX(NotificationManager.IMPORTANCE_HIGH, NotificationCompat.PRIORITY_MAX);
62+
63+
/**
64+
* The notification channel importance corresponding to this interruption level on Android O+.
65+
*
66+
* @hide
67+
*/
68+
public final int channelImportance;
69+
70+
/**
71+
* The notification priority corresponding to this interruption level on older platforms.
72+
*
73+
* @hide
74+
*/
75+
public final int notificationPriority;
76+
77+
InterruptionLevel(int channelImportance, int notificationPriority) {
78+
this.channelImportance = channelImportance;
79+
this.notificationPriority = notificationPriority;
80+
}
81+
}

firebase-appdistribution-api/src/main/java/com/google/firebase/appdistribution/internal/FirebaseAppDistributionProxy.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
import android.net.Uri;
1818
import androidx.annotation.NonNull;
1919
import androidx.annotation.Nullable;
20+
import androidx.annotation.StringRes;
2021
import com.google.android.gms.tasks.Task;
2122
import com.google.firebase.appdistribution.AppDistributionRelease;
2223
import com.google.firebase.appdistribution.FirebaseAppDistribution;
2324
import com.google.firebase.appdistribution.FirebaseAppDistributionException;
25+
import com.google.firebase.appdistribution.InterruptionLevel;
2426
import com.google.firebase.appdistribution.UpdateTask;
2527
import com.google.firebase.inject.Provider;
2628

@@ -75,7 +77,7 @@ public UpdateTask updateApp() {
7577
}
7678

7779
@Override
78-
public void startFeedback(int infoTextResourceId) {
80+
public void startFeedback(@StringRes int infoTextResourceId) {
7981
delegate.startFeedback(infoTextResourceId);
8082
}
8183

@@ -85,12 +87,29 @@ public void startFeedback(@NonNull CharSequence infoText) {
8587
}
8688

8789
@Override
88-
public void startFeedback(@NonNull int infoTextResourceId, @Nullable Uri screenshotUri) {
90+
public void startFeedback(@StringRes int infoTextResourceId, @Nullable Uri screenshotUri) {
8991
delegate.startFeedback(infoTextResourceId, screenshotUri);
9092
}
9193

9294
@Override
9395
public void startFeedback(@NonNull CharSequence infoText, @Nullable Uri screenshotUri) {
9496
delegate.startFeedback(infoText, screenshotUri);
9597
}
98+
99+
@Override
100+
public void showFeedbackNotification(
101+
@StringRes int infoTextResourceId, @NonNull InterruptionLevel interruptionLevel) {
102+
delegate.showFeedbackNotification(infoTextResourceId, interruptionLevel);
103+
}
104+
105+
@Override
106+
public void showFeedbackNotification(
107+
@NonNull CharSequence infoText, @NonNull InterruptionLevel interruptionLevel) {
108+
delegate.showFeedbackNotification(infoText, interruptionLevel);
109+
}
110+
111+
@Override
112+
public void cancelFeedbackNotification() {
113+
delegate.cancelFeedbackNotification();
114+
}
96115
}

firebase-appdistribution-api/src/main/java/com/google/firebase/appdistribution/internal/FirebaseAppDistributionStub.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import android.net.Uri;
1919
import androidx.annotation.NonNull;
2020
import androidx.annotation.Nullable;
21+
import androidx.annotation.StringRes;
2122
import com.google.android.gms.tasks.Continuation;
2223
import com.google.android.gms.tasks.OnCanceledListener;
2324
import com.google.android.gms.tasks.OnCompleteListener;
@@ -30,6 +31,7 @@
3031
import com.google.firebase.appdistribution.FirebaseAppDistribution;
3132
import com.google.firebase.appdistribution.FirebaseAppDistributionException;
3233
import com.google.firebase.appdistribution.FirebaseAppDistributionException.Status;
34+
import com.google.firebase.appdistribution.InterruptionLevel;
3335
import com.google.firebase.appdistribution.OnProgressListener;
3436
import com.google.firebase.appdistribution.UpdateTask;
3537
import java.util.concurrent.Executor;
@@ -73,17 +75,28 @@ public UpdateTask updateApp() {
7375
}
7476

7577
@Override
76-
public void startFeedback(int infoTextResourceId) {}
78+
public void startFeedback(@StringRes int infoTextResourceId) {}
7779

7880
@Override
7981
public void startFeedback(@NonNull CharSequence infoText) {}
8082

8183
@Override
82-
public void startFeedback(@NonNull int infoTextResourceId, @Nullable Uri screenshotUri) {}
84+
public void startFeedback(@StringRes int infoTextResourceId, @Nullable Uri screenshotUri) {}
8385

8486
@Override
8587
public void startFeedback(@NonNull CharSequence infoText, @Nullable Uri screenshotUri) {}
8688

89+
@Override
90+
public void showFeedbackNotification(
91+
@StringRes int infoTextResourceId, @NonNull InterruptionLevel interruptionLevel) {}
92+
93+
@Override
94+
public void showFeedbackNotification(
95+
@NonNull CharSequence infoText, @NonNull InterruptionLevel interruptionLevel) {}
96+
97+
@Override
98+
public void cancelFeedbackNotification() {}
99+
87100
private static <TResult> Task<TResult> getNotImplementedTask() {
88101
return Tasks.forException(
89102
new FirebaseAppDistributionException(

firebase-appdistribution/src/main/AndroidManifest.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
package="com.google.firebase.appdistribution.impl">
1818

1919
<uses-permission android:name="android.permission.INTERNET" />
20-
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
20+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
2121
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
22+
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
2223
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2324

2425
<application>
@@ -51,6 +52,10 @@
5152
android:exported="false"
5253
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
5354

55+
<activity
56+
android:name=".TakeScreenshotAndStartFeedbackActivity"
57+
android:exported="false" />
58+
5459
<provider
5560
android:name="com.google.firebase.appdistribution.impl.FirebaseAppDistributionFileProvider"
5661
android:authorities="${applicationId}.FirebaseAppDistributionFileProvider"

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/ApkUpdater.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ private void postUpdateProgress(
307307
.build());
308308
}
309309
if (showNotification) {
310-
appDistributionNotificationsManager.updateNotification(
310+
appDistributionNotificationsManager.showAppUpdateNotification(
311311
totalBytes, downloadedBytes, stringResourceId);
312312
}
313313
}

0 commit comments

Comments
 (0)