blob: 97d5e1078312ffbfda1b82fc086e909292b0875f [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
Philip P. Moltmann75b230b2016-04-11 15:27:58 -070019import android.annotation.NonNull;
San Mehat64e6a452010-02-04 20:53:48 -080020import android.app.Notification;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070021import android.app.Notification.Action;
San Mehat64e6a452010-02-04 20:53:48 -080022import android.app.NotificationManager;
23import android.app.PendingIntent;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070024import android.content.BroadcastReceiver;
25import android.content.Context;
San Mehat64e6a452010-02-04 20:53:48 -080026import android.content.Intent;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070027import android.content.IntentFilter;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070028import android.content.pm.PackageManager;
29import android.content.pm.PackageManager.MoveCallback;
Jeff Sharkey50a05452015-04-29 11:24:52 -070030import android.os.Bundle;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070031import android.os.Handler;
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -070032import android.os.UserHandle;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070033import android.os.storage.DiskInfo;
San Mehatb1043402010-02-05 08:26:50 -080034import android.os.storage.StorageEventListener;
35import android.os.storage.StorageManager;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070036import android.os.storage.VolumeInfo;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070037import android.os.storage.VolumeRecord;
38import android.text.TextUtils;
39import android.text.format.DateUtils;
John Spurlockcd686b52013-06-05 10:13:46 -040040import android.util.Log;
Jeff Sharkey50a05452015-04-29 11:24:52 -070041import android.util.SparseArray;
San Mehat64e6a452010-02-04 20:53:48 -080042
Jeff Sharkey56bd3122015-04-14 10:30:34 -070043import com.android.internal.R;
John Spurlock3e309b22013-06-25 11:01:29 -040044import com.android.systemui.SystemUI;
45
Jeff Sharkey56bd3122015-04-14 10:30:34 -070046import java.util.List;
47
John Spurlock3e309b22013-06-25 11:01:29 -040048public class StorageNotification extends SystemUI {
San Mehat64e6a452010-02-04 20:53:48 -080049 private static final String TAG = "StorageNotification";
50
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070051 private static final int PUBLIC_ID = 0x53505542; // SPUB
52 private static final int PRIVATE_ID = 0x53505256; // SPRV
53 private static final int DISK_ID = 0x5344534b; // SDSK
54 private static final int MOVE_ID = 0x534d4f56; // SMOV
Daniel Sandlerc07907e2010-02-22 15:08:41 -050055
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070056 private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
Jeff Sharkeydf27d3b2015-06-16 13:51:48 -070057 private static final String ACTION_FINISH_WIZARD = "com.android.systemui.action.FINISH_WIZARD";
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070058
Jeff Sharkey56bd3122015-04-14 10:30:34 -070059 // TODO: delay some notifications to avoid bumpy fast operations
San Mehat64e6a452010-02-04 20:53:48 -080060
Jeff Sharkey56bd3122015-04-14 10:30:34 -070061 private NotificationManager mNotificationManager;
San Mehatb1043402010-02-05 08:26:50 -080062 private StorageManager mStorageManager;
San Mehat64e6a452010-02-04 20:53:48 -080063
Jeff Sharkey50a05452015-04-29 11:24:52 -070064 private static class MoveInfo {
65 public int moveId;
66 public Bundle extras;
67 public String packageName;
68 public String label;
69 public String volumeUuid;
70 }
71
72 private final SparseArray<MoveInfo> mMoves = new SparseArray<>();
73
Jeff Sharkey56bd3122015-04-14 10:30:34 -070074 private final StorageEventListener mListener = new StorageEventListener() {
75 @Override
76 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070077 onVolumeStateChangedInternal(vol);
John Spurlock3e309b22013-06-25 11:01:29 -040078 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070079
80 @Override
Jeff Sharkey50a05452015-04-29 11:24:52 -070081 public void onVolumeRecordChanged(VolumeRecord rec) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070082 // Avoid kicking notifications when getting early metadata before
83 // mounted. If already mounted, we're being kicked because of a
84 // nickname or init'ed change.
Jeff Sharkey50a05452015-04-29 11:24:52 -070085 final VolumeInfo vol = mStorageManager.findVolumeByUuid(rec.getFsUuid());
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070086 if (vol != null && vol.isMountedReadable()) {
87 onVolumeStateChangedInternal(vol);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -070088 }
Jeff Sharkey50a05452015-04-29 11:24:52 -070089 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070090
Jeff Sharkey50a05452015-04-29 11:24:52 -070091 @Override
92 public void onVolumeForgotten(String fsUuid) {
93 // Stop annoying the user
94 mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -070095 }
96
97 @Override
98 public void onDiskScanned(DiskInfo disk, int volumeCount) {
99 onDiskScannedInternal(disk, volumeCount);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700100 }
Philip P. Moltmann75b230b2016-04-11 15:27:58 -0700101
102 @Override
103 public void onDiskDestroyed(DiskInfo disk) {
104 onDiskDestroyedInternal(disk);
105 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700106 };
107
108 private final BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() {
109 @Override
110 public void onReceive(Context context, Intent intent) {
111 // TODO: kick this onto background thread
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700112 final String fsUuid = intent.getStringExtra(VolumeRecord.EXTRA_FS_UUID);
113 mStorageManager.setVolumeSnoozed(fsUuid, true);
114 }
115 };
116
Jeff Sharkeydf27d3b2015-06-16 13:51:48 -0700117 private final BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
118 @Override
119 public void onReceive(Context context, Intent intent) {
120 // When finishing the adoption wizard, clean up any notifications
121 // for moving primary storage
122 mNotificationManager.cancelAsUser(null, MOVE_ID, UserHandle.ALL);
123 }
124 };
125
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700126 private final MoveCallback mMoveCallback = new MoveCallback() {
127 @Override
Jeff Sharkey50a05452015-04-29 11:24:52 -0700128 public void onCreated(int moveId, Bundle extras) {
129 final MoveInfo move = new MoveInfo();
130 move.moveId = moveId;
131 move.extras = extras;
132 if (extras != null) {
133 move.packageName = extras.getString(Intent.EXTRA_PACKAGE_NAME);
134 move.label = extras.getString(Intent.EXTRA_TITLE);
135 move.volumeUuid = extras.getString(VolumeRecord.EXTRA_FS_UUID);
136 }
137 mMoves.put(moveId, move);
138 }
139
140 @Override
141 public void onStatusChanged(int moveId, int status, long estMillis) {
142 final MoveInfo move = mMoves.get(moveId);
143 if (move == null) {
144 Log.w(TAG, "Ignoring unknown move " + moveId);
145 return;
146 }
147
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700148 if (PackageManager.isMoveStatusFinished(status)) {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700149 onMoveFinished(move, status);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700150 } else {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700151 onMoveProgress(move, status, estMillis);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700152 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700153 }
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700154 };
San Mehat64e6a452010-02-04 20:53:48 -0800155
John Spurlock3e309b22013-06-25 11:01:29 -0400156 @Override
157 public void start() {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700158 mNotificationManager = mContext.getSystemService(NotificationManager.class);
John Spurlock3e309b22013-06-25 11:01:29 -0400159
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700160 mStorageManager = mContext.getSystemService(StorageManager.class);
161 mStorageManager.registerListener(mListener);
Daniel Sandler5b8743f2010-11-03 09:43:46 -0400162
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700163 mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME),
164 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
Jeff Sharkeydf27d3b2015-06-16 13:51:48 -0700165 mContext.registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_WIZARD),
166 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700167
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700168 // Kick current state into place
Jeff Sharkeyf5a6bd72015-05-19 14:42:38 -0700169 final List<DiskInfo> disks = mStorageManager.getDisks();
170 for (DiskInfo disk : disks) {
171 onDiskScannedInternal(disk, disk.volumeCount);
172 }
173
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700174 final List<VolumeInfo> vols = mStorageManager.getVolumes();
175 for (VolumeInfo vol : vols) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700176 onVolumeStateChangedInternal(vol);
177 }
178
179 mContext.getPackageManager().registerMoveCallback(mMoveCallback, new Handler());
180
181 updateMissingPrivateVolumes();
182 }
183
184 private void updateMissingPrivateVolumes() {
185 final List<VolumeRecord> recs = mStorageManager.getVolumeRecords();
186 for (VolumeRecord rec : recs) {
187 if (rec.getType() != VolumeInfo.TYPE_PRIVATE) continue;
188
189 final String fsUuid = rec.getFsUuid();
190 final VolumeInfo info = mStorageManager.findVolumeByUuid(fsUuid);
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700191 if ((info != null && info.isMountedWritable()) || rec.isSnoozed()) {
192 // Yay, private volume is here, or user snoozed
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700193 mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL);
194
195 } else {
196 // Boo, annoy the user to reinsert the private volume
197 final CharSequence title = mContext.getString(R.string.ext_media_missing_title,
198 rec.getNickname());
199 final CharSequence text = mContext.getString(R.string.ext_media_missing_message);
200
Adrian Roose25c18d2016-06-17 15:59:49 -0700201 Notification.Builder builder = new Notification.Builder(mContext)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700202 .setSmallIcon(R.drawable.ic_sd_card_48dp)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700203 .setColor(mContext.getColor(R.color.system_notification_accent_color))
204 .setContentTitle(title)
205 .setContentText(text)
Jeff Sharkey50a05452015-04-29 11:24:52 -0700206 .setContentIntent(buildForgetPendingIntent(rec))
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700207 .setStyle(new Notification.BigTextStyle().bigText(text))
208 .setVisibility(Notification.VISIBILITY_PUBLIC)
209 .setLocalOnly(true)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700210 .setCategory(Notification.CATEGORY_SYSTEM)
Adrian Roose25c18d2016-06-17 15:59:49 -0700211 .setDeleteIntent(buildSnoozeIntent(fsUuid));
212 SystemUI.overrideNotificationAppName(mContext, builder);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700213
Adrian Roose25c18d2016-06-17 15:59:49 -0700214 mNotificationManager.notifyAsUser(fsUuid, PRIVATE_ID, builder
215 .build(), UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700216 }
San Mehat64e6a452010-02-04 20:53:48 -0800217 }
218 }
219
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700220 private void onDiskScannedInternal(DiskInfo disk, int volumeCount) {
Jeff Sharkeyf5a6bd72015-05-19 14:42:38 -0700221 if (volumeCount == 0 && disk.size > 0) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700222 // No supported volumes found, give user option to format
223 final CharSequence title = mContext.getString(
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700224 R.string.ext_media_unsupported_notification_title, disk.getDescription());
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700225 final CharSequence text = mContext.getString(
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700226 R.string.ext_media_unsupported_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800227
Adrian Roose25c18d2016-06-17 15:59:49 -0700228 Notification.Builder builder = new Notification.Builder(mContext)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700229 .setSmallIcon(getSmallIcon(disk, VolumeInfo.STATE_UNMOUNTABLE))
230 .setColor(mContext.getColor(R.color.system_notification_accent_color))
231 .setContentTitle(title)
232 .setContentText(text)
Jeff Sharkey50a05452015-04-29 11:24:52 -0700233 .setContentIntent(buildInitPendingIntent(disk))
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700234 .setStyle(new Notification.BigTextStyle().bigText(text))
235 .setVisibility(Notification.VISIBILITY_PUBLIC)
236 .setLocalOnly(true)
Adrian Roose25c18d2016-06-17 15:59:49 -0700237 .setCategory(Notification.CATEGORY_ERROR);
238 SystemUI.overrideNotificationAppName(mContext, builder);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700239
Adrian Roose25c18d2016-06-17 15:59:49 -0700240 mNotificationManager.notifyAsUser(disk.getId(), DISK_ID, builder.build(),
241 UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700242
243 } else {
244 // Yay, we have volumes!
245 mNotificationManager.cancelAsUser(disk.getId(), DISK_ID, UserHandle.ALL);
246 }
247 }
248
Philip P. Moltmann75b230b2016-04-11 15:27:58 -0700249 /**
250 * Remove all notifications for a disk when it goes away.
251 *
252 * @param disk The disk that went away.
253 */
254 private void onDiskDestroyedInternal(@NonNull DiskInfo disk) {
255 mNotificationManager.cancelAsUser(disk.getId(), DISK_ID, UserHandle.ALL);
256 }
257
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700258 private void onVolumeStateChangedInternal(VolumeInfo vol) {
259 switch (vol.getType()) {
260 case VolumeInfo.TYPE_PRIVATE:
261 onPrivateVolumeStateChangedInternal(vol);
262 break;
263 case VolumeInfo.TYPE_PUBLIC:
264 onPublicVolumeStateChangedInternal(vol);
265 break;
266 }
267 }
268
269 private void onPrivateVolumeStateChangedInternal(VolumeInfo vol) {
270 Log.d(TAG, "Notifying about private volume: " + vol.toString());
271
272 updateMissingPrivateVolumes();
273 }
274
275 private void onPublicVolumeStateChangedInternal(VolumeInfo vol) {
276 Log.d(TAG, "Notifying about public volume: " + vol.toString());
San Mehat64e6a452010-02-04 20:53:48 -0800277
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700278 final Notification notif;
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700279 switch (vol.getState()) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700280 case VolumeInfo.STATE_UNMOUNTED:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700281 notif = onVolumeUnmounted(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700282 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700283 case VolumeInfo.STATE_CHECKING:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700284 notif = onVolumeChecking(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700285 break;
286 case VolumeInfo.STATE_MOUNTED:
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700287 case VolumeInfo.STATE_MOUNTED_READ_ONLY:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700288 notif = onVolumeMounted(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700289 break;
290 case VolumeInfo.STATE_FORMATTING:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700291 notif = onVolumeFormatting(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700292 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700293 case VolumeInfo.STATE_EJECTING:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700294 notif = onVolumeEjecting(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700295 break;
296 case VolumeInfo.STATE_UNMOUNTABLE:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700297 notif = onVolumeUnmountable(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700298 break;
299 case VolumeInfo.STATE_REMOVED:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700300 notif = onVolumeRemoved(vol);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700301 break;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700302 case VolumeInfo.STATE_BAD_REMOVAL:
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700303 notif = onVolumeBadRemoval(vol);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700304 break;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700305 default:
306 notif = null;
307 break;
308 }
309
310 if (notif != null) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700311 mNotificationManager.notifyAsUser(vol.getId(), PUBLIC_ID, notif, UserHandle.ALL);
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700312 } else {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700313 mNotificationManager.cancelAsUser(vol.getId(), PUBLIC_ID, UserHandle.ALL);
San Mehat64e6a452010-02-04 20:53:48 -0800314 }
315 }
316
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700317 private Notification onVolumeUnmounted(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700318 // Ignored
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700319 return null;
San Mehat64e6a452010-02-04 20:53:48 -0800320 }
321
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700322 private Notification onVolumeChecking(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700323 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700324 final CharSequence title = mContext.getString(
325 R.string.ext_media_checking_notification_title, disk.getDescription());
326 final CharSequence text = mContext.getString(
327 R.string.ext_media_checking_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800328
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700329 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700330 .setCategory(Notification.CATEGORY_PROGRESS)
331 .setPriority(Notification.PRIORITY_LOW)
332 .setOngoing(true)
333 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700334 }
San Mehat64e6a452010-02-04 20:53:48 -0800335
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700336 private Notification onVolumeMounted(VolumeInfo vol) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700337 final VolumeRecord rec = mStorageManager.findRecordByUuid(vol.getFsUuid());
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700338 final DiskInfo disk = vol.getDisk();
Makoto Onukib138cb22015-06-23 17:32:02 -0700339
340 // Don't annoy when user dismissed in past. (But make sure the disk is adoptable; we
341 // used to allow snoozing non-adoptable disks too.)
342 if (rec.isSnoozed() && disk.isAdoptable()) {
343 return null;
344 }
345
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700346 if (disk.isAdoptable() && !rec.isInited()) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700347 final CharSequence title = disk.getDescription();
348 final CharSequence text = mContext.getString(
349 R.string.ext_media_new_notification_message, disk.getDescription());
San Mehat64e6a452010-02-04 20:53:48 -0800350
Jeff Sharkey50a05452015-04-29 11:24:52 -0700351 final PendingIntent initIntent = buildInitPendingIntent(vol);
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700352 return buildNotificationBuilder(vol, title, text)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700353 .addAction(new Action(R.drawable.ic_settings_24dp,
354 mContext.getString(R.string.ext_media_init_action), initIntent))
355 .addAction(new Action(R.drawable.ic_eject_24dp,
356 mContext.getString(R.string.ext_media_unmount_action),
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700357 buildUnmountPendingIntent(vol)))
Jeff Sharkey50a05452015-04-29 11:24:52 -0700358 .setContentIntent(initIntent)
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700359 .setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700360 .setCategory(Notification.CATEGORY_SYSTEM)
361 .build();
John Spurlock209bede2013-07-17 12:23:27 -0400362
San Mehat64e6a452010-02-04 20:53:48 -0800363 } else {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700364 final CharSequence title = disk.getDescription();
365 final CharSequence text = mContext.getString(
366 R.string.ext_media_ready_notification_message, disk.getDescription());
367
Jeff Sharkey50a05452015-04-29 11:24:52 -0700368 final PendingIntent browseIntent = buildBrowsePendingIntent(vol);
Makoto Onukib138cb22015-06-23 17:32:02 -0700369 final Notification.Builder builder = buildNotificationBuilder(vol, title, text)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700370 .addAction(new Action(R.drawable.ic_folder_24dp,
371 mContext.getString(R.string.ext_media_browse_action),
Jeff Sharkey50a05452015-04-29 11:24:52 -0700372 browseIntent))
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700373 .addAction(new Action(R.drawable.ic_eject_24dp,
374 mContext.getString(R.string.ext_media_unmount_action),
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700375 buildUnmountPendingIntent(vol)))
Jeff Sharkey50a05452015-04-29 11:24:52 -0700376 .setContentIntent(browseIntent)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700377 .setCategory(Notification.CATEGORY_SYSTEM)
Makoto Onukib138cb22015-06-23 17:32:02 -0700378 .setPriority(Notification.PRIORITY_LOW);
379 // Non-adoptable disks can't be snoozed.
380 if (disk.isAdoptable()) {
381 builder.setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()));
382 }
383
384 return builder.build();
San Mehat64e6a452010-02-04 20:53:48 -0800385 }
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700386 }
387
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700388 private Notification onVolumeFormatting(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700389 // Ignored
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700390 return null;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700391 }
392
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700393 private Notification onVolumeEjecting(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700394 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700395 final CharSequence title = mContext.getString(
396 R.string.ext_media_unmounting_notification_title, disk.getDescription());
397 final CharSequence text = mContext.getString(
398 R.string.ext_media_unmounting_notification_message, disk.getDescription());
399
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700400 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700401 .setCategory(Notification.CATEGORY_PROGRESS)
402 .setPriority(Notification.PRIORITY_LOW)
403 .setOngoing(true)
404 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700405 }
406
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700407 private Notification onVolumeUnmountable(VolumeInfo vol) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700408 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700409 final CharSequence title = mContext.getString(
410 R.string.ext_media_unmountable_notification_title, disk.getDescription());
411 final CharSequence text = mContext.getString(
412 R.string.ext_media_unmountable_notification_message, disk.getDescription());
413
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700414 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700415 .setContentIntent(buildInitPendingIntent(vol))
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 onVolumeRemoved(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700421 if (!vol.isPrimary()) {
422 // Ignore non-primary media
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700423 return null;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700424 }
425
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700426 final DiskInfo disk = vol.getDisk();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700427 final CharSequence title = mContext.getString(
428 R.string.ext_media_nomedia_notification_title, disk.getDescription());
429 final CharSequence text = mContext.getString(
430 R.string.ext_media_nomedia_notification_message, disk.getDescription());
431
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700432 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700433 .setCategory(Notification.CATEGORY_ERROR)
434 .build();
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700435 }
436
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700437 private Notification onVolumeBadRemoval(VolumeInfo vol) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700438 if (!vol.isPrimary()) {
439 // Ignore non-primary media
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700440 return null;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700441 }
442
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700443 final DiskInfo disk = vol.getDisk();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700444 final CharSequence title = mContext.getString(
445 R.string.ext_media_badremoval_notification_title, disk.getDescription());
446 final CharSequence text = mContext.getString(
447 R.string.ext_media_badremoval_notification_message, disk.getDescription());
448
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700449 return buildNotificationBuilder(vol, title, text)
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700450 .setCategory(Notification.CATEGORY_ERROR)
451 .build();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700452 }
453
Jeff Sharkey50a05452015-04-29 11:24:52 -0700454 private void onMoveProgress(MoveInfo move, int status, long estMillis) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700455 final CharSequence title;
Jeff Sharkey50a05452015-04-29 11:24:52 -0700456 if (!TextUtils.isEmpty(move.label)) {
457 title = mContext.getString(R.string.ext_media_move_specific_title, move.label);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700458 } else {
459 title = mContext.getString(R.string.ext_media_move_title);
460 }
461
462 final CharSequence text;
463 if (estMillis < 0) {
464 text = null;
465 } else {
466 text = DateUtils.formatDuration(estMillis);
467 }
468
Jeff Sharkey50a05452015-04-29 11:24:52 -0700469 final PendingIntent intent;
470 if (move.packageName != null) {
471 intent = buildWizardMovePendingIntent(move);
472 } else {
473 intent = buildWizardMigratePendingIntent(move);
474 }
475
Adrian Roose25c18d2016-06-17 15:59:49 -0700476 Notification.Builder builder = new Notification.Builder(mContext)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700477 .setSmallIcon(R.drawable.ic_sd_card_48dp)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700478 .setColor(mContext.getColor(R.color.system_notification_accent_color))
479 .setContentTitle(title)
480 .setContentText(text)
Jeff Sharkey50a05452015-04-29 11:24:52 -0700481 .setContentIntent(intent)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700482 .setStyle(new Notification.BigTextStyle().bigText(text))
483 .setVisibility(Notification.VISIBILITY_PUBLIC)
484 .setLocalOnly(true)
485 .setCategory(Notification.CATEGORY_PROGRESS)
486 .setPriority(Notification.PRIORITY_LOW)
487 .setProgress(100, status, false)
Adrian Roose25c18d2016-06-17 15:59:49 -0700488 .setOngoing(true);
489 SystemUI.overrideNotificationAppName(mContext, builder);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700490
Adrian Roose25c18d2016-06-17 15:59:49 -0700491 mNotificationManager.notifyAsUser(move.packageName, MOVE_ID,
492 builder.build(), UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700493 }
494
Jeff Sharkey50a05452015-04-29 11:24:52 -0700495 private void onMoveFinished(MoveInfo move, int status) {
496 if (move.packageName != null) {
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700497 // We currently ignore finished app moves; just clear the last
498 // published progress
Jeff Sharkey50a05452015-04-29 11:24:52 -0700499 mNotificationManager.cancelAsUser(move.packageName, MOVE_ID, UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700500 return;
501 }
502
Jeff Sharkey50a05452015-04-29 11:24:52 -0700503 final VolumeInfo privateVol = mContext.getPackageManager().getPrimaryStorageCurrentVolume();
504 final String descrip = mStorageManager.getBestVolumeDescription(privateVol);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700505
506 final CharSequence title;
507 final CharSequence text;
508 if (status == PackageManager.MOVE_SUCCEEDED) {
509 title = mContext.getString(R.string.ext_media_move_success_title);
510 text = mContext.getString(R.string.ext_media_move_success_message, descrip);
511 } else {
512 title = mContext.getString(R.string.ext_media_move_failure_title);
513 text = mContext.getString(R.string.ext_media_move_failure_message);
514 }
515
Jeff Sharkey50a05452015-04-29 11:24:52 -0700516 // Jump back into the wizard flow if we moved to a real disk
517 final PendingIntent intent;
518 if (privateVol != null && privateVol.getDisk() != null) {
519 intent = buildWizardReadyPendingIntent(privateVol.getDisk());
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700520 } else if (privateVol != null) {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700521 intent = buildVolumeSettingsPendingIntent(privateVol);
Jeff Sharkeyef10ee02015-07-05 14:17:27 -0700522 } else {
523 intent = null;
Jeff Sharkey50a05452015-04-29 11:24:52 -0700524 }
525
Adrian Roose25c18d2016-06-17 15:59:49 -0700526 Notification.Builder builder = new Notification.Builder(mContext)
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700527 .setSmallIcon(R.drawable.ic_sd_card_48dp)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700528 .setColor(mContext.getColor(R.color.system_notification_accent_color))
529 .setContentTitle(title)
530 .setContentText(text)
Jeff Sharkey50a05452015-04-29 11:24:52 -0700531 .setContentIntent(intent)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700532 .setStyle(new Notification.BigTextStyle().bigText(text))
533 .setVisibility(Notification.VISIBILITY_PUBLIC)
534 .setLocalOnly(true)
535 .setCategory(Notification.CATEGORY_SYSTEM)
536 .setPriority(Notification.PRIORITY_LOW)
Adrian Roose25c18d2016-06-17 15:59:49 -0700537 .setAutoCancel(true);
538 SystemUI.overrideNotificationAppName(mContext, builder);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700539
Adrian Roose25c18d2016-06-17 15:59:49 -0700540 mNotificationManager.notifyAsUser(move.packageName, MOVE_ID, builder.build(),
541 UserHandle.ALL);
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700542 }
543
544 private int getSmallIcon(DiskInfo disk, int state) {
545 if (disk.isSd()) {
546 switch (state) {
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700547 case VolumeInfo.STATE_CHECKING:
548 case VolumeInfo.STATE_EJECTING:
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700549 return R.drawable.ic_sd_card_48dp;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700550 default:
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700551 return R.drawable.ic_sd_card_48dp;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700552 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700553 } else if (disk.isUsb()) {
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700554 return R.drawable.ic_usb_48dp;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700555 } else {
Jeff Sharkeya49d5fc2015-05-13 11:40:30 -0700556 return R.drawable.ic_sd_card_48dp;
Jeff Sharkeye6c04f92015-04-18 21:38:05 -0700557 }
558 }
559
560 private Notification.Builder buildNotificationBuilder(VolumeInfo vol, CharSequence title,
561 CharSequence text) {
Adrian Roose25c18d2016-06-17 15:59:49 -0700562 Notification.Builder builder = new Notification.Builder(mContext)
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700563 .setSmallIcon(getSmallIcon(vol.getDisk(), vol.getState()))
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700564 .setColor(mContext.getColor(R.color.system_notification_accent_color))
565 .setContentTitle(title)
566 .setContentText(text)
567 .setStyle(new Notification.BigTextStyle().bigText(text))
568 .setVisibility(Notification.VISIBILITY_PUBLIC)
569 .setLocalOnly(true);
Adrian Roose25c18d2016-06-17 15:59:49 -0700570 overrideNotificationAppName(mContext, builder);
571 return builder;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700572 }
573
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700574 private PendingIntent buildInitPendingIntent(DiskInfo disk) {
575 final Intent intent = new Intent();
576 intent.setClassName("com.android.settings",
577 "com.android.settings.deviceinfo.StorageWizardInit");
578 intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId());
579
580 final int requestKey = disk.getId().hashCode();
581 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
582 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
583 }
584
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700585 private PendingIntent buildInitPendingIntent(VolumeInfo vol) {
586 final Intent intent = new Intent();
587 intent.setClassName("com.android.settings",
588 "com.android.settings.deviceinfo.StorageWizardInit");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700589 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
590
591 final int requestKey = vol.getId().hashCode();
592 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
593 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700594 }
595
596 private PendingIntent buildUnmountPendingIntent(VolumeInfo vol) {
597 final Intent intent = new Intent();
598 intent.setClassName("com.android.settings",
599 "com.android.settings.deviceinfo.StorageUnmountReceiver");
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700600 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
601
602 final int requestKey = vol.getId().hashCode();
603 return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
604 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700605 }
606
607 private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) {
608 final Intent intent = vol.buildBrowseIntent();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700609
610 final int requestKey = vol.getId().hashCode();
611 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
612 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700613 }
614
Jeff Sharkey50a05452015-04-29 11:24:52 -0700615 private PendingIntent buildVolumeSettingsPendingIntent(VolumeInfo vol) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700616 final Intent intent = new Intent();
Jeff Sharkey50a05452015-04-29 11:24:52 -0700617 switch (vol.getType()) {
618 case VolumeInfo.TYPE_PRIVATE:
619 intent.setClassName("com.android.settings",
620 "com.android.settings.Settings$PrivateVolumeSettingsActivity");
621 break;
622 case VolumeInfo.TYPE_PUBLIC:
623 intent.setClassName("com.android.settings",
624 "com.android.settings.Settings$PublicVolumeSettingsActivity");
625 break;
626 default:
627 return null;
628 }
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700629 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
630
631 final int requestKey = vol.getId().hashCode();
632 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
633 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
634 }
635
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700636 private PendingIntent buildSnoozeIntent(String fsUuid) {
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700637 final Intent intent = new Intent(ACTION_SNOOZE_VOLUME);
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700638 intent.putExtra(VolumeRecord.EXTRA_FS_UUID, fsUuid);
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700639
Jeff Sharkey52fc3c0f2015-06-14 20:58:54 -0700640 final int requestKey = fsUuid.hashCode();
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700641 return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
642 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
San Mehat64e6a452010-02-04 20:53:48 -0800643 }
Jeff Sharkeyb36586a2015-04-27 08:42:28 -0700644
645 private PendingIntent buildForgetPendingIntent(VolumeRecord rec) {
646 final Intent intent = new Intent();
647 intent.setClassName("com.android.settings",
648 "com.android.settings.Settings$PrivateVolumeForgetActivity");
649 intent.putExtra(VolumeRecord.EXTRA_FS_UUID, rec.getFsUuid());
650
651 final int requestKey = rec.getFsUuid().hashCode();
652 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
653 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
654 }
Jeff Sharkey50a05452015-04-29 11:24:52 -0700655
656 private PendingIntent buildWizardMigratePendingIntent(MoveInfo move) {
657 final Intent intent = new Intent();
658 intent.setClassName("com.android.settings",
659 "com.android.settings.deviceinfo.StorageWizardMigrateProgress");
660 intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId);
661
662 final VolumeInfo vol = mStorageManager.findVolumeByQualifiedUuid(move.volumeUuid);
Suprabh Shuklaaf6c4192016-04-27 14:05:54 -0700663 if (vol != null) {
664 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
665 }
Jeff Sharkey50a05452015-04-29 11:24:52 -0700666 return PendingIntent.getActivityAsUser(mContext, move.moveId, intent,
667 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
668 }
669
670 private PendingIntent buildWizardMovePendingIntent(MoveInfo move) {
671 final Intent intent = new Intent();
672 intent.setClassName("com.android.settings",
673 "com.android.settings.deviceinfo.StorageWizardMoveProgress");
674 intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId);
675
676 return PendingIntent.getActivityAsUser(mContext, move.moveId, intent,
677 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
678 }
679
680 private PendingIntent buildWizardReadyPendingIntent(DiskInfo disk) {
681 final Intent intent = new Intent();
682 intent.setClassName("com.android.settings",
683 "com.android.settings.deviceinfo.StorageWizardReady");
684 intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId());
685
686 final int requestKey = disk.getId().hashCode();
687 return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
688 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
689 }
San Mehat64e6a452010-02-04 20:53:48 -0800690}