blob: 818f5ee6f9851269de72f034d4388ab4888f393c [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.usb;
import android.app.Notification;
import android.app.Notification.Action;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserHandle;
import android.os.storage.DiskInfo;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.Log;
import com.android.internal.R;
import com.android.systemui.SystemUI;
import java.util.List;
public class StorageNotification extends SystemUI {
private static final String TAG = "StorageNotification";
private static final int NOTIF_ID = 0x53544f52; // STOR
private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
// TODO: delay some notifications to avoid bumpy fast operations
// TODO: annoy user when private media is missing
private NotificationManager mNotificationManager;
private StorageManager mStorageManager;
private final StorageEventListener mListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
onVolumeStateChangedInternal(vol, oldState, newState);
}
@Override
public void onVolumeMetadataChanged(VolumeInfo vol) {
// Avoid kicking notifications when getting early metadata before
// mounted. If already mounted, we're being kicked because of a
// nickname or init'ed change.
if (vol.getState() == VolumeInfo.STATE_MOUNTED) {
onVolumeStateChangedInternal(vol, vol.getState(), vol.getState());
}
}
};
private final BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: kick this onto background thread
final String volId = intent.getStringExtra(VolumeInfo.EXTRA_VOLUME_ID);
mStorageManager.setVolumeSnoozed(volId, true);
}
};
@Override
public void start() {
mNotificationManager = mContext.getSystemService(NotificationManager.class);
mStorageManager = mContext.getSystemService(StorageManager.class);
mStorageManager.registerListener(mListener);
mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME),
android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
// Kick current state into place
final List<VolumeInfo> vols = mStorageManager.getVolumes();
for (VolumeInfo vol : vols) {
onVolumeStateChangedInternal(vol, vol.getState(), vol.getState());
}
}
public void onVolumeStateChangedInternal(VolumeInfo vol, int oldState, int newState) {
// We only care about public volumes
if (vol.getType() != VolumeInfo.TYPE_PUBLIC) {
return;
}
Log.d(TAG, vol.toString());
// New state means we tear down any old notifications
mNotificationManager.cancelAsUser(vol.getId(), NOTIF_ID, UserHandle.ALL);
switch (newState) {
case VolumeInfo.STATE_UNMOUNTED:
onVolumeUnmounted(vol);
break;
case VolumeInfo.STATE_MOUNTING:
onVolumeMounting(vol);
break;
case VolumeInfo.STATE_MOUNTED:
onVolumeMounted(vol);
break;
case VolumeInfo.STATE_FORMATTING:
onVolumeFormatting(vol);
break;
case VolumeInfo.STATE_UNMOUNTING:
onVolumeUnmounting(vol);
break;
case VolumeInfo.STATE_UNMOUNTABLE:
onVolumeUnmountable(vol);
break;
case VolumeInfo.STATE_REMOVED:
onVolumeRemoved(vol);
break;
}
}
private void onVolumeUnmounted(VolumeInfo vol) {
// Ignored
}
private void onVolumeMounting(VolumeInfo vol) {
final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId());
final CharSequence title = mContext.getString(
R.string.ext_media_checking_notification_title, disk.getDescription());
final CharSequence text = mContext.getString(
R.string.ext_media_checking_notification_message, disk.getDescription());
final Notification notif = buildNotificationBuilder(title, text)
.setSmallIcon(R.drawable.stat_notify_sdcard_prepare)
.setCategory(Notification.CATEGORY_PROGRESS)
.setPriority(Notification.PRIORITY_LOW)
.setOngoing(true)
.build();
mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
}
private void onVolumeMounted(VolumeInfo vol) {
// Don't annoy when user dismissed in past
if (vol.isSnoozed()) return;
final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId());
final Notification notif;
if (disk.isAdoptable() && !vol.isInited()) {
final CharSequence title = disk.getDescription();
final CharSequence text = mContext.getString(
R.string.ext_media_new_notification_message, disk.getDescription());
notif = buildNotificationBuilder(title, text)
.setSmallIcon(R.drawable.stat_notify_sdcard)
.addAction(new Action(0, mContext.getString(R.string.ext_media_init_action),
buildInitPendingIntent(vol)))
.addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action),
buildUnmountPendingIntent(vol)))
.setDeleteIntent(buildSnoozeIntent(vol))
.setCategory(Notification.CATEGORY_SYSTEM)
.build();
} else {
final CharSequence title = disk.getDescription();
final CharSequence text = mContext.getString(
R.string.ext_media_ready_notification_message, disk.getDescription());
notif = buildNotificationBuilder(title, text)
.setSmallIcon(R.drawable.stat_notify_sdcard)
.addAction(new Action(0, mContext.getString(R.string.ext_media_browse_action),
buildBrowsePendingIntent(vol)))
.addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action),
buildUnmountPendingIntent(vol)))
.setDeleteIntent(buildSnoozeIntent(vol))
.setCategory(Notification.CATEGORY_SYSTEM)
.setPriority(Notification.PRIORITY_LOW)
.build();
}
mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
}
private void onVolumeFormatting(VolumeInfo vol) {
// Ignored
}
private void onVolumeUnmounting(VolumeInfo vol) {
final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId());
final CharSequence title = mContext.getString(
R.string.ext_media_unmounting_notification_title, disk.getDescription());
final CharSequence text = mContext.getString(
R.string.ext_media_unmounting_notification_message, disk.getDescription());
final Notification notif = buildNotificationBuilder(title, text)
.setSmallIcon(R.drawable.stat_notify_sdcard_prepare)
.setCategory(Notification.CATEGORY_PROGRESS)
.setPriority(Notification.PRIORITY_LOW)
.setOngoing(true)
.build();
mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
}
private void onVolumeUnmountable(VolumeInfo vol) {
final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId());
final CharSequence title = mContext.getString(
R.string.ext_media_unmountable_notification_title, disk.getDescription());
final CharSequence text = mContext.getString(
R.string.ext_media_unmountable_notification_message, disk.getDescription());
final Notification notif = buildNotificationBuilder(title, text)
.setSmallIcon(R.drawable.stat_notify_sdcard)
.setContentIntent(buildDetailsPendingIntent(vol))
.setCategory(Notification.CATEGORY_ERROR)
.build();
mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
}
private void onVolumeRemoved(VolumeInfo vol) {
if (!vol.isPrimary()) {
// Ignore non-primary media
return;
}
final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId());
final CharSequence title = mContext.getString(
R.string.ext_media_nomedia_notification_title, disk.getDescription());
final CharSequence text = mContext.getString(
R.string.ext_media_nomedia_notification_message, disk.getDescription());
final Notification notif = buildNotificationBuilder(title, text)
.setSmallIcon(R.drawable.stat_notify_sdcard)
.setCategory(Notification.CATEGORY_ERROR)
.build();
mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
}
private Notification.Builder buildNotificationBuilder(CharSequence title, CharSequence text) {
return new Notification.Builder(mContext)
.setColor(mContext.getColor(R.color.system_notification_accent_color))
.setContentTitle(title)
.setContentText(text)
.setStyle(new Notification.BigTextStyle().bigText(text))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setLocalOnly(true);
}
private PendingIntent buildInitPendingIntent(VolumeInfo vol) {
final Intent intent = new Intent();
intent.setClassName("com.android.settings",
"com.android.settings.deviceinfo.StorageWizardInit");
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
final int requestKey = vol.getId().hashCode();
return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
}
private PendingIntent buildUnmountPendingIntent(VolumeInfo vol) {
final Intent intent = new Intent();
intent.setClassName("com.android.settings",
"com.android.settings.deviceinfo.StorageUnmountReceiver");
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
final int requestKey = vol.getId().hashCode();
return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
}
private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) {
final Intent intent = vol.buildBrowseIntent();
final int requestKey = vol.getId().hashCode();
return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
}
private PendingIntent buildDetailsPendingIntent(VolumeInfo vol) {
final Intent intent = new Intent();
intent.setClassName("com.android.settings",
"com.android.settings.Settings$StorageVolumeSettingsActivity");
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
final int requestKey = vol.getId().hashCode();
return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
}
private PendingIntent buildSnoozeIntent(VolumeInfo vol) {
final Intent intent = new Intent(ACTION_SNOOZE_VOLUME);
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
final int requestKey = vol.getId().hashCode();
return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
}
}