blob: 180d9180a9f7ea47eac9c9705e5c1b0d5801fc32 [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;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070027import android.content.pm.PackageManager;
28import android.content.pm.PackageManager.MoveCallback;
Jeff Sharkey50a05452015-04-29 11:24:52 -070029import android.os.Bundle;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070030import android.os.Handler;
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -070031import android.os.UserHandle;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070032import android.os.storage.DiskInfo;
San Mehatb1043402010-02-05 08:26:50 -080033import android.os.storage.StorageEventListener;
34import android.os.storage.StorageManager;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070035import android.os.storage.VolumeInfo;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070036import android.os.storage.VolumeRecord;
37import android.text.TextUtils;
38import android.text.format.DateUtils;
John Spurlockcd686b52013-06-05 10:13:46 -040039import android.util.Log;
Jeff Sharkey50a05452015-04-29 11:24:52 -070040import android.util.SparseArray;
San Mehat64e6a452010-02-04 20:53:48 -080041
Jeff Sharkey56bd3122015-04-14 10:30:34 -070042import com.android.internal.R;
John Spurlock3e309b22013-06-25 11:01:29 -040043import com.android.systemui.SystemUI;
44
Jeff Sharkey56bd3122015-04-14 10:30:34 -070045import java.util.List;
46
John Spurlock3e309b22013-06-25 11:01:29 -040047public class StorageNotification extends SystemUI {
San Mehat64e6a452010-02-04 20:53:48 -080048 private static final String TAG = "StorageNotification";
49
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070050 private static final int PUBLIC_ID = 0x53505542; // SPUB
51 private static final int PRIVATE_ID = 0x53505256; // SPRV
52 private static final int DISK_ID = 0x5344534b; // SDSK
53 private static final int MOVE_ID = 0x534d4f56; // SMOV
Daniel Sandlerc07907e2010-02-22 15:08:41 -050054
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070055 private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
Jeff Sharkeydf27d3b2015-06-16 13:51:48 -070056 private static final String ACTION_FINISH_WIZARD = "com.android.systemui.action.FINISH_WIZARD";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070057
Jeff Sharkey56bd3122015-04-14 10:30:34 -070058 // TODO: delay some notifications to avoid bumpy fast operations
San Mehat64e6a452010-02-04 20:53:48 -080059
Jeff Sharkey56bd3122015-04-14 10:30:34 -070060 private NotificationManager mNotificationManager;
San Mehatb1043402010-02-05 08:26:50 -080061 private StorageManager mStorageManager;
San Mehat64e6a452010-02-04 20:53:48 -080062
Jeff Sharkey50a05452015-04-29 11:24:52 -070063 private static class MoveInfo {
64 public int moveId;
65 public Bundle extras;
66 public String packageName;
67 public String label;
68 public String volumeUuid;
69 }
70
71 private final SparseArray<MoveInfo> mMoves = new SparseArray<>();
72
Jeff Sharkey56bd3122015-04-14 10:30:34 -070073 private final StorageEventListener mListener = new StorageEventListener() {
74 @Override
75 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070076 onVolumeStateChangedInternal(vol);
John Spurlock3e309b22013-06-25 11:01:29 -040077 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070078
79 @Override
Jeff Sharkey50a05452015-04-29 11:24:52 -070080 public void onVolumeRecordChanged(VolumeRecord rec) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070081 // Avoid kicking notifications when getting early metadata before
82 // mounted. If already mounted, we're being kicked because of a
83 // nickname or init'ed change.
Jeff Sharkey50a05452015-04-29 11:24:52 -070084 final VolumeInfo vol = mStorageManager.findVolumeByUuid(rec.getFsUuid());
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070085 if (vol != null && vol.isMountedReadable()) {
86 onVolumeStateChangedInternal(vol);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070087 }
Jeff Sharkey50a05452015-04-29 11:24:52 -070088 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070089
Jeff Sharkey50a05452015-04-29 11:24:52 -070090 @Override
91 public void onVolumeForgotten(String fsUuid) {
92 // Stop annoying the user
93 mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070094 }
95
96 @Override
97 public void onDiskScanned(DiskInfo disk, int volumeCount) {
98 onDiskScannedInternal(disk, volumeCount);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070099 }
100 };
101
102 private final BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() {
103 @Override
104 public void onReceive(Context context, Intent intent) {
105 // TODO: kick this onto background thread
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700106 final String fsUuid = intent.getStringExtra(VolumeRecord.EXTRA_FS_UUID);
107 mStorageManager.setVolumeSnoozed(fsUuid, true);
108 }
109 };
110
Jeff Sharkeydf27d3b2015-06-16 13:51:48 -0700111 private final BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
112 @Override
113 public void onReceive(Context context, Intent intent) {
114 // When finishing the adoption wizard, clean up any notifications
115 // for moving primary storage
116 mNotificationManager.cancelAsUser(null, MOVE_ID, UserHandle.ALL);
117 }
118 };
119
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700120 private final MoveCallback mMoveCallback = new MoveCallback() {
121 @Override
Jeff Sharkey50a05452015-04-29 11:24:52 -0700122 public void onCreated(int moveId, Bundle extras) {
123 final MoveInfo move = new MoveInfo();
124 move.moveId = moveId;
125 move.extras = extras;
126 if (extras != null) {
127 move.packageName = extras.getString(Intent.EXTRA_PACKAGE_NAME);
128 move.label = extras.getString(Intent.EXTRA_TITLE);
129 move.volumeUuid = extras.getString(VolumeRecord.EXTRA_FS_UUID);
130 }
131 mMoves.put(moveId, move);
132 }
133
134 @Override
135 public void onStatusChanged(int moveId, int status, long estMillis) {
136 final MoveInfo move = mMoves.get(moveId);
137 if (move == null) {
138 Log.w(TAG, "Ignoring unknown move " + moveId);
139 return;
140 }
141
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700142 if (PackageManager.isMoveStatusFinished(status)) {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700143 onMoveFinished(move, status);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700144 } else {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700145 onMoveProgress(move, status, estMillis);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700146 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700147 }
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700148 };
San Mehat64e6a452010-02-04 20:53:48 -0800149
John Spurlock3e309b22013-06-25 11:01:29 -0400150 @Override
151 public void start() {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700152 mNotificationManager = mContext.getSystemService(NotificationManager.class);
John Spurlock3e309b22013-06-25 11:01:29 -0400153
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700154 mStorageManager = mContext.getSystemService(StorageManager.class);
155 mStorageManager.registerListener(mListener);
Daniel Sandler5b8743f2010-11-03 09:43:46 -0400156
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700157 mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME),
158 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
Jeff Sharkeydf27d3b2015-06-16 13:51:48 -0700159 mContext.registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_WIZARD),
160 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700161
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700162 // Kick current state into place
Jeff Sharkeyf5a6bd72015-05-19 14:42:38 -0700163 final List<DiskInfo> disks = mStorageManager.getDisks();
164 for (DiskInfo disk : disks) {
165 onDiskScannedInternal(disk, disk.volumeCount);
166 }
167
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700168 final List<VolumeInfo> vols = mStorageManager.getVolumes();
169 for (VolumeInfo vol : vols) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700170 onVolumeStateChangedInternal(vol);
171 }
172
173 mContext.getPackageManager().registerMoveCallback(mMoveCallback, new Handler());
174
175 updateMissingPrivateVolumes();
176 }
177
178 private void updateMissingPrivateVolumes() {
179 final List<VolumeRecord> recs = mStorageManager.getVolumeRecords();
180 for (VolumeRecord rec : recs) {
181 if (rec.getType() != VolumeInfo.TYPE_PRIVATE) continue;
182
183 final String fsUuid = rec.getFsUuid();
184 final VolumeInfo info = mStorageManager.findVolumeByUuid(fsUuid);
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700185 if ((info != null && info.isMountedWritable()) || rec.isSnoozed()) {
186 // Yay, private volume is here, or user snoozed
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700187 mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL);
188
189 } else {
190 // Boo, annoy the user to reinsert the private volume
191 final CharSequence title = mContext.getString(R.string.ext_media_missing_title,
192 rec.getNickname());
193 final CharSequence text = mContext.getString(R.string.ext_media_missing_message);
194
195 final Notification notif = new Notification.Builder(mContext)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700196 .setSmallIcon(R.drawable.ic_sd_card_48dp)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700197 .setColor(mContext.getColor(R.color.system_notification_accent_color))
198 .setContentTitle(title)
199 .setContentText(text)
Jeff Sharkey50a05452015-04-29 11:24:52 -0700200 .setContentIntent(buildForgetPendingIntent(rec))
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700201 .setStyle(new Notification.BigTextStyle().bigText(text))
202 .setVisibility(Notification.VISIBILITY_PUBLIC)
203 .setLocalOnly(true)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700204 .setCategory(Notification.CATEGORY_SYSTEM)
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700205 .setDeleteIntent(buildSnoozeIntent(fsUuid))
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700206 .build();
207
208 mNotificationManager.notifyAsUser(fsUuid, PRIVATE_ID, notif, UserHandle.ALL);
209 }
San Mehat64e6a452010-02-04 20:53:48 -0800210 }
211 }
212
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700213 private void onDiskScannedInternal(DiskInfo disk, int volumeCount) {
Jeff Sharkeyf5a6bd72015-05-19 14:42:38 -0700214 if (volumeCount == 0 && disk.size > 0) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700215 // No supported volumes found, give user option to format
216 final CharSequence title = mContext.getString(
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700217 R.string.ext_media_unsupported_notification_title, disk.getDescription());
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700218 final CharSequence text = mContext.getString(
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700219 R.string.ext_media_unsupported_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800220
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700221 final Notification notif = new Notification.Builder(mContext)
222 .setSmallIcon(getSmallIcon(disk, VolumeInfo.STATE_UNMOUNTABLE))
223 .setColor(mContext.getColor(R.color.system_notification_accent_color))
224 .setContentTitle(title)
225 .setContentText(text)
Jeff Sharkey50a05452015-04-29 11:24:52 -0700226 .setContentIntent(buildInitPendingIntent(disk))
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700227 .setStyle(new Notification.BigTextStyle().bigText(text))
228 .setVisibility(Notification.VISIBILITY_PUBLIC)
229 .setLocalOnly(true)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700230 .setCategory(Notification.CATEGORY_ERROR)
231 .build();
232
233 mNotificationManager.notifyAsUser(disk.getId(), DISK_ID, notif, UserHandle.ALL);
234
235 } else {
236 // Yay, we have volumes!
237 mNotificationManager.cancelAsUser(disk.getId(), DISK_ID, UserHandle.ALL);
238 }
239 }
240
241 private void onVolumeStateChangedInternal(VolumeInfo vol) {
242 switch (vol.getType()) {
243 case VolumeInfo.TYPE_PRIVATE:
244 onPrivateVolumeStateChangedInternal(vol);
245 break;
246 case VolumeInfo.TYPE_PUBLIC:
247 onPublicVolumeStateChangedInternal(vol);
248 break;
249 }
250 }
251
252 private void onPrivateVolumeStateChangedInternal(VolumeInfo vol) {
253 Log.d(TAG, "Notifying about private volume: " + vol.toString());
254
255 updateMissingPrivateVolumes();
256 }
257
258 private void onPublicVolumeStateChangedInternal(VolumeInfo vol) {
259 Log.d(TAG, "Notifying about public volume: " + vol.toString());
San Mehat64e6a452010-02-04 20:53:48 -0800260
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700261 final Notification notif;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700262 switch (vol.getState()) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700263 case VolumeInfo.STATE_UNMOUNTED:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700264 notif = onVolumeUnmounted(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700265 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700266 case VolumeInfo.STATE_CHECKING:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700267 notif = onVolumeChecking(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700268 break;
269 case VolumeInfo.STATE_MOUNTED:
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700270 case VolumeInfo.STATE_MOUNTED_READ_ONLY:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700271 notif = onVolumeMounted(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700272 break;
273 case VolumeInfo.STATE_FORMATTING:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700274 notif = onVolumeFormatting(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700275 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700276 case VolumeInfo.STATE_EJECTING:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700277 notif = onVolumeEjecting(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700278 break;
279 case VolumeInfo.STATE_UNMOUNTABLE:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700280 notif = onVolumeUnmountable(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700281 break;
282 case VolumeInfo.STATE_REMOVED:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700283 notif = onVolumeRemoved(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700284 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700285 case VolumeInfo.STATE_BAD_REMOVAL:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700286 notif = onVolumeBadRemoval(vol);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700287 break;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700288 default:
289 notif = null;
290 break;
291 }
292
293 if (notif != null) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700294 mNotificationManager.notifyAsUser(vol.getId(), PUBLIC_ID, notif, UserHandle.ALL);
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700295 } else {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700296 mNotificationManager.cancelAsUser(vol.getId(), PUBLIC_ID, UserHandle.ALL);
San Mehat64e6a452010-02-04 20:53:48 -0800297 }
298 }
299
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700300 private Notification onVolumeUnmounted(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700301 // Ignored
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700302 return null;
San Mehat64e6a452010-02-04 20:53:48 -0800303 }
304
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700305 private Notification onVolumeChecking(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700306 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700307 final CharSequence title = mContext.getString(
308 R.string.ext_media_checking_notification_title, disk.getDescription());
309 final CharSequence text = mContext.getString(
310 R.string.ext_media_checking_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800311
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700312 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700313 .setCategory(Notification.CATEGORY_PROGRESS)
314 .setPriority(Notification.PRIORITY_LOW)
315 .setOngoing(true)
316 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700317 }
San Mehat64e6a452010-02-04 20:53:48 -0800318
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700319 private Notification onVolumeMounted(VolumeInfo vol) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700320 final VolumeRecord rec = mStorageManager.findRecordByUuid(vol.getFsUuid());
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700321 final DiskInfo disk = vol.getDisk();
Makoto Onukib138cb22015-06-23 17:32:02 -0700322
323 // Don't annoy when user dismissed in past. (But make sure the disk is adoptable; we
324 // used to allow snoozing non-adoptable disks too.)
325 if (rec.isSnoozed() && disk.isAdoptable()) {
326 return null;
327 }
328
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700329 if (disk.isAdoptable() && !rec.isInited()) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700330 final CharSequence title = disk.getDescription();
331 final CharSequence text = mContext.getString(
332 R.string.ext_media_new_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800333
Jeff Sharkey50a05452015-04-29 11:24:52 -0700334 final PendingIntent initIntent = buildInitPendingIntent(vol);
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700335 return buildNotificationBuilder(vol, title, text)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700336 .addAction(new Action(R.drawable.ic_settings_24dp,
337 mContext.getString(R.string.ext_media_init_action), initIntent))
338 .addAction(new Action(R.drawable.ic_eject_24dp,
339 mContext.getString(R.string.ext_media_unmount_action),
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700340 buildUnmountPendingIntent(vol)))
Jeff Sharkey50a05452015-04-29 11:24:52 -0700341 .setContentIntent(initIntent)
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700342 .setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700343 .setCategory(Notification.CATEGORY_SYSTEM)
344 .build();
John Spurlock209bede2013-07-17 12:23:27 -0400345
San Mehat64e6a452010-02-04 20:53:48 -0800346 } else {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700347 final CharSequence title = disk.getDescription();
348 final CharSequence text = mContext.getString(
349 R.string.ext_media_ready_notification_message, disk.getDescription());
350
Jeff Sharkey50a05452015-04-29 11:24:52 -0700351 final PendingIntent browseIntent = buildBrowsePendingIntent(vol);
Makoto Onukib138cb22015-06-23 17:32:02 -0700352 final Notification.Builder builder = buildNotificationBuilder(vol, title, text)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700353 .addAction(new Action(R.drawable.ic_folder_24dp,
354 mContext.getString(R.string.ext_media_browse_action),
Jeff Sharkey50a05452015-04-29 11:24:52 -0700355 browseIntent))
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700356 .addAction(new Action(R.drawable.ic_eject_24dp,
357 mContext.getString(R.string.ext_media_unmount_action),
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700358 buildUnmountPendingIntent(vol)))
Jeff Sharkey50a05452015-04-29 11:24:52 -0700359 .setContentIntent(browseIntent)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700360 .setCategory(Notification.CATEGORY_SYSTEM)
Makoto Onukib138cb22015-06-23 17:32:02 -0700361 .setPriority(Notification.PRIORITY_LOW);
362 // Non-adoptable disks can't be snoozed.
363 if (disk.isAdoptable()) {
364 builder.setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()));
365 }
366
367 return builder.build();
San Mehat64e6a452010-02-04 20:53:48 -0800368 }
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700369 }
370
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700371 private Notification onVolumeFormatting(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700372 // Ignored
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700373 return null;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700374 }
375
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700376 private Notification onVolumeEjecting(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700377 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700378 final CharSequence title = mContext.getString(
379 R.string.ext_media_unmounting_notification_title, disk.getDescription());
380 final CharSequence text = mContext.getString(
381 R.string.ext_media_unmounting_notification_message, disk.getDescription());
382
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700383 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700384 .setCategory(Notification.CATEGORY_PROGRESS)
385 .setPriority(Notification.PRIORITY_LOW)
386 .setOngoing(true)
387 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700388 }
389
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700390 private Notification onVolumeUnmountable(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700391 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700392 final CharSequence title = mContext.getString(
393 R.string.ext_media_unmountable_notification_title, disk.getDescription());
394 final CharSequence text = mContext.getString(
395 R.string.ext_media_unmountable_notification_message, disk.getDescription());
396
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700397 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700398 .setContentIntent(buildInitPendingIntent(vol))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700399 .setCategory(Notification.CATEGORY_ERROR)
400 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700401 }
402
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700403 private Notification onVolumeRemoved(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700404 if (!vol.isPrimary()) {
405 // Ignore non-primary media
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700406 return null;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700407 }
408
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700409 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700410 final CharSequence title = mContext.getString(
411 R.string.ext_media_nomedia_notification_title, disk.getDescription());
412 final CharSequence text = mContext.getString(
413 R.string.ext_media_nomedia_notification_message, disk.getDescription());
414
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700415 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700416 .setCategory(Notification.CATEGORY_ERROR)
417 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700418 }
419
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700420 private Notification onVolumeBadRemoval(VolumeInfo vol) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700421 if (!vol.isPrimary()) {
422 // Ignore non-primary media
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700423 return null;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700424 }
425
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700426 final DiskInfo disk = vol.getDisk();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700427 final CharSequence title = mContext.getString(
428 R.string.ext_media_badremoval_notification_title, disk.getDescription());
429 final CharSequence text = mContext.getString(
430 R.string.ext_media_badremoval_notification_message, disk.getDescription());
431
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700432 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700433 .setCategory(Notification.CATEGORY_ERROR)
434 .build();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700435 }
436
Jeff Sharkey50a05452015-04-29 11:24:52 -0700437 private void onMoveProgress(MoveInfo move, int status, long estMillis) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700438 final CharSequence title;
Jeff Sharkey50a05452015-04-29 11:24:52 -0700439 if (!TextUtils.isEmpty(move.label)) {
440 title = mContext.getString(R.string.ext_media_move_specific_title, move.label);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700441 } else {
442 title = mContext.getString(R.string.ext_media_move_title);
443 }
444
445 final CharSequence text;
446 if (estMillis < 0) {
447 text = null;
448 } else {
449 text = DateUtils.formatDuration(estMillis);
450 }
451
Jeff Sharkey50a05452015-04-29 11:24:52 -0700452 final PendingIntent intent;
453 if (move.packageName != null) {
454 intent = buildWizardMovePendingIntent(move);
455 } else {
456 intent = buildWizardMigratePendingIntent(move);
457 }
458
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700459 final Notification notif = new Notification.Builder(mContext)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700460 .setSmallIcon(R.drawable.ic_sd_card_48dp)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700461 .setColor(mContext.getColor(R.color.system_notification_accent_color))
462 .setContentTitle(title)
463 .setContentText(text)
Jeff Sharkey50a05452015-04-29 11:24:52 -0700464 .setContentIntent(intent)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700465 .setStyle(new Notification.BigTextStyle().bigText(text))
466 .setVisibility(Notification.VISIBILITY_PUBLIC)
467 .setLocalOnly(true)
468 .setCategory(Notification.CATEGORY_PROGRESS)
469 .setPriority(Notification.PRIORITY_LOW)
470 .setProgress(100, status, false)
471 .setOngoing(true)
472 .build();
473
Jeff Sharkey50a05452015-04-29 11:24:52 -0700474 mNotificationManager.notifyAsUser(move.packageName, MOVE_ID, notif, UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700475 }
476
Jeff Sharkey50a05452015-04-29 11:24:52 -0700477 private void onMoveFinished(MoveInfo move, int status) {
478 if (move.packageName != null) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700479 // We currently ignore finished app moves; just clear the last
480 // published progress
Jeff Sharkey50a05452015-04-29 11:24:52 -0700481 mNotificationManager.cancelAsUser(move.packageName, MOVE_ID, UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700482 return;
483 }
484
Jeff Sharkey50a05452015-04-29 11:24:52 -0700485 final VolumeInfo privateVol = mContext.getPackageManager().getPrimaryStorageCurrentVolume();
486 final String descrip = mStorageManager.getBestVolumeDescription(privateVol);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700487
488 final CharSequence title;
489 final CharSequence text;
490 if (status == PackageManager.MOVE_SUCCEEDED) {
491 title = mContext.getString(R.string.ext_media_move_success_title);
492 text = mContext.getString(R.string.ext_media_move_success_message, descrip);
493 } else {
494 title = mContext.getString(R.string.ext_media_move_failure_title);
495 text = mContext.getString(R.string.ext_media_move_failure_message);
496 }
497
Jeff Sharkey50a05452015-04-29 11:24:52 -0700498 // Jump back into the wizard flow if we moved to a real disk
499 final PendingIntent intent;
500 if (privateVol != null && privateVol.getDisk() != null) {
501 intent = buildWizardReadyPendingIntent(privateVol.getDisk());
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700502 } else if (privateVol != null) {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700503 intent = buildVolumeSettingsPendingIntent(privateVol);
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700504 } else {
505 intent = null;
Jeff Sharkey50a05452015-04-29 11:24:52 -0700506 }
507
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700508 final Notification notif = new Notification.Builder(mContext)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700509 .setSmallIcon(R.drawable.ic_sd_card_48dp)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700510 .setColor(mContext.getColor(R.color.system_notification_accent_color))
511 .setContentTitle(title)
512 .setContentText(text)
Jeff Sharkey50a05452015-04-29 11:24:52 -0700513 .setContentIntent(intent)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700514 .setStyle(new Notification.BigTextStyle().bigText(text))
515 .setVisibility(Notification.VISIBILITY_PUBLIC)
516 .setLocalOnly(true)
517 .setCategory(Notification.CATEGORY_SYSTEM)
518 .setPriority(Notification.PRIORITY_LOW)
Jeff Sharkey50a05452015-04-29 11:24:52 -0700519 .setAutoCancel(true)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700520 .build();
521
Jeff Sharkey50a05452015-04-29 11:24:52 -0700522 mNotificationManager.notifyAsUser(move.packageName, MOVE_ID, notif, UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700523 }
524
525 private int getSmallIcon(DiskInfo disk, int state) {
526 if (disk.isSd()) {
527 switch (state) {
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700528 case VolumeInfo.STATE_CHECKING:
529 case VolumeInfo.STATE_EJECTING:
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700530 return R.drawable.ic_sd_card_48dp;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700531 default:
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700532 return R.drawable.ic_sd_card_48dp;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700533 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700534 } else if (disk.isUsb()) {
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700535 return R.drawable.ic_usb_48dp;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700536 } else {
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700537 return R.drawable.ic_sd_card_48dp;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700538 }
539 }
540
541 private Notification.Builder buildNotificationBuilder(VolumeInfo vol, CharSequence title,
542 CharSequence text) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700543 return new Notification.Builder(mContext)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700544 .setSmallIcon(getSmallIcon(vol.getDisk(), vol.getState()))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700545 .setColor(mContext.getColor(R.color.system_notification_accent_color))
546 .setContentTitle(title)
547 .setContentText(text)
548 .setStyle(new Notification.BigTextStyle().bigText(text))
549 .setVisibility(Notification.VISIBILITY_PUBLIC)
550 .setLocalOnly(true);
551 }
552
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700553 private PendingIntent buildInitPendingIntent(DiskInfo disk) {
554 final Intent intent = new Intent();
555 intent.setClassName("com.android.settings",
556 "com.android.settings.deviceinfo.StorageWizardInit");
557 intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId());
558
559 final int requestKey = disk.getId().hashCode();
560 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
561 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
562 }
563
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700564 private PendingIntent buildInitPendingIntent(VolumeInfo vol) {
565 final Intent intent = new Intent();
566 intent.setClassName("com.android.settings",
567 "com.android.settings.deviceinfo.StorageWizardInit");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700568 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
569
570 final int requestKey = vol.getId().hashCode();
571 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
572 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700573 }
574
575 private PendingIntent buildUnmountPendingIntent(VolumeInfo vol) {
576 final Intent intent = new Intent();
577 intent.setClassName("com.android.settings",
578 "com.android.settings.deviceinfo.StorageUnmountReceiver");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700579 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
580
581 final int requestKey = vol.getId().hashCode();
582 return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
583 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700584 }
585
586 private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) {
587 final Intent intent = vol.buildBrowseIntent();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700588
589 final int requestKey = vol.getId().hashCode();
590 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
591 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700592 }
593
Jeff Sharkey50a05452015-04-29 11:24:52 -0700594 private PendingIntent buildVolumeSettingsPendingIntent(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700595 final Intent intent = new Intent();
Jeff Sharkey50a05452015-04-29 11:24:52 -0700596 switch (vol.getType()) {
597 case VolumeInfo.TYPE_PRIVATE:
598 intent.setClassName("com.android.settings",
599 "com.android.settings.Settings$PrivateVolumeSettingsActivity");
600 break;
601 case VolumeInfo.TYPE_PUBLIC:
602 intent.setClassName("com.android.settings",
603 "com.android.settings.Settings$PublicVolumeSettingsActivity");
604 break;
605 default:
606 return null;
607 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700608 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
609
610 final int requestKey = vol.getId().hashCode();
611 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
612 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
613 }
614
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700615 private PendingIntent buildSnoozeIntent(String fsUuid) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700616 final Intent intent = new Intent(ACTION_SNOOZE_VOLUME);
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700617 intent.putExtra(VolumeRecord.EXTRA_FS_UUID, fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700618
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700619 final int requestKey = fsUuid.hashCode();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700620 return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
621 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
San Mehat64e6a452010-02-04 20:53:48 -0800622 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700623
624 private PendingIntent buildForgetPendingIntent(VolumeRecord rec) {
625 final Intent intent = new Intent();
626 intent.setClassName("com.android.settings",
627 "com.android.settings.Settings$PrivateVolumeForgetActivity");
628 intent.putExtra(VolumeRecord.EXTRA_FS_UUID, rec.getFsUuid());
629
630 final int requestKey = rec.getFsUuid().hashCode();
631 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
632 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
633 }
Jeff Sharkey50a05452015-04-29 11:24:52 -0700634
635 private PendingIntent buildWizardMigratePendingIntent(MoveInfo move) {
636 final Intent intent = new Intent();
637 intent.setClassName("com.android.settings",
638 "com.android.settings.deviceinfo.StorageWizardMigrateProgress");
639 intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId);
640
641 final VolumeInfo vol = mStorageManager.findVolumeByQualifiedUuid(move.volumeUuid);
642 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
643
644 return PendingIntent.getActivityAsUser(mContext, move.moveId, intent,
645 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
646 }
647
648 private PendingIntent buildWizardMovePendingIntent(MoveInfo move) {
649 final Intent intent = new Intent();
650 intent.setClassName("com.android.settings",
651 "com.android.settings.deviceinfo.StorageWizardMoveProgress");
652 intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId);
653
654 return PendingIntent.getActivityAsUser(mContext, move.moveId, intent,
655 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
656 }
657
658 private PendingIntent buildWizardReadyPendingIntent(DiskInfo disk) {
659 final Intent intent = new Intent();
660 intent.setClassName("com.android.settings",
661 "com.android.settings.deviceinfo.StorageWizardReady");
662 intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId());
663
664 final int requestKey = disk.getId().hashCode();
665 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
666 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
667 }
San Mehat64e6a452010-02-04 20:53:48 -0800668}