Skip to content

Commit 00deaa6

Browse files
notif: Support migration of Android notification channels
Needed for #340, when updating notification channels to use a custom notification sound.
1 parent 5bdd0aa commit 00deaa6

File tree

2 files changed

+104
-6
lines changed

2 files changed

+104
-6
lines changed

lib/notifications/display.dart

+29-6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ AndroidNotificationHostApi get _androidHost => ZulipBinding.instance.androidNoti
2525

2626
/// Service for configuring our Android "notification channel".
2727
class NotificationChannelManager {
28+
/// The channel ID we use for our one notification channel, which we use for
29+
/// all notifications.
30+
// TODO(launch) check this doesn't match zulip-mobile's current or previous
31+
// channel IDs
2832
@visibleForTesting
2933
static const kChannelId = 'messages-1';
3034

@@ -36,6 +40,8 @@ class NotificationChannelManager {
3640
static final kVibrationPattern = Int64List.fromList([0, 125, 100, 450]);
3741

3842
/// Create our notification channel, if it doesn't already exist.
43+
///
44+
/// Deletes obsolete channels, if present, from old versions of the app.
3945
//
4046
// NOTE when changing anything here: the changes will not take effect
4147
// for existing installs of the app! That's because we'll have already
@@ -52,11 +58,28 @@ class NotificationChannelManager {
5258
// settings for the channel -- like "override Do Not Disturb", or "use
5359
// a different sound", or "don't pop on screen" -- their changes get
5460
// reset. So this has to be done sparingly.
55-
//
56-
// If we do this, we should also look for any channel with the old
57-
// channel ID and delete it. See zulip-mobile's `createNotificationChannel`
58-
// in android/app/src/main/java/com/zulipmobile/notifications/NotificationChannelManager.kt .
59-
static Future<void> _ensureChannel() async {
61+
@visibleForTesting
62+
static Future<void> ensureChannel() async {
63+
// See if our current-version channel already exists; delete any obsolete
64+
// previous channels.
65+
var found = false;
66+
final channels = await _androidHost.getNotificationChannels();
67+
for (final channel in channels) {
68+
assert(channel != null); // TODO(#942)
69+
if (channel!.id == kChannelId) {
70+
found = true;
71+
} else {
72+
await _androidHost.deleteNotificationChannel(channel.id);
73+
}
74+
}
75+
76+
if (found) {
77+
// The channel already exists; nothing to do.
78+
return;
79+
}
80+
81+
// The channel doesn't exist. Create it.
82+
6083
await _androidHost.createNotificationChannel(NotificationChannel(
6184
id: kChannelId,
6285
name: 'Messages', // TODO(i18n)
@@ -81,7 +104,7 @@ class NotificationDisplayManager {
81104
if (launchDetails?.didNotificationLaunchApp ?? false) {
82105
_handleNotificationAppLaunch(launchDetails!.notificationResponse);
83106
}
84-
await NotificationChannelManager._ensureChannel();
107+
await NotificationChannelManager.ensureChannel();
85108
}
86109

87110
static void onFcmMessage(FcmMessage data, Map<String, dynamic> dataJson) {

test/notifications/display_test.dart

+75
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,81 @@ void main() {
131131
NotificationChannelManager.kVibrationPattern)
132132
;
133133
});
134+
135+
test('channel is not recreated if one with same id already exists', () async {
136+
addTearDown(testBinding.reset);
137+
138+
// Setup initial channel.
139+
await testBinding.androidNotificationHost.createNotificationChannel(
140+
NotificationChannel(
141+
id: NotificationChannelManager.kChannelId,
142+
name: 'Messages',
143+
importance: NotificationImportance.high,
144+
lightsEnabled: true,
145+
vibrationPattern: NotificationChannelManager.kVibrationPattern));
146+
// Clear the log.
147+
check(testBinding.androidNotificationHost.takeCreatedChannels())
148+
.length.equals(1);
149+
150+
// Ensure that no calls were made to the deleteChannel or createChannel
151+
// functions.
152+
await NotificationChannelManager.ensureChannel();
153+
check(testBinding.androidNotificationHost.takeDeletedChannels())
154+
.isEmpty();
155+
check(testBinding.androidNotificationHost.takeCreatedChannels())
156+
.isEmpty();
157+
check(testBinding.androidNotificationHost.activeChannels).single
158+
..id.equals(NotificationChannelManager.kChannelId)
159+
..name.equals('Messages')
160+
..importance.equals(NotificationImportance.high)
161+
..lightsEnabled.equals(true)
162+
..vibrationPattern.isNotNull().deepEquals(
163+
NotificationChannelManager.kVibrationPattern);
164+
});
165+
166+
test('obsolete channels are removed', () async {
167+
addTearDown(testBinding.reset);
168+
169+
// Setup initial channels.
170+
await testBinding.androidNotificationHost.createNotificationChannel(
171+
NotificationChannel(
172+
id: 'obsolete-1',
173+
name: 'Obsolete 1',
174+
importance: NotificationImportance.high,
175+
lightsEnabled: true,
176+
vibrationPattern: NotificationChannelManager.kVibrationPattern));
177+
await testBinding.androidNotificationHost.createNotificationChannel(
178+
NotificationChannel(
179+
id: 'obsolete-2',
180+
name: 'Obsolete 2',
181+
importance: NotificationImportance.high,
182+
lightsEnabled: true,
183+
vibrationPattern: NotificationChannelManager.kVibrationPattern));
184+
// Clear the log.
185+
check(testBinding.androidNotificationHost.takeCreatedChannels())
186+
.length.equals(2);
187+
188+
// Ensure that any channel whose channel-id differs from the desired
189+
// channel-id (NotificationChannelManager.kChannelId) is deleted, and a
190+
// new one with the desired channel-id is created.
191+
await NotificationChannelManager.ensureChannel();
192+
check(testBinding.androidNotificationHost.takeDeletedChannels())
193+
.deepEquals(['obsolete-1', 'obsolete-2']);
194+
check(testBinding.androidNotificationHost.takeCreatedChannels()).single
195+
..id.equals(NotificationChannelManager.kChannelId)
196+
..name.equals('Messages')
197+
..importance.equals(NotificationImportance.high)
198+
..lightsEnabled.equals(true)
199+
..vibrationPattern.isNotNull().deepEquals(
200+
NotificationChannelManager.kVibrationPattern);
201+
check(testBinding.androidNotificationHost.activeChannels).single
202+
..id.equals(NotificationChannelManager.kChannelId)
203+
..name.equals('Messages')
204+
..importance.equals(NotificationImportance.high)
205+
..lightsEnabled.equals(true)
206+
..vibrationPattern.isNotNull().deepEquals(
207+
NotificationChannelManager.kVibrationPattern);
208+
});
134209
});
135210

136211
group('NotificationDisplayManager show', () {

0 commit comments

Comments
 (0)