blob: 240c210bd20973f1c5ff892590450b7b8139fa27 [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 Sharkeye6c04f92015-04-18 21:38:05 -0700103 final Notification notif;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700104 switch (newState) {
105 case VolumeInfo.STATE_UNMOUNTED:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700106 notif = onVolumeUnmounted(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700107 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700108 case VolumeInfo.STATE_CHECKING:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700109 notif = onVolumeChecking(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700110 break;
111 case VolumeInfo.STATE_MOUNTED:
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700112 case VolumeInfo.STATE_MOUNTED_READ_ONLY:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700113 notif = onVolumeMounted(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700114 break;
115 case VolumeInfo.STATE_FORMATTING:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700116 notif = onVolumeFormatting(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700117 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700118 case VolumeInfo.STATE_EJECTING:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700119 notif = onVolumeEjecting(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700120 break;
121 case VolumeInfo.STATE_UNMOUNTABLE:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700122 notif = onVolumeUnmountable(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700123 break;
124 case VolumeInfo.STATE_REMOVED:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700125 notif = onVolumeRemoved(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700126 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700127 case VolumeInfo.STATE_BAD_REMOVAL:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700128 notif = onVolumeBadRemoval(vol);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700129 break;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700130 default:
131 notif = null;
132 break;
133 }
134
135 if (notif != null) {
136 mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
137 } else {
138 mNotificationManager.cancelAsUser(vol.getId(), NOTIF_ID, UserHandle.ALL);
San Mehat64e6a452010-02-04 20:53:48 -0800139 }
140 }
141
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700142 private Notification onVolumeUnmounted(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700143 // Ignored
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700144 return null;
San Mehat64e6a452010-02-04 20:53:48 -0800145 }
146
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700147 private Notification onVolumeChecking(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700148 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700149 final CharSequence title = mContext.getString(
150 R.string.ext_media_checking_notification_title, disk.getDescription());
151 final CharSequence text = mContext.getString(
152 R.string.ext_media_checking_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800153
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700154 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700155 .setCategory(Notification.CATEGORY_PROGRESS)
156 .setPriority(Notification.PRIORITY_LOW)
157 .setOngoing(true)
158 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700159 }
San Mehat64e6a452010-02-04 20:53:48 -0800160
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700161 private Notification onVolumeMounted(VolumeInfo vol) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700162 // Don't annoy when user dismissed in past
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700163 if (vol.isSnoozed()) return null;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700164
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700165 final DiskInfo disk = vol.getDisk();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700166 if (disk.isAdoptable() && !vol.isInited()) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700167 final CharSequence title = disk.getDescription();
168 final CharSequence text = mContext.getString(
169 R.string.ext_media_new_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800170
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700171 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700172 .addAction(new Action(0, mContext.getString(R.string.ext_media_init_action),
173 buildInitPendingIntent(vol)))
174 .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action),
175 buildUnmountPendingIntent(vol)))
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700176 .setDeleteIntent(buildSnoozeIntent(vol))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700177 .setCategory(Notification.CATEGORY_SYSTEM)
178 .build();
John Spurlock209bede2013-07-17 12:23:27 -0400179
San Mehat64e6a452010-02-04 20:53:48 -0800180 } else {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700181 final CharSequence title = disk.getDescription();
182 final CharSequence text = mContext.getString(
183 R.string.ext_media_ready_notification_message, disk.getDescription());
184
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700185 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700186 .addAction(new Action(0, mContext.getString(R.string.ext_media_browse_action),
187 buildBrowsePendingIntent(vol)))
188 .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action),
189 buildUnmountPendingIntent(vol)))
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700190 .setDeleteIntent(buildSnoozeIntent(vol))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700191 .setCategory(Notification.CATEGORY_SYSTEM)
192 .setPriority(Notification.PRIORITY_LOW)
193 .build();
San Mehat64e6a452010-02-04 20:53:48 -0800194 }
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700195 }
196
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700197 private Notification onVolumeFormatting(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700198 // Ignored
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700199 return null;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700200 }
201
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700202 private Notification onVolumeEjecting(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700203 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700204 final CharSequence title = mContext.getString(
205 R.string.ext_media_unmounting_notification_title, disk.getDescription());
206 final CharSequence text = mContext.getString(
207 R.string.ext_media_unmounting_notification_message, disk.getDescription());
208
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700209 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700210 .setCategory(Notification.CATEGORY_PROGRESS)
211 .setPriority(Notification.PRIORITY_LOW)
212 .setOngoing(true)
213 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700214 }
215
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700216 private Notification onVolumeUnmountable(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700217 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700218 final CharSequence title = mContext.getString(
219 R.string.ext_media_unmountable_notification_title, disk.getDescription());
220 final CharSequence text = mContext.getString(
221 R.string.ext_media_unmountable_notification_message, disk.getDescription());
222
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700223 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700224 .setContentIntent(buildDetailsPendingIntent(vol))
225 .setCategory(Notification.CATEGORY_ERROR)
226 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700227 }
228
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700229 private Notification onVolumeRemoved(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700230 if (!vol.isPrimary()) {
231 // Ignore non-primary media
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700232 return null;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700233 }
234
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700235 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700236 final CharSequence title = mContext.getString(
237 R.string.ext_media_nomedia_notification_title, disk.getDescription());
238 final CharSequence text = mContext.getString(
239 R.string.ext_media_nomedia_notification_message, disk.getDescription());
240
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700241 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700242 .setCategory(Notification.CATEGORY_ERROR)
243 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700244 }
245
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700246 private Notification onVolumeBadRemoval(VolumeInfo vol) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700247 if (!vol.isPrimary()) {
248 // Ignore non-primary media
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700249 return null;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700250 }
251
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700252 final DiskInfo disk = vol.getDisk();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700253 final CharSequence title = mContext.getString(
254 R.string.ext_media_badremoval_notification_title, disk.getDescription());
255 final CharSequence text = mContext.getString(
256 R.string.ext_media_badremoval_notification_message, disk.getDescription());
257
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700258 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700259 .setCategory(Notification.CATEGORY_ERROR)
260 .build();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700261 }
262
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700263 private int getSmallIcon(VolumeInfo vol) {
264 if (vol.disk.isSd()) {
265 switch (vol.getState()) {
266 case VolumeInfo.STATE_CHECKING:
267 case VolumeInfo.STATE_EJECTING:
268 return R.drawable.stat_notify_sdcard_prepare;
269 default:
270 return R.drawable.stat_notify_sdcard;
271 }
272 } else if (vol.disk.isUsb()) {
273 return R.drawable.stat_sys_data_usb;
274 } else {
275 return R.drawable.stat_notify_sdcard;
276 }
277 }
278
279 private Notification.Builder buildNotificationBuilder(VolumeInfo vol, CharSequence title,
280 CharSequence text) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700281 return new Notification.Builder(mContext)
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700282 .setSmallIcon(getSmallIcon(vol))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700283 .setColor(mContext.getColor(R.color.system_notification_accent_color))
284 .setContentTitle(title)
285 .setContentText(text)
286 .setStyle(new Notification.BigTextStyle().bigText(text))
287 .setVisibility(Notification.VISIBILITY_PUBLIC)
288 .setLocalOnly(true);
289 }
290
291 private PendingIntent buildInitPendingIntent(VolumeInfo vol) {
292 final Intent intent = new Intent();
293 intent.setClassName("com.android.settings",
294 "com.android.settings.deviceinfo.StorageWizardInit");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700295 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
296
297 final int requestKey = vol.getId().hashCode();
298 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
299 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700300 }
301
302 private PendingIntent buildUnmountPendingIntent(VolumeInfo vol) {
303 final Intent intent = new Intent();
304 intent.setClassName("com.android.settings",
305 "com.android.settings.deviceinfo.StorageUnmountReceiver");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700306 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
307
308 final int requestKey = vol.getId().hashCode();
309 return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
310 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700311 }
312
313 private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) {
314 final Intent intent = vol.buildBrowseIntent();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700315
316 final int requestKey = vol.getId().hashCode();
317 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
318 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700319 }
320
321 private PendingIntent buildDetailsPendingIntent(VolumeInfo vol) {
322 final Intent intent = new Intent();
323 intent.setClassName("com.android.settings",
324 "com.android.settings.Settings$StorageVolumeSettingsActivity");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700325 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
326
327 final int requestKey = vol.getId().hashCode();
328 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
329 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
330 }
331
332 private PendingIntent buildSnoozeIntent(VolumeInfo vol) {
333 final Intent intent = new Intent(ACTION_SNOOZE_VOLUME);
334 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
335
336 final int requestKey = vol.getId().hashCode();
337 return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
338 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
San Mehat64e6a452010-02-04 20:53:48 -0800339 }
340}