From 3c4076a2465a89cf21ffd2e94cabd15be1d31689 Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Tue, 24 Feb 2015 12:12:25 -0500 Subject: DND: Add a hidden DND tile to quick settings. Invisible by default, not activatable when the platform volume controller is active. However, when the platform volume controller is not active, it can be enabled via a broadcast intent. When enabled, the status bar icon also changes to a single dnd icon. Very little more than embedding the existing zen mode panel into the detail pane of a new QS tile. Change-Id: I1e0ff6fbb99b00f67b53bceda8cbf121f3ef6b52 --- .../src/com/android/systemui/qs/QSPanel.java | 6 - .../src/com/android/systemui/qs/tiles/DndTile.java | 205 +++++++++++++++++++++ .../statusbar/phone/PhoneStatusBarPolicy.java | 7 +- .../systemui/statusbar/phone/QSTileHost.java | 2 + .../src/com/android/systemui/volume/VolumeUI.java | 2 + .../com/android/systemui/volume/ZenModePanel.java | 32 +++- 6 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java (limited to 'packages/SystemUI/src/com') diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 4dacacf..7f944f0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -253,12 +253,6 @@ public class QSPanel extends ViewGroup { @Override public void onStateChanged(QSTile.State state) { int visibility = state.visible ? VISIBLE : GONE; - if (state.visible && !mGridContentVisible) { - - // We don't want to show it if the content is hidden, - // then we just set it to invisible, to ensure that it gets visible again - visibility = INVISIBLE; - } setTileVisibility(r.tileView, visibility); r.tileView.onStateChanged(state); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java new file mode 100644 index 0000000..79600f5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -0,0 +1,205 @@ +/* + * 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.qs.tiles; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.provider.Settings; +import android.provider.Settings.Global; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnAttachStateChangeListener; +import android.view.ViewGroup; + +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.volume.ZenModePanel; + +/** Quick settings tile: Do not disturb **/ +public class DndTile extends QSTile { + private static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS); + + private static final String ACTION_SET_VISIBLE = "com.android.systemui.dndtile.SET_VISIBLE"; + private static final String EXTRA_VISIBLE = "visible"; + private static final String PREF_KEY_VISIBLE = "DndTileVisible"; + + private final ZenModeController mController; + private final DndDetailAdapter mDetailAdapter; + + private boolean mListening; + private boolean mVisible; + private boolean mShowingDetail; + + public DndTile(Host host) { + super(host); + mController = host.getZenModeController(); + mDetailAdapter = new DndDetailAdapter(); + mVisible = getSharedPrefs(mContext).getBoolean(PREF_KEY_VISIBLE, false); + mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE)); + } + + public static void setVisible(Context context, boolean visible) { + context.sendBroadcast(new Intent(DndTile.ACTION_SET_VISIBLE) + .putExtra(DndTile.EXTRA_VISIBLE, visible)); + } + + public static boolean isVisible(Context context) { + return getSharedPrefs(context).getBoolean(PREF_KEY_VISIBLE, false); + } + + @Override + public DetailAdapter getDetailAdapter() { + return mDetailAdapter; + } + + @Override + protected BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void handleClick() { + if (mState.value) { + mController.setZen(Global.ZEN_MODE_OFF); + } else { + mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); + showDetail(true); + } + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen(); + state.value = zen != Global.ZEN_MODE_OFF; + state.visible = mVisible; + switch (zen) { + case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: + state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on); + state.label = mContext.getString(R.string.quick_settings_dnd_priority_label); + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_dnd_priority_on); + break; + case Global.ZEN_MODE_NO_INTERRUPTIONS: + state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on); + state.label = mContext.getString(R.string.quick_settings_dnd_none_label); + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_dnd_none_on); + break; + default: + state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_off); + state.label = mContext.getString(R.string.quick_settings_dnd_label); + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_dnd_off); + break; + } + if (mShowingDetail && !state.value) { + showDetail(false); + } + } + + @Override + protected String composeChangeAnnouncement() { + if (mState.value) { + return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_on); + } else { + return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_off); + } + } + + @Override + public void setListening(boolean listening) { + if (mListening == listening) return; + mListening = listening; + if (mListening) { + mController.addCallback(mZenCallback); + } else { + mController.removeCallback(mZenCallback); + } + } + + private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() { + public void onZenChanged(int zen) { + refreshState(zen); + } + }; + + private static SharedPreferences getSharedPrefs(Context context) { + return context.getSharedPreferences(context.getPackageName(), 0); + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mVisible = intent.getBooleanExtra(EXTRA_VISIBLE, false); + getSharedPrefs(mContext).edit().putBoolean(PREF_KEY_VISIBLE, mVisible).commit(); + refreshState(); + } + }; + + private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener { + + @Override + public int getTitle() { + return R.string.quick_settings_dnd_label; + } + + @Override + public Boolean getToggleState() { + return mState.value; + } + + @Override + public Intent getSettingsIntent() { + return ZEN_SETTINGS; + } + + @Override + public void setToggleState(boolean state) { + if (!state) { + mController.setZen(Global.ZEN_MODE_OFF); + showDetail(false); + } + } + + @Override + public View createDetailView(Context context, View convertView, ViewGroup parent) { + final ZenModePanel zmp = convertView != null ? (ZenModePanel) convertView + : (ZenModePanel) LayoutInflater.from(context).inflate( + R.layout.zen_mode_panel, parent, false); + if (convertView == null) { + zmp.init(mController); + zmp.setEmbedded(true); + zmp.addOnAttachStateChangeListener(this); + } + return zmp; + } + + @Override + public void onViewAttachedToWindow(View v) { + mShowingDetail = true; + } + + @Override + public void onViewDetachedFromWindow(View v) { + mShowingDetail = false; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index e8a000c..5da8457 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -33,6 +33,7 @@ import android.util.Log; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.systemui.R; +import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.HotspotController; @@ -198,7 +199,11 @@ public class PhoneStatusBarPolicy { int volumeIconId = 0; String volumeDescription = null; - if (mZen == Global.ZEN_MODE_NO_INTERRUPTIONS) { + if (DndTile.isVisible(mContext)) { + zenVisible = mZen != Global.ZEN_MODE_OFF; + zenIconId = R.drawable.stat_sys_dnd; + zenDescription = mContext.getString(R.string.quick_settings_dnd_label); + } else if (mZen == Global.ZEN_MODE_NO_INTERRUPTIONS) { zenVisible = true; zenIconId = R.drawable.stat_sys_zen_none; zenDescription = mContext.getString(R.string.zen_no_interruptions); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 45a1386..954eb10 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -41,6 +41,7 @@ import com.android.systemui.qs.tiles.IntentTile; import com.android.systemui.qs.tiles.LocationTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.qs.tiles.WifiTile; +import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; @@ -256,6 +257,7 @@ public class QSTileHost implements QSTile.Host { else if (tileSpec.equals("inversion")) return new ColorInversionTile(this); else if (tileSpec.equals("cell")) return new CellularTile(this); else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this); + else if (tileSpec.equals("dnd")) return new DndTile(this); else if (tileSpec.equals("rotation")) return new RotationLockTile(this); else if (tileSpec.equals("flashlight")) return new FlashlightTile(this); else if (tileSpec.equals("location")) return new LocationTile(this); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index 8048a48..5e3ec3f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -44,6 +44,7 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.statusbar.ServiceMonitor; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.SystemUIDialog; @@ -114,6 +115,7 @@ public class VolumeUI extends SystemUI { if (LOGD) Log.d(TAG, "Registering volume controller"); mAudioManager.setVolumeController(mVolumeController); mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController); + DndTile.setVisible(mContext, false); } else { if (LOGD) Log.d(TAG, "Unregistering volume controller"); mAudioManager.setVolumeController(null); diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java index 5726fa7..6cecc8f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java @@ -90,9 +90,11 @@ public class ZenModePanel extends LinearLayout { private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this)); private SegmentedButtons mZenButtons; + private ViewGroup mZenButtonsContainer; private View mZenSubhead; private TextView mZenSubheadCollapsed; private TextView mZenSubheadExpanded; + private View mZenEmbeddedDivider; private View mMoreSettings; private LinearLayout mZenConditions; @@ -114,6 +116,7 @@ public class ZenModePanel extends LinearLayout { private Condition mSessionExitCondition; private Condition[] mConditions; private Condition mTimeCondition; + private boolean mEmbedded; public ZenModePanel(Context context, AttributeSet attrs) { super(context, attrs); @@ -140,9 +143,25 @@ public class ZenModePanel extends LinearLayout { pw.print(" mExpanded="); pw.println(mExpanded); pw.print(" mSessionZen="); pw.println(mSessionZen); pw.print(" mAttachedZen="); pw.println(mAttachedZen); + pw.print(" mEmbedded="); pw.println(mEmbedded); mTransitionHelper.dump(fd, pw, args); } + public void setEmbedded(boolean embedded) { + if (mEmbedded == embedded) return; + mEmbedded = embedded; + mZenButtonsContainer.setLayoutTransition(mEmbedded ? null : newLayoutTransition(null)); + if (mEmbedded) { + mZenButtonsContainer.setBackground(null); + } else { + mZenButtonsContainer.setBackgroundResource(R.drawable.qs_background_secondary); + } + mZenButtons.getChildAt(2).setVisibility(mEmbedded ? GONE : VISIBLE); + mZenEmbeddedDivider.setVisibility(mEmbedded ? VISIBLE : GONE); + setExpanded(mEmbedded); + updateWidgets(); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -156,10 +175,11 @@ public class ZenModePanel extends LinearLayout { Global.ZEN_MODE_OFF); mZenButtons.setCallback(mZenButtonsCallback); - final ViewGroup zenButtonsContainer = (ViewGroup) findViewById(R.id.zen_buttons_container); - zenButtonsContainer.setLayoutTransition(newLayoutTransition(null)); + mZenButtonsContainer = (ViewGroup) findViewById(R.id.zen_buttons_container); + mZenButtonsContainer.setLayoutTransition(newLayoutTransition(null)); mZenSubhead = findViewById(R.id.zen_subhead); + mZenEmbeddedDivider = findViewById(R.id.zen_embedded_divider); mZenSubheadCollapsed = (TextView) findViewById(R.id.zen_subhead_collapsed); mZenSubheadCollapsed.setOnClickListener(new View.OnClickListener() { @@ -222,7 +242,9 @@ public class ZenModePanel extends LinearLayout { mAttachedZen = -1; mSessionZen = -1; setSessionExitCondition(null); - setExpanded(false); + if (!mEmbedded) { + setExpanded(false); + } setRequestingConditions(false); mTransitionHelper.clear(); } @@ -361,7 +383,7 @@ public class ZenModePanel extends LinearLayout { private void handleUpdateZen(int zen) { if (mSessionZen != -1 && mSessionZen != zen) { - setExpanded(zen != Global.ZEN_MODE_OFF); + setExpanded(mEmbedded || zen != Global.ZEN_MODE_OFF); mSessionZen = zen; } mZenButtons.setSelectedValue(zen); @@ -403,7 +425,7 @@ public class ZenModePanel extends LinearLayout { final boolean expanded = !mHidden && mExpanded; mZenButtons.setVisibility(mHidden ? GONE : VISIBLE); - mZenSubhead.setVisibility(!mHidden && !zenOff ? VISIBLE : GONE); + mZenSubhead.setVisibility(!mHidden && !zenOff && !mEmbedded ? VISIBLE : GONE); mZenSubheadExpanded.setVisibility(expanded ? VISIBLE : GONE); mZenSubheadCollapsed.setVisibility(!expanded ? VISIBLE : GONE); mMoreSettings.setVisibility(zenImportant && expanded ? VISIBLE : GONE); -- cgit v1.1