blob: ecd1a2fbbc0a6bc21ee193e7d79ca40b6d5e47ec [file] [log] [blame]
San Mehat64e6a452010-02-04 20:53:48 -08001/*
Jeff Sharkey56bd3122015-04-14 10:30:34 -07002 * Copyright (C) 2015 The Android Open Source Project
San Mehat64e6a452010-02-04 20:53:48 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Joe Onoratofe4f3ae2010-06-04 11:25:26 -070017package com.android.systemui.usb;
San Mehat64e6a452010-02-04 20:53:48 -080018
San Mehat64e6a452010-02-04 20:53:48 -080019import android.app.Notification;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070020import android.app.Notification.Action;
San Mehat64e6a452010-02-04 20:53:48 -080021import android.app.NotificationManager;
22import android.app.PendingIntent;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070023import android.content.BroadcastReceiver;
24import android.content.Context;
San Mehat64e6a452010-02-04 20:53:48 -080025import android.content.Intent;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070026import android.content.IntentFilter;
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -070027import android.os.UserHandle;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070028import android.os.storage.DiskInfo;
San Mehatb1043402010-02-05 08:26:50 -080029import android.os.storage.StorageEventListener;
30import android.os.storage.StorageManager;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070031import android.os.storage.VolumeInfo;
John Spurlockcd686b52013-06-05 10:13:46 -040032import android.util.Log;
San Mehat64e6a452010-02-04 20:53:48 -080033
Jeff Sharkey56bd3122015-04-14 10:30:34 -070034import com.android.internal.R;
John Spurlock3e309b22013-06-25 11:01:29 -040035import com.android.systemui.SystemUI;
36
Jeff Sharkey56bd3122015-04-14 10:30:34 -070037import java.util.List;
38
John Spurlock3e309b22013-06-25 11:01:29 -040039public class StorageNotification extends SystemUI {
San Mehat64e6a452010-02-04 20:53:48 -080040 private static final String TAG = "StorageNotification";
41
Jeff Sharkey56bd3122015-04-14 10:30:34 -070042 private static final int NOTIF_ID = 0x53544f52; // STOR
Daniel Sandlerc07907e2010-02-22 15:08:41 -050043
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070044 private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
45
Jeff Sharkey56bd3122015-04-14 10:30:34 -070046 // TODO: delay some notifications to avoid bumpy fast operations
47 // TODO: annoy user when private media is missing
San Mehat64e6a452010-02-04 20:53:48 -080048
Jeff Sharkey56bd3122015-04-14 10:30:34 -070049 private NotificationManager mNotificationManager;
San Mehatb1043402010-02-05 08:26:50 -080050 private StorageManager mStorageManager;
San Mehat64e6a452010-02-04 20:53:48 -080051
Jeff Sharkey56bd3122015-04-14 10:30:34 -070052 private final StorageEventListener mListener = new StorageEventListener() {
53 @Override
54 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
55 onVolumeStateChangedInternal(vol, oldState, newState);
John Spurlock3e309b22013-06-25 11:01:29 -040056 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070057
58 @Override
59 public void onVolumeMetadataChanged(VolumeInfo vol) {
60 // Avoid kicking notifications when getting early metadata before
61 // mounted. If already mounted, we're being kicked because of a
62 // nickname or init'ed change.
Jeff Sharkey27de30d2015-04-18 16:20:27 -070063 if (vol.isMountedReadable()) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070064 onVolumeStateChangedInternal(vol, vol.getState(), vol.getState());
65 }
66 }
67 };
68
69 private final BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() {
70 @Override
71 public void onReceive(Context context, Intent intent) {
72 // TODO: kick this onto background thread
73 final String volId = intent.getStringExtra(VolumeInfo.EXTRA_VOLUME_ID);
74 mStorageManager.setVolumeSnoozed(volId, true);
75 }
Jeff Sharkey56bd3122015-04-14 10:30:34 -070076 };
San Mehat64e6a452010-02-04 20:53:48 -080077
John Spurlock3e309b22013-06-25 11:01:29 -040078 @Override
79 public void start() {
Jeff Sharkey56bd3122015-04-14 10:30:34 -070080 mNotificationManager = mContext.getSystemService(NotificationManager.class);
John Spurlock3e309b22013-06-25 11:01:29 -040081
Jeff Sharkey56bd3122015-04-14 10:30:34 -070082 mStorageManager = mContext.getSystemService(StorageManager.class);
83 mStorageManager.registerListener(mListener);
Daniel Sandler5b8743f2010-11-03 09:43:46 -040084
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070085 mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME),
86 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
87
Jeff Sharkey56bd3122015-04-14 10:30:34 -070088 // Kick current state into place
89 final List<VolumeInfo> vols = mStorageManager.getVolumes();
90 for (VolumeInfo vol : vols) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070091 onVolumeStateChangedInternal(vol, vol.getState(), vol.getState());
San Mehat64e6a452010-02-04 20:53:48 -080092 }
93 }
94
Jeff Sharkey56bd3122015-04-14 10:30:34 -070095 public void onVolumeStateChangedInternal(VolumeInfo vol, int oldState, int newState) {
96 // We only care about public volumes
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070097 if (vol.getType() != VolumeInfo.TYPE_PUBLIC) {
San Mehat64e6a452010-02-04 20:53:48 -080098 return;
99 }
100
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700101 Log.d(TAG, vol.toString());
San Mehat64e6a452010-02-04 20:53:48 -0800102
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700103 // New state means we tear down any old notifications
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700104 mNotificationManager.cancelAsUser(vol.getId(), NOTIF_ID, UserHandle.ALL);
John Spurlock209bede2013-07-17 12:23:27 -0400105
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700106 switch (newState) {
107 case VolumeInfo.STATE_UNMOUNTED:
108 onVolumeUnmounted(vol);
109 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700110 case VolumeInfo.STATE_CHECKING:
111 onVolumeChecking(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700112 break;
113 case VolumeInfo.STATE_MOUNTED:
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700114 case VolumeInfo.STATE_MOUNTED_READ_ONLY:
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700115 onVolumeMounted(vol);
116 break;
117 case VolumeInfo.STATE_FORMATTING:
118 onVolumeFormatting(vol);
119 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700120 case VolumeInfo.STATE_EJECTING:
121 onVolumeEjecting(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700122 break;
123 case VolumeInfo.STATE_UNMOUNTABLE:
124 onVolumeUnmountable(vol);
125 break;
126 case VolumeInfo.STATE_REMOVED:
127 onVolumeRemoved(vol);
128 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700129 case VolumeInfo.STATE_BAD_REMOVAL:
130 onVolumeBadRemoval(vol);
131 break;
San Mehat64e6a452010-02-04 20:53:48 -0800132 }
133 }
134
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700135 private void onVolumeUnmounted(VolumeInfo vol) {
136 // Ignored
San Mehat64e6a452010-02-04 20:53:48 -0800137 }
138
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700139 private void onVolumeChecking(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700140 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700141 final CharSequence title = mContext.getString(
142 R.string.ext_media_checking_notification_title, disk.getDescription());
143 final CharSequence text = mContext.getString(
144 R.string.ext_media_checking_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800145
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700146 final Notification notif = buildNotificationBuilder(title, text)
147 .setSmallIcon(R.drawable.stat_notify_sdcard_prepare)
148 .setCategory(Notification.CATEGORY_PROGRESS)
149 .setPriority(Notification.PRIORITY_LOW)
150 .setOngoing(true)
151 .build();
San Mehat64e6a452010-02-04 20:53:48 -0800152
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700153 mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700154 }
San Mehat64e6a452010-02-04 20:53:48 -0800155
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700156 private void onVolumeMounted(VolumeInfo vol) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700157 // Don't annoy when user dismissed in past
158 if (vol.isSnoozed()) return;
159
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700160 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700161 final Notification notif;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700162 if (disk.isAdoptable() && !vol.isInited()) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700163 final CharSequence title = disk.getDescription();
164 final CharSequence text = mContext.getString(
165 R.string.ext_media_new_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800166
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700167 notif = buildNotificationBuilder(title, text)
168 .setSmallIcon(R.drawable.stat_notify_sdcard)
169 .addAction(new Action(0, mContext.getString(R.string.ext_media_init_action),
170 buildInitPendingIntent(vol)))
171 .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action),
172 buildUnmountPendingIntent(vol)))
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700173 .setDeleteIntent(buildSnoozeIntent(vol))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700174 .setCategory(Notification.CATEGORY_SYSTEM)
175 .build();
John Spurlock209bede2013-07-17 12:23:27 -0400176
San Mehat64e6a452010-02-04 20:53:48 -0800177 } else {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700178 final CharSequence title = disk.getDescription();
179 final CharSequence text = mContext.getString(
180 R.string.ext_media_ready_notification_message, disk.getDescription());
181
182 notif = buildNotificationBuilder(title, text)
183 .setSmallIcon(R.drawable.stat_notify_sdcard)
184 .addAction(new Action(0, mContext.getString(R.string.ext_media_browse_action),
185 buildBrowsePendingIntent(vol)))
186 .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action),
187 buildUnmountPendingIntent(vol)))
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700188 .setDeleteIntent(buildSnoozeIntent(vol))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700189 .setCategory(Notification.CATEGORY_SYSTEM)
190 .setPriority(Notification.PRIORITY_LOW)
191 .build();
San Mehat64e6a452010-02-04 20:53:48 -0800192 }
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700193
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700194 mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700195 }
196
197 private void onVolumeFormatting(VolumeInfo vol) {
198 // Ignored
199 }
200
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700201 private void onVolumeEjecting(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700202 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700203 final CharSequence title = mContext.getString(
204 R.string.ext_media_unmounting_notification_title, disk.getDescription());
205 final CharSequence text = mContext.getString(
206 R.string.ext_media_unmounting_notification_message, disk.getDescription());
207
208 final Notification notif = buildNotificationBuilder(title, text)
209 .setSmallIcon(R.drawable.stat_notify_sdcard_prepare)
210 .setCategory(Notification.CATEGORY_PROGRESS)
211 .setPriority(Notification.PRIORITY_LOW)
212 .setOngoing(true)
213 .build();
214
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700215 mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700216 }
217
218 private void onVolumeUnmountable(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700219 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700220 final CharSequence title = mContext.getString(
221 R.string.ext_media_unmountable_notification_title, disk.getDescription());
222 final CharSequence text = mContext.getString(
223 R.string.ext_media_unmountable_notification_message, disk.getDescription());
224
225 final Notification notif = buildNotificationBuilder(title, text)
226 .setSmallIcon(R.drawable.stat_notify_sdcard)
227 .setContentIntent(buildDetailsPendingIntent(vol))
228 .setCategory(Notification.CATEGORY_ERROR)
229 .build();
230
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700231 mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700232 }
233
234 private void onVolumeRemoved(VolumeInfo vol) {
235 if (!vol.isPrimary()) {
236 // Ignore non-primary media
237 return;
238 }
239
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700240 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700241 final CharSequence title = mContext.getString(
242 R.string.ext_media_nomedia_notification_title, disk.getDescription());
243 final CharSequence text = mContext.getString(
244 R.string.ext_media_nomedia_notification_message, disk.getDescription());
245
246 final Notification notif = buildNotificationBuilder(title, text)
247 .setSmallIcon(R.drawable.stat_notify_sdcard)
248 .setCategory(Notification.CATEGORY_ERROR)
249 .build();
250
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700251 mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700252 }
253
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700254 private void onVolumeBadRemoval(VolumeInfo vol) {
255 if (!vol.isPrimary()) {
256 // Ignore non-primary media
257 return;
258 }
259
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700260 final DiskInfo disk = vol.getDisk();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700261 final CharSequence title = mContext.getString(
262 R.string.ext_media_badremoval_notification_title, disk.getDescription());
263 final CharSequence text = mContext.getString(
264 R.string.ext_media_badremoval_notification_message, disk.getDescription());
265
266 final Notification notif = buildNotificationBuilder(title, text)
267 .setSmallIcon(R.drawable.stat_notify_sdcard)
268 .setCategory(Notification.CATEGORY_ERROR)
269 .build();
270
271 mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
272 }
273
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700274 private Notification.Builder buildNotificationBuilder(CharSequence title, CharSequence text) {
275 return new Notification.Builder(mContext)
276 .setColor(mContext.getColor(R.color.system_notification_accent_color))
277 .setContentTitle(title)
278 .setContentText(text)
279 .setStyle(new Notification.BigTextStyle().bigText(text))
280 .setVisibility(Notification.VISIBILITY_PUBLIC)
281 .setLocalOnly(true);
282 }
283
284 private PendingIntent buildInitPendingIntent(VolumeInfo vol) {
285 final Intent intent = new Intent();
286 intent.setClassName("com.android.settings",
287 "com.android.settings.deviceinfo.StorageWizardInit");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700288 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
289
290 final int requestKey = vol.getId().hashCode();
291 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
292 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700293 }
294
295 private PendingIntent buildUnmountPendingIntent(VolumeInfo vol) {
296 final Intent intent = new Intent();
297 intent.setClassName("com.android.settings",
298 "com.android.settings.deviceinfo.StorageUnmountReceiver");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700299 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
300
301 final int requestKey = vol.getId().hashCode();
302 return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
303 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700304 }
305
306 private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) {
307 final Intent intent = vol.buildBrowseIntent();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700308
309 final int requestKey = vol.getId().hashCode();
310 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
311 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700312 }
313
314 private PendingIntent buildDetailsPendingIntent(VolumeInfo vol) {
315 final Intent intent = new Intent();
316 intent.setClassName("com.android.settings",
317 "com.android.settings.Settings$StorageVolumeSettingsActivity");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700318 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
319
320 final int requestKey = vol.getId().hashCode();
321 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
322 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
323 }
324
325 private PendingIntent buildSnoozeIntent(VolumeInfo vol) {
326 final Intent intent = new Intent(ACTION_SNOOZE_VOLUME);
327 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
328
329 final int requestKey = vol.getId().hashCode();
330 return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
331 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
San Mehat64e6a452010-02-04 20:53:48 -0800332 }
333}