Merge "Show toast when DND is blocking alarms" into ub-deskclock-fantasy
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d2a9394..f9a140e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -294,6 +294,9 @@
<!-- Text for action that presents a volume control to adjust alarm volume. -->
<string name="unmute_alarm_volume">Unmute</string>
+ <!-- Text to display when do-not-disturb is blocking alarms. -->
+ <string name="alarms_blocked_by_dnd">Device is set to total silence</string>
+
<!-- Title of the setting to change hardware button behavior. This string
should be changed for each piece of hardware. [CHAR LIMIT=20] -->
<string name="volume_button_setting_title">Volume buttons</string>
diff --git a/src/com/android/deskclock/DeskClock.java b/src/com/android/deskclock/DeskClock.java
index ab5b8c0..7e7fa55 100644
--- a/src/com/android/deskclock/DeskClock.java
+++ b/src/com/android/deskclock/DeskClock.java
@@ -20,13 +20,19 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.app.Fragment;
import android.app.FragmentManager;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.Snackbar;
@@ -34,6 +40,7 @@
import android.support.design.widget.TabLayout.ViewPagerOnTabSelectedListener;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.content.ContextCompat;
+import android.support.v4.os.BuildCompat;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
@@ -45,9 +52,9 @@
import android.widget.ImageButton;
import android.widget.ImageView;
-import com.android.deskclock.actionbarmenu.OptionsMenuManager;
import com.android.deskclock.actionbarmenu.MenuItemControllerFactory;
import com.android.deskclock.actionbarmenu.NightModeMenuItemController;
+import com.android.deskclock.actionbarmenu.OptionsMenuManager;
import com.android.deskclock.actionbarmenu.SettingsMenuItemController;
import com.android.deskclock.alarms.AlarmStateManager;
import com.android.deskclock.data.DataModel;
@@ -59,6 +66,8 @@
import com.android.deskclock.widget.RtlViewPager;
import com.android.deskclock.widget.toast.SnackbarManager;
+import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.media.AudioManager.FLAG_SHOW_UI;
import static android.media.AudioManager.STREAM_ALARM;
import static android.provider.Settings.System.CONTENT_URI;
@@ -80,6 +89,11 @@
/** The Uri to the settings entry that stores alarm stream volume. */
private static final Uri VOLUME_URI = Uri.withAppendedPath(CONTENT_URI, "volume_alarm_speaker");
+ /** The intent filter that identifies do-not-disturb change broadcasts. */
+ @SuppressLint("NewApi")
+ private static final IntentFilter DND_CHANGE_FILTER
+ = new IntentFilter(ACTION_INTERRUPTION_FILTER_CHANGED);
+
/** Models the interesting state of display the {@link #mFab} button may inhabit. */
private enum FabState { SHOWING, HIDE_ARMED, HIDING }
@@ -101,17 +115,26 @@
/** Updates the user interface to reflect the selected tab from the backing model. */
private final TabListener mTabChangeWatcher = new TabChangeWatcher();
- /** Displays a snackbar explaining that the alarm volume is muted, possibly after a delay. */
+ /** Displays a snackbar explaining that the alarm volume is muted. */
private final Runnable mShowMutedVolumeSnackbarRunnable = new ShowMutedVolumeSnackbarRunnable();
/** Observes alarm volume changes while the app is in the foreground. */
private final ContentObserver mAlarmVolumeChangeObserver = new AlarmVolumeChangeObserver();
+ /** Displays a snackbar explaining that do-not-disturb is blocking alarms. */
+ private final Runnable mShowDNDBlockingSnackbarRunnable = new ShowDNDBlockingSnackbarRunnable();
+
+ /** Observes do-not-disturb changes while the app is in the foreground. */
+ private final BroadcastReceiver mDoNotDisturbChangeReceiver = new DoNotDisturbChangeReceiver();
+
/** Used to query the alarm volume and display the system control to change the alarm volume. */
private AudioManager mAudioManager;
+ /** Used to query the do-not-disturb setting value, also called "interruption filter". */
+ private NotificationManager mNotificationManager;
+
/** {@code true} permits the muted alarm volume snackbar to show when starting this activity. */
- private boolean mShowMutedVolumeSnackbar;
+ private boolean mShowSilencedAlarmsSnackbar;
/** The view to which snackbar items are anchored. */
private View mSnackbarAnchor;
@@ -165,8 +188,10 @@
setContentView(R.layout.desk_clock);
mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
+ mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+
// Don't show the volume muted snackbar on rotations.
- mShowMutedVolumeSnackbar = savedInstanceState == null;
+ mShowSilencedAlarmsSnackbar = savedInstanceState == null;
mSnackbarAnchor = findViewById(R.id.coordinator);
// Configure the toolbar.
@@ -273,13 +298,26 @@
protected void onStart() {
super.onStart();
- if (mShowMutedVolumeSnackbar && mAudioManager.getStreamVolume(STREAM_ALARM) <= 0) {
- // Show the volume muted snackbar after a brief delay so it is more noticeable.
- mSnackbarAnchor.postDelayed(mShowMutedVolumeSnackbarRunnable, SECOND_IN_MILLIS);
+ if (mShowSilencedAlarmsSnackbar) {
+ if (isDoNotDisturbBlockingAlarms()) {
+ // Show the alarms blocked snackbar after a brief delay so it is more noticeable.
+ mSnackbarAnchor.postDelayed(mShowDNDBlockingSnackbarRunnable, SECOND_IN_MILLIS);
+ } else if (isAlarmStreamMuted()) {
+ // Show the volume muted snackbar after a brief delay so it is more noticeable.
+ mSnackbarAnchor.postDelayed(mShowMutedVolumeSnackbarRunnable, SECOND_IN_MILLIS);
+ }
+ }
+
+ // Watch for alarm volume changes while the app is in the foreground.
+ getContentResolver().registerContentObserver(VOLUME_URI, true, mAlarmVolumeChangeObserver);
+
+ if (Utils.isMOrLater()) {
+ // Watch for do-not-disturb changes while the app is in the foreground.
+ registerReceiver(mDoNotDisturbChangeReceiver, DND_CHANGE_FILTER);
}
// Subsequent starts of this activity should show the snackbar by default.
- mShowMutedVolumeSnackbar = true;
+ mShowSilencedAlarmsSnackbar = true;
}
@Override
@@ -289,9 +327,6 @@
final View dropShadow = findViewById(R.id.drop_shadow);
mDropShadowController = new DropShadowController(dropShadow, UiDataModel.getUiDataModel());
- // Watch for alarm volume changes while the app is in the foreground.
- getContentResolver().registerContentObserver(VOLUME_URI, true, mAlarmVolumeChangeObserver);
-
// Honor the selected tab in case it changed while the app was paused.
updateCurrentTab(UiDataModel.getUiDataModel().getSelectedTabIndex());
@@ -318,9 +353,6 @@
@Override
public void onPause() {
- // Stop watching for alarm volume changes while the app is in the background.
- getContentResolver().unregisterContentObserver(mAlarmVolumeChangeObserver);
-
DataModel.getDataModel().setApplicationInForeground(false);
mDropShadowController.stop();
@@ -331,7 +363,16 @@
@Override
protected void onStop() {
- // Remove any scheduled work to show the muted volume snackbar; it is no longer relevant.
+ // Stop watching for alarm volume changes while the app is in the background.
+ getContentResolver().unregisterContentObserver(mAlarmVolumeChangeObserver);
+
+ if (Utils.isMOrLater()) {
+ // Stop watching for do-not-disturb changes while the app is in the background.
+ unregisterReceiver(mDoNotDisturbChangeReceiver);
+ }
+
+ // Remove any scheduled work to show snackbars; it is no longer relevant.
+ mSnackbarAnchor.removeCallbacks(mShowDNDBlockingSnackbarRunnable);
mSnackbarAnchor.removeCallbacks(mShowMutedVolumeSnackbarRunnable);
super.onStop();
}
@@ -435,6 +476,10 @@
return (DeskClockFragment) mFragmentTabPagerAdapter.getItem(index);
}
+ private boolean isAlarmStreamMuted() {
+ return mAudioManager.getStreamVolume(STREAM_ALARM) <= 0;
+ }
+
private void showAlarmVolumeMutedSnackbar() {
final OnClickListener unmuteClickListener = new OnClickListener() {
@Override
@@ -451,6 +496,18 @@
);
}
+ @TargetApi(Build.VERSION_CODES.M)
+ private boolean isDoNotDisturbBlockingAlarms() {
+ if (!Utils.isMOrLater()) {
+ return false;
+ }
+ return mNotificationManager.getCurrentInterruptionFilter() == INTERRUPTION_FILTER_NONE;
+ }
+
+ private void showDoNotDisturbIsBlockingAlarmsSnackbar() {
+ SnackbarManager.show(Snackbar.make(mSnackbarAnchor, R.string.alarms_blocked_by_dnd, 5000));
+ }
+
/**
* As the view pager changes the selected page, update the model to record the new selected tab.
*/
@@ -541,9 +598,7 @@
/**
* Displays a snackbar that indicates the alarm volume is currently muted and offers an action
- * that displays the system volume control to adjust it. This runnable may be executed
- * immediately (if the volume is changed while this app is in the foreground) or after a delay
- * (if the volume is detected to be zero while bringing the app to the foreground).
+ * that displays the system volume control to adjust it.
*/
private final class ShowMutedVolumeSnackbarRunnable implements Runnable {
@Override
@@ -553,6 +608,16 @@
}
/**
+ * Displays a snackbar that indicates the do-not-disturb setting is currently blocking alarms.
+ */
+ private final class ShowDNDBlockingSnackbarRunnable implements Runnable {
+ @Override
+ public void run() {
+ showDoNotDisturbIsBlockingAlarmsSnackbar();
+ }
+ }
+
+ /**
* Observe changes to the alarm stream volume while the application is in the foreground and
* show/hide the snackbar that warns when the alarm volume is muted.
*/
@@ -563,7 +628,7 @@
@Override
public void onChange(boolean selfChange) {
- if (mAudioManager.getStreamVolume(STREAM_ALARM) <= 0) {
+ if (isAlarmStreamMuted()) {
showAlarmVolumeMutedSnackbar();
} else {
SnackbarManager.dismiss();
@@ -572,6 +637,21 @@
}
/**
+ * Observe changes to the do-not-disturb setting while the application is in the foreground
+ * and show/hide the snackbar that warns when the setting is blocking alarms.
+ */
+ private class DoNotDisturbChangeReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (isDoNotDisturbBlockingAlarms()) {
+ showDoNotDisturbIsBlockingAlarmsSnackbar();
+ } else {
+ SnackbarManager.dismiss();
+ }
+ }
+ }
+
+ /**
* As the model reports changes to the selected tab, update the user interface.
*/
private class TabChangeWatcher implements TabListener {
@@ -651,4 +731,4 @@
return "android:switcher:" + viewId + ":" + id;
}
}
-}
+}
\ No newline at end of file