diff options
Diffstat (limited to 'packages/SystemUI/src')
83 files changed, 2981 insertions, 1304 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/EventLogConstants.java b/packages/SystemUI/src/com/android/systemui/EventLogConstants.java index c8af2d4..43a1be1 100644 --- a/packages/SystemUI/src/com/android/systemui/EventLogConstants.java +++ b/packages/SystemUI/src/com/android/systemui/EventLogConstants.java @@ -34,4 +34,10 @@ public class EventLogConstants { public static final int SYSUI_LOCKSCREEN_GESTURE_TAP_LOCK = 6; /** The user tapped a notification, needs to tap again to launch. */ public static final int SYSUI_LOCKSCREEN_GESTURE_TAP_NOTIFICATION_ACTIVATE = 7; + /** The user swiped down to open quick settings, from keyguard. */ + public static final int SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_QS = 8; + /** The user swiped down to open quick settings, from shade. */ + public static final int SYSUI_SHADE_GESTURE_SWIPE_DOWN_QS = 9; + /** The user tapped on the status bar to open quick settings, from shade. */ + public static final int SYSUI_TAP_TO_OPEN_QS = 10; } diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 68b1968..29d2a01 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -37,8 +37,10 @@ public final class Prefs { Key.DND_TILE_VISIBLE, Key.DND_TILE_COMBINED_ICON, Key.DND_CONFIRMED_PRIORITY_INTRODUCTION, + Key.DND_CONFIRMED_SILENCE_INTRODUCTION, Key.DND_FAVORITE_BUCKET_INDEX, Key.DND_NONE_SELECTED, + Key.DND_FAVORITE_ZEN, }) public @interface Key { String SEARCH_APP_WIDGET_ID = "searchAppWidgetId"; @@ -48,8 +50,10 @@ public final class Prefs { String DND_TILE_VISIBLE = "DndTileVisible"; String DND_TILE_COMBINED_ICON = "DndTileCombinedIcon"; String DND_CONFIRMED_PRIORITY_INTRODUCTION = "DndConfirmedPriorityIntroduction"; + String DND_CONFIRMED_SILENCE_INTRODUCTION = "DndConfirmedSilenceIntroduction"; String DND_FAVORITE_BUCKET_INDEX = "DndCountdownMinuteIndex"; String DND_NONE_SELECTED = "DndNoneSelected"; + String DND_FAVORITE_ZEN = "DndFavoriteZen"; } public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 36be355..08659e9 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -33,11 +33,11 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.PhoneStatusBar; /** - * Class to manage everything around the assist gesture. + * Class to manage everything related to assist in SystemUI. */ -public class AssistGestureManager { +public class AssistManager { - private static final String TAG = "AssistGestureManager"; + private static final String TAG = "AssistManager"; private static final String ASSIST_ICON_METADATA_NAME = "com.android.systemui.action_assist_icon"; @@ -77,7 +77,7 @@ public class AssistGestureManager { } }; - public AssistGestureManager(PhoneStatusBar bar, Context context) { + public AssistManager(PhoneStatusBar bar, Context context) { mContext = context; mBar = bar; mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); @@ -194,16 +194,35 @@ public class AssistGestureManager { } } + public void launchVoiceAssistFromKeyguard() { + try { + mVoiceInteractionManagerService.launchVoiceAssistFromKeyguard(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to call launchVoiceAssistFromKeyguard", e); + } + } + private boolean getVoiceInteractorSupportsAssistGesture() { try { - return mVoiceInteractionManagerService.activeServiceSupportsAssistGesture(); + return mVoiceInteractionManagerService != null + && mVoiceInteractionManagerService.activeServiceSupportsAssist(); } catch (RemoteException e) { Log.w(TAG, "Failed to call activeServiceSupportsAssistGesture", e); return false; } } - private ComponentName getVoiceInteractorComponentName() { + public boolean canVoiceAssistBeLaunchedFromKeyguard() { + try { + return mVoiceInteractionManagerService != null + && mVoiceInteractionManagerService.activeServiceSupportsLaunchFromKeyguard(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to call activeServiceSupportsLaunchFromKeyguard", e); + return false; + } + } + + public ComponentName getVoiceInteractorComponentName() { try { return mVoiceInteractionManagerService.getActiveServiceComponentName(); } catch (RemoteException e) { @@ -214,7 +233,8 @@ public class AssistGestureManager { private boolean isVoiceSessionRunning() { try { - return mVoiceInteractionManagerService.isSessionRunning(); + return mVoiceInteractionManagerService != null + && mVoiceInteractionManagerService.isSessionRunning(); } catch (RemoteException e) { Log.w(TAG, "Failed to call isSessionRunning", e); return false; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 7b555fc..74962ec 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -539,10 +539,11 @@ public class KeyguardViewMediator extends SystemUI { mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); mLockPatternUtils = new LockPatternUtils(mContext); - mLockPatternUtils.setCurrentUser(ActivityManager.getCurrentUser()); + KeyguardUpdateMonitor.setCurrentUser(ActivityManager.getCurrentUser()); // Assume keyguard is showing (unless it's disabled) until we know for sure... - setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled()); + setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled( + KeyguardUpdateMonitor.getCurrentUser())); mTrustManager.reportKeyguardShowingChanged(); mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(mContext, @@ -623,8 +624,10 @@ public class KeyguardViewMediator extends SystemUI { // Lock immediately based on setting if secure (user has a pin/pattern/password). // This also "locks" the device when not secure to provide easy access to the // camera while preventing unwanted input. + int currentUser = KeyguardUpdateMonitor.getCurrentUser(); final boolean lockImmediately = - mLockPatternUtils.getPowerButtonInstantlyLocks() || !mLockPatternUtils.isSecure(); + mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser) + || !mLockPatternUtils.isSecure(currentUser); notifyScreenOffLocked(); @@ -670,7 +673,7 @@ public class KeyguardViewMediator extends SystemUI { // From DevicePolicyAdmin final long policyTimeout = mLockPatternUtils.getDevicePolicyManager() - .getMaximumTimeToLock(null, mLockPatternUtils.getCurrentUser()); + .getMaximumTimeToLock(null, KeyguardUpdateMonitor.getCurrentUser()); long timeout; if (policyTimeout > 0) { @@ -719,7 +722,8 @@ public class KeyguardViewMediator extends SystemUI { } private void maybeSendUserPresentBroadcast() { - if (mSystemReady && mLockPatternUtils.isLockScreenDisabled()) { + if (mSystemReady && mLockPatternUtils.isLockScreenDisabled( + KeyguardUpdateMonitor.getCurrentUser())) { // Lock screen is disabled because the user has set the preference to "None". // In this case, send out ACTION_USER_PRESENT here instead of in // handleKeyguardDone() @@ -733,7 +737,7 @@ public class KeyguardViewMediator extends SystemUI { */ public void onDreamingStarted() { synchronized (this) { - if (mScreenOn && mLockPatternUtils.isSecure()) { + if (mScreenOn && mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) { doKeyguardLaterLocked(); } } @@ -974,12 +978,13 @@ public class KeyguardViewMediator extends SystemUI { return; } - if (mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) { + if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser()) + && !lockedOrMissing) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off"); return; } - if (mLockPatternUtils.checkVoldPassword()) { + if (mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())) { if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted"); // Without this, settings is not enabled until the lock screen first appears setShowingLocked(false); @@ -1072,7 +1077,7 @@ public class KeyguardViewMediator extends SystemUI { } public boolean isSecure() { - return mLockPatternUtils.isSecure() + return mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()) || KeyguardUpdateMonitor.getInstance(mContext).isSimPinSecure(); } @@ -1083,7 +1088,7 @@ public class KeyguardViewMediator extends SystemUI { * @param newUserId The id of the incoming user. */ public void setCurrentUser(int newUserId) { - mLockPatternUtils.setCurrentUser(newUserId); + KeyguardUpdateMonitor.setCurrentUser(newUserId); } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -1213,7 +1218,7 @@ public class KeyguardViewMediator extends SystemUI { private void sendUserPresentBroadcast() { synchronized (this) { if (mBootCompleted) { - final UserHandle currentUser = new UserHandle(mLockPatternUtils.getCurrentUser()); + final UserHandle currentUser = new UserHandle(KeyguardUpdateMonitor.getCurrentUser()); final UserManager um = (UserManager) mContext.getSystemService( Context.USER_SERVICE); List <UserInfo> userHandles = um.getProfiles(currentUser.getIdentifier()); @@ -1330,7 +1335,7 @@ public class KeyguardViewMediator extends SystemUI { // Don't actually hide the Keyguard at the moment, wait for window // manager until it tells us it's safe to do so with // startKeyguardExitAnimation. - mWM.keyguardGoingAway( + ActivityManagerNative.getDefault().keyguardGoingAway( mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock(), mStatusBarKeyguardViewManager.isGoingToNotificationShade()); } catch (RemoteException e) { @@ -1393,14 +1398,9 @@ public class KeyguardViewMediator extends SystemUI { updateActivityLockScreenState(); adjustStatusBarLocked(); sendUserPresentBroadcast(); - maybeStopListeningForFingerprint(); } } - private void maybeStopListeningForFingerprint() { - mUpdateMonitor.stopListeningForFingerprint(); - } - private void adjustStatusBarLocked() { if (mStatusBarManager == null) { mStatusBarManager = (StatusBarManager) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index d8e3984..f59e864 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -110,21 +110,15 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene } private void handleRefreshState() { - if (mSecurityController.hasDeviceOwner()) { + boolean hasDeviceOwner = mSecurityController.hasDeviceOwner(); + boolean hasVpn = mSecurityController.isVpnEnabled(); + + mIsVisible = (hasVpn || hasDeviceOwner); + mIsIconVisible = hasVpn; + if (hasDeviceOwner) { mFooterTextId = R.string.device_owned_footer; - mIsVisible = true; - mIsIconVisible = false; - } else if (mSecurityController.hasProfileOwner()) { - mFooterTextId = R.string.profile_owned_footer; - mIsVisible = true; - mIsIconVisible = false; - } else if (mSecurityController.isVpnEnabled()) { - mFooterTextId = R.string.vpn_footer; - mIsVisible = true; - mIsIconVisible = true; } else { - mIsVisible = false; - mIsIconVisible = false; + mFooterTextId = R.string.vpn_footer; } mMainHandler.post(mUpdateDisplayState); } @@ -162,37 +156,17 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene private String getMessage(boolean hasDeviceOwner, boolean hasProfile, boolean hasVpn) { if (hasDeviceOwner) { - if (hasProfile) { - if (hasVpn) { - return mContext.getString( - R.string.monitoring_description_vpn_device_and_profile_owned, - mSecurityController.getDeviceOwnerName(), - mSecurityController.getProfileOwnerName()); - } else { - return mContext.getString( - R.string.monitoring_description_device_and_profile_owned, - mSecurityController.getDeviceOwnerName(), - mSecurityController.getProfileOwnerName()); - } - } else { - if (hasVpn) { - return mContext.getString(R.string.monitoring_description_vpn_device_owned, - mSecurityController.getDeviceOwnerName()); - } else { - return mContext.getString(R.string.monitoring_description_device_owned, - mSecurityController.getDeviceOwnerName()); - } - } - } else if (hasProfile) { if (hasVpn) { - return mContext.getString( - R.string.monitoring_description_vpn_profile_owned, - mSecurityController.getProfileOwnerName()); + return mContext.getString(R.string.monitoring_description_vpn_device_owned, + mSecurityController.getDeviceOwnerName()); } else { - return mContext.getString( - R.string.monitoring_description_profile_owned, - mSecurityController.getProfileOwnerName()); + return mContext.getString(R.string.monitoring_description_device_owned, + mSecurityController.getDeviceOwnerName()); } + } else if (hasProfile) { + return mContext.getString( + R.string.monitoring_description_vpn_profile_owned, + mSecurityController.getProfileOwnerName()); } else { return mContext.getString(R.string.monitoring_description_vpn); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index b5c1ca8..b4ae20d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -33,6 +33,7 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.qs.QSTile.DetailAdapter; @@ -49,12 +50,12 @@ public class QSPanel extends ViewGroup { private static final float TILE_ASPECT = 1.2f; private final Context mContext; - private final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>(); + protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>(); private final View mDetail; private final ViewGroup mDetailContent; private final TextView mDetailSettingsButton; private final TextView mDetailDoneButton; - private final View mBrightnessView; + protected final View mBrightnessView; private final QSDetailClipper mClipper; private final H mHandler = new H(); @@ -182,8 +183,11 @@ public class QSPanel extends ViewGroup { public void setExpanded(boolean expanded) { if (mExpanded == expanded) return; mExpanded = expanded; + MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, mExpanded); if (!mExpanded) { closeDetail(); + } else { + logTiles(); } } @@ -243,6 +247,12 @@ public class QSPanel extends ViewGroup { } } + private void drawTile(TileRecord r, QSTile.State state) { + final int visibility = state.visible ? VISIBLE : GONE; + setTileVisibility(r.tileView, visibility); + r.tileView.onStateChanged(state); + } + private void addTile(final QSTile<?> tile) { final TileRecord r = new TileRecord(); r.tile = tile; @@ -251,9 +261,9 @@ public class QSPanel extends ViewGroup { final QSTile.Callback callback = new QSTile.Callback() { @Override public void onStateChanged(QSTile.State state) { - int visibility = state.visible ? VISIBLE : GONE; - setTileVisibility(r.tileView, visibility); - r.tileView.onStateChanged(state); + if (!r.openingDetail) { + drawTile(r, state); + } } @Override public void onShowDetail(boolean show) { @@ -365,9 +375,14 @@ public class QSPanel extends ViewGroup { mDetailContent.removeAllViews(); mDetail.bringToFront(); mDetailContent.addView(r.detailView); + MetricsLogger.visible(mContext, detailAdapter.getMetricsCategory()); setDetailRecord(r); listener = mHideGridContentWhenDone; + if (r instanceof TileRecord) { + ((TileRecord) r).openingDetail = true; + } } else { + MetricsLogger.hidden(mContext, mDetailRecord.detailAdapter.getMetricsCategory()); mClosingDetail = true; setGridContentVisibility(true); listener = mTeardownDetailWhenDone; @@ -387,9 +402,21 @@ public class QSPanel extends ViewGroup { } } mBrightnessView.setVisibility(newVis); + if (mGridContentVisible != visible) { + MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, newVis); + } mGridContentVisible = visible; } + private void logTiles() { + for (int i = 0; i < mRecords.size(); i++) { + TileRecord tileRecord = mRecords.get(i); + if (tileRecord.tile.getState().visible) { + MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory()); + } + } + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = MeasureSpec.getSize(widthMeasureSpec); @@ -533,12 +560,13 @@ public class QSPanel extends ViewGroup { DetailAdapter detailAdapter; } - private static final class TileRecord extends Record { - QSTile<?> tile; - QSTileView tileView; - int row; - int col; - boolean scanState; + protected static final class TileRecord extends Record { + public QSTile<?> tile; + public QSTileView tileView; + public int row; + public int col; + public boolean scanState; + public boolean openingDetail; } private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() { @@ -554,6 +582,7 @@ public class QSPanel extends ViewGroup { // If we have been cancelled, remove the listener so that onAnimationEnd doesn't get // called, this will avoid accidentally turning off the grid when we don't want to. animation.removeListener(this); + redrawTile(); }; @Override @@ -561,6 +590,15 @@ public class QSPanel extends ViewGroup { // Only hide content if still in detail state. if (mDetailRecord != null) { setGridContentVisibility(false); + redrawTile(); + } + } + + private void redrawTile() { + if (mDetailRecord instanceof TileRecord) { + final TileRecord tileRecord = (TileRecord) mDetailRecord; + tileRecord.openingDetail = false; + drawTile(tileRecord, tileRecord.tile.getState()); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index b9574dc..3b217df 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -24,11 +24,13 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.qs.QSTile.State; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; @@ -69,6 +71,15 @@ public abstract class QSTile<TState extends State> implements Listenable { abstract protected void handleClick(); abstract protected void handleUpdateState(TState state, Object arg); + /** + * Declare the category of this tile. + * + * Categories are defined in {@link com.android.internal.logging.MetricsLogger} + * or if there is no relevant existing category you may define one in + * {@link com.android.systemui.qs.QSTile}. + */ + abstract public int getMetricsCategory(); + protected QSTile(Host host) { mHost = host; mContext = host.getContext(); @@ -97,6 +108,7 @@ public abstract class QSTile<TState extends State> implements Listenable { View createDetailView(Context context, View convertView, ViewGroup parent); Intent getSettingsIntent(); void setToggleState(boolean state); + int getMetricsCategory(); } // safe to call from any thread diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 2bc31fc..49f8d1c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -23,6 +23,7 @@ import android.content.IntentFilter; import android.net.ConnectivityManager; import android.provider.Settings.Global; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.qs.GlobalSetting; import com.android.systemui.qs.QSTile; @@ -55,6 +56,7 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { @Override public void handleClick() { + MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); setEnabled(!mState.value); mEnable.setAllowAnimation(true); mDisable.setAllowAnimation(true); @@ -85,6 +87,11 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_AIRPLANEMODE; + } + + @Override protected String composeChangeAnnouncement() { if (mState.value) { return mContext.getString(R.string.accessibility_quick_settings_airplane_changed_on); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index b42b5f6..ed954bb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -25,6 +25,7 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; +import com.android.internal.logging.MetricsLogger; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.systemui.R; import com.android.systemui.qs.QSDetailItems; @@ -75,6 +76,7 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> { @Override protected void handleClick() { final boolean isEnabled = (Boolean)mState.value; + MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled); mController.setBluetoothEnabled(!isEnabled); } @@ -132,6 +134,11 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> { } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_BLUETOOTH; + } + + @Override protected String composeChangeAnnouncement() { if (mState.value) { return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_on); @@ -177,11 +184,17 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState> { @Override public void setToggleState(boolean state) { + MetricsLogger.action(mContext, MetricsLogger.QS_BLUETOOTH_TOGGLE, state); mController.setBluetoothEnabled(state); showDetail(false); } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_BLUETOOTH_DETAILS; + } + + @Override public View createDetailView(Context context, View convertView, ViewGroup parent) { mItems = QSDetailItems.convertOrInflate(context, convertView, parent); mItems.setTagSuffix("Bluetooth"); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 5bf6fb5..c06ea66 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -24,6 +24,7 @@ import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewGroup; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.qs.QSDetailItems; import com.android.systemui.qs.QSDetailItems.Item; @@ -85,6 +86,7 @@ public class CastTile extends QSTile<QSTile.BooleanState> { @Override protected void handleClick() { + MetricsLogger.action(mContext, getMetricsCategory()); showDetail(true); } @@ -113,6 +115,11 @@ public class CastTile extends QSTile<QSTile.BooleanState> { } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_CAST; + } + + @Override protected String composeChangeAnnouncement() { if (!mState.value) { // We only announce when it's turned off to avoid vocal overflow. @@ -164,6 +171,11 @@ public class CastTile extends QSTile<QSTile.BooleanState> { } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_CAST_DETAILS; + } + + @Override public View createDetailView(Context context, View convertView, ViewGroup parent) { mItems = QSDetailItems.convertOrInflate(context, convertView, parent); mItems.setTagSuffix("Cast"); @@ -234,6 +246,7 @@ public class CastTile extends QSTile<QSTile.BooleanState> { @Override public void onDetailItemClick(Item item) { if (item == null || item.tag == null) return; + MetricsLogger.action(mContext, MetricsLogger.QS_CAST_SELECT); final CastDevice device = (CastDevice) item.tag; mController.startCasting(device); } @@ -241,6 +254,7 @@ public class CastTile extends QSTile<QSTile.BooleanState> { @Override public void onDetailItemDisconnect(Item item) { if (item == null || item.tag == null) return; + MetricsLogger.action(mContext, MetricsLogger.QS_CAST_DISCONNECT); final CastDevice device = (CastDevice) item.tag; mController.stopCasting(device); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 30f92b9..1721335 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -24,6 +24,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.qs.QSTileView; @@ -75,6 +76,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { @Override protected void handleClick() { + MetricsLogger.action(mContext, getMetricsCategory()); if (mDataController.isMobileDataSupported()) { showDetail(true); } else { @@ -118,6 +120,11 @@ public class CellularTile extends QSTile<QSTile.SignalState> { state.label); } + @Override + public int getMetricsCategory() { + return MetricsLogger.QS_CELLULAR; + } + // Remove the period from the network name public static String removeTrailingPeriod(String string) { if (string == null) return null; @@ -223,10 +230,16 @@ public class CellularTile extends QSTile<QSTile.SignalState> { @Override public void setToggleState(boolean state) { + MetricsLogger.action(mContext, MetricsLogger.QS_CELLULAR_TOGGLE, state); mDataController.setMobileDataEnabled(state); } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_DATAUSAGEDETAIL; + } + + @Override public View createDetailView(Context context, View convertView, ViewGroup parent) { final DataUsageDetailView v = (DataUsageDetailView) (convertView != null ? convertView diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 4a33f55..c6fc6ff 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles; import android.provider.Settings.Secure; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.qs.QSTile; @@ -86,6 +87,7 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { @Override protected void handleClick() { + MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); mSetting.setValue(mState.value ? 0 : 1); mEnable.setAllowAnimation(true); mDisable.setAllowAnimation(true); @@ -115,6 +117,11 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_COLORINVERSION; + } + + @Override protected String composeChangeAnnouncement() { if (mState.value) { return mContext.getString( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 6ce63d6..f4d6f04 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -29,6 +29,7 @@ import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewGroup; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.qs.QSTile; @@ -37,7 +38,11 @@ import com.android.systemui.volume.ZenModePanel; /** Quick settings tile: Do not disturb **/ public class DndTile extends QSTile<QSTile.BooleanState> { - private static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS); + private static final Intent ZEN_SETTINGS = + new Intent(Settings.ACTION_ZEN_MODE_SETTINGS); + + private static final Intent ZEN_PRIORITY_SETTINGS = + new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS); private static final String ACTION_SET_VISIBLE = "com.android.systemui.dndtile.SET_VISIBLE"; private static final String EXTRA_VISIBLE = "visible"; @@ -84,10 +89,12 @@ public class DndTile extends QSTile<QSTile.BooleanState> { @Override public void handleClick() { + MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); if (mState.value) { mController.setZen(Global.ZEN_MODE_OFF, null, TAG); } else { - mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG); + int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS); + mController.setZen(zen, null, TAG); showDetail(true); } } @@ -95,7 +102,9 @@ public class DndTile extends QSTile<QSTile.BooleanState> { @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; + final boolean newValue = zen != Global.ZEN_MODE_OFF; + final boolean valueChanged = state.value != newValue; + state.value = newValue; state.visible = isVisible(mContext); switch (zen) { case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: @@ -105,7 +114,7 @@ public class DndTile extends QSTile<QSTile.BooleanState> { 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.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on_total_silence); state.label = mContext.getString(R.string.quick_settings_dnd_none_label); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_dnd_none_on); @@ -126,6 +135,14 @@ public class DndTile extends QSTile<QSTile.BooleanState> { if (mShowingDetail && !state.value) { showDetail(false); } + if (valueChanged) { + fireToggleStateChanged(state.value); + } + } + + @Override + public int getMetricsCategory() { + return MetricsLogger.QS_DND; } @Override @@ -196,6 +213,7 @@ public class DndTile extends QSTile<QSTile.BooleanState> { @Override public void setToggleState(boolean state) { + MetricsLogger.action(mContext, MetricsLogger.QS_DND_TOGGLE, state); if (!state) { mController.setZen(Global.ZEN_MODE_OFF, null, TAG); showDetail(false); @@ -203,14 +221,19 @@ public class DndTile extends QSTile<QSTile.BooleanState> { } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_DND_DETAILS; + } + + @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); + zmp.setCallback(mZenModePanelCallback); } return zmp; } @@ -225,4 +248,22 @@ public class DndTile extends QSTile<QSTile.BooleanState> { mShowingDetail = false; } } + + private final ZenModePanel.Callback mZenModePanelCallback = new ZenModePanel.Callback() { + @Override + public void onPrioritySettings() { + mHost.startSettingsActivity(ZEN_PRIORITY_SETTINGS); + } + + @Override + public void onInteraction() { + // noop + } + + @Override + public void onExpanded(boolean expanded) { + // noop + } + }; + } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index cb78deb..0369ab5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles; import android.app.ActivityManager; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.policy.FlashlightController; @@ -62,6 +63,7 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements if (ActivityManager.isUserAMonkey()) { return; } + MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); boolean newState = !mState.value; refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE); mFlashlightController.setFlashlight(newState); @@ -84,6 +86,11 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_FLASHLIGHT; + } + + @Override protected String composeChangeAnnouncement() { if (mState.value) { return mContext.getString(R.string.accessibility_quick_settings_flashlight_changed_on); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 6063f80..f28a24b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.qs.UsageTracker; @@ -69,6 +70,7 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { @Override protected void handleClick() { final boolean isEnabled = (Boolean) mState.value; + MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled); mController.setHotspotEnabled(!isEnabled); mEnable.setAllowAnimation(true); mDisable.setAllowAnimation(true); @@ -97,6 +99,11 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_HOTSPOT; + } + + @Override protected String composeChangeAnnouncement() { if (mState.value) { return mContext.getString(R.string.accessibility_quick_settings_hotspot_changed_on); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java index 2736530..19f4df6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java @@ -29,6 +29,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.qs.QSTile; import java.util.Arrays; @@ -42,6 +43,7 @@ public class IntentTile extends QSTile<QSTile.State> { private PendingIntent mOnLongClick; private String mOnLongClickUri; private int mCurrentUserId; + private String mIntentPackage; private IntentTile(Host host, String action) { super(host); @@ -82,6 +84,7 @@ public class IntentTile extends QSTile<QSTile.State> { @Override protected void handleClick() { + MetricsLogger.action(mContext, getMetricsCategory(), mIntentPackage); sendIntent("click", mOnClick, mOnClickUri); } @@ -133,6 +136,13 @@ public class IntentTile extends QSTile<QSTile.State> { mOnClickUri = intent.getStringExtra("onClickUri"); mOnLongClick = intent.getParcelableExtra("onLongClick"); mOnLongClickUri = intent.getStringExtra("onLongClickUri"); + mIntentPackage = intent.getStringExtra("package"); + mIntentPackage = mIntentPackage == null ? "" : mIntentPackage; + } + + @Override + public int getMetricsCategory() { + return MetricsLogger.QS_INTENT; } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index 11ec722..e6fade4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.policy.KeyguardMonitor; @@ -59,6 +60,7 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { @Override protected void handleClick() { final boolean wasEnabled = (Boolean) mState.value; + MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled); mController.setLocationEnabled(!wasEnabled); mEnable.setAllowAnimation(true); mDisable.setAllowAnimation(true); @@ -87,6 +89,11 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_LOCATION; + } + + @Override protected String composeChangeAnnouncement() { if (mState.value) { return mContext.getString(R.string.accessibility_quick_settings_location_changed_on); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index f46b9a6..7c378f0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles; import android.content.res.Configuration; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.policy.RotationLockController; @@ -59,6 +60,7 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> { @Override protected void handleClick() { if (mController == null) return; + MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); final boolean newState = !mState.value; mController.setRotationLocked(newState); refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE); @@ -92,6 +94,11 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> { R.string.accessibility_rotation_lock_off); } + @Override + public int getMetricsCategory() { + return MetricsLogger.QS_ROTATIONLOCK; + } + /** * Get the correct accessibility string based on the state * diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java index 6bad652..d4f54b6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.qs.PseudoGridView; import com.android.systemui.statusbar.policy.UserSwitcherController; @@ -84,6 +85,7 @@ public class UserDetailView extends PseudoGridView { public void onClick(View view) { UserSwitcherController.UserRecord tag = (UserSwitcherController.UserRecord) view.getTag(); + MetricsLogger.action(mContext, MetricsLogger.QS_SWITCH_USER); switchTo(tag); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index d589366..c3f9e33 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -26,6 +26,7 @@ import android.util.Log; import android.view.View; import android.view.ViewGroup; +import com.android.internal.logging.MetricsLogger; import com.android.settingslib.wifi.AccessPoint; import com.android.systemui.R; import com.android.systemui.qs.QSDetailItems; @@ -94,6 +95,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { @Override protected void handleClick() { mState.copyTo(mStateBeforeClick); + MetricsLogger.action(mContext, getMetricsCategory(), !mState.enabled); mController.setWifiEnabled(!mState.enabled); } @@ -159,6 +161,11 @@ public class WifiTile extends QSTile<QSTile.SignalState> { } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_WIFI; + } + + @Override protected boolean shouldAnnouncementBeDelayed() { return mStateBeforeClick.enabled == mState.enabled; } @@ -269,11 +276,17 @@ public class WifiTile extends QSTile<QSTile.SignalState> { @Override public void setToggleState(boolean state) { if (DEBUG) Log.d(TAG, "setToggleState " + state); + MetricsLogger.action(mContext, MetricsLogger.QS_WIFI_TOGGLE, state); mController.setWifiEnabled(state); showDetail(false); } @Override + public int getMetricsCategory() { + return MetricsLogger.QS_WIFI_DETAILS; + } + + @Override public View createDetailView(Context context, View convertView, ViewGroup parent) { if (DEBUG) Log.d(TAG, "createDetailView convertView=" + (convertView != null)); mAccessPoints = null; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 1001feb..ad97f91 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -39,7 +39,6 @@ import com.android.systemui.R; import com.android.systemui.recents.misc.DebugTrigger; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; import com.android.systemui.recents.model.RecentsTaskLoadPlan; import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; @@ -392,15 +391,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); registerReceiver(mSystemBroadcastReceiver, filter); - - // Private API calls to make the shadows look better - try { - Utilities.setShadowProperty("ambientRatio", String.valueOf(1.5f)); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } } /** Inflates the debug overlay if debug mode is enabled. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index d60df9c..e3fb16a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -438,6 +438,33 @@ public class SystemServicesProxy { return info.loadLabel(mPm).toString(); } + /** Returns the application label */ + public String getApplicationLabel(Intent baseIntent, int userId) { + if (mPm == null) return null; + + // If we are mocking, then return a mock label + if (Constants.DebugFlags.App.EnableSystemServicesProxy) { + return "Recent Task"; + } + + ResolveInfo ri = mPm.resolveActivityAsUser(baseIntent, 0, userId); + CharSequence label = (ri != null) ? ri.loadLabel(mPm) : null; + return (label != null) ? label.toString() : null; + } + + /** Returns the content description for a given task */ + public String getContentDescription(Intent baseIntent, int userId, String activityLabel, + Resources res) { + String applicationLabel = getApplicationLabel(baseIntent, userId); + if (applicationLabel == null) { + return getBadgedLabel(activityLabel, userId); + } + String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId); + return applicationLabel.equals(activityLabel) ? badgedApplicationLabel + : res.getString(R.string.accessibility_recents_task_header, + badgedApplicationLabel, activityLabel); + } + /** * Returns the activity icon for the ActivityInfo for a user, badging if * necessary. @@ -464,6 +491,16 @@ public class SystemServicesProxy { return icon; } + /** + * Returns the given label for a user, badging if necessary. + */ + public String getBadgedLabel(String label, int userId) { + if (userId != UserHandle.myUserId()) { + label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString(); + } + return label; + } + /** Returns the package name of the home activity. */ public String getHomeActivityPackageName() { if (mPm == null) return null; diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java index 84544ff..e810410 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java @@ -22,27 +22,11 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.view.View; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; /* Common code */ public class Utilities { - // Reflection methods for altering shadows - private static Method sPropertyMethod; - static { - try { - Class<?> c = Class.forName("android.view.DisplayListCanvas"); - sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class); - if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - } - /** Scales a rect about its centroid */ public static void scaleRectAboutCenter(Rect r, float scale) { if (scale != 1.0f) { @@ -163,12 +147,6 @@ public class Utilities { (1f - overlayAlpha) * Color.blue(overlayColor))); } - /** Sets some private shadow properties. */ - public static void setShadowProperty(String property, String value) - throws IllegalAccessException, InvocationTargetException { - sPropertyMethod.invoke(null, property, value); - } - /** * Cancels an animation ensuring that if it has listeners, onCancel and onEnd * are not called. diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 5d98dda..40cd211 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -130,6 +130,8 @@ public class RecentsTaskLoadPlan { // Load the label, icon, and color String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription, mSystemServicesProxy, infoHandle); + String contentDescription = loader.getAndUpdateContentDescription(taskKey, + activityLabel, mSystemServicesProxy, res); Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, mSystemServicesProxy, res, infoHandle, false); int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig); @@ -148,9 +150,9 @@ public class RecentsTaskLoadPlan { // Add the task to the stack Task task = new Task(taskKey, (t.id != RecentsTaskLoader.INVALID_TASK_ID), - t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, activityIcon, - activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled, icon, - iconFilename); + t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, contentDescription, + activityIcon, activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled, + icon, iconFilename); task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false); if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index 3192fe6..b2aa2b6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -259,6 +259,7 @@ public class RecentsTaskLoader { DrawableLruCache mApplicationIconCache; BitmapLruCache mThumbnailCache; StringLruCache mActivityLabelCache; + StringLruCache mContentDescriptionCache; TaskResourceLoadQueue mLoadQueue; TaskResourceLoader mLoader; @@ -298,6 +299,7 @@ public class RecentsTaskLoader { mApplicationIconCache = new DrawableLruCache(iconCacheSize); mThumbnailCache = new BitmapLruCache(thumbnailCacheSize); mActivityLabelCache = new StringLruCache(100); + mContentDescriptionCache = new StringLruCache(100); mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache, mDefaultThumbnail, mDefaultApplicationIcon); } @@ -348,6 +350,24 @@ public class RecentsTaskLoader { return label; } + /** Returns the content description using as many cached values as we can. */ + public String getAndUpdateContentDescription(Task.TaskKey taskKey, String activityLabel, + SystemServicesProxy ssp, Resources res) { + // Return the cached content description if it exists + String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey); + if (label != null) { + return label; + } + label = ssp.getContentDescription(taskKey.baseIntent, taskKey.userId, activityLabel, res); + if (label != null) { + mContentDescriptionCache.put(taskKey, label); + } else { + Log.w(TAG, "Missing content description for " + taskKey.baseIntent.getComponent() + + " u=" + taskKey.userId); + } + return label; + } + /** Returns the activity icon using as many cached values as we can. */ public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td, SystemServicesProxy ssp, @@ -541,6 +561,7 @@ public class RecentsTaskLoader { mApplicationIconCache.evictAll(); // The cache is small, only clear the label cache when we are critical mActivityLabelCache.evictAll(); + mContentDescriptionCache.evictAll(); break; default: break; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index 0cd55d7..c14adf4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -124,6 +124,7 @@ public class Task { public boolean isLaunchTarget; public Drawable applicationIcon; public Drawable activityIcon; + public String contentDescription; public String activityLabel; public int colorPrimary; public boolean useLightOnPrimaryColor; @@ -140,8 +141,8 @@ public class Task { } public Task(TaskKey key, boolean isActive, int taskAffiliation, int taskAffiliationColor, - String activityTitle, Drawable activityIcon, int colorPrimary, - boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon, + String activityTitle, String contentDescription, Drawable activityIcon, + int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon, String iconFilename) { boolean isInAffiliationGroup = (taskAffiliation != key.id); boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0); @@ -149,6 +150,7 @@ public class Task { this.taskAffiliation = taskAffiliation; this.taskAffiliationColor = taskAffiliationColor; this.activityLabel = activityTitle; + this.contentDescription = contentDescription; this.activityIcon = activityIcon; this.colorPrimary = hasAffiliationGroupColor ? taskAffiliationColor : colorPrimary; this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary, @@ -166,6 +168,7 @@ public class Task { this.taskAffiliation = o.taskAffiliation; this.taskAffiliationColor = o.taskAffiliationColor; this.activityLabel = o.activityLabel; + this.contentDescription = o.contentDescription; this.activityIcon = o.activityIcon; this.colorPrimary = o.colorPrimary; this.useLightOnPrimaryColor = o.useLightOnPrimaryColor; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index 60a91bf..f397bc3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -190,10 +190,12 @@ public class TaskViewHeader extends FrameLayout { } else if (t.applicationIcon != null) { mApplicationIcon.setImageDrawable(t.applicationIcon); } - mApplicationIcon.setContentDescription(t.activityLabel); + mApplicationIcon.setContentDescription(t.contentDescription); if (!mActivityDescription.getText().toString().equals(t.activityLabel)) { mActivityDescription.setText(t.activityLabel); } + mActivityDescription.setContentDescription(t.contentDescription); + // Try and apply the system ui tint int existingBgColor = getBackgroundColor(); if (existingBgColor != t.colorPrimary) { @@ -207,7 +209,7 @@ public class TaskViewHeader extends FrameLayout { mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ? mLightDismissDrawable : mDarkDismissDrawable); mDismissButton.setContentDescription(String.format(mDismissContentDescription, - t.activityLabel)); + t.contentDescription)); mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE); if (mConfig.multiStackEnabled) { updateResizeTaskBarIcon(t); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 7271469..2913c7d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -87,6 +87,7 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; import com.android.internal.util.NotificationColorUtil; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SwipeHelper; @@ -237,6 +238,8 @@ public abstract class BaseStatusBar extends SystemUI implements protected DismissView mDismissView; protected EmptyShadeView mEmptyShadeView; + private NotificationClicker mNotificationClicker = new NotificationClicker(); + @Override // NotificationData.Environment public boolean isDeviceProvisioned() { return mDeviceProvisioned; @@ -392,11 +395,6 @@ public abstract class BaseStatusBar extends SystemUI implements Toast.LENGTH_SHORT).show(); } } - } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals( - action)) { - mUsersAllowingPrivateNotifications.clear(); - updateLockscreenNotificationSetting(); - updateNotifications(); } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) { NotificationManager noMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); @@ -416,6 +414,19 @@ public abstract class BaseStatusBar extends SystemUI implements } }; + private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) && + isCurrentProfile(getSendingUserId())) { + mUsersAllowingPrivateNotifications.clear(); + updateLockscreenNotificationSetting(); + updateNotifications(); + } + } + }; + private final NotificationListenerService mNotificationListener = new NotificationListenerService() { @Override @@ -628,9 +639,13 @@ public abstract class BaseStatusBar extends SystemUI implements filter.addAction(Intent.ACTION_USER_PRESENT); filter.addAction(BANNER_ACTION_CANCEL); filter.addAction(BANNER_ACTION_SETUP); - filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); + IntentFilter allUsersFilter = new IntentFilter(); + allUsersFilter.addAction( + DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter, + null, null); updateCurrentProfilesCache(); } @@ -639,7 +654,7 @@ public abstract class BaseStatusBar extends SystemUI implements Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) { Log.d(TAG, "user hasn't seen notification about hidden notifications"); final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); - if (!lockPatternUtils.isSecure()) { + if (!lockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) { Log.d(TAG, "insecure lockscreen, skipping notification"); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); @@ -708,18 +723,6 @@ public abstract class BaseStatusBar extends SystemUI implements mNotificationListener.setNotificationsShown(keys); } - protected void setNotificationsShownAll() { - ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); - final int N = activeNotifications.size(); - - String[] keys = new String[N]; - for (int i = 0; i < N; i++) { - NotificationData.Entry entry = activeNotifications.get(i); - keys[i] = entry.key; - } - setNotificationsShown(keys); - } - protected boolean isCurrentProfile(int userId) { synchronized (mCurrentProfiles) { return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null; @@ -795,7 +798,8 @@ public abstract class BaseStatusBar extends SystemUI implements protected void applyColorsAndBackgrounds(StatusBarNotification sbn, NotificationData.Entry entry) { - if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) { + if (entry.getContentView().getId() + != com.android.internal.R.id.status_bar_latest_event_content) { // Using custom RemoteViews if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) { @@ -820,8 +824,9 @@ public abstract class BaseStatusBar extends SystemUI implements public boolean isMediaNotification(NotificationData.Entry entry) { // TODO: confirm that there's a valid media key - return entry.expandedBig != null && - entry.expandedBig.findViewById(com.android.internal.R.id.media_actions) != null; + return entry.getExpandedContentView() != null && + entry.getExpandedContentView() + .findViewById(com.android.internal.R.id.media_actions) != null; } // The gear button in the guts that links to the app's own notification settings @@ -1145,9 +1150,9 @@ public abstract class BaseStatusBar extends SystemUI implements } /** - * if the interrupting notification had a fullscreen intent, fire it now. + * If there is an active heads-up notification and it has a fullscreen intent, fire it now. */ - public abstract void escalateHeadsUp(); + public abstract void maybeEscalateHeadsUp(); /** * Save the current "public" (locked and secure) state of the lockscreen. @@ -1301,13 +1306,7 @@ public abstract class BaseStatusBar extends SystemUI implements row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); } - PendingIntent contentIntent = sbn.getNotification().contentIntent; - if (contentIntent != null) { - final View.OnClickListener listener = makeClicker(contentIntent, sbn.getKey()); - row.setOnClickListener(listener); - } else { - row.setOnClickListener(null); - } + mNotificationClicker.register(row, sbn); // set up the adaptive layout View contentViewLocal = null; @@ -1348,8 +1347,8 @@ public abstract class BaseStatusBar extends SystemUI implements View publicViewLocal = null; if (publicNotification != null) { try { - publicViewLocal = publicNotification.contentView.apply(mContext, contentContainerPublic, - mOnClickHandler); + publicViewLocal = publicNotification.contentView.apply(mContext, + contentContainerPublic, mOnClickHandler); if (publicViewLocal != null) { publicViewLocal.setIsRootNamespace(true); @@ -1456,9 +1455,7 @@ public abstract class BaseStatusBar extends SystemUI implements entry.row = row; entry.row.setHeightRange(mRowMinHeight, maxHeight); entry.row.setOnActivatedListener(this); - entry.expanded = contentViewLocal; - entry.expandedPublic = publicViewLocal; - entry.setBigContentView(bigContentViewLocal); + entry.row.setExpandable(bigContentViewLocal != null); applyColorsAndBackgrounds(sbn, entry); @@ -1547,12 +1544,13 @@ public abstract class BaseStatusBar extends SystemUI implements // See if we have somewhere to put that remote input if (remoteInput != null) { - if (entry.expandedBig != null) { - inflateRemoteInput(entry.expandedBig, remoteInput, actions); + View bigContentView = entry.getExpandedContentView(); + if (bigContentView != null) { + inflateRemoteInput(bigContentView, remoteInput, actions); } - View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild(); - if (headsUpChild != null) { - inflateRemoteInput(headsUpChild, remoteInput, actions); + View headsUpContentView = entry.getHeadsUpContentView(); + if (headsUpContentView != null) { + inflateRemoteInput(headsUpContentView, remoteInput, actions); } } @@ -1569,35 +1567,38 @@ public abstract class BaseStatusBar extends SystemUI implements } } - public NotificationClicker makeClicker(PendingIntent intent, String notificationKey) { - return new NotificationClicker(intent, notificationKey); - } + private final class NotificationClicker implements View.OnClickListener { + public void onClick(final View v) { + if (!(v instanceof ExpandableNotificationRow)) { + Log.e(TAG, "NotificationClicker called on a view that is not a notification row."); + return; + } - protected class NotificationClicker implements View.OnClickListener { - private PendingIntent mIntent; - private final String mNotificationKey; + final ExpandableNotificationRow row = (ExpandableNotificationRow) v; + final StatusBarNotification sbn = row.getStatusBarNotification(); + if (sbn == null) { + Log.e(TAG, "NotificationClicker called on an unclickable notification,"); + return; + } - public NotificationClicker(PendingIntent intent, String notificationKey) { - mIntent = intent; - mNotificationKey = notificationKey; - } + final PendingIntent intent = sbn.getNotification().contentIntent; + final String notificationKey = sbn.getKey(); - public void onClick(final View v) { if (NOTIFICATION_CLICK_DEBUG) { - Log.d(TAG, "Clicked on content of " + mNotificationKey); + Log.d(TAG, "Clicked on content of " + notificationKey); } final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); - final boolean afterKeyguardGone = mIntent.isActivity() - && PreviewInflater.wouldLaunchResolverActivity(mContext, mIntent.getIntent(), + final boolean afterKeyguardGone = intent.isActivity() + && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), mCurrentUserId); dismissKeyguardThenExecute(new OnDismissAction() { public boolean onDismiss() { - if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(mNotificationKey)) { + if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) { // Release the HUN notification to the shade. // // In most cases, when FLAG_AUTO_CANCEL is set, the notification will // become canceled shortly by NoMan, but we can't assume that. - mHeadsUpManager.releaseImmediately(mNotificationKey); + mHeadsUpManager.releaseImmediately(notificationKey); } new Thread() { @Override @@ -1616,9 +1617,9 @@ public abstract class BaseStatusBar extends SystemUI implements } catch (RemoteException e) { } - if (mIntent != null) { + if (intent != null) { try { - mIntent.send(); + intent.send(); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -1626,14 +1627,14 @@ public abstract class BaseStatusBar extends SystemUI implements // TODO: Dismiss Keyguard. } - if (mIntent.isActivity()) { + if (intent.isActivity()) { overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone); } } try { - mBarService.onNotificationClick(mNotificationKey); + mBarService.onNotificationClick(notificationKey); } catch (RemoteException ex) { // system process is dead if we're here. } @@ -1645,10 +1646,19 @@ public abstract class BaseStatusBar extends SystemUI implements true /* force */, true /* delayed */); visibilityChanged(false); - return mIntent != null && mIntent.isActivity(); + return intent != null && intent.isActivity(); } }, afterKeyguardGone); } + + public void register(ExpandableNotificationRow row, StatusBarNotification sbn) { + final PendingIntent contentIntent = sbn.getNotification().contentIntent; + if (contentIntent != null) { + row.setOnClickListener(this); + } else { + row.setOnClickListener(null); + } + } } public void animateCollapsePanels(int flags, boolean force) { @@ -1701,7 +1711,6 @@ public abstract class BaseStatusBar extends SystemUI implements boolean clearNotificationEffects = (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED); mBarService.onPanelRevealed(clearNotificationEffects); - setNotificationsShownAll(); } else { mBarService.onPanelHidden(); } @@ -1895,15 +1904,14 @@ public abstract class BaseStatusBar extends SystemUI implements logUpdate(entry, n); } boolean applyInPlace = shouldApplyInPlace(entry, n); - final boolean shouldInterrupt = shouldInterrupt(notification); - final boolean alertAgain = alertAgain(entry, n); + boolean shouldInterrupt = shouldInterrupt(notification); + boolean alertAgain = alertAgain(entry, n); entry.notification = notification; mGroupManager.onEntryUpdated(entry, entry.notification); boolean updateSuccessful = false; if (applyInPlace) { - // We can just reapply the notifications in place if (DEBUG) Log.d(TAG, "reusing notification for key: " + key); try { if (entry.icon != null) { @@ -1924,7 +1932,7 @@ public abstract class BaseStatusBar extends SystemUI implements updateSuccessful = true; } catch (RuntimeException e) { - // It failed to add cleanly. Log, and remove the view from the panel. + // It failed to apply cleanly. Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e); } } @@ -1948,11 +1956,12 @@ public abstract class BaseStatusBar extends SystemUI implements // swipe-dismissable) updateNotificationVetoButton(entry.row, notification); - // Is this for you? - boolean isForCurrentUser = isNotificationForCurrentProfiles(notification); - if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you"); + if (DEBUG) { + // Is this for you? + boolean isForCurrentUser = isNotificationForCurrentProfiles(notification); + Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you"); + } - // Recalculate the position of the sliding windows and the titles. setAreThereNotifications(); } @@ -1963,7 +1972,7 @@ public abstract class BaseStatusBar extends SystemUI implements StatusBarNotification oldNotification = oldEntry.notification; Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when + " ongoing=" + oldNotification.isOngoing() - + " expanded=" + oldEntry.expanded + + " expanded=" + oldEntry.getContentView() + " contentView=" + oldNotification.getNotification().contentView + " bigContentView=" + oldNotification.getNotification().bigContentView + " publicView=" + oldNotification.getNotification().publicVersion @@ -1976,7 +1985,8 @@ public abstract class BaseStatusBar extends SystemUI implements } /** - * @return whether we can just reapply the RemoteViews in place when it is updated + * @return whether we can just reapply the RemoteViews from a notification in-place when it is + * updated */ private boolean shouldApplyInPlace(Entry entry, Notification n) { StatusBarNotification oldNotification = entry.notification; @@ -1994,15 +2004,15 @@ public abstract class BaseStatusBar extends SystemUI implements final Notification publicNotification = n.publicVersion; final RemoteViews publicContentView = publicNotification != null ? publicNotification.contentView : null; - boolean contentsUnchanged = entry.expanded != null + boolean contentsUnchanged = entry.getContentView() != null && contentView.getPackage() != null && oldContentView.getPackage() != null && oldContentView.getPackage().equals(contentView.getPackage()) && oldContentView.getLayoutId() == contentView.getLayoutId(); // large view may be null boolean bigContentsUnchanged = - (entry.getBigContentView() == null && bigContentView == null) - || ((entry.getBigContentView() != null && bigContentView != null) + (entry.getExpandedContentView() == null && bigContentView == null) + || ((entry.getExpandedContentView() != null && bigContentView != null) && bigContentView.getPackage() != null && oldBigContentView.getPackage() != null && oldBigContentView.getPackage().equals(bigContentView.getPackage()) @@ -2034,12 +2044,12 @@ public abstract class BaseStatusBar extends SystemUI implements : null; // Reapply the RemoteViews - contentView.reapply(mContext, entry.expanded, mOnClickHandler); - if (bigContentView != null && entry.getBigContentView() != null) { - bigContentView.reapply(mContext, entry.getBigContentView(), + contentView.reapply(mContext, entry.getContentView(), mOnClickHandler); + if (bigContentView != null && entry.getExpandedContentView() != null) { + bigContentView.reapply(mContext, entry.getExpandedContentView(), mOnClickHandler); } - View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild(); + View headsUpChild = entry.getHeadsUpContentView(); if (headsUpContentView != null && headsUpChild != null) { headsUpContentView.reapply(mContext, headsUpChild, mOnClickHandler); } @@ -2047,13 +2057,8 @@ public abstract class BaseStatusBar extends SystemUI implements publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler); } // update the contentIntent - final PendingIntent contentIntent = notification.getNotification().contentIntent; - if (contentIntent != null) { - final View.OnClickListener listener = makeClicker(contentIntent, notification.getKey()); - entry.row.setOnClickListener(listener); - } else { - entry.row.setOnClickListener(null); - } + mNotificationClicker.register(entry.row, notification); + entry.row.setStatusBarNotification(notification); entry.row.notifyContentUpdated(); entry.row.resetHeight(); @@ -2062,7 +2067,7 @@ public abstract class BaseStatusBar extends SystemUI implements } protected void notifyHeadsUpScreenOff() { - escalateHeadsUp(); + maybeEscalateHeadsUp(); } private boolean alertAgain(Entry oldEntry, Notification newNotification) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 4542054..80fdd28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -157,7 +157,8 @@ public class CommandQueue extends IStatusBar.Stub { public void setSystemUiVisibility(int vis, int mask) { synchronized (mList) { - mHandler.removeMessages(MSG_SET_SYSTEMUI_VISIBILITY); + // Don't coalesce these, since it might have one time flags set such as + // STATUS_BAR_UNHIDE which might get lost. mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index cb8217e..9ef495d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -84,7 +84,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private ExpansionLogger mLogger; private String mLoggingKey; private boolean mWasReset; - private NotificationGuts mGuts; private StatusBarNotification mStatusBarNotification; private boolean mIsHeadsUp; @@ -102,6 +101,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private ViewStub mGutsStub; private boolean mHasExpandAction; private boolean mIsSystemChildExpanded; + private boolean mIsPinned; private OnClickListener mExpandClickListener = new OnClickListener() { @Override public void onClick(View v) { @@ -109,7 +109,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { !mChildrenExpanded); } }; - private boolean mInShade; public NotificationContentView getPrivateLayout() { return mPrivateLayout; @@ -284,12 +283,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { return realActualHeight; } - public void setInShade(boolean inShade) { - mInShade = inShade; + /** + * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this + * the notification will be rendered on top of the screen. + * + * @param pinned whether it is pinned + */ + public void setPinned(boolean pinned) { + mIsPinned = pinned; } - public boolean isInShade() { - return mInShade; + public boolean isPinned() { + return mIsPinned; } public int getHeadsUpHeight() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index e632cc8..17e2cb5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -59,13 +59,6 @@ public abstract class ExpandableView extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int ownMaxHeight = mMaxViewHeight; - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY; - boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST; - if (hasFixedHeight || isHeightLimited) { - int size = MeasureSpec.getSize(heightMeasureSpec); - ownMaxHeight = Math.min(ownMaxHeight, size); - } int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST); int maxChildHeight = 0; int childCount = getChildCount(); @@ -92,8 +85,7 @@ public abstract class ExpandableView extends FrameLayout { mMatchParentViews.add(child); } } - int ownHeight = hasFixedHeight ? ownMaxHeight : - isHeightLimited ? Math.min(ownMaxHeight, maxChildHeight) : maxChildHeight; + int ownHeight = Math.min(ownMaxHeight, maxChildHeight); newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY); for (View child : mMatchParentViews) { child.measure(getChildMeasureSpec( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 1c53655..110b14c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -32,37 +32,34 @@ import android.widget.FrameLayout; import com.android.systemui.R; /** - * A frame layout containing the actual payload of the notification, including the contracted and - * expanded layout. This class is responsible for clipping the content and and switching between the - * expanded and contracted view depending on its clipped size. + * A frame layout containing the actual payload of the notification, including the contracted, + * expanded and heads up layout. This class is responsible for clipping the content and and + * switching between the expanded, contracted and the heads up view depending on its clipped size. */ public class NotificationContentView extends FrameLayout { private static final long ANIMATION_DURATION_LENGTH = 170; - private static final int CONTRACTED = 1; - private static final int EXPANDED = 2; - private static final int HEADSUP = 3; + private static final int VISIBLE_TYPE_CONTRACTED = 0; + private static final int VISIBLE_TYPE_EXPANDED = 1; + private static final int VISIBLE_TYPE_HEADSUP = 2; private final Rect mClipBounds = new Rect(); + private final int mSmallHeight; + private final int mHeadsUpHeight; + private final Interpolator mLinearInterpolator = new LinearInterpolator(); private View mContractedChild; private View mExpandedChild; private View mHeadsUpChild; private NotificationViewWrapper mContractedWrapper; - - private final int mSmallHeight; - private final int mHeadsUpHeight; private int mClipTopAmount; - private int mContentHeight; - - private final Interpolator mLinearInterpolator = new LinearInterpolator(); - private int mVisibleView = CONTRACTED; - + private int mVisibleType = VISIBLE_TYPE_CONTRACTED; private boolean mDark; private final Paint mFadePaint = new Paint(); private boolean mAnimate; + private boolean mIsHeadsUp; private ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener = new ViewTreeObserver.OnPreDrawListener() { @Override @@ -72,7 +69,6 @@ public class NotificationContentView extends FrameLayout { return true; } }; - private boolean mIsHeadsUp; public NotificationContentView(Context context, AttributeSet attrs) { super(context, attrs); @@ -105,9 +101,9 @@ public class NotificationContentView extends FrameLayout { // An actual height is set size = Math.min(maxSize, layoutParams.height); } - int spec = size == Integer.MAX_VALUE ? - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) : - MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST); + int spec = size == Integer.MAX_VALUE + ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) + : MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST); mExpandedChild.measure(widthMeasureSpec, spec); maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight()); } @@ -153,7 +149,7 @@ public class NotificationContentView extends FrameLayout { mContractedChild = null; mExpandedChild = null; mHeadsUpChild = null; - mVisibleView = CONTRACTED; + mVisibleType = VISIBLE_TYPE_CONTRACTED; if (resetActualHeight) { mContentHeight = mSmallHeight; } @@ -263,30 +259,32 @@ public class NotificationContentView extends FrameLayout { if (mContractedChild == null) { return; } - int visibleView = calculateVisibleView(); - if (visibleView != mVisibleView || force) { - if (animate && mExpandedChild != null) { - runSwitchAnimation(visibleView); + int visibleType = calculateVisibleType(); + if (visibleType != mVisibleType || force) { + if (animate && (visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null) + || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null) + || visibleType == VISIBLE_TYPE_CONTRACTED) { + runSwitchAnimation(visibleType); } else { - updateViewVisibilities(visibleView); + updateViewVisibilities(visibleType); } - mVisibleView = visibleView; + mVisibleType = visibleType; } } - private void updateViewVisibilities(int visibleView) { - boolean contractedVisible = visibleView == CONTRACTED; + private void updateViewVisibilities(int visibleType) { + boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED; mContractedChild.setVisibility(contractedVisible ? View.VISIBLE : View.INVISIBLE); mContractedChild.setAlpha(contractedVisible ? 1f : 0f); mContractedChild.setLayerType(LAYER_TYPE_NONE, null); if (mExpandedChild != null) { - boolean expandedVisible = visibleView == EXPANDED; + boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED; mExpandedChild.setVisibility(expandedVisible ? View.VISIBLE : View.INVISIBLE); mExpandedChild.setAlpha(expandedVisible ? 1f : 0f); mExpandedChild.setLayerType(LAYER_TYPE_NONE, null); } if (mHeadsUpChild != null) { - boolean headsUpVisible = visibleView == HEADSUP; + boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP; mHeadsUpChild.setVisibility(headsUpVisible ? View.VISIBLE : View.INVISIBLE); mHeadsUpChild.setAlpha(headsUpVisible ? 1f : 0f); mHeadsUpChild.setLayerType(LAYER_TYPE_NONE, null); @@ -294,9 +292,9 @@ public class NotificationContentView extends FrameLayout { setLayerType(LAYER_TYPE_NONE, null); } - private void runSwitchAnimation(int visibleView) { - View shownView = getViewFromFlag(visibleView); - View hiddenView = getViewFromFlag(mVisibleView); + private void runSwitchAnimation(int visibleType) { + View shownView = getViewForVisibleType(visibleType); + View hiddenView = getViewForVisibleType(mVisibleType); shownView.setVisibility(View.VISIBLE); hiddenView.setVisibility(View.VISIBLE); shownView.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint); @@ -314,34 +312,42 @@ public class NotificationContentView extends FrameLayout { .withEndAction(new Runnable() { @Override public void run() { - updateViewVisibilities(mVisibleView); + updateViewVisibilities(mVisibleType); } }); } - private View getViewFromFlag(int visibleView) { - switch (visibleView) { - case EXPANDED: + /** + * @param visibleType one of the static enum types in this view + * @return the corresponding view according to the given visible type + */ + private View getViewForVisibleType(int visibleType) { + switch (visibleType) { + case VISIBLE_TYPE_EXPANDED: return mExpandedChild; - case HEADSUP: + case VISIBLE_TYPE_HEADSUP: return mHeadsUpChild; + default: + return mContractedChild; } - return mContractedChild; } - private int calculateVisibleView() { + /** + * @return one of the static enum types in this view, calculated form the current state + */ + private int calculateVisibleType() { boolean noExpandedChild = mExpandedChild == null; if (mIsHeadsUp && mHeadsUpChild != null) { if (mContentHeight <= mHeadsUpChild.getHeight() || noExpandedChild) { - return HEADSUP; + return VISIBLE_TYPE_HEADSUP; } else { - return EXPANDED; + return VISIBLE_TYPE_EXPANDED; } } else { if (mContentHeight <= mSmallHeight || noExpandedChild) { - return CONTRACTED; + return VISIBLE_TYPE_CONTRACTED; } else { - return EXPANDED; + return VISIBLE_TYPE_EXPANDED; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 429889d..2a8b4ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -45,9 +45,6 @@ public class NotificationData { public StatusBarNotification notification; public StatusBarIconView icon; public ExpandableNotificationRow row; // the outer expanded view - public View expanded; // the inflated RemoteViews - public View expandedPublic; // for insecure lockscreens - public View expandedBig; private boolean interruption; public boolean autoRedacted; // whether the redacted notification was generated by us public boolean legacy; // whether the notification has a legacy, dark background @@ -58,14 +55,6 @@ public class NotificationData { this.notification = n; this.icon = ic; } - public void setBigContentView(View bigContentView) { - this.expandedBig = bigContentView; - row.setExpandable(bigContentView != null); - } - public View getBigContentView() { - return expandedBig; - } - public View getPublicContentView() { return expandedPublic; } public void setInterruption() { interruption = true; @@ -81,15 +70,28 @@ public class NotificationData { public void reset() { // NOTE: Icon needs to be preserved for now. // We should fix this at some point. - expanded = null; - expandedPublic = null; - expandedBig = null; autoRedacted = false; legacy = false; if (row != null) { row.reset(); } } + + public View getContentView() { + return row.getPrivateLayout().getContractedChild(); + } + + public View getExpandedContentView() { + return row.getPrivateLayout().getExpandedChild(); + } + + public View getHeadsUpContentView() { + return row.getPrivateLayout().getHeadsUpChild(); + } + + public View getPublicContentView() { + return row.getPublicLayout().getContractedChild(); + } } private final ArrayMap<String, Entry> mEntries = new ArrayMap<>(); @@ -258,7 +260,7 @@ public class NotificationData { */ public boolean hasActiveClearableNotifications() { for (Entry e : mSortedAndFiltered) { - if (e.expanded != null) { // the view successfully inflated + if (e.getContentView() != null) { // the view successfully inflated if (e.notification.isClearable()) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index b2bb021..f6629dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -53,18 +53,21 @@ public class SignalClusterView private boolean mNoSimsVisible = false; private boolean mVpnVisible = false; + private boolean mEthernetVisible = false; + private int mEthernetIconId = 0; private boolean mWifiVisible = false; private int mWifiStrengthId = 0; private boolean mIsAirplaneMode = false; private int mAirplaneIconId = 0; private int mAirplaneContentDescription; private String mWifiDescription; + private String mEthernetDescription; private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>(); private int mIconTint = Color.WHITE; private float mDarkIntensity; - ViewGroup mWifiGroup; - ImageView mVpn, mWifi, mAirplane, mNoSims, mWifiDark, mNoSimsDark; + ViewGroup mEthernetGroup, mWifiGroup; + ImageView mVpn, mEthernet, mWifi, mAirplane, mNoSims, mEthernetDark, mWifiDark, mNoSimsDark; View mWifiAirplaneSpacer; View mWifiSignalSpacer; LinearLayout mMobileSignalGroup; @@ -116,6 +119,9 @@ public class SignalClusterView super.onAttachedToWindow(); mVpn = (ImageView) findViewById(R.id.vpn); + mEthernetGroup = (ViewGroup) findViewById(R.id.ethernet_combo); + mEthernet = (ImageView) findViewById(R.id.ethernet); + mEthernetDark = (ImageView) findViewById(R.id.ethernet_dark); mWifiGroup = (ViewGroup) findViewById(R.id.wifi_combo); mWifi = (ImageView) findViewById(R.id.wifi_signal); mWifiDark = (ImageView) findViewById(R.id.wifi_signal_dark); @@ -136,6 +142,8 @@ public class SignalClusterView @Override protected void onDetachedFromWindow() { mVpn = null; + mEthernetGroup = null; + mEthernet = null; mWifiGroup = null; mWifi = null; mAirplane = null; @@ -183,6 +191,15 @@ public class SignalClusterView } @Override + public void setEthernetIndicators(boolean visible, int icon, String contentDescription) { + mEthernetVisible = visible; + mEthernetIconId = icon; + mEthernetDescription = contentDescription; + + apply(); + } + + @Override public void setNoSims(boolean show) { mNoSimsVisible = show; } @@ -234,6 +251,9 @@ public class SignalClusterView public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { // Standard group layout onPopulateAccessibilityEvent() implementations // ignore content description, so populate manually + if (mEthernetVisible && mEthernetGroup != null && + mEthernetGroup.getContentDescription() != null) + event.getText().add(mEthernetGroup.getContentDescription()); if (mWifiVisible && mWifiGroup != null && mWifiGroup.getContentDescription() != null) event.getText().add(mWifiGroup.getContentDescription()); for (PhoneState state : mPhoneStates) { @@ -246,6 +266,10 @@ public class SignalClusterView public void onRtlPropertiesChanged(int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); + if (mEthernet != null) { + mEthernet.setImageDrawable(null); + } + if (mWifi != null) { mWifi.setImageDrawable(null); } @@ -277,6 +301,21 @@ public class SignalClusterView mVpn.setVisibility(mVpnVisible ? View.VISIBLE : View.GONE); if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE")); + + if (mEthernetVisible) { + mEthernet.setImageResource(mEthernetIconId); + mEthernetDark.setImageResource(mEthernetIconId); + mEthernetGroup.setContentDescription(mEthernetDescription); + mEthernetGroup.setVisibility(View.VISIBLE); + } else { + mEthernetGroup.setVisibility(View.GONE); + } + + if (DEBUG) Log.d(TAG, + String.format("ethernet: %s", + (mEthernetVisible ? "VISIBLE" : "GONE"))); + + if (mWifiVisible) { mWifi.setImageResource(mWifiStrengthId); mWifiDark.setImageResource(mWifiStrengthId); @@ -327,7 +366,7 @@ public class SignalClusterView mNoSimsDark.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE); boolean anythingVisible = mNoSimsVisible || mWifiVisible || mIsAirplaneMode - || anyMobileVisible || mVpnVisible; + || anyMobileVisible || mVpnVisible || mEthernetVisible; setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0); } @@ -345,6 +384,7 @@ public class SignalClusterView setTint(mAirplane, mIconTint); applyDarkIntensity(mDarkIntensity, mNoSims, mNoSimsDark); applyDarkIntensity(mDarkIntensity, mWifi, mWifiDark); + applyDarkIntensity(mDarkIntensity, mEthernet, mEthernetDark); for (int i = 0; i < mPhoneStates.size(); i++) { mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java index 23810f9..ee5eb38 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java @@ -25,4 +25,5 @@ import android.content.Intent; */ public interface ActivityStarter { public void startActivity(Intent intent, boolean dismissShade); + void preventNextAnimation(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index 3997807..e6da81e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -27,7 +27,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; /** - * A Helper class to handle touches on the heads-up views + * A helper class to handle touches on the heads-up views. */ public class HeadsUpTouchHelper implements Gefingerpoken { @@ -37,19 +37,30 @@ public class HeadsUpTouchHelper implements Gefingerpoken { private float mTouchSlop; private float mInitialTouchX; private float mInitialTouchY; - private boolean mMotionOnHeadsUpView; + private boolean mTouchingHeadsUpView; private boolean mTrackingHeadsUp; private boolean mCollapseSnoozes; private NotificationPanelView mPanel; private ExpandableNotificationRow mPickedChild; + public HeadsUpTouchHelper(HeadsUpManager headsUpManager, + NotificationStackScrollLayout stackScroller, + NotificationPanelView notificationPanelView) { + mHeadsUpManager = headsUpManager; + mStackScroller = stackScroller; + mPanel = notificationPanelView; + Context context = stackScroller.getContext(); + final ViewConfiguration configuration = ViewConfiguration.get(context); + mTouchSlop = configuration.getScaledTouchSlop(); + } + public boolean isTrackingHeadsUp() { return mTrackingHeadsUp; } @Override public boolean onInterceptTouchEvent(MotionEvent event) { - if (!mMotionOnHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) { + if (!mTouchingHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) { return false; } int pointerIndex = event.findPointerIndex(mTrackingPointer); @@ -64,11 +75,11 @@ public class HeadsUpTouchHelper implements Gefingerpoken { mInitialTouchY = y; mInitialTouchX = x; setTrackingHeadsUp(false); - ExpandableView child = mStackScroller.getChildAtPosition(x, y); - mMotionOnHeadsUpView = false; + ExpandableView child = mStackScroller.getChildAtRawPosition(x, y); + mTouchingHeadsUpView = false; if (child instanceof ExpandableNotificationRow) { mPickedChild = (ExpandableNotificationRow) child; - mMotionOnHeadsUpView = mPickedChild.isHeadsUp() && !mPickedChild.isInShade(); + mTouchingHeadsUpView = mPickedChild.isHeadsUp() && mPickedChild.isPinned(); } break; case MotionEvent.ACTION_POINTER_UP: @@ -84,20 +95,23 @@ public class HeadsUpTouchHelper implements Gefingerpoken { case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; - if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) { + if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop + && Math.abs(h) > Math.abs(x - mInitialTouchX)) { setTrackingHeadsUp(true); mCollapseSnoozes = h < 0; mInitialTouchX = x; mInitialTouchY = y; int expandedHeight = mPickedChild.getActualHeight(); mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight); + mHeadsUpManager.unpinAll(); return true; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: - if (mPickedChild != null && mMotionOnHeadsUpView) { + if (mPickedChild != null && mTouchingHeadsUpView) { + // We may swallow this click if the heads up just came in. if (mHeadsUpManager.shouldSwallowClick( mPickedChild.getStatusBarNotification().getKey())) { endMotion(); @@ -141,20 +155,6 @@ public class HeadsUpTouchHelper implements Gefingerpoken { private void endMotion() { mTrackingPointer = -1; mPickedChild = null; - mMotionOnHeadsUpView = false; - } - - public ExpandableView getPickedChild() { - return mPickedChild; - } - - public void bind(HeadsUpManager headsUpManager, NotificationStackScrollLayout stackScroller, - NotificationPanelView notificationPanelView) { - mHeadsUpManager = headsUpManager; - mStackScroller = stackScroller; - mPanel = notificationPanelView; - Context context = stackScroller.getContext(); - final ViewConfiguration configuration = ViewConfiguration.get(context); - mTouchSlop = configuration.getScaledTouchSlop(); + mTouchingHeadsUpView = false; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java index 8343497..8bffdc9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java @@ -131,6 +131,10 @@ public class KeyguardAffordanceHelper { mCenterIcon = mCallback.getCenterIcon(); mRightIcon = mCallback.getRightIcon(); mRightIcon.setIsLeft(false); + updatePreviews(); + } + + public void updatePreviews() { mLeftIcon.setPreviewView(mCallback.getLeftPreview()); mRightIcon.setPreviewView(mCallback.getRightPreview()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index e5ef6ff..efc3ea0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -25,17 +25,13 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; -import android.hardware.fingerprint.FingerprintManager; import android.os.AsyncTask; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; -import android.os.Vibrator; import android.provider.MediaStore; -import android.provider.Settings; import android.telecom.TelecomManager; import android.util.AttributeSet; import android.util.Log; @@ -54,6 +50,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; import com.android.systemui.R; +import com.android.systemui.assist.AssistManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -85,12 +82,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300; private KeyguardAffordanceView mCameraImageView; - private KeyguardAffordanceView mPhoneImageView; - private KeyguardAffordanceView mLockIcon; + private KeyguardAffordanceView mLeftAffordanceView; + private LockIcon mLockIcon; private TextView mIndicationText; private ViewGroup mPreviewContainer; - private View mPhonePreview; + private View mLeftPreview; private View mCameraPreview; private ActivityStarter mActivityStarter; @@ -102,11 +99,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private AccessibilityController mAccessibilityController; private PhoneStatusBar mPhoneStatusBar; - private final TrustDrawable mTrustDrawable; private final Interpolator mLinearOutSlowInInterpolator; - private int mLastUnlockIconRes = 0; private boolean mPrewarmSent; - private boolean mTransientFpError; + private boolean mLeftIsVoiceAssist; + private AssistManager mAssistManager; public KeyguardBottomAreaView(Context context) { this(context, null); @@ -123,7 +119,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mTrustDrawable = new TrustDrawable(mContext); mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in); } @@ -137,8 +132,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL label = getResources().getString(R.string.unlock_label); } else if (host == mCameraImageView) { label = getResources().getString(R.string.camera_label); - } else if (host == mPhoneImageView) { - label = getResources().getString(R.string.phone_label); + } else if (host == mLeftAffordanceView) { + if (mLeftIsVoiceAssist) { + label = getResources().getString(R.string.voice_assist_label); + } else { + label = getResources().getString(R.string.phone_label); + } } info.addAction(new AccessibilityAction(ACTION_CLICK, label)); } @@ -153,8 +152,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } else if (host == mCameraImageView) { launchCamera(); return true; - } else if (host == mPhoneImageView) { - launchPhone(); + } else if (host == mLeftAffordanceView) { + launchLeftAffordance(); return true; } } @@ -168,30 +167,28 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLockPatternUtils = new LockPatternUtils(mContext); mPreviewContainer = (ViewGroup) findViewById(R.id.preview_container); mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button); - mPhoneImageView = (KeyguardAffordanceView) findViewById(R.id.phone_button); - mLockIcon = (KeyguardAffordanceView) findViewById(R.id.lock_icon); + mLeftAffordanceView = (KeyguardAffordanceView) findViewById(R.id.left_button); + mLockIcon = (LockIcon) findViewById(R.id.lock_icon); mIndicationText = (TextView) findViewById(R.id.keyguard_indication_text); watchForCameraPolicyChanges(); updateCameraVisibility(); - updatePhoneVisibility(); mUnlockMethodCache = UnlockMethodCache.getInstance(getContext()); mUnlockMethodCache.addListener(this); - updateLockIcon(); + mLockIcon.update(); setClipChildren(false); setClipToPadding(false); mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext)); - inflatePreviews(); + inflateCameraPreview(); mLockIcon.setOnClickListener(this); - mLockIcon.setBackground(mTrustDrawable); mLockIcon.setOnLongClickListener(this); mCameraImageView.setOnClickListener(this); - mPhoneImageView.setOnClickListener(this); + mLeftAffordanceView.setOnClickListener(this); initAccessibility(); } private void initAccessibility() { mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate); - mPhoneImageView.setAccessibilityDelegate(mAccessibilityDelegate); + mLeftAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate); mCameraImageView.setAccessibilityDelegate(mAccessibilityDelegate); } @@ -222,6 +219,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public void setAccessibilityController(AccessibilityController accessibilityController) { mAccessibilityController = accessibilityController; + mLockIcon.setAccessibilityController(accessibilityController); accessibilityController.addStateChangedCallback(this); } @@ -233,9 +231,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private Intent getCameraIntent() { KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); boolean currentUserHasTrust = updateMonitor.getUserHasTrust( - mLockPatternUtils.getCurrentUser()); - return mLockPatternUtils.isSecure() && !currentUserHasTrust - ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; + KeyguardUpdateMonitor.getCurrentUser()); + boolean secure = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()); + return (secure && !currentUserHasTrust) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; } private void updateCameraVisibility() { @@ -245,15 +243,32 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } ResolveInfo resolved = mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(), PackageManager.MATCH_DEFAULT_ONLY, - mLockPatternUtils.getCurrentUser()); + KeyguardUpdateMonitor.getCurrentUser()); boolean visible = !isCameraDisabledByDpm() && resolved != null && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance); mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE); } - private void updatePhoneVisibility() { - boolean visible = isPhoneVisible(); - mPhoneImageView.setVisibility(visible ? View.VISIBLE : View.GONE); + private void updateLeftAffordanceIcon() { + mLeftIsVoiceAssist = canLaunchVoiceAssist(); + int drawableId; + int contentDescription; + if (mLeftIsVoiceAssist) { + mLeftAffordanceView.setVisibility(View.VISIBLE); + drawableId = R.drawable.ic_mic_26dp; + contentDescription = R.string.accessibility_voice_assist_button; + } else { + boolean visible = isPhoneVisible(); + mLeftAffordanceView.setVisibility(visible ? View.VISIBLE : View.GONE); + drawableId = R.drawable.ic_phone_24dp; + contentDescription = R.string.accessibility_phone_button; + } + mLeftAffordanceView.setImageDrawable(mContext.getDrawable(drawableId)); + mLeftAffordanceView.setContentDescription(mContext.getString(contentDescription)); + } + + public boolean isLeftVoiceAssist() { + return mLeftIsVoiceAssist; } private boolean isPhoneVisible() { @@ -291,32 +306,18 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) { mCameraImageView.setClickable(touchExplorationEnabled); - mPhoneImageView.setClickable(touchExplorationEnabled); + mLeftAffordanceView.setClickable(touchExplorationEnabled); mCameraImageView.setFocusable(accessibilityEnabled); - mPhoneImageView.setFocusable(accessibilityEnabled); - updateLockIconClickability(); - } - - private void updateLockIconClickability() { - if (mAccessibilityController == null) { - return; - } - boolean clickToUnlock = mAccessibilityController.isTouchExplorationEnabled(); - boolean clickToForceLock = mUnlockMethodCache.isTrustManaged() - && !mAccessibilityController.isAccessibilityEnabled(); - boolean longClickToForceLock = mUnlockMethodCache.isTrustManaged() - && !clickToForceLock; - mLockIcon.setClickable(clickToForceLock || clickToUnlock); - mLockIcon.setLongClickable(longClickToForceLock); - mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled()); + mLeftAffordanceView.setFocusable(accessibilityEnabled); + mLockIcon.update(); } @Override public void onClick(View v) { if (v == mCameraImageView) { launchCamera(); - } else if (v == mPhoneImageView) { - launchPhone(); + } else if (v == mLeftAffordanceView) { + launchLeftAffordance(); } if (v == mLockIcon) { if (!mAccessibilityController.isAccessibilityEnabled()) { handleTrustCircleClick(); @@ -339,13 +340,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL 0 /* velocityDp - N/A */); mIndicationController.showTransientIndication( R.string.keyguard_indication_trust_disabled); - mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser()); + mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser()); } public void prewarmCamera() { Intent intent = getCameraIntent(); String targetPackage = PreviewInflater.getTargetPackage(mContext, intent, - mLockPatternUtils.getCurrentUser()); + KeyguardUpdateMonitor.getCurrentUser()); if (targetPackage != null) { Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_PREWARM); prewarm.setPackage(targetPackage); @@ -361,7 +362,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mPrewarmSent = false; Intent intent = getCameraIntent(); String targetPackage = PreviewInflater.getTargetPackage(mContext, intent, - mLockPatternUtils.getCurrentUser()); + KeyguardUpdateMonitor.getCurrentUser()); if (targetPackage != null) { Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_COOLDOWN); prewarm.setPackage(targetPackage); @@ -375,12 +376,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mPrewarmSent = false; final Intent intent = getCameraIntent(); boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity( - mContext, intent, mLockPatternUtils.getCurrentUser()); + mContext, intent, KeyguardUpdateMonitor.getCurrentUser()); if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) { AsyncTask.execute(new Runnable() { @Override public void run() { mContext.startActivityAsUser(intent, UserHandle.CURRENT); + mActivityStarter.preventNextAnimation(); } }); } else { @@ -391,7 +393,35 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } } - public void launchPhone() { + public void launchLeftAffordance() { + if (mLeftIsVoiceAssist) { + launchVoiceAssist(); + } else { + launchPhone(); + } + } + + private void launchVoiceAssist() { + Runnable runnable = new Runnable() { + @Override + public void run() { + mAssistManager.launchVoiceAssistFromKeyguard(); + mActivityStarter.preventNextAnimation(); + } + }; + if (mPhoneStatusBar.isKeyguardCurrentlySecure()) { + AsyncTask.execute(runnable); + } else { + mPhoneStatusBar.executeRunnableDismissingKeyguard(runnable, false /* dismissShade */, + false /* afterKeyguardGone */); + } + } + + private boolean canLaunchVoiceAssist() { + return mAssistManager.canVoiceAssistBeLaunchedFromKeyguard(); + } + + private void launchPhone() { final TelecomManager tm = TelecomManager.from(mContext); if (tm.isInCall()) { AsyncTask.execute(new Runnable() { @@ -409,82 +439,25 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); - if (isShown()) { - mTrustDrawable.start(); - } else { - mTrustDrawable.stop(); - } if (changedView == this && visibility == VISIBLE) { - updateLockIcon(); + mLockIcon.update(); updateCameraVisibility(); } } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mTrustDrawable.stop(); - } - - private void updateLockIcon() { - boolean visible = isShown() && KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); - if (visible) { - mTrustDrawable.start(); - } else { - mTrustDrawable.stop(); - } - if (!visible) { - return; - } - // TODO: Real icon for facelock. - boolean isFingerprintIcon = - KeyguardUpdateMonitor.getInstance(mContext).isFingerprintDetectionRunning(); - boolean anyFingerprintIcon = isFingerprintIcon || mTransientFpError; - int iconRes = mTransientFpError ? R.drawable.ic_fingerprint_error - : isFingerprintIcon ? R.drawable.ic_fingerprint - : mUnlockMethodCache.isFaceUnlockRunning() - ? com.android.internal.R.drawable.ic_account_circle - : mUnlockMethodCache.isCurrentlyInsecure() ? R.drawable.ic_lock_open_24dp - : R.drawable.ic_lock_24dp; - - if (mLastUnlockIconRes != iconRes) { - Drawable icon = mContext.getDrawable(iconRes); - int iconHeight = getResources().getDimensionPixelSize( - R.dimen.keyguard_affordance_icon_height); - int iconWidth = getResources().getDimensionPixelSize( - R.dimen.keyguard_affordance_icon_width); - if (!anyFingerprintIcon && (icon.getIntrinsicHeight() != iconHeight - || icon.getIntrinsicWidth() != iconWidth)) { - icon = new IntrinsicSizeDrawable(icon, iconWidth, iconHeight); - } - mLockIcon.setImageDrawable(icon); - mLockIcon.setPaddingRelative(0, 0, 0, anyFingerprintIcon - ? getResources().getDimensionPixelSize( - R.dimen.fingerprint_icon_additional_padding) - : 0); - mLockIcon.setRestingAlpha( - anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT); - } - - // Hide trust circle when fingerprint is running. - boolean trustManaged = mUnlockMethodCache.isTrustManaged() && !anyFingerprintIcon; - mTrustDrawable.setTrustManaged(trustManaged); - updateLockIconClickability(); - } - - public KeyguardAffordanceView getPhoneView() { - return mPhoneImageView; + public KeyguardAffordanceView getLeftView() { + return mLeftAffordanceView; } - public KeyguardAffordanceView getCameraView() { + public KeyguardAffordanceView getRightView() { return mCameraImageView; } - public View getPhonePreview() { - return mPhonePreview; + public View getLeftPreview() { + return mLeftPreview; } - public View getCameraPreview() { + public View getRightPreview() { return mCameraPreview; } @@ -503,27 +476,39 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public void onUnlockMethodStateChanged() { - updateLockIcon(); + mLockIcon.update(); updateCameraVisibility(); } - private void inflatePreviews() { - mPhonePreview = mPreviewInflater.inflatePreview(PHONE_INTENT); + private void inflateCameraPreview() { mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent()); - if (mPhonePreview != null) { - mPreviewContainer.addView(mPhonePreview); - mPhonePreview.setVisibility(View.INVISIBLE); - } if (mCameraPreview != null) { mPreviewContainer.addView(mCameraPreview); mCameraPreview.setVisibility(View.INVISIBLE); } } + private void updateLeftPreview() { + View previewBefore = mLeftPreview; + if (previewBefore != null) { + mPreviewContainer.removeView(previewBefore); + } + if (mLeftIsVoiceAssist) { + mLeftPreview = mPreviewInflater.inflatePreviewFromService( + mAssistManager.getVoiceInteractorComponentName()); + } else { + mLeftPreview = mPreviewInflater.inflatePreview(PHONE_INTENT); + } + if (mLeftPreview != null) { + mPreviewContainer.addView(mLeftPreview); + mLeftPreview.setVisibility(View.INVISIBLE); + } + } + public void startFinishDozeAnimation() { long delay = 0; - if (mPhoneImageView.getVisibility() == View.VISIBLE) { - startFinishDozeAnimationElement(mPhoneImageView, delay); + if (mLeftAffordanceView.getVisibility() == View.VISIBLE) { + startFinishDozeAnimationElement(mLeftAffordanceView, delay); delay += DOZE_ANIMATION_STAGGER_DELAY; } startFinishDozeAnimationElement(mLockIcon, delay); @@ -563,9 +548,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private final Runnable mTransientFpErrorClearRunnable = new Runnable() { @Override public void run() { - mTransientFpError = false; + mLockIcon.setTransientFpError(false); mIndicationController.hideTransientIndication(); - updateLockIcon(); } }; @@ -578,17 +562,17 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public void onScreenTurnedOn() { - updateLockIcon(); + mLockIcon.update(); } @Override public void onScreenTurnedOff(int why) { - updateLockIcon(); + mLockIcon.update(); } @Override public void onKeyguardVisibilityChanged(boolean showing) { - updateLockIcon(); + mLockIcon.update(); } @Override @@ -597,24 +581,21 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public void onFingerprintRunningStateChanged(boolean running) { - updateLockIcon(); + mLockIcon.update(); } @Override public void onFingerprintHelp(int msgId, String helpString) { - mTransientFpError = true; + mLockIcon.setTransientFpError(true); mIndicationController.showTransientIndication(helpString, getResources().getColor(R.color.system_warning_color, null)); removeCallbacks(mTransientFpErrorClearRunnable); postDelayed(mTransientFpErrorClearRunnable, TRANSIENT_FP_ERROR_TIMEOUT); - updateLockIcon(); } @Override public void onFingerprintError(int msgId, String errString) { // TODO: Go to bouncer if this is "too many attempts" (lockout) error. - Log.i(TAG, "FP Error: " + errString); - updateLockIcon(); } }; @@ -623,28 +604,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mIndicationController = keyguardIndicationController; } - /** - * A wrapper around another Drawable that overrides the intrinsic size. - */ - private static class IntrinsicSizeDrawable extends InsetDrawable { - - private final int mIntrinsicWidth; - private final int mIntrinsicHeight; - - public IntrinsicSizeDrawable(Drawable drawable, int intrinsicWidth, int intrinsicHeight) { - super(drawable, 0); - mIntrinsicWidth = intrinsicWidth; - mIntrinsicHeight = intrinsicHeight; - } - - @Override - public int getIntrinsicWidth() { - return mIntrinsicWidth; - } + public void setAssistManager(AssistManager assistManager) { + mAssistManager = assistManager; + updateLeftAffordance(); + } - @Override - public int getIntrinsicHeight() { - return mIntrinsicHeight; - } + public void updateLeftAffordance() { + updateLeftAffordanceIcon(); + updateLeftPreview(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java new file mode 100644 index 0000000..66f3232 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -0,0 +1,212 @@ +/* + * 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.statusbar.phone; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; +import android.util.AttributeSet; +import android.view.View; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.R; +import com.android.systemui.statusbar.KeyguardAffordanceView; +import com.android.systemui.statusbar.policy.AccessibilityController; + +/** + * Manages the different states and animations of the unlock icon. + */ +public class LockIcon extends KeyguardAffordanceView { + + + private static final int STATE_LOCKED = 0; + private static final int STATE_LOCK_OPEN = 1; + private static final int STATE_FACE_UNLOCK = 2; + private static final int STATE_FINGERPRINT = 3; + private static final int STATE_FINGERPRINT_ERROR = 4; + + private int mLastState = 0; + private boolean mTransientFpError; + private final TrustDrawable mTrustDrawable; + private final UnlockMethodCache mUnlockMethodCache; + private AccessibilityController mAccessibilityController; + + public LockIcon(Context context, AttributeSet attrs) { + super(context, attrs); + mTrustDrawable = new TrustDrawable(context); + setBackground(mTrustDrawable); + mUnlockMethodCache = UnlockMethodCache.getInstance(context); + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + if (isShown()) { + mTrustDrawable.start(); + } else { + mTrustDrawable.stop(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mTrustDrawable.stop(); + } + + public void setTransientFpError(boolean transientFpError) { + mTransientFpError = transientFpError; + update(); + } + + public void update() { + boolean visible = isShown() && KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); + if (visible) { + mTrustDrawable.start(); + } else { + mTrustDrawable.stop(); + } + if (!visible) { + return; + } + // TODO: Real icon for facelock. + int state = getState(); + boolean anyFingerprintIcon = state == STATE_FINGERPRINT || state == STATE_FINGERPRINT_ERROR; + if (state != mLastState) { + int iconRes = getAnimationResForTransition(mLastState, state); + if (iconRes == -1) { + iconRes = getIconForState(state); + } + Drawable icon = mContext.getDrawable(iconRes); + AnimatedVectorDrawable animation = null; + if (icon instanceof AnimatedVectorDrawable) { + animation = (AnimatedVectorDrawable) icon; + } + int iconHeight = getResources().getDimensionPixelSize( + R.dimen.keyguard_affordance_icon_height); + int iconWidth = getResources().getDimensionPixelSize( + R.dimen.keyguard_affordance_icon_width); + if (!anyFingerprintIcon && (icon.getIntrinsicHeight() != iconHeight + || icon.getIntrinsicWidth() != iconWidth)) { + icon = new IntrinsicSizeDrawable(icon, iconWidth, iconHeight); + } + setPaddingRelative(0, 0, 0, anyFingerprintIcon + ? getResources().getDimensionPixelSize( + R.dimen.fingerprint_icon_additional_padding) + : 0); + setRestingAlpha( + anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT); + setImageDrawable(icon); + if (animation != null) { + animation.start(); + } + } + + // Hide trust circle when fingerprint is running. + boolean trustManaged = mUnlockMethodCache.isTrustManaged() && !anyFingerprintIcon; + mTrustDrawable.setTrustManaged(trustManaged); + mLastState = state; + updateClickability(); + } + + private void updateClickability() { + if (mAccessibilityController == null) { + return; + } + boolean clickToUnlock = mAccessibilityController.isTouchExplorationEnabled(); + boolean clickToForceLock = mUnlockMethodCache.isTrustManaged() + && !mAccessibilityController.isAccessibilityEnabled(); + boolean longClickToForceLock = mUnlockMethodCache.isTrustManaged() + && !clickToForceLock; + setClickable(clickToForceLock || clickToUnlock); + setLongClickable(longClickToForceLock); + setFocusable(mAccessibilityController.isAccessibilityEnabled()); + } + + public void setAccessibilityController(AccessibilityController accessibilityController) { + mAccessibilityController = accessibilityController; + } + + private int getIconForState(int state) { + switch (state) { + case STATE_LOCKED: + return R.drawable.ic_lock_24dp; + case STATE_LOCK_OPEN: + return R.drawable.ic_lock_open_24dp; + case STATE_FACE_UNLOCK: + return com.android.internal.R.drawable.ic_account_circle; + case STATE_FINGERPRINT: + return R.drawable.ic_fingerprint; + case STATE_FINGERPRINT_ERROR: + return R.drawable.ic_fingerprint_error; + default: + throw new IllegalArgumentException(); + } + } + + private int getAnimationResForTransition(int oldState, int newState) { + if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) { + return R.drawable.lockscreen_fingerprint_error_state_animation; + } else { + return -1; + } + } + + private int getState() { + boolean fingerprintRunning = + KeyguardUpdateMonitor.getInstance(mContext).isFingerprintDetectionRunning(); + if (mTransientFpError) { + return STATE_FINGERPRINT_ERROR; + } else if (fingerprintRunning) { + return STATE_FINGERPRINT; + } else if (mUnlockMethodCache.isFaceUnlockRunning()) { + return STATE_FACE_UNLOCK; + } else if (mUnlockMethodCache.isCurrentlyInsecure()) { + return STATE_LOCK_OPEN; + } else { + return STATE_LOCKED; + } + } + + /** + * A wrapper around another Drawable that overrides the intrinsic size. + */ + private static class IntrinsicSizeDrawable extends InsetDrawable { + + private final int mIntrinsicWidth; + private final int mIntrinsicHeight; + + public IntrinsicSizeDrawable(Drawable drawable, int intrinsicWidth, int intrinsicHeight) { + super(drawable, 0); + mIntrinsicWidth = intrinsicWidth; + mIntrinsicHeight = intrinsicHeight; + } + + @Override + public int getIntrinsicWidth() { + return mIntrinsicWidth; + } + + @Override + public int getIntrinsicHeight() { + return mIntrinsicHeight; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index c62ad66..7077a17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -144,7 +144,7 @@ public class NavigationBarView extends LinearLayout { @Override public void onClick(View view) { ((InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE)) - .showInputMethodPicker(); + .showInputMethodPicker(true /* showAuxiliarySubtypes */); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index b87c25b..9ef9211 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -39,6 +39,7 @@ import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.TextView; +import com.android.internal.logging.MetricsLogger; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; @@ -73,6 +74,10 @@ public class NotificationPanelView extends PanelView implements private static final float HEADER_RUBBERBAND_FACTOR = 2.05f; private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f; + private static final String COUNTER_PANEL_OPEN = "panel_open"; + private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; + private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek"; + public static final long DOZE_ANIMATION_DURATION = 700; private KeyguardAffordanceHelper mAfforanceHelper; @@ -182,11 +187,11 @@ public class NotificationPanelView extends PanelView implements private float mKeyguardStatusBarAnimateAlpha = 1f; private int mOldLayoutDirection; - private HeadsUpTouchHelper mHeadsUpTouchHelper = new HeadsUpTouchHelper(); - private boolean mPinnedHeadsUpExist; - private boolean mExpansionIsFromHeadsUp; - private int mBottomBarHeight; + private HeadsUpTouchHelper mHeadsUpTouchHelper; + private boolean mIsExpansionFromHeadsUp; + private int mNavigationBarBottomHeight; private boolean mExpandingFromHeadsUp; + private boolean mCollapsedOnDown; private int mPositionMinSideMargin; private int mLastOrientation = -1; @@ -327,7 +332,7 @@ public class NotificationPanelView extends PanelView implements } else if (!mQsExpanded) { setQsExpansion(mQsMinExpansionHeight + mLastOverscroll); } - mNotificationStackScroller.setStackHeight(getExpandedHeight()); + updateStackHeight(getExpandedHeight()); updateHeader(); mNotificationStackScroller.updateIsSmallScreen( mHeader.getCollapsedHeight() + mQsPeekHeight); @@ -534,17 +539,18 @@ public class NotificationPanelView extends PanelView implements } @Override - public boolean - onInterceptTouchEvent(MotionEvent event) { + public boolean onInterceptTouchEvent(MotionEvent event) { if (mBlockTouches) { return false; } initDownStates(event); if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { - mExpansionIsFromHeadsUp = true; + mIsExpansionFromHeadsUp = true; + MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); + MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1); return true; } - if (!isShadeCollapsed() && onQsIntercept(event)) { + if (!isFullyCollapsed() && onQsIntercept(event)) { return true; } return super.onInterceptTouchEvent(event); @@ -618,7 +624,7 @@ public class NotificationPanelView extends PanelView implements case MotionEvent.ACTION_UP: trackMovement(event); if (mQsTracking) { - flingQsWithCurrentVelocity( + flingQsWithCurrentVelocity(y, event.getActionMasked() == MotionEvent.ACTION_CANCEL); mQsTracking = false; } @@ -641,6 +647,7 @@ public class NotificationPanelView extends PanelView implements mOnlyAffordanceInThisMotion = false; mQsTouchAboveFalsingThreshold = mQsFullyExpanded; mDozingOnDown = isDozing(); + mCollapsedOnDown = isFullyCollapsed(); } } @@ -655,9 +662,24 @@ public class NotificationPanelView extends PanelView implements super.requestDisallowInterceptTouchEvent(disallowIntercept); } - private void flingQsWithCurrentVelocity(boolean isCancelMotionEvent) { + private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) { + float vel = getCurrentVelocity(); + final boolean expandsQs = flingExpandsQs(vel); + if (expandsQs) { + logQsSwipeDown(y); + } + flingSettings(vel, expandsQs && !isCancelMotionEvent); + } + + private void logQsSwipeDown(float y) { float vel = getCurrentVelocity(); - flingSettings(vel, flingExpandsQs(vel) && !isCancelMotionEvent); + final int gesture = mStatusBarState == StatusBarState.KEYGUARD + ? EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_QS + : EventLogConstants.SYSUI_SHADE_GESTURE_SWIPE_DOWN_QS; + EventLogTags.writeSysuiLockscreenGesture( + gesture, + (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()), + (int) (vel / mStatusBar.getDisplayDensity())); } private boolean flingExpandsQs(float vel) { @@ -695,18 +717,20 @@ public class NotificationPanelView extends PanelView implements return true; } mHeadsUpTouchHelper.onTouchEvent(event); - if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQSTouch(event)) { + if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) { return true; } if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { + MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); updateVerticalPanelPosition(event.getX()); } super.onTouchEvent(event); return true; } - private boolean handleQSTouch(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f + private boolean handleQsTouch(MotionEvent event) { + final int action = event.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f && mStatusBar.getBarState() != StatusBarState.KEYGUARD && !mQsExpanded && mQsExpansionEnabled) { @@ -718,7 +742,7 @@ public class NotificationPanelView extends PanelView implements mInitialTouchY = event.getX(); mInitialTouchX = event.getY(); } - if (!isShadeCollapsed()) { + if (!isFullyCollapsed()) { handleQsDown(event); } if (!mQsExpandImmediate && mQsTracking) { @@ -727,17 +751,23 @@ public class NotificationPanelView extends PanelView implements return true; } } - if (event.getActionMasked() == MotionEvent.ACTION_CANCEL - || event.getActionMasked() == MotionEvent.ACTION_UP) { + if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mConflictingQsExpansionGesture = false; } - if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isShadeCollapsed() + if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && mQsExpansionEnabled) { mTwoFingerQsExpandPossible = true; } - if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN - && event.getPointerCount() == 2 + final int pointerCount = event.getPointerCount(); + final boolean twoFingerDrag = action == MotionEvent.ACTION_POINTER_DOWN + && pointerCount == 2; + final boolean stylusClickDrag = action == MotionEvent.ACTION_DOWN + && pointerCount == 1 && event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS + && (event.isButtonPressed(MotionEvent.BUTTON_SECONDARY) + || event.isButtonPressed(MotionEvent.BUTTON_TERTIARY)); + if (mTwoFingerQsExpandPossible && (twoFingerDrag || stylusClickDrag) && event.getY(event.getActionIndex()) < mStatusBarMinHeight) { + MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_QS, 1); mQsExpandImmediate = true; requestPanelHeightUpdate(); @@ -799,6 +829,7 @@ public class NotificationPanelView extends PanelView implements } final float y = event.getY(pointerIndex); final float x = event.getX(pointerIndex); + final float h = y - mInitialTouchY; switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: @@ -826,7 +857,6 @@ public class NotificationPanelView extends PanelView implements break; case MotionEvent.ACTION_MOVE: - final float h = y - mInitialTouchY; setQsExpansion(h + mInitialHeightOnTouch); if (h >= getFalsingThreshold()) { mQsTouchAboveFalsingThreshold = true; @@ -842,9 +872,10 @@ public class NotificationPanelView extends PanelView implements float fraction = getQsExpansionFraction(); if ((fraction != 0f || y >= mInitialTouchY) && (fraction != 1f || y <= mInitialTouchY)) { - flingQsWithCurrentVelocity( + flingQsWithCurrentVelocity(y, event.getActionMasked() == MotionEvent.ACTION_CANCEL); } else { + logQsSwipeDown(y); mScrollYOverride = -1; } if (mVelocityTracker != null) { @@ -952,6 +983,10 @@ public class NotificationPanelView extends PanelView implements } else { mKeyguardStatusBar.setAlpha(1f); mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE); + if (keyguardShowing && oldState != mStatusBarState) { + mKeyguardBottomArea.updateLeftAffordance(); + mAfforanceHelper.updatePreviews(); + } } resetVerticalPanelPosition(); updateQsState(); @@ -1191,8 +1226,8 @@ public class NotificationPanelView extends PanelView implements updateEmptyShadeView(); mQsNavbarScrim.setVisibility(mStatusBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling && mQsScrimEnabled - ? View.VISIBLE - : View.INVISIBLE); + ? View.VISIBLE + : View.INVISIBLE); if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) { mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); } @@ -1386,7 +1421,7 @@ public class NotificationPanelView extends PanelView implements * @return Whether we should intercept a gesture to open Quick Settings. */ private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) { - if (!mQsExpansionEnabled) { + if (!mQsExpansionEnabled || mCollapsedOnDown) { return false; } View header = mKeyguardShowing ? mKeyguardStatusBar : mHeader; @@ -1457,12 +1492,12 @@ public class NotificationPanelView extends PanelView implements setQsExpansion(mQsMinExpansionHeight + t * (getTempQsMaxExpansion() - mQsMinExpansionHeight)); } - mNotificationStackScroller.setStackHeight(expandedHeight); + updateStackHeight(expandedHeight); updateHeader(); updateUnlockIcon(); updateNotificationTranslucency(); - mHeadsUpManager.setIsExpanded(!isShadeCollapsed()); - mNotificationStackScroller.setShadeExpanded(!isShadeCollapsed()); + mHeadsUpManager.setIsExpanded(!isFullyCollapsed()); + mNotificationStackScroller.setShadeExpanded(!isFullyCollapsed()); if (DEBUG) { invalidate(); } @@ -1535,21 +1570,19 @@ public class NotificationPanelView extends PanelView implements float alpha; if (mExpandingFromHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) { alpha = 1f; - if (mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) { - mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null); - } } else { alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight()) / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize() - mNotificationStackScroller.getCollapseSecondCardPadding()); alpha = Math.max(0, Math.min(alpha, 1)); alpha = (float) Math.pow(alpha, 0.75); - if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) { - mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null); - } else if (alpha == 1f - && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) { - mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null); - } + } + + if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) { + mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null); + } else if (alpha == 1f + && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) { + mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null); } mNotificationStackScroller.setAlpha(alpha); } @@ -1615,7 +1648,7 @@ public class NotificationPanelView extends PanelView implements } float stackTranslation = mNotificationStackScroller.getStackTranslation(); float translation = stackTranslation / HEADER_RUBBERBAND_FACTOR; - if (mHeadsUpManager.hasPinnedHeadsUp() || mExpansionIsFromHeadsUp) { + if (mHeadsUpManager.hasPinnedHeadsUp() || mIsExpansionFromHeadsUp) { translation = mNotificationStackScroller.getTopPadding() + stackTranslation - mNotificationTopPadding - mQsMinExpansionHeight; } @@ -1683,16 +1716,16 @@ public class NotificationPanelView extends PanelView implements mHeadsUpManager.onExpandingFinished(); mIsExpanding = false; mScrollYOverride = -1; - if (isShadeCollapsed()) { + if (isFullyCollapsed()) { setListening(false); } else { setListening(true); } mQsExpandImmediate = false; mTwoFingerQsExpandPossible = false; - mExpansionIsFromHeadsUp = false; - mNotificationStackScroller.setTrackingHeadsUp(mHeadsUpTouchHelper.isTrackingHeadsUp()); - mExpandingFromHeadsUp = mHeadsUpTouchHelper.isTrackingHeadsUp(); + mIsExpansionFromHeadsUp = false; + mNotificationStackScroller.setTrackingHeadsUp(false); + mExpandingFromHeadsUp = false; } private void setListening(boolean listening) { @@ -1793,13 +1826,13 @@ public class NotificationPanelView extends PanelView implements @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { - mBottomBarHeight = insets.getSystemWindowInsetBottom(); + mNavigationBarBottomHeight = insets.getSystemWindowInsetBottom(); updateMaxHeadsUpTranslation(); return insets; } private void updateMaxHeadsUpTranslation() { - mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mBottomBarHeight); + mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight); } @Override @@ -1817,6 +1850,9 @@ public class NotificationPanelView extends PanelView implements if (mQsExpanded) { flingSettings(0 /* vel */, false /* expand */); } else if (mQsExpansionEnabled) { + EventLogTags.writeSysuiLockscreenGesture( + EventLogConstants.SYSUI_TAP_TO_OPEN_QS, + 0, 0); flingSettings(0 /* vel */, true /* expand */); } } @@ -1833,7 +1869,7 @@ public class NotificationPanelView extends PanelView implements if (start) { EventLogTags.writeSysuiLockscreenGesture( EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER, lengthDp, velocityDp); - mKeyguardBottomArea.launchPhone(); + mKeyguardBottomArea.launchLeftAffordance(); } else { EventLogTags.writeSysuiLockscreenGesture( EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp); @@ -1914,15 +1950,19 @@ public class NotificationPanelView extends PanelView implements if (rightIcon) { mStatusBar.onCameraHintStarted(); } else { - mStatusBar.onPhoneHintStarted(); + if (mKeyguardBottomArea.isLeftVoiceAssist()) { + mStatusBar.onVoiceAssistHintStarted(); + } else { + mStatusBar.onPhoneHintStarted(); + } } } @Override public KeyguardAffordanceView getLeftIcon() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getCameraView() - : mKeyguardBottomArea.getPhoneView(); + ? mKeyguardBottomArea.getRightView() + : mKeyguardBottomArea.getLeftView(); } @Override @@ -1933,22 +1973,22 @@ public class NotificationPanelView extends PanelView implements @Override public KeyguardAffordanceView getRightIcon() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getPhoneView() - : mKeyguardBottomArea.getCameraView(); + ? mKeyguardBottomArea.getLeftView() + : mKeyguardBottomArea.getRightView(); } @Override public View getLeftPreview() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getCameraPreview() - : mKeyguardBottomArea.getPhonePreview(); + ? mKeyguardBottomArea.getRightPreview() + : mKeyguardBottomArea.getLeftPreview(); } @Override public View getRightPreview() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getPhonePreview() - : mKeyguardBottomArea.getCameraPreview(); + ? mKeyguardBottomArea.getLeftPreview() + : mKeyguardBottomArea.getRightPreview(); } @Override @@ -2160,48 +2200,43 @@ public class NotificationPanelView extends PanelView implements } @Override - public void OnPinnedHeadsUpExistChanged(final boolean exist, boolean changeImmediatly) { - if (exist != mPinnedHeadsUpExist) { - mPinnedHeadsUpExist = exist; - if (exist) { - mHeadsUpExistenceChangedRunnable.run(); - updateNotificationTranslucency(); - } else { - mNotificationStackScroller.performOnAnimationFinished( - mHeadsUpExistenceChangedRunnable); - } + public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { + if (inPinnedMode) { + mHeadsUpExistenceChangedRunnable.run(); + updateNotificationTranslucency(); + } else { + mNotificationStackScroller.runAfterAnimationFinished( + mHeadsUpExistenceChangedRunnable); } } @Override - public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) { - if (isHeadsUp) { - mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true); - } + public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { + mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true); } @Override - public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { - mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp); + public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { } @Override - protected boolean isShadeCollapsed() { - return mExpandedHeight == 0; + public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { + mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp); } @Override public void setHeadsUpManager(HeadsUpManager headsUpManager) { super.setHeadsUpManager(headsUpManager); - mHeadsUpTouchHelper.bind(headsUpManager, mNotificationStackScroller, this); + mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller, + this); } public void setTrackingHeadsUp(boolean tracking) { if (tracking) { - // otherwise we update the state when the expansion is finished mNotificationStackScroller.setTrackingHeadsUp(true); mExpandingFromHeadsUp = true; } + // otherwise we update the state when the expansion is finished } @Override @@ -2241,4 +2276,9 @@ public class NotificationPanelView extends PanelView implements mScrollView.setTranslationX(translation); mHeader.setTranslationX(translation); } + + private void updateStackHeight(float stackHeight) { + mNotificationStackScroller.setStackHeight(stackHeight); + updateKeyguardBottomAreaAlpha(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java index f3d4c7f..d5209ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java @@ -80,6 +80,14 @@ public class PanelBar extends FrameLayout { } } + public void setBouncerShowing(boolean showing) { + if (mPanelHolder != null) { + mPanelHolder.setImportantForAccessibility( + showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + : IMPORTANT_FOR_ACCESSIBILITY_AUTO); + } + } + public float getBarHeight() { return getMeasuredHeight(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 4452dd7..38812ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -46,13 +46,14 @@ import java.io.PrintWriter; public abstract class PanelView extends FrameLayout { public static final boolean DEBUG = PanelBar.DEBUG; public static final String TAG = PanelView.class.getSimpleName(); - protected HeadsUpManager mHeadsUpManager; private final void logf(String fmt, Object... args) { Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); } protected PhoneStatusBar mStatusBar; + protected HeadsUpManager mHeadsUpManager; + private float mPeekHeight; private float mHintDistance; private int mEdgeTapAreaWidth; @@ -77,6 +78,7 @@ public abstract class PanelView extends FrameLayout { private boolean mTouchStartedInEmptyArea; private boolean mMotionAborted; private boolean mUpwardsWhenTresholdReached; + private boolean mAnimatingOnDown; private ValueAnimator mHeightAnimator; private ObjectAnimator mPeekAnimator; @@ -242,15 +244,15 @@ public abstract class PanelView extends FrameLayout { final float y = event.getY(pointerIndex); if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - mGestureWaitForTouchSlop = isShadeCollapsed() || hasConflictingGestures(); - mIgnoreXTouchSlop = isShadeCollapsed() || shouldGestureIgnoreXTouchSlop(x, y); + mGestureWaitForTouchSlop = isFullyCollapsed() || hasConflictingGestures(); + mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y); } switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: startExpandMotion(x, y, false /* startTracking */, mExpandedHeight); mJustPeeked = false; - mPanelClosedOnDown = isShadeCollapsed(); + mPanelClosedOnDown = isFullyCollapsed(); mHasLayoutedSinceDown = false; mUpdateFlingOnLayout = false; mMotionAborted = false; @@ -268,7 +270,7 @@ public abstract class PanelView extends FrameLayout { || mPeekPending || mPeekAnimator != null; onTrackingStarted(); } - if (isShadeCollapsed()) { + if (isFullyCollapsed()) { schedulePeek(); } break; @@ -459,8 +461,8 @@ public abstract class PanelView extends FrameLayout { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mStatusBar.userActivity(); - if (mHeightAnimator != null && !mHintAnimationRunning || - mPeekPending || mPeekAnimator != null) { + mAnimatingOnDown = mHeightAnimator != null; + if (mAnimatingOnDown && mClosing && !mHintAnimationRunning || mPeekPending || mPeekAnimator != null) { cancelHeightAnimator(); cancelPeek(); mTouchSlopExceeded = true; @@ -472,7 +474,7 @@ public abstract class PanelView extends FrameLayout { mTouchSlopExceeded = false; mJustPeeked = false; mMotionAborted = false; - mPanelClosedOnDown = isShadeCollapsed(); + mPanelClosedOnDown = isFullyCollapsed(); mHasLayoutedSinceDown = false; mUpdateFlingOnLayout = false; mTouchAboveFalsingThreshold = false; @@ -501,8 +503,10 @@ public abstract class PanelView extends FrameLayout { case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; trackMovement(event); - if (scrolledToBottom || mTouchStartedInEmptyArea) { - if (h < -mTouchSlop && h < -Math.abs(x - mInitialTouchX)) { + if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) { + float hAbs = Math.abs(h); + if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop)) + && hAbs > Math.abs(x - mInitialTouchX)) { cancelHeightAnimator(); startExpandMotion(x, y, true /* startTracking */, mExpandedHeight); return true; @@ -707,7 +711,7 @@ public abstract class PanelView extends FrameLayout { // If the user isn't actively poking us, let's update the height if ((!mTracking || isTrackingBlocked()) && mHeightAnimator == null - && !isShadeCollapsed() + && !isFullyCollapsed() && currentMaxPanelHeight != mExpandedHeight && !mPeekPending && mPeekAnimator == null @@ -1057,8 +1061,6 @@ public abstract class PanelView extends FrameLayout { */ protected abstract int getClearAllHeight(); - protected abstract boolean isShadeCollapsed(); - public void setHeadsUpManager(HeadsUpManager headsUpManager) { mHeadsUpManager = headsUpManager; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 6b17589..887b8f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -76,8 +76,10 @@ import android.view.Display; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.ThreadedRenderer; import android.view.VelocityTracker; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup.LayoutParams; import android.view.ViewStub; import android.view.WindowManager; @@ -91,6 +93,7 @@ import android.view.animation.PathInterpolator; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.StatusBarIcon; import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.keyguard.ViewMediatorCallback; @@ -100,7 +103,7 @@ import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; import com.android.systemui.Prefs; import com.android.systemui.R; -import com.android.systemui.assist.AssistGestureManager; +import com.android.systemui.assist.AssistManager; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -297,7 +300,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, int mTrackingPosition; // the position of the top of the tracking view. // Tracking finger for opening/closing. - int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore boolean mTracking; VelocityTracker mVelocityTracker; @@ -311,6 +313,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // tracking calls to View.setSystemUiVisibility() int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; + // last value sent to window manager + private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE; + DisplayMetrics mDisplayMetrics = new DisplayMetrics(); // XXX: gesture research @@ -323,7 +328,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int mNavigationIconHints = 0; private HandlerThread mHandlerThread; - private AssistGestureManager mAssistGestureManager; + private AssistManager mAssistManager; // ensure quick settings is disabled until the current user makes it through the setup wizard private boolean mUserSetup = false; @@ -528,6 +533,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, = new HashMap<>(); private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>(); private RankingMap mLatestRankingMap; + private boolean mNoAnimationOnNextBarModeChange; @Override public void start() { @@ -617,7 +623,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, R.color.notification_panel_solid_background))); } - mHeadsUpManager = new HeadsUpManager(context, mNotificationPanel.getViewTreeObserver()); + mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow); mHeadsUpManager.setBar(this); mHeadsUpManager.addListener(this); mHeadsUpManager.addListener(mNotificationPanel); @@ -645,8 +651,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, new NavigationBarView.OnVerticalChangedListener() { @Override public void onVerticalChanged(boolean isVertical) { - if (mAssistGestureManager != null) { - mAssistGestureManager.onConfigurationChanged(); + if (mAssistManager != null) { + mAssistManager.onConfigurationChanged(); } mNotificationPanel.setQsScrimEnabled(!isVertical); } @@ -662,6 +668,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // no window manager? good luck with that } + mAssistManager = new AssistManager(this, context); + // figure out which pixel-format to use for the status bar. mPixelFormat = PixelFormat.OPAQUE; @@ -691,6 +699,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mDismissView.setOnButtonClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + MetricsLogger.action(mContext, MetricsLogger.ACTION_DISMISS_ALL_NOTES); clearAllNotifications(); } }); @@ -719,13 +728,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardBottomArea = (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area); mKeyguardBottomArea.setActivityStarter(this); + mKeyguardBottomArea.setAssistManager(mAssistManager); mKeyguardIndicationController = new KeyguardIndicationController(mContext, (KeyguardIndicationTextView) mStatusBarWindow.findViewById( R.id.keyguard_indication_text)); mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController); - mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); - // set the inital view visibility setAreThereNotifications(); @@ -752,7 +760,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // noop } }); - mNetworkController = new NetworkControllerImpl(mContext); + mNetworkController = new NetworkControllerImpl(mContext, mHandlerThread.getLooper()); mHotspotController = new HotspotControllerImpl(mContext); mBluetoothController = new BluetoothControllerImpl(mContext, mHandlerThread.getLooper()); mSecurityController = new SecurityControllerImpl(mContext); @@ -842,7 +850,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mBroadcastReceiver.onReceive(mContext, new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); - mAssistGestureManager = new AssistGestureManager(this, context); // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -858,6 +865,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // listen for USER_SETUP_COMPLETE setting (per-user) resetUserSetupObserver(); + // disable profiling bars, since they overlap and clutter the output on app windows + ThreadedRenderer.overrideProperty("disableProfileBars", "true"); + + // Private API call to make the shadows look better for Recents + ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); + return mStatusBarView; } @@ -965,7 +978,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void invokeAssistGesture(boolean vibrate) { mHandler.removeCallbacks(mInvokeAssist); - mAssistGestureManager.onGestureInvoked(vibrate); + mAssistManager.onGestureInvoked(vibrate); } public int getStatusBarHeight() { @@ -1045,7 +1058,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNavigationBarView.getBackButton().setLongClickable(true); mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener); mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener); - mAssistGestureManager.onConfigurationChanged(); + mAssistManager.onConfigurationChanged(); } // For small-screen devices (read: phones) that lack hardware navigation buttons @@ -1152,11 +1165,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void removeNotification(String key, RankingMap ranking) { - boolean defferRemoval = false; + boolean deferRemoval = false; if (mHeadsUpManager.isHeadsUp(key)) { - defferRemoval = !mHeadsUpManager.removeNotification(key); + deferRemoval = !mHeadsUpManager.removeNotification(key); } - if (defferRemoval) { + if (deferRemoval) { mLatestRankingMap = ranking; mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key)); return; @@ -1185,8 +1198,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } private void updateShowSearchHoldoff() { - mShowSearchHoldoff = mContext.getResources().getInteger( - R.integer.config_show_search_delay); + mShowSearchHoldoff = ViewConfiguration.getLongPressTimeout(); } private void updateNotificationShade() { @@ -1293,13 +1305,20 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, updateClearAll(); updateEmptyShadeView(); - // Disable QS if device not provisioned. - // If the user switcher is simple then disable QS during setup because - // the user intends to use the lock screen user switcher, QS in not needed. + updateQsExpansionEnabled(); + mShadeUpdates.check(); + } + + /** + * Disable QS if device not provisioned. + * If the user switcher is simple then disable QS during setup because + * the user intends to use the lock screen user switcher, QS in not needed. + */ + private void updateQsExpansionEnabled() { mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && (mUserSetup || mUserSwitcherController == null - || !mUserSwitcherController.isSimpleUserSwitcher())); - mShadeUpdates.check(); + || !mUserSwitcherController.isSimpleUserSwitcher()) + && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)); } private void updateNotificationShadeForChildren() { @@ -1583,9 +1602,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey - + " metadata=" + mMediaMetadata - + " metaDataChanged=" + metaDataChanged - + " state=" + mState); + + " metadata=" + mMediaMetadata + + " metaDataChanged=" + metaDataChanged + + " state=" + mState); } Bitmap artworkBitmap = null; @@ -1694,6 +1713,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, * State is one or more of the DISABLE constants from StatusBarManager. */ public void disable(int state1, int state2, boolean animate) { + animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN; mDisabledUnmodified1 = state1; mDisabledUnmodified2 = state2; state1 = adjustDisableFlags(state1); @@ -1732,6 +1752,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, flagdbg.append(((diff1 & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " "); flagdbg.append(((state1 & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " "); + flagdbg.append(((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) ? "QUICK_SETTINGS" + : "quick_settings"); + flagdbg.append(((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) ? "* " : " "); flagdbg.append(">"); Log.d(TAG, flagdbg.toString()); @@ -1780,6 +1803,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; mHeadsUpObserver.onChange(true); } + + if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) { + updateQsExpansionEnabled(); + } } @Override @@ -1792,6 +1819,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, startActivityDismissingKeyguard(intent, false, dismissShade); } + @Override + public void preventNextAnimation() { + overrideActivityPendingAppTransition(true /* keyguardShowing */); + } + public void setQsExpanded(boolean expanded) { mStatusBarWindowManager.setQsExpanded(expanded); } @@ -1838,32 +1870,57 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly) { - if (exist) { + public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { + if (inPinnedMode) { mStatusBarWindowManager.setHeadsUpShowing(true); - } else { - Runnable endRunnable = new Runnable() { - @Override - public void run() { - if (!mHeadsUpManager.hasPinnedHeadsUp()) { - mStatusBarWindowManager.setHeadsUpShowing(false); + mStatusBarWindowManager.setForceStatusBarVisible(true); + if (mNotificationPanel.isFullyCollapsed()) { + // We need to ensure that the touchable region is updated before the window will be + // resized, in order to not catch any touches. A layout will ensure that + // onComputeInternalInsets will be called and after that we can resize the layout. Let's + // make sure that the window stays small for one frame until the touchableRegion is set. + mNotificationPanel.requestLayout(); + mStatusBarWindowManager.setForceWindowCollapsed(true); + mNotificationPanel.post(new Runnable() { + @Override + public void run() { + mStatusBarWindowManager.setForceWindowCollapsed(false); } - } - }; - if (changeImmediatly) { - endRunnable.run(); + }); + } + } else { + if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()) { + // We are currently tracking or is open and the shade doesn't need to be kept + // open artificially. + mStatusBarWindowManager.setHeadsUpShowing(false); } else { - mStackScroller.performOnAnimationFinished(endRunnable); + // we need to keep the panel open artificially, let's wait until the animation + // is finished. + mHeadsUpManager.setHeadsUpGoingAway(true); + mStackScroller.runAfterAnimationFinished(new Runnable() { + @Override + public void run() { + if (!mHeadsUpManager.hasPinnedHeadsUp()) { + mStatusBarWindowManager.setHeadsUpShowing(false); + mHeadsUpManager.setHeadsUpGoingAway(false); + } + } + }); } } } @Override - public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) { + public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { + dismissVolumeDialog(); } @Override - public void OnHeadsUpStateChanged(Entry entry, boolean isHeadsUp) { + public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { + } + + @Override + public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) { if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) { removeNotification(entry.key, mLatestRankingMap); mHeadsUpEntriesToRemoveOnSwitch.remove(entry); @@ -1880,10 +1937,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, boolean alertAgain) { final boolean wasHeadsUp = isHeadsUp(key); if (wasHeadsUp) { - mHeadsUpManager.updateNotification(entry, alertAgain); if (!shouldInterrupt) { // We don't want this to be interrupting anymore, lets remove it mHeadsUpManager.removeNotification(key); + } else { + mHeadsUpManager.updateNotification(entry, alertAgain); } } else if (shouldInterrupt && alertAgain) { // This notification was updated to be a heads-up, show it! @@ -1905,6 +1963,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return mHeadsUpManager.isSnoozed(sbn.getPackageName()); } + public boolean isKeyguardCurrentlySecure() { + return !mUnlockMethodCache.isCurrentlyInsecure(); + } + /** * All changes to the status bar and notifications funnel through here and are batched. */ @@ -1929,7 +1991,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void escalateHeadsUp() { + public void maybeEscalateHeadsUp() { TreeSet<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getSortedEntries(); for (HeadsUpManager.HeadsUpEntry entry : entries) { final StatusBarNotification sbn = entry.entry.notification; @@ -2095,6 +2157,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Shrink the window to the size of the status bar only mStatusBarWindowManager.setStatusBarExpanded(false); + mStatusBarWindowManager.setForceStatusBarVisible(false); mStatusBarView.setFocusable(true); // Close any "App info" popups that might have snuck on-screen @@ -2235,6 +2298,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, setAreThereNotifications(); } + // ready to unhide + if ((vis & View.STATUS_BAR_UNHIDE) != 0) { + mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; + mNoAnimationOnNextBarModeChange = true; + } + // update status bar mode final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(), View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT); @@ -2266,10 +2335,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - // ready to unhide - if ((vis & View.STATUS_BAR_UNHIDE) != 0) { - mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; - } if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) { mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; } @@ -2314,17 +2379,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void checkBarModes() { if (mDemoMode) return; - checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); + checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions(), + mNoAnimationOnNextBarModeChange); if (mNavigationBarView != null) { checkBarMode(mNavigationBarMode, - mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); + mNavigationBarWindowState, mNavigationBarView.getBarTransitions(), + mNoAnimationOnNextBarModeChange); } + mNoAnimationOnNextBarModeChange = false; } - private void checkBarMode(int mode, int windowState, BarTransitions transitions) { + private void checkBarMode(int mode, int windowState, BarTransitions transitions, + boolean noAnimation) { final boolean powerSave = mBatteryController.isPowerSave(); - final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN - && !powerSave; + final boolean anim = !noAnimation && (mScreenOn == null || mScreenOn) + && windowState != WINDOW_STATE_HIDDEN && !powerSave; if (powerSave && getBarState() == StatusBarState.SHADE) { mode = MODE_WARNING; } @@ -2358,13 +2427,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } // manually dismiss the volume panel when interacting with the nav bar if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) { - if (mVolumeComponent != null) { - mVolumeComponent.dismissNow(); - } + dismissVolumeDialog(); } checkBarModes(); } + private void dismissVolumeDialog() { + if (mVolumeComponent != null) { + mVolumeComponent.dismissNow(); + } + } + private void resumeSuspendedAutohide() { if (mAutohideSuspended) { scheduleAutohide(); @@ -2417,7 +2490,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void notifyUiVisibilityChanged(int vis) { try { - mWindowManagerService.statusBarVisibilityChanged(vis); + if (mLastDispatchedSystemUiVisibility != vis) { + mWindowManagerService.statusBarVisibilityChanged(vis); + mLastDispatchedSystemUiVisibility = vis; + } } catch (RemoteException ex) { } } @@ -2627,6 +2703,23 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( mContext, intent, mCurrentUserId); final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); + Runnable runnable = new Runnable() { + public void run() { + intent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + mContext.startActivityAsUser( + intent, new UserHandle(UserHandle.USER_CURRENT)); + overrideActivityPendingAppTransition( + keyguardShowing && !afterKeyguardGone); + } + }; + executeRunnableDismissingKeyguard(runnable, dismissShade, afterKeyguardGone); + } + + public void executeRunnableDismissingKeyguard(final Runnable runnable, + final boolean dismissShade, + final boolean afterKeyguardGone) { + final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); dismissKeyguardThenExecute(new OnDismissAction() { @Override public boolean onDismiss() { @@ -2637,12 +2730,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, ActivityManagerNative.getDefault() .keyguardWaitingForActivityDrawn(); } - intent.setFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivityAsUser( - intent, new UserHandle(UserHandle.USER_CURRENT)); - overrideActivityPendingAppTransition( - keyguardShowing && !afterKeyguardGone); + if (runnable != null) { + runnable.run(); + } } catch (RemoteException e) { } } @@ -2794,8 +2884,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNaturalBarHeight = res.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); - mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); - mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); @@ -2852,6 +2940,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } catch (RemoteException e) { // Ignore. } + setNotificationsShown(newlyVisibleAr); } // State logging @@ -2997,7 +3086,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mHandlerThread = null; } mContext.unregisterReceiver(mBroadcastReceiver); - mAssistGestureManager.destroy(); + mAssistManager.destroy(); } private boolean mDemoModeAllowed; @@ -3445,6 +3534,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); } + public void onVoiceAssistHintStarted() { + mKeyguardIndicationController.showTransientIndication(R.string.voice_hint); + } + public void onPhoneHintStarted() { mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); } @@ -3557,6 +3650,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void setBouncerShowing(boolean bouncerShowing) { super.setBouncerShowing(bouncerShowing); + mStatusBarView.setBouncerShowing(bouncerShowing); disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */); } 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 fb42ba1..5942b46 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -219,19 +219,20 @@ public class PhoneStatusBarPolicy { if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) { zenVisible = mZen != Global.ZEN_MODE_OFF; - zenIconId = R.drawable.stat_sys_dnd; + zenIconId = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS + ? R.drawable.stat_sys_dnd_total_silence : 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); + zenDescription = mContext.getString(R.string.interruption_level_none); } else if (mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { zenVisible = true; zenIconId = R.drawable.stat_sys_zen_important; - zenDescription = mContext.getString(R.string.zen_important_interruptions); + zenDescription = mContext.getString(R.string.interruption_level_priority); } - if (DndTile.isVisible(mContext) + if (DndTile.isVisible(mContext) && !DndTile.isCombinedIcon(mContext) && audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) { volumeVisible = true; volumeIconId = R.drawable.stat_sys_ringer_silent; 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 954eb10..0ef0fd9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -67,11 +67,12 @@ public class QSTileHost implements QSTile.Host { private static final String TAG = "QSTileHost"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final String TILES_SETTING = "sysui_qs_tiles"; + protected static final String TILES_SETTING = "sysui_qs_tiles"; private final Context mContext; private final PhoneStatusBar mStatusBar; private final LinkedHashMap<String, QSTile<?>> mTiles = new LinkedHashMap<>(); + private final ArrayList<String> mTileSpecs = new ArrayList<>(); private final Observer mObserver = new Observer(); private final BluetoothController mBluetooth; private final LocationController mLocation; @@ -81,7 +82,7 @@ public class QSTileHost implements QSTile.Host { private final HotspotController mHotspot; private final CastController mCast; private final Looper mLooper; - private final CurrentUserTracker mUserTracker; + protected final CurrentUserTracker mUserTracker; private final FlashlightController mFlashlight; private final UserSwitcherController mUserSwitcherController; private final KeyguardMonitor mKeyguard; @@ -224,6 +225,7 @@ public class QSTileHost implements QSTile.Host { private void recreateTiles() { if (DEBUG) Log.d(TAG, "Recreating tiles"); final List<String> tileSpecs = loadTileSpecs(); + if (tileSpecs.equals(mTileSpecs)) return; for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) { if (!tileSpecs.contains(tile.getKey())) { if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey()); @@ -243,7 +245,8 @@ public class QSTileHost implements QSTile.Host { } } } - if (mTiles.equals(newTiles)) return; + mTileSpecs.clear(); + mTileSpecs.addAll(tileSpecs); mTiles.clear(); mTiles.putAll(newTiles); if (mCallback != null) { @@ -251,7 +254,7 @@ public class QSTileHost implements QSTile.Host { } } - private QSTile<?> createTile(String tileSpec) { + protected QSTile<?> createTile(String tileSpec) { if (tileSpec.equals("wifi")) return new WifiTile(this); else if (tileSpec.equals("bt")) return new BluetoothTile(this); else if (tileSpec.equals("inversion")) return new ColorInversionTile(this); @@ -267,7 +270,7 @@ public class QSTileHost implements QSTile.Host { else throw new IllegalArgumentException("Bad tile spec: " + tileSpec); } - private List<String> loadTileSpecs() { + protected List<String> loadTileSpecs() { final Resources res = mContext.getResources(); final String defaultTileList = res.getString(R.string.quick_settings_tiles_default); String tileList = Secure.getStringForUser(mContext.getContentResolver(), TILES_SETTING, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index e701783..808f1ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -80,7 +80,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private float mCurrentInFrontAlpha; private float mCurrentBehindAlpha; private float mCurrentHeadsUpAlpha = 1; - private int mAmountOfPinnedHeadsUps; + private int mPinnedHeadsUpCount; private float mTopHeadsUpDragAmount; private View mDraggedHeadsUpView; @@ -115,6 +115,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, if (mFraction != fraction) { mFraction = fraction; scheduleUpdate(); + if (mPinnedHeadsUpCount != 0) { + updateHeadsUpScrim(false); + } } } @@ -347,25 +350,27 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } @Override - public void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly) { + public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { } @Override - public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) { - if (isHeadsUp) { - mAmountOfPinnedHeadsUps++; - } else { - mAmountOfPinnedHeadsUps--; - if (headsUp == mDraggedHeadsUpView) { - mDraggedHeadsUpView = null; - mTopHeadsUpDragAmount = 0.0f; - } + public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { + mPinnedHeadsUpCount++; + updateHeadsUpScrim(true); + } + + @Override + public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { + mPinnedHeadsUpCount--; + if (headsUp == mDraggedHeadsUpView) { + mDraggedHeadsUpView = null; + mTopHeadsUpDragAmount = 0.0f; } updateHeadsUpScrim(true); } @Override - public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { + public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { } private void updateHeadsUpScrim(boolean animate) { @@ -374,12 +379,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, TAG_KEY_ANIM); float animEndValue = -1; if (previousAnimator != null) { - if ((animate || alpha == mCurrentHeadsUpAlpha)) { - // lets cancel any running animators + if (animate || alpha == mCurrentHeadsUpAlpha) { previousAnimator.cancel(); + } else { + animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_END_ALPHA); } - animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, - TAG_HUN_START_ALPHA); } if (alpha != mCurrentHeadsUpAlpha && alpha != animEndValue) { if (animate) { @@ -390,7 +394,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, if (previousAnimator != null) { float previousStartValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_START_ALPHA); - float previousEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, + float previousEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_END_ALPHA); // we need to increase all animation keyframes of the previous animator by the // relative change to the end value @@ -410,6 +414,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } } + /** + * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means + * the heads up is in its resting space and 1 means it's fully dragged out. + * + * @param draggedHeadsUpView the dragged view + * @param topHeadsUpDragAmount how far is it dragged + */ public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) { mTopHeadsUpDragAmount = topHeadsUpDragAmount; mDraggedHeadsUpView = draggedHeadsUpView; @@ -417,12 +428,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } private float calculateHeadsUpAlpha() { - if (mAmountOfPinnedHeadsUps >= 2) { - return 1.0f; - } else if (mAmountOfPinnedHeadsUps == 0) { - return 0.0f; + float alpha; + if (mPinnedHeadsUpCount >= 2) { + alpha = 1.0f; + } else if (mPinnedHeadsUpCount == 0) { + alpha = 0.0f; } else { - return 1.0f - mTopHeadsUpDragAmount; + alpha = 1.0f - mTopHeadsUpDragAmount; } + float expandFactor = (1.0f - mFraction); + expandFactor = Math.max(expandFactor, 0.0f); + return alpha * expandFactor; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java index 4a43c47..45c8938 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java @@ -27,6 +27,7 @@ import android.provider.MediaStore; import android.util.Log; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import java.util.HashMap; import java.util.List; @@ -228,7 +229,7 @@ public class SecureCameraLaunchManager { // Get the list of applications that can handle the intent. final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser( - intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser()); + intent, PackageManager.MATCH_DEFAULT_ONLY, KeyguardUpdateMonitor.getCurrentUser()); if (appList.size() == 0) { if (DEBUG) Log.d(TAG, "No targets found for secure camera intent"); return false; @@ -237,7 +238,7 @@ public class SecureCameraLaunchManager { // Get the application that the intent resolves to. ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, - mLockPatternUtils.getCurrentUser()); + KeyguardUpdateMonitor.getCurrentUser()); if (resolved == null || resolved.activityInfo == null) { return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index 84a9f64..422d868 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -129,8 +129,9 @@ public class StatusBarWindowManager { } private void applyHeight(State state) { - boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded - || state.keyguardFadingAway || state.bouncerShowing || state.headsUpShowing; + boolean expanded = !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded() + || state.statusBarExpanded || state.keyguardFadingAway || state.bouncerShowing + || state.headsUpShowing); if (expanded) { mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT; } else { @@ -166,6 +167,7 @@ public class StatusBarWindowManager { private void apply(State state) { applyKeyguardFlags(state); + applyForceStatusBarVisibleFlag(state); applyFocusableFlag(state); adjustScreenOrientation(state); applyHeight(state); @@ -178,6 +180,16 @@ public class StatusBarWindowManager { } } + private void applyForceStatusBarVisibleFlag(State state) { + if (state.forceStatusBarVisible) { + mLpChanged.privateFlags |= WindowManager + .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; + } else { + mLpChanged.privateFlags &= ~WindowManager + .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; + } + } + private void applyModalFlag(State state) { if (state.headsUpShowing) { mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; @@ -240,6 +252,21 @@ public class StatusBarWindowManager { apply(mCurrentState); } + public void setForceStatusBarVisible(boolean forceStatusBarVisible) { + mCurrentState.forceStatusBarVisible = forceStatusBarVisible; + apply(mCurrentState); + } + + /** + * Force the window to be collapsed, even if it should theoretically be expanded. + * Used for when a heads-up comes in but we still need to wait for the touchable regions to + * be computed. + */ + public void setForceWindowCollapsed(boolean force) { + mCurrentState.forceCollapsed = force; + apply(mCurrentState); + } + private static class State { boolean keyguardShowing; boolean keyguardOccluded; @@ -250,6 +277,8 @@ public class StatusBarWindowManager { boolean keyguardFadingAway; boolean qsExpanded; boolean headsUpShowing; + boolean forceStatusBarVisible; + boolean forceCollapsed; /** * The {@link BaseStatusBar} state from the status bar. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java index 65cd268..66d71f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java @@ -80,8 +80,8 @@ public class UnlockMethodCache { } private void update(boolean updateAlways) { - int user = mLockPatternUtils.getCurrentUser(); - boolean secure = mLockPatternUtils.isSecure(); + int user = KeyguardUpdateMonitor.getCurrentUser(); + boolean secure = mLockPatternUtils.isSecure(user); boolean currentlyInsecure = !secure || mKeyguardUpdateMonitor.getUserHasTrust(user); boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user); boolean faceUnlockRunning = mKeyguardUpdateMonitor.isFaceUnlockRunning(user) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java index 18983ff..5893cb2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java @@ -20,6 +20,7 @@ import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.net.wifi.WifiManager.ActionListener; +import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -58,10 +59,10 @@ public class AccessPointControllerImpl private int mCurrentUser; - public AccessPointControllerImpl(Context context) { + public AccessPointControllerImpl(Context context, Looper bgLooper) { mContext = context; mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mWifiTracker = new WifiTracker(context, this, false, true); + mWifiTracker = new WifiTracker(context, this, bgLooper, false, true); mCurrentUser = ActivityManager.getCurrentUser(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java index 63fcbc5..8f86e2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java @@ -35,4 +35,9 @@ public class AccessibilityContentDescriptions { }; static final int WIFI_NO_CONNECTION = R.string.accessibility_no_wifi; + + static final int[] ETHERNET_CONNECTION_VALUES = { + R.string.accessibility_ethernet_disconnected, + R.string.accessibility_ethernet_connected, + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java new file mode 100644 index 0000000..b391bd9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java @@ -0,0 +1,26 @@ +/* + * 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.statusbar.policy; + +import com.android.systemui.R; + +class EthernetIcons { + static final int[][] ETHERNET_ICONS = { + { R.drawable.stat_sys_ethernet }, + { R.drawable.stat_sys_ethernet_fully }, + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java new file mode 100644 index 0000000..9c044c4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java @@ -0,0 +1,68 @@ +/* + * 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.statusbar.policy; + +import android.content.Context; +import android.net.NetworkCapabilities; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; +import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster; + +import java.util.List; +import java.util.Objects; + + +public class EthernetSignalController extends + SignalController<SignalController.State, SignalController.IconGroup> { + + public EthernetSignalController(Context context, + List<NetworkSignalChangedCallback> signalCallbacks, + List<SignalCluster> signalClusters, NetworkControllerImpl networkController) { + super("EthernetSignalController", context, NetworkCapabilities.TRANSPORT_ETHERNET, + signalCallbacks, signalClusters, networkController); + mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup( + "Ethernet Icons", + EthernetIcons.ETHERNET_ICONS, + null, + AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES, + 0, 0, 0, 0, + AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]); + } + + @Override + public void notifyListeners() { + boolean ethernetVisible = mCurrentState.connected; + String contentDescription = getStringIfExists(getContentDescription()); + + // TODO: wire up data transfer using WifiSignalPoller. + int signalClustersLength = mSignalClusters.size(); + for (int i = 0; i < signalClustersLength; i++) { + mSignalClusters.get(i).setEthernetIndicators(ethernetVisible, getCurrentIconId(), + contentDescription); + } + } + + @Override + public SignalController.State cleanState() { + return new SignalController.State(); + } + + public void setConnected(boolean connected) { + mCurrentState.connected = connected; + notifyListenersIfNecessary(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index b4e4773..14060df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * 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. @@ -25,6 +25,7 @@ import android.provider.Settings; import android.util.ArrayMap; import android.util.Log; import android.util.Pools; +import android.view.View; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; @@ -35,11 +36,16 @@ import com.android.systemui.statusbar.phone.PhoneStatusBar; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Stack; import java.util.TreeSet; +/** + * A manager which handles heads up notifications which is a special mode where + * they simply peek from the top of the screen. + */ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener { private static final String TAG = "HeadsUpManager"; private static final boolean DEBUG = false; @@ -48,7 +54,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL private final int mHeadsUpNotificationDecay; private final int mMinimumDisplayTime; - private final int mTouchSensitivityDelay; + private final int mTouchAcceptanceDelay; private final ArrayMap<String, Long> mSnoozedPackages; private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>(); private final int mDefaultSnoozeLengthMs; @@ -67,13 +73,14 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL @Override public boolean release(HeadsUpEntry instance) { - instance.removeAutoCancelCallbacks(); + instance.reset(); mPoolObjects.push(instance); return true; } }; - + private final View mStatusBarWindowView; + private final int mStatusBarHeight; private PhoneStatusBar mBar; private int mSnoozeLengthMs; private ContentObserver mSettingsObserver; @@ -86,13 +93,15 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL private boolean mTrackingHeadsUp; private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>(); private boolean mIsExpanded; - private boolean mHasPinnedHeadsUp; + private boolean mHasPinnedNotification; private int[] mTmpTwoArray = new int[2]; + private boolean mHeadsUpGoingAway; + private boolean mWaitingOnCollapseWhenGoingAway; + private boolean mIsObserving; - public HeadsUpManager(final Context context, ViewTreeObserver observer) { + public HeadsUpManager(final Context context, View statusBarWindowView) { Resources resources = context.getResources(); - mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay); - if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay); + mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay); mSnoozedPackages = new ArrayMap<>(); mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms); mSnoozeLengthMs = mDefaultSnoozeLengthMs; @@ -116,8 +125,24 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL context.getContentResolver().registerContentObserver( Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false, mSettingsObserver); - if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs); - observer.addOnComputeInternalInsetsListener(this); + mStatusBarWindowView = statusBarWindowView; + mStatusBarHeight = resources.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); + } + + private void updateTouchableRegionListener() { + boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway + || mWaitingOnCollapseWhenGoingAway; + if (shouldObserve == mIsObserving) { + return; + } + if (shouldObserve) { + mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); + mStatusBarWindowView.requestLayout(); + } else { + mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this); + } + mIsObserving = shouldObserve; } public void setBar(PhoneStatusBar bar) { @@ -154,7 +179,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL if (alert) { HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key); headsUpEntry.updateEntry(); - setEntryToShade(headsUpEntry, mIsExpanded, false /* justAdded */, false); + setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp)); } } @@ -165,22 +190,31 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL headsUpEntry.setEntry(entry); mHeadsUpEntries.put(entry.key, headsUpEntry); entry.row.setHeadsUp(true); - setEntryToShade(headsUpEntry, mIsExpanded /* inShade */, true /* justAdded */, false); + setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(entry)); for (OnHeadsUpChangedListener listener : mListeners) { - listener.OnHeadsUpStateChanged(entry, true); + listener.onHeadsUpStateChanged(entry, true); } entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); } - private void setEntryToShade(HeadsUpEntry headsUpEntry, boolean inShade, boolean justAdded, - boolean forceImmediate) { + private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) { + return !mIsExpanded || hasFullScreenIntent(entry); + } + + private boolean hasFullScreenIntent(NotificationData.Entry entry) { + return entry.notification.getNotification().fullScreenIntent != null; + } + + private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) { ExpandableNotificationRow row = headsUpEntry.entry.row; - if (row.isInShade() != inShade || justAdded) { - row.setInShade(inShade); - if (!justAdded || !inShade) { - updatePinnedHeadsUpState(forceImmediate); - for (OnHeadsUpChangedListener listener : mListeners) { - listener.OnHeadsUpPinnedChanged(row, !inShade); + if (row.isPinned() != isPinned) { + row.setPinned(isPinned); + updatePinnedMode(); + for (OnHeadsUpChangedListener listener : mListeners) { + if (isPinned) { + listener.onHeadsUpPinned(row); + } else { + listener.onHeadsUpUnPinned(row); } } } @@ -189,24 +223,24 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL private void removeHeadsUpEntry(NotificationData.Entry entry) { HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key); mSortedEntries.remove(remove); - mEntryPool.release(remove); entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); entry.row.setHeadsUp(false); - setEntryToShade(remove, true /* inShade */, false /* justAdded */, - false /* forceImmediate */); + setEntryPinned(remove, false /* isPinned */); for (OnHeadsUpChangedListener listener : mListeners) { - listener.OnHeadsUpStateChanged(entry, false); + listener.onHeadsUpStateChanged(entry, false); } + mEntryPool.release(remove); } - private void updatePinnedHeadsUpState(boolean forceImmediate) { - boolean hasPinnedHeadsUp = hasPinnedHeadsUpInternal(); - if (hasPinnedHeadsUp == mHasPinnedHeadsUp) { + private void updatePinnedMode() { + boolean hasPinnedNotification = hasPinnedNotificationInternal(); + if (hasPinnedNotification == mHasPinnedNotification) { return; } - mHasPinnedHeadsUp = hasPinnedHeadsUp; - for (OnHeadsUpChangedListener listener :mListeners) { - listener.OnPinnedHeadsUpExistChanged(hasPinnedHeadsUp, forceImmediate); + mHasPinnedNotification = hasPinnedNotification; + updateTouchableRegionListener(); + for (OnHeadsUpChangedListener listener : mListeners) { + listener.onHeadsUpPinnedModeChanged(hasPinnedNotification); } } @@ -222,7 +256,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL releaseImmediately(key); return true; } else { - getHeadsUpEntry(key).hideAsSoonAsPossible(); + getHeadsUpEntry(key).removeAsSoonAsPossible(); return false; } } @@ -245,14 +279,13 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL return mHeadsUpEntries.containsKey(key); } - /** * Push any current Heads Up notification down into the shade. */ public void releaseAllImmediately() { if (DEBUG) Log.v(TAG, "releaseAllImmediately"); - HashSet<String> keys = new HashSet<>(mHeadsUpEntries.keySet()); - for (String key: keys) { + ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet()); + for (String key : keys) { releaseImmediately(key); } } @@ -280,7 +313,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL } public void snooze() { - for (String key: mHeadsUpEntries.keySet()) { + for (String key : mHeadsUpEntries.keySet()) { HeadsUpEntry entry = mHeadsUpEntries.get(key); String packageName = entry.entry.notification.getPackageName(); mSnoozedPackages.put(snoozeKey(packageName, mUser), @@ -310,8 +343,11 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL } /** + * Decides whether a click is invalid for a notification, i.e it has not been shown long enough + * that a user might have consciously clicked on it. + * * @param key the key of the touched notification - * @return whether the touch is valid and should not be discarded + * @return whether the touch is invalid and should be discarded */ public boolean shouldSwallowClick(String key) { HeadsUpEntry entry = mHeadsUpEntries.get(key); @@ -322,14 +358,18 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL } public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { - if (!mIsExpanded && mHasPinnedHeadsUp) { + if (mIsExpanded) { + // The touchable region is always the full area when expanded + return; + } + if (mHasPinnedNotification) { int minX = Integer.MAX_VALUE; int maxX = 0; int minY = Integer.MAX_VALUE; int maxY = 0; - for (HeadsUpEntry entry: mSortedEntries) { + for (HeadsUpEntry entry : mSortedEntries) { ExpandableNotificationRow row = entry.entry.row; - if (!row.isInShade()) { + if (row.isPinned()) { row.getLocationOnScreen(mTmpTwoArray); minX = Math.min(minX, mTmpTwoArray[0]); minY = Math.min(minY, 0); @@ -340,6 +380,9 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); info.touchableRegion.set(minX, minY, maxX, maxY); + } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) { + info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); } } @@ -349,7 +392,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("HeadsUpManager state:"); - pw.print(" mTouchSensitivityDelay="); pw.println(mTouchSensitivityDelay); + pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay); pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs); pw.print(" now="); pw.println(SystemClock.elapsedRealtime()); pw.print(" mUser="); pw.println(mUser); @@ -365,38 +408,32 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL } public boolean hasPinnedHeadsUp() { - return mHasPinnedHeadsUp; + return mHasPinnedNotification; } - private boolean hasPinnedHeadsUpInternal() { - for (String key: mHeadsUpEntries.keySet()) { + private boolean hasPinnedNotificationInternal() { + for (String key : mHeadsUpEntries.keySet()) { HeadsUpEntry entry = mHeadsUpEntries.get(key); - if (!entry.entry.row.isInShade()) { + if (entry.entry.row.isPinned()) { return true; } } return false; } - public void addSwipedOutKey(String key) { + /** + * Notifies that a notification was swiped out and will be removed. + * + * @param key the notification key + */ + public void addSwipedOutNotification(String key) { mSwipedOutKeys.add(key); } - public float getHighestPinnedHeadsUp() { - float max = 0; - for (HeadsUpEntry entry: mSortedEntries) { - if (!entry.entry.row.isInShade()) { - max = Math.max(max, entry.entry.row.getActualHeight()); - } - } - return max; - } - - public void releaseAllToShade() { - for (String key: mHeadsUpEntries.keySet()) { + public void unpinAll() { + for (String key : mHeadsUpEntries.keySet()) { HeadsUpEntry entry = mHeadsUpEntries.get(key); - setEntryToShade(entry, true /* toShade */, false /* justAdded */, - true /* forceImmediate */); + setEntryPinned(entry, false /* isPinned */); } } @@ -420,7 +457,10 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL if (isExpanded != mIsExpanded) { mIsExpanded = isExpanded; if (isExpanded) { - releaseAllToShade(); + // make sure our state is sane + mWaitingOnCollapseWhenGoingAway = false; + mHeadsUpGoingAway = false; + updateTouchableRegionListener(); } } } @@ -430,6 +470,12 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0; } + /** + * Compare two entries and decide how they should be ranked. + * + * @return -1 if the first argument should be ranked higher than the second, 1 if the second + * one should be ranked higher and 0 if they are equal. + */ public int compare(NotificationData.Entry a, NotificationData.Entry b) { HeadsUpEntry aEntry = getHeadsUpEntry(a.key); HeadsUpEntry bEntry = getHeadsUpEntry(b.key); @@ -439,6 +485,45 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL return aEntry.compareTo(bEntry); } + /** + * Set that we are exiting the headsUp pinned mode, but some notifications might still be + * animating out. This is used to keep the touchable regions in a sane state. + */ + public void setHeadsUpGoingAway(boolean headsUpGoingAway) { + if (headsUpGoingAway != mHeadsUpGoingAway) { + mHeadsUpGoingAway = headsUpGoingAway; + if (!headsUpGoingAway) { + waitForStatusBarLayout(); + } + updateTouchableRegionListener(); + } + } + + /** + * We need to wait on the whole panel to collapse, before we can remove the touchable region + * listener. + */ + private void waitForStatusBarLayout() { + mWaitingOnCollapseWhenGoingAway = true; + mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, + int oldTop, int oldRight, int oldBottom) { + if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) { + mStatusBarWindowView.removeOnLayoutChangeListener(this); + mWaitingOnCollapseWhenGoingAway = false; + updateTouchableRegionListener(); + } + } + }); + } + + + /** + * This represents a notification and how long it is in a heads up mode. It also manages its + * lifecycle automatically when created. + */ public class HeadsUpEntry implements Comparable<HeadsUpEntry> { public NotificationData.Entry entry; public long postTime; @@ -449,7 +534,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL this.entry = entry; // The actual post time will be just after the heads-up really slided in - postTime = mClock.currentTimeMillis() + mTouchSensitivityDelay; + postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay; mRemoveHeadsUpRunnable = new Runnable() { @Override public void run() { @@ -467,8 +552,8 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL long currentTime = mClock.currentTimeMillis(); earliestRemovaltime = currentTime + mMinimumDisplayTime; postTime = Math.max(postTime, currentTime); - removeAutoCancelCallbacks(); - if (canEntryDecay()) { + removeAutoRemovalCallbacks(); + if (!hasFullScreenIntent(entry)) { long finishTime = postTime + mHeadsUpNotificationDecay; long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime); mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay); @@ -476,10 +561,6 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL updateSortOrder(HeadsUpEntry.this); } - private boolean canEntryDecay() { - return entry.notification.getNotification().fullScreenIntent == null; - } - @Override public int compareTo(HeadsUpEntry o) { return postTime < o.postTime ? 1 @@ -487,7 +568,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL : -1; } - public void removeAutoCancelCallbacks() { + public void removeAutoRemovalCallbacks() { mHandler.removeCallbacks(mRemoveHeadsUpRunnable); } @@ -495,11 +576,17 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL return earliestRemovaltime < mClock.currentTimeMillis(); } - public void hideAsSoonAsPossible() { - removeAutoCancelCallbacks(); + public void removeAsSoonAsPossible() { + removeAutoRemovalCallbacks(); mHandler.postDelayed(mRemoveHeadsUpRunnable, earliestRemovaltime - mClock.currentTimeMillis()); } + + public void reset() { + removeAutoRemovalCallbacks(); + entry = null; + mRemoveHeadsUpRunnable = null; + } } /** @@ -519,8 +606,29 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL } public interface OnHeadsUpChangedListener { - void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly); - void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp); - void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp); + /** + * The state whether there exist pinned heads-ups or not changed. + * + * @param inPinnedMode whether there are any pinned heads-ups + */ + void onHeadsUpPinnedModeChanged(boolean inPinnedMode); + + /** + * A notification was just pinned to the top. + */ + void onHeadsUpPinned(ExpandableNotificationRow headsUp); + + /** + * A notification was just unpinned from the top. + */ + void onHeadsUpUnPinned(ExpandableNotificationRow headsUp); + + /** + * A notification just became a heads up or turned back to its normal state. + * + * @param entry the entry of the changed notification + * @param isHeadsUp whether the notification is now a headsUp notification + */ + void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java index f2b2f66..a7fdadc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java @@ -162,7 +162,7 @@ public class MobileDataControllerImpl implements NetworkController.MobileDataCon usage.warningLevel = DEFAULT_WARNING_LEVEL; } if (usage != null) { - usage.carrier = mNetworkController.getMobileNetworkName(); + usage.carrier = mNetworkController.getMobileDataNetworkName(); } return usage; } catch (RemoteException e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index c3c6b12..22bf47c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -84,6 +84,7 @@ public class MobileSignalController extends SignalController< mapIconSets(); mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault; + mLastState.networkNameData = mCurrentState.networkNameData = mNetworkNameDefault; mLastState.enabled = mCurrentState.enabled = hasMobileData; mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons; // Get initial data sim state. @@ -294,6 +295,7 @@ public class MobileSignalController extends SignalController< if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) { updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false), intent.getStringExtra(TelephonyIntents.EXTRA_SPN), + intent.getStringExtra(TelephonyIntents.EXTRA_DATA_SPN), intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false), intent.getStringExtra(TelephonyIntents.EXTRA_PLMN)); notifyListenersIfNecessary(); @@ -322,14 +324,18 @@ public class MobileSignalController extends SignalController< /** * Updates the network's name based on incoming spn and plmn. */ - void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { + void updateNetworkName(boolean showSpn, String spn, String dataSpn, + boolean showPlmn, String plmn) { if (CHATTY) { - Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn + Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + + " spn=" + spn + " dataSpn=" + dataSpn + " showPlmn=" + showPlmn + " plmn=" + plmn); } StringBuilder str = new StringBuilder(); + StringBuilder strData = new StringBuilder(); if (showPlmn && plmn != null) { str.append(plmn); + strData.append(plmn); } if (showSpn && spn != null) { if (str.length() != 0) { @@ -342,6 +348,17 @@ public class MobileSignalController extends SignalController< } else { mCurrentState.networkName = mNetworkNameDefault; } + if (showSpn && dataSpn != null) { + if (strData.length() != 0) { + strData.append(mNetworkNameSeparator); + } + strData.append(dataSpn); + } + if (strData.length() != 0) { + mCurrentState.networkNameData = strData.toString(); + } else { + mCurrentState.networkNameData = mNetworkNameDefault; + } } /** @@ -492,6 +509,7 @@ public class MobileSignalController extends SignalController< static class MobileState extends SignalController.State { String networkName; + String networkNameData; boolean dataSim; boolean dataConnected; boolean isEmergency; @@ -505,6 +523,7 @@ public class MobileSignalController extends SignalController< MobileState state = (MobileState) s; dataSim = state.dataSim; networkName = state.networkName; + networkNameData = state.networkNameData; dataConnected = state.dataConnected; inetForNetwork = state.inetForNetwork; isEmergency = state.isEmergency; @@ -518,6 +537,7 @@ public class MobileSignalController extends SignalController< builder.append(','); builder.append("dataSim=").append(dataSim).append(','); builder.append("networkName=").append(networkName).append(','); + builder.append("networkNameData=").append(networkNameData).append(','); builder.append("dataConnected=").append(dataConnected).append(','); builder.append("inetForNetwork=").append(inetForNetwork).append(','); builder.append("isEmergency=").append(isEmergency).append(','); @@ -529,6 +549,7 @@ public class MobileSignalController extends SignalController< public boolean equals(Object o) { return super.equals(o) && Objects.equals(((MobileState) o).networkName, networkName) + && Objects.equals(((MobileState) o).networkNameData, networkNameData) && ((MobileState) o).dataSim == dataSim && ((MobileState) o).dataConnected == dataConnected && ((MobileState) o).isEmergency == isEmergency diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 9adf028..92e0365 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -28,6 +28,7 @@ import android.net.NetworkCapabilities; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Looper; import android.provider.Settings; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -73,6 +74,10 @@ public class NetworkControllerImpl extends BroadcastReceiver // Subcontrollers. @VisibleForTesting final WifiSignalController mWifiSignalController; + + @VisibleForTesting + final EthernetSignalController mEthernetSignalController; + @VisibleForTesting final Map<Integer, MobileSignalController> mMobileSignalControllers = new HashMap<Integer, MobileSignalController>(); @@ -111,12 +116,13 @@ public class NetworkControllerImpl extends BroadcastReceiver /** * Construct this controller object and register for updates. */ - public NetworkControllerImpl(Context context) { + public NetworkControllerImpl(Context context, Looper bgLooper) { this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), (WifiManager) context.getSystemService(Context.WIFI_SERVICE), SubscriptionManager.from(context), Config.readConfig(context), - new AccessPointControllerImpl(context), new MobileDataControllerImpl(context)); + new AccessPointControllerImpl(context, bgLooper), + new MobileDataControllerImpl(context)); registerListeners(); } @@ -154,6 +160,9 @@ public class NetworkControllerImpl extends BroadcastReceiver mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature, mSignalsChangedCallbacks, mSignalClusters, this); + mEthernetSignalController = new EthernetSignalController(mContext, mSignalsChangedCallbacks, + mSignalClusters, this); + // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it updateAirplaneMode(true /* force callback */); } @@ -239,9 +248,9 @@ public class NetworkControllerImpl extends BroadcastReceiver return mDefaultSignalController; } - public String getMobileNetworkName() { + public String getMobileDataNetworkName() { MobileSignalController controller = getDataController(); - return controller != null ? controller.getState().networkName : ""; + return controller != null ? controller.getState().networkNameData : ""; } public boolean isEmergencyOnly() { @@ -281,6 +290,7 @@ public class NetworkControllerImpl extends BroadcastReceiver R.string.accessibility_airplane_mode); cluster.setNoSims(mHasNoSims); mWifiSignalController.notifyListeners(); + mEthernetSignalController.notifyListeners(); for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { mobileSignalController.notifyListeners(); } @@ -291,6 +301,7 @@ public class NetworkControllerImpl extends BroadcastReceiver cb.onAirplaneModeChanged(mAirplaneMode); cb.onNoSimVisibleChanged(mHasNoSims); mWifiSignalController.notifyListeners(); + mEthernetSignalController.notifyListeners(); for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { mobileSignalController.notifyListeners(); } @@ -501,6 +512,7 @@ public class NetworkControllerImpl extends BroadcastReceiver mobileSignalController.notifyListeners(); } mWifiSignalController.notifyListeners(); + mEthernetSignalController.notifyListeners(); } /** @@ -560,6 +572,11 @@ public class NetworkControllerImpl extends BroadcastReceiver } mWifiSignalController.setInetCondition( mValidatedTransports.get(mWifiSignalController.getTransportType()) ? 1 : 0); + + mEthernetSignalController.setConnected( + mConnectedTransports.get(mEthernetSignalController.getTransportType())); + mEthernetSignalController.setInetCondition( + mValidatedTransports.get(mEthernetSignalController.getTransportType()) ? 1 : 0); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -586,6 +603,8 @@ public class NetworkControllerImpl extends BroadcastReceiver } mWifiSignalController.dump(pw); + mEthernetSignalController.dump(pw); + mAccessPoints.dump(pw); } @@ -731,6 +750,8 @@ public class NetworkControllerImpl extends BroadcastReceiver void setSubs(List<SubscriptionInfo> subs); void setNoSims(boolean show); + void setEthernetIndicators(boolean visible, int icon, String contentDescription); + void setIsAirplaneMode(boolean is, int airplaneIcon, int contentDescription); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java index 0dce82f..4269c19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java @@ -21,11 +21,13 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.statusbar.phone.KeyguardPreviewContainer; import java.util.List; @@ -49,6 +51,15 @@ public class PreviewInflater { public View inflatePreview(Intent intent) { WidgetInfo info = getWidgetInfo(intent); + return inflatePreview(info); + } + + public View inflatePreviewFromService(ComponentName componentName) { + WidgetInfo info = getWidgetInfoFromService(componentName); + return inflatePreview(info); + } + + private KeyguardPreviewContainer inflatePreview(WidgetInfo info) { if (info == null) { return null; } @@ -76,33 +87,53 @@ public class PreviewInflater { return widgetView; } - private WidgetInfo getWidgetInfo(Intent intent) { + private WidgetInfo getWidgetInfoFromService(ComponentName componentName) { + PackageManager packageManager = mContext.getPackageManager(); + // Look for the preview specified in the service meta-data + try { + Bundle metaData = packageManager.getServiceInfo( + componentName, PackageManager.GET_META_DATA).metaData; + return getWidgetInfoFromMetaData(componentName.getPackageName(), metaData); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Failed to load preview; " + componentName.flattenToShortString() + + " not found", e); + } + return null; + } + + private WidgetInfo getWidgetInfoFromMetaData(String contextPackage, + Bundle metaData) { + if (metaData == null) { + return null; + } + int layoutId = metaData.getInt(META_DATA_KEYGUARD_LAYOUT); + if (layoutId == 0) { + return null; + } WidgetInfo info = new WidgetInfo(); + info.contextPackage = contextPackage; + info.layoutId = layoutId; + return info; + } + + private WidgetInfo getWidgetInfo(Intent intent) { PackageManager packageManager = mContext.getPackageManager(); final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser( - intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser()); + intent, PackageManager.MATCH_DEFAULT_ONLY, KeyguardUpdateMonitor.getCurrentUser()); if (appList.size() == 0) { return null; } ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, - mLockPatternUtils.getCurrentUser()); + KeyguardUpdateMonitor.getCurrentUser()); if (wouldLaunchResolverActivity(resolved, appList)) { return null; } if (resolved == null || resolved.activityInfo == null) { return null; } - if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) { - return null; - } - int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT); - if (layoutId == 0) { - return null; - } - info.contextPackage = resolved.activityInfo.packageName; - info.layoutId = layoutId; - return info; + return getWidgetInfoFromMetaData(resolved.activityInfo.packageName, + resolved.activityInfo.metaData); } public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java index c204814..f3322a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java @@ -153,7 +153,11 @@ public abstract class SignalController<T extends SignalController.State, return getIcons().mSbDarkIcons[mCurrentState.inetCondition][mCurrentState.level]; } } else if (mCurrentState.enabled) { - return getIcons().mSbDiscState; + if (light) { + return getIcons().mSbDiscState; + } else { + return getIcons().mSbDarkDiscState; + } } else { return getIcons().mSbNullState; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 194bcfa..ad27c6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -43,6 +43,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; +import com.android.internal.logging.MetricsLogger; import com.android.internal.util.UserIcons; import com.android.systemui.BitmapHelper; import com.android.systemui.GuestResumeSessionReceiver; @@ -548,6 +549,11 @@ public class UserSwitcherController { @Override public void setToggleState(boolean state) { } + + @Override + public int getMetricsCategory() { + return MetricsLogger.QS_USERDETAIL; + } }; private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java index 67cc788..9d84a85 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java @@ -35,6 +35,7 @@ public interface ZenModeController { boolean isZenAvailable(); ComponentName getEffectsSuppressor(); boolean isCountdownConditionSupported(); + int getCurrentUser(); public static class Callback { public void onZenChanged(int zen) {} @@ -45,4 +46,5 @@ public interface ZenModeController { public void onManualRuleChanged(ZenRule rule) {} public void onConfigChanged(ZenModeConfig config) {} } + }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index 830a197..5b80ac2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy; +import android.app.ActivityManager; import android.app.AlarmManager; import android.app.NotificationManager; import android.content.BroadcastReceiver; @@ -159,6 +160,11 @@ public class ZenModeControllerImpl implements ZenModeController { .isSystemConditionProviderEnabled(ZenModeConfig.COUNTDOWN_PATH); } + @Override + public int getCurrentUser() { + return ActivityManager.getCurrentUser(); + } + private void fireNextAlarmChanged() { for (Callback cb : mCallbacks) { cb.onNextAlarmChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java new file mode 100644 index 0000000..05c0099 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java @@ -0,0 +1,51 @@ +/* + * 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.statusbar.stack; + +import android.graphics.Path; +import android.view.animation.PathInterpolator; + +/** + * An interpolator specifically designed for the appear animation of heads up notifications. + */ +public class HeadsUpAppearInterpolator extends PathInterpolator { + public HeadsUpAppearInterpolator() { + super(getAppearPath()); + } + + private static Path getAppearPath() { + Path path = new Path(); + path.moveTo(0, 0); + float x1 = 250f; + float x2 = 150f; + float x3 = 100f; + float y1 = 90f; + float y2 = 78f; + float y3 = 80f; + float xTot = (x1 + x2 + x3); + path.cubicTo(x1 * 0.9f / xTot, 0f, + x1 * 0.8f / xTot, y1 / y3, + x1 / xTot , y1 / y3); + path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3, + (x1 + x2 * 0.2f) / xTot, y2 / y3, + (x1 + x2) / xTot, y2 / y3); + path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3, + (x1 + x2 + x3 * 0.2f) / xTot, 1f, + 1f, 1f); + return path; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 88fc602..bd3b9eb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -204,7 +204,6 @@ public class NotificationStackScrollLayout extends ViewGroup private ViewGroup mScrollView; private boolean mInterceptDelegateEnabled; private boolean mDelegateToScrollView; - private boolean mDisallowScrollingInThisMotion; private long mGoToFullShadeDelay; private ViewTreeObserver.OnPreDrawListener mChildrenUpdater @@ -486,10 +485,10 @@ public class NotificationStackScrollLayout extends ViewGroup int minStackHeight = getMinStackHeight(); int stackHeight; float paddingOffset; - boolean trackingHeadsUp = mTrackingHeadsUp; - int normalExpandPositionStart = trackingHeadsUp ? mHeadsUpManager.getTopHeadsUpHeight() + boolean trackingHeadsUp = mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp(); + int normalUnfoldPositionStart = trackingHeadsUp ? mHeadsUpManager.getTopHeadsUpHeight() : minStackHeight; - if (newStackHeight - mTopPadding - mTopPaddingOverflow >= normalExpandPositionStart + if (newStackHeight - mTopPadding - mTopPaddingOverflow >= normalUnfoldPositionStart || getNotGoneChildCount() == 0) { paddingOffset = mTopPaddingOverflow; stackHeight = newStackHeight; @@ -582,7 +581,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (v instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) v; if (row.isHeadsUp()) { - mHeadsUpManager.addSwipedOutKey(row.getStatusBarNotification().getKey()); + mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey()); } } final View veto = v.findViewById(R.id.veto); @@ -609,7 +608,7 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) { - if (isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) { + if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) { mScrimController.setTopHeadsUpDragAmount(animView, Math.min(Math.abs(swipeProgress - 1.0f), 1.0f)); } @@ -619,17 +618,17 @@ public class NotificationStackScrollLayout extends ViewGroup public void onBeginDrag(View v) { setSwipingInProgress(true); mAmbientState.onBeginDrag(v); - if (mAnimationsEnabled && !isPinnedHeadsUp(v)) { + if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) { mDragAnimPendingChildren.add(v); mNeedsAnimation = true; } requestChildrenUpdate(); } - public boolean isPinnedHeadsUp(View v) { + public static boolean isPinnedHeadsUp(View v) { if (v instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) v; - return row.isHeadsUp() && !row.isInShade(); + return row.isHeadsUp() && row.isPinned(); } return false; } @@ -711,7 +710,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) { if (slidingChild instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild; - if (row.isHeadsUp() && !row.isInShade() + if (!mIsExpanded && row.isHeadsUp() && row.isPinned() && mHeadsUpManager.getTopEntry().entry.row != row) { continue; } @@ -812,7 +811,8 @@ public class NotificationStackScrollLayout extends ViewGroup } handleEmptySpaceClick(ev); boolean expandWantsIt = false; - if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) { + if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion + && isScrollingEnabled()) { if (isCancelOrUp) { mExpandHelper.onlyObserveMovements(false); } @@ -824,7 +824,8 @@ public class NotificationStackScrollLayout extends ViewGroup } } boolean scrollerWantsIt = false; - if (!mSwipingInProgress && !mExpandingNotification && !mDisallowScrollingInThisMotion) { + if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification + && !mDisallowScrollingInThisMotion) { scrollerWantsIt = onScrollTouch(ev); } boolean horizontalSwipeWantsIt = false; @@ -1870,17 +1871,18 @@ public class NotificationStackScrollLayout extends ViewGroup boolean isHeadsUp = eventPair.second; int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER; boolean onBottom = false; + boolean pinnedAndClosed = row.isPinned() && !mIsExpanded; if (!mIsExpanded && !isHeadsUp) { type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR; - } else if (mAddedHeadsUpChildren.contains(row) || (!row.isInShade() && !mIsExpanded)) { - if (!row.isInShade() || shouldHunAppearFromBottom(row)) { + } else if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) { + if (pinnedAndClosed || shouldHunAppearFromBottom(row)) { // Our custom add animation type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR; } else { // Normal add animation type = AnimationEvent.ANIMATION_TYPE_ADD; } - onBottom = row.isInShade(); + onBottom = !pinnedAndClosed; } AnimationEvent event = new AnimationEvent(row, type); event.headsUpFromBottom = onBottom; @@ -2670,7 +2672,7 @@ public class NotificationStackScrollLayout extends ViewGroup } } - public void performOnAnimationFinished(Runnable runnable) { + public void runAfterAnimationFinished(Runnable runnable) { mAnimationFinishedRunnables.add(runnable); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 2a49a4c..081a9c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -311,7 +311,7 @@ public class StackScrollAlgorithm { StackViewState viewState = resultState.getViewStateForView( nextChild); // The child below the dragged one must be fully visible - if (!isPinnedHeadsUpView(draggedView) || isPinnedHeadsUpView(nextChild)) { + if (ambientState.isShadeExpanded()) { viewState.alpha = 1; } } @@ -324,14 +324,6 @@ public class StackScrollAlgorithm { } } - private boolean isPinnedHeadsUpView(View view) { - if (view instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - return row.isHeadsUp() && !row.isInShade(); - } - return false; - } - /** * Update the visible children on the state. */ @@ -380,7 +372,7 @@ public class StackScrollAlgorithm { /** * Determine the positions for the views. This is the main part of the algorithm. * - * @param resultState The result state to update if a change to the properties of a child occurs + * @param resultState The result state to update if a change to the properties of a child occurs * @param algorithmState The state in which the current pass of the algorithm is currently in * @param ambientState The current ambient state */ @@ -515,26 +507,28 @@ public class StackScrollAlgorithm { } StackViewState childState = resultState.getViewStateForView(row); boolean isTopEntry = topHeadsUpEntry == row; - if (!row.isInShade()) { - childState.yTranslation = 0; - childState.height = row.getHeadsUpHeight(); - if (!isTopEntry) { - // Ensure that a headsUp is never below the topmost headsUp - StackViewState topState = resultState.getViewStateForView(topHeadsUpEntry); - childState.height = row.getHeadsUpHeight(); - childState.yTranslation = topState.yTranslation + topState.height - - childState.height; - } - } else if (mIsExpanded) { + if (mIsExpanded) { if (isTopEntry) { childState.height += row.getHeadsUpHeight() - mCollapsedSize; } childState.height = Math.max(childState.height, row.getHeadsUpHeight()); - // Ensure that the heads up is always visible even when scrolled of from the bottom + // Ensure that the heads up is always visible even when scrolled off from the bottom float bottomPosition = ambientState.getMaxHeadsUpTranslation() - childState.height; childState.yTranslation = Math.min(childState.yTranslation, bottomPosition); } + if (row.isPinned()) { + childState.yTranslation = Math.max(childState.yTranslation, 0); + childState.height = row.getHeadsUpHeight(); + if (!isTopEntry) { + // Ensure that a headsUp doesn't vertically extend further than the heads-up at + // the top most z-position + StackViewState topState = resultState.getViewStateForView(topHeadsUpEntry); + childState.height = row.getHeadsUpHeight(); + childState.yTranslation = topState.yTranslation + topState.height + - childState.height; + } + } } } @@ -588,7 +582,7 @@ public class StackScrollAlgorithm { return row.getIntrinsicHeight(); } else if (child instanceof ExpandableView) { ExpandableView expandableView = (ExpandableView) child; - return expandableView.getActualHeight(); + return expandableView.getIntrinsicHeight(); } return child == null? mCollapsedSize : child.getHeight(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index f5d94c8..eac5e79 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -21,11 +21,9 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; -import android.graphics.Path; import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; @@ -78,6 +76,7 @@ public class StackStateAnimator { private final Interpolator mFastOutSlowInInterpolator; private final Interpolator mHeadsUpAppearInterpolator; private final int mGoToFullShadeAppearingTranslation; + private final StackViewState mTmpState = new StackViewState(); public NotificationStackScrollLayout mHostLayout; private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = new ArrayList<>(); @@ -95,7 +94,6 @@ public class StackStateAnimator { private ValueAnimator mTopOverScrollAnimator; private ValueAnimator mBottomOverScrollAnimator; private ExpandableNotificationRow mChildExpandingView; - private StackViewState mTmpState = new StackViewState(); private int mHeadsUpAppearHeightBottom; private boolean mShadeExpanded; @@ -106,25 +104,7 @@ public class StackStateAnimator { mGoToFullShadeAppearingTranslation = hostLayout.getContext().getResources().getDimensionPixelSize( R.dimen.go_to_full_shade_appearing_translation); - Path path = new Path(); - path.moveTo(0, 0); - float x1 = 250f; - float x2 = 150f; - float x3 = 100f; - float y1 = 90f; - float y2 = 78f; - float y3 = 80f; - float xTot = (x1 + x2 + x3); - path.cubicTo(x1 * 0.9f / xTot, 0f, - x1 * 0.8f / xTot, y1 / y3, - x1 / xTot , y1 / y3); - path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3, - (x1 + x2 * 0.2f) / xTot, y2 / y3, - (x1 + x2) / xTot, y2 / y3); - path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3, - (x1 + x2 + x3 * 0.2f) / xTot, 1f, - 1f, 1f); - mHeadsUpAppearInterpolator = new PathInterpolator(path); + mHeadsUpAppearInterpolator = new HeadsUpAppearInterpolator(); } public boolean isRunning() { @@ -183,7 +163,7 @@ public class StackStateAnimator { // This is a heads up animation return false; } - if (mHostLayout.isPinnedHeadsUp(child)) { + if (NotificationStackScrollLayout.isPinnedHeadsUp(child)) { // This is another headsUp which might move. Let's animate! return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index dda40d3..a5684a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -121,7 +121,7 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void escalateHeadsUp() { + public void maybeEscalateHeadsUp() { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/tuner/AutoScrollView.java b/packages/SystemUI/src/com/android/systemui/tuner/AutoScrollView.java new file mode 100644 index 0000000..6aea2cb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/AutoScrollView.java @@ -0,0 +1,46 @@ +/* + * 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.tuner; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.DragEvent; +import android.widget.ScrollView; + +public class AutoScrollView extends ScrollView { + + private static final float SCROLL_PERCENT = .10f; + + public AutoScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public boolean onDragEvent(DragEvent event) { + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_LOCATION: + int y = (int) event.getY(); + int height = getHeight(); + int scrollPadding = (int) (height * SCROLL_PERCENT); + if (y < scrollPadding) { + scrollBy(0, y - scrollPadding); + } else if (y > height - scrollPadding) { + scrollBy(0, y - height + scrollPadding); + } + break; + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java new file mode 100644 index 0000000..5cf0813 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java @@ -0,0 +1,451 @@ +/* + * 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.tuner; + +import android.app.AlertDialog; +import android.app.Fragment; +import android.content.ClipData; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.provider.Settings.Secure; +import android.util.Log; +import android.view.DragEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnDragListener; +import android.view.View.OnLongClickListener; +import android.view.View.OnTouchListener; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ScrollView; + +import com.android.systemui.R; +import com.android.systemui.qs.QSPanel; +import com.android.systemui.qs.QSTile; +import com.android.systemui.qs.QSTile.Host.Callback; +import com.android.systemui.qs.QSTile.ResourceIcon; +import com.android.systemui.qs.QSTileView; +import com.android.systemui.qs.tiles.IntentTile; +import com.android.systemui.statusbar.phone.QSTileHost; + +import java.util.List; + +public class QsTuner extends Fragment implements Callback { + + private static final String TAG = "QsTuner"; + + private static final int MENU_RESET = Menu.FIRST; + + private DraggableQsPanel mQsPanel; + private CustomHost mTileHost; + + private FrameLayout mDropTarget; + + private ScrollView mScrollRoot; + + private FrameLayout mAddTarget; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add(0, MENU_RESET, 0, com.android.internal.R.string.reset); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_RESET: + mTileHost.reset(); + break; + } + return super.onOptionsItemSelected(item); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mScrollRoot = (ScrollView) inflater.inflate(R.layout.tuner_qs, container, false); + + mQsPanel = new DraggableQsPanel(getContext()); + mTileHost = new CustomHost(getContext()); + mTileHost.setCallback(this); + mQsPanel.setTiles(mTileHost.getTiles()); + mQsPanel.setHost(mTileHost); + mQsPanel.refreshAllTiles(); + ((ViewGroup) mScrollRoot.findViewById(R.id.all_details)).addView(mQsPanel, 0); + + mDropTarget = (FrameLayout) mScrollRoot.findViewById(R.id.remove_target); + setupDropTarget(); + mAddTarget = (FrameLayout) mScrollRoot.findViewById(R.id.add_target); + setupAddTarget(); + return mScrollRoot; + } + + private void setupDropTarget() { + QSTileView tileView = new QSTileView(getContext()); + QSTile.State state = new QSTile.State(); + state.visible = true; + state.icon = ResourceIcon.get(R.drawable.ic_delete); + state.label = getString(com.android.internal.R.string.delete); + tileView.onStateChanged(state); + mDropTarget.addView(tileView); + mDropTarget.setVisibility(View.GONE); + new DragHelper(tileView, new DropListener() { + @Override + public void onDrop(String sourceText) { + mTileHost.remove(sourceText); + } + }); + } + + private void setupAddTarget() { + QSTileView tileView = new QSTileView(getContext()); + QSTile.State state = new QSTile.State(); + state.visible = true; + state.icon = ResourceIcon.get(R.drawable.ic_add_circle_qs); + state.label = getString(R.string.add_tile); + tileView.onStateChanged(state); + mAddTarget.addView(tileView); + tileView.setClickable(true); + tileView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mTileHost.showAddDialog(); + } + }); + } + + public void onStartDrag() { + mDropTarget.setVisibility(View.VISIBLE); + mAddTarget.setVisibility(View.GONE); + } + + public void stopDrag() { + mDropTarget.setVisibility(View.GONE); + mAddTarget.setVisibility(View.VISIBLE); + } + + @Override + public void onTilesChanged() { + mQsPanel.setTiles(mTileHost.getTiles()); + } + + private static int getLabelResource(String spec) { + if (spec.equals("wifi")) return R.string.quick_settings_wifi_label; + else if (spec.equals("bt")) return R.string.quick_settings_bluetooth_label; + else if (spec.equals("inversion")) return R.string.quick_settings_inversion_label; + else if (spec.equals("cell")) return R.string.quick_settings_cellular_detail_title; + else if (spec.equals("airplane")) return R.string.quick_settings_airplane_mode_label; + else if (spec.equals("dnd")) return R.string.quick_settings_dnd_label; + else if (spec.equals("rotation")) return R.string.quick_settings_rotation_locked_label; + else if (spec.equals("flashlight")) return R.string.quick_settings_flashlight_label; + else if (spec.equals("location")) return R.string.quick_settings_location_label; + else if (spec.equals("cast")) return R.string.quick_settings_cast_title; + else if (spec.equals("hotspot")) return R.string.quick_settings_hotspot_label; + return 0; + } + + private static class CustomHost extends QSTileHost { + + public CustomHost(Context context) { + super(context, null, null, null, null, null, null, null, null, null, + null, null, null); + } + + @Override + protected QSTile<?> createTile(String tileSpec) { + return new DraggableTile(this, tileSpec); + } + + public void replace(String oldTile, String newTile) { + if (oldTile.equals(newTile)) { + return; + } + List<String> order = loadTileSpecs(); + int index = order.indexOf(oldTile); + if (index < 0) { + Log.e(TAG, "Can't find " + oldTile); + return; + } + order.remove(newTile); + order.add(index, newTile); + setTiles(order); + } + + public void remove(String tile) { + List<String> tiles = loadTileSpecs(); + tiles.remove(tile); + setTiles(tiles); + } + + public void add(String tile) { + List<String> tiles = loadTileSpecs(); + tiles.add(tile); + setTiles(tiles); + } + + public void reset() { + Secure.putStringForUser(getContext().getContentResolver(), + TILES_SETTING, "default", mUserTracker.getCurrentUserId()); + } + + private void setTiles(List<String> tiles) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < tiles.size(); i++) { + if (builder.length() != 0) { + builder.append(','); + } + builder.append(tiles.get(i)); + } + Secure.putStringForUser(getContext().getContentResolver(), + TILES_SETTING, builder.toString(), mUserTracker.getCurrentUserId()); + } + + public void showAddDialog() { + List<String> tiles = loadTileSpecs(); + String[] defaults = + getContext().getString(R.string.quick_settings_tiles_default).split(","); + final String[] available = new String[defaults.length + 1 - tiles.size()]; + int index = 0; + for (int i = 0; i < defaults.length; i++) { + if (tiles.contains(defaults[i])) { + continue; + } + int resource = getLabelResource(defaults[i]); + if (resource != 0) { + available[index++] = getContext().getString(resource); + } else { + available[index++] = defaults[i]; + } + } + available[index++] = getContext().getString(R.string.broadcast_tile); + new AlertDialog.Builder(getContext()) + .setTitle(R.string.add_tile) + .setItems(available, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (which < available.length - 1) { + add(available[which]); + } else { + showBroadcastTileDialog(); + } + } + }).show(); + } + + public void showBroadcastTileDialog() { + final EditText editText = new EditText(getContext()); + new AlertDialog.Builder(getContext()) + .setTitle(R.string.broadcast_tile) + .setView(editText) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String action = editText.getText().toString(); + if (isValid(action)) { + add(IntentTile.PREFIX + action + ')'); + } + } + }).show(); + } + + private boolean isValid(String action) { + for (int i = 0; i < action.length(); i++) { + char c = action.charAt(i); + if (!Character.isAlphabetic(c) && !Character.isDigit(c) && c != '.') { + return false; + } + } + return true; + } + } + + private static class DraggableTile extends QSTile<QSTile.State> + implements DropListener { + private String mSpec; + private QSTileView mView; + + protected DraggableTile(QSTile.Host host, String tileSpec) { + super(host); + Log.d(TAG, "Creating tile " + tileSpec); + mSpec = tileSpec; + } + + @Override + public QSTileView createTileView(Context context) { + mView = super.createTileView(context); + return mView; + } + + @Override + public boolean supportsDualTargets() { + return "wifi".equals(mSpec) || "bt".equals(mSpec); + } + + @Override + public void setListening(boolean listening) { + } + + @Override + protected QSTile.State newTileState() { + return new QSTile.State(); + } + + @Override + protected void handleClick() { + } + + @Override + protected void handleUpdateState(QSTile.State state, Object arg) { + state.visible = true; + state.icon = ResourceIcon.get(getIcon()); + state.label = getLabel(); + } + + private String getLabel() { + int resource = getLabelResource(mSpec); + if (resource != 0) { + return mContext.getString(resource); + } + if (mSpec.startsWith(IntentTile.PREFIX)) { + int lastDot = mSpec.lastIndexOf('.'); + if (lastDot >= 0) { + return mSpec.substring(lastDot + 1, mSpec.length() - 1); + } else { + return mSpec.substring(IntentTile.PREFIX.length(), mSpec.length() - 1); + } + } + return mSpec; + } + + private int getIcon() { + if (mSpec.equals("wifi")) return R.drawable.ic_qs_wifi_full_3; + else if (mSpec.equals("bt")) return R.drawable.ic_qs_bluetooth_connected; + else if (mSpec.equals("inversion")) return R.drawable.ic_invert_colors_enable; + else if (mSpec.equals("cell")) return R.drawable.ic_qs_signal_full_3; + else if (mSpec.equals("airplane")) return R.drawable.ic_signal_airplane_enable; + else if (mSpec.equals("dnd")) return R.drawable.ic_qs_dnd_on; + else if (mSpec.equals("rotation")) return R.drawable.ic_portrait_from_auto_rotate; + else if (mSpec.equals("flashlight")) return R.drawable.ic_signal_flashlight_enable; + else if (mSpec.equals("location")) return R.drawable.ic_signal_location_enable; + else if (mSpec.equals("cast")) return R.drawable.ic_qs_cast_on; + else if (mSpec.equals("hotspot")) return R.drawable.ic_hotspot_enable; + return R.drawable.android; + } + + @Override + public int getMetricsCategory() { + return 20000; + } + + @Override + public boolean equals(Object o) { + if (o instanceof DraggableTile) { + return mSpec.equals(((DraggableTile) o).mSpec); + } + return false; + } + + @Override + public void onDrop(String sourceText) { + ((CustomHost) mHost).replace(mSpec, sourceText); + } + + } + + private class DragHelper implements OnDragListener { + + private final View mView; + private final DropListener mListener; + + public DragHelper(View view, DropListener dropListener) { + mView = view; + mListener = dropListener; + mView.setOnDragListener(this); + } + + @Override + public boolean onDrag(View v, DragEvent event) { + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_ENTERED: + mView.setBackgroundColor(0x77ffffff); + break; + case DragEvent.ACTION_DRAG_ENDED: + stopDrag(); + case DragEvent.ACTION_DRAG_EXITED: + mView.setBackgroundColor(0x0); + break; + case DragEvent.ACTION_DROP: + stopDrag(); + String text = event.getClipData().getItemAt(0).getText().toString(); + mListener.onDrop(text); + break; + } + return true; + } + + } + + public interface DropListener { + void onDrop(String sourceText); + } + + private class DraggableQsPanel extends QSPanel implements OnTouchListener { + public DraggableQsPanel(Context context) { + super(context); + mBrightnessView.setVisibility(View.GONE); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + for (TileRecord r : mRecords) { + new DragHelper(r.tileView, (DraggableTile) r.tile); + r.tileView.setTag(r.tile); + r.tileView.setOnTouchListener(this); + + for (int i = 0; i < r.tileView.getChildCount(); i++) { + r.tileView.getChildAt(i).setClickable(false); + } + } + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + String tileSpec = (String) ((DraggableTile) v.getTag()).mSpec; + ClipData data = ClipData.newPlainText(tileSpec, tileSpec); + v.startDrag(data, new View.DragShadowBuilder(v), null, 0); + onStartDrag(); + return true; + } + return false; + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java new file mode 100644 index 0000000..c84f618 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java @@ -0,0 +1,30 @@ +/* + * 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.tuner; + +import android.app.Activity; +import android.os.Bundle; + +public class TunerActivity extends Activity { + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction().replace(android.R.id.content, new TunerFragment()) + .commit(); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java new file mode 100644 index 0000000..457bade --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -0,0 +1,60 @@ +/* + * 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.tuner; + +import android.app.FragmentTransaction; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceFragment; +import android.view.MenuItem; + +import com.android.systemui.R; + +public class TunerFragment extends PreferenceFragment { + + private static final String KEY_QS_TUNER = "qs_tuner"; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.tuner_prefs); + getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); + setHasOptionsMenu(true); + + findPreference(KEY_QS_TUNER).setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.replace(android.R.id.content, new QsTuner(), "QsTuner"); + ft.addToBackStack(null); + ft.commit(); + return false; + } + }); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + getActivity().finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index 240c210..23813d1 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -24,12 +24,20 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.MoveCallback; +import android.os.Bundle; +import android.os.Handler; 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.os.storage.VolumeRecord; +import android.text.TextUtils; +import android.text.format.DateUtils; import android.util.Log; +import android.util.SparseArray; import com.android.internal.R; import com.android.systemui.SystemUI; @@ -39,39 +47,93 @@ 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 int PUBLIC_ID = 0x53505542; // SPUB + private static final int PRIVATE_ID = 0x53505256; // SPRV + private static final int DISK_ID = 0x5344534b; // SDSK + private static final int MOVE_ID = 0x534d4f56; // SMOV 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 static class MoveInfo { + public int moveId; + public Bundle extras; + public String packageName; + public String label; + public String volumeUuid; + } + + private final SparseArray<MoveInfo> mMoves = new SparseArray<>(); + private final StorageEventListener mListener = new StorageEventListener() { @Override public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { - onVolumeStateChangedInternal(vol, oldState, newState); + onVolumeStateChangedInternal(vol); } @Override - public void onVolumeMetadataChanged(VolumeInfo vol) { + public void onVolumeRecordChanged(VolumeRecord rec) { // 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.isMountedReadable()) { - onVolumeStateChangedInternal(vol, vol.getState(), vol.getState()); + final VolumeInfo vol = mStorageManager.findVolumeByUuid(rec.getFsUuid()); + if (vol != null && vol.isMountedReadable()) { + onVolumeStateChangedInternal(vol); } } + + @Override + public void onVolumeForgotten(String fsUuid) { + // Stop annoying the user + mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL); + } + + @Override + public void onDiskScanned(DiskInfo disk, int volumeCount) { + onDiskScannedInternal(disk, volumeCount); + } }; 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); + final String fsUuid = intent.getStringExtra(VolumeRecord.EXTRA_FS_UUID); + mStorageManager.setVolumeSnoozed(fsUuid, true); + } + }; + + private final MoveCallback mMoveCallback = new MoveCallback() { + @Override + public void onCreated(int moveId, Bundle extras) { + final MoveInfo move = new MoveInfo(); + move.moveId = moveId; + move.extras = extras; + if (extras != null) { + move.packageName = extras.getString(Intent.EXTRA_PACKAGE_NAME); + move.label = extras.getString(Intent.EXTRA_TITLE); + move.volumeUuid = extras.getString(VolumeRecord.EXTRA_FS_UUID); + } + mMoves.put(moveId, move); + } + + @Override + public void onStatusChanged(int moveId, int status, long estMillis) { + final MoveInfo move = mMoves.get(moveId); + if (move == null) { + Log.w(TAG, "Ignoring unknown move " + moveId); + return; + } + + if (PackageManager.isMoveStatusFinished(status)) { + onMoveFinished(move, status); + } else { + onMoveProgress(move, status, estMillis); + } } }; @@ -88,20 +150,99 @@ public class StorageNotification extends SystemUI { // Kick current state into place final List<VolumeInfo> vols = mStorageManager.getVolumes(); for (VolumeInfo vol : vols) { - onVolumeStateChangedInternal(vol, vol.getState(), vol.getState()); + onVolumeStateChangedInternal(vol); } + + mContext.getPackageManager().registerMoveCallback(mMoveCallback, new Handler()); + + updateMissingPrivateVolumes(); } - public void onVolumeStateChangedInternal(VolumeInfo vol, int oldState, int newState) { - // We only care about public volumes - if (vol.getType() != VolumeInfo.TYPE_PUBLIC) { - return; + private void updateMissingPrivateVolumes() { + final List<VolumeRecord> recs = mStorageManager.getVolumeRecords(); + for (VolumeRecord rec : recs) { + if (rec.getType() != VolumeInfo.TYPE_PRIVATE) continue; + + final String fsUuid = rec.getFsUuid(); + final VolumeInfo info = mStorageManager.findVolumeByUuid(fsUuid); + if (info != null && info.isMountedWritable()) { + // Yay, private volume is here! + mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL); + + } else { + // Boo, annoy the user to reinsert the private volume + final CharSequence title = mContext.getString(R.string.ext_media_missing_title, + rec.getNickname()); + final CharSequence text = mContext.getString(R.string.ext_media_missing_message); + + final Notification notif = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.ic_sd_card_48dp) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setContentIntent(buildForgetPendingIntent(rec)) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setCategory(Notification.CATEGORY_SYSTEM) + .setOngoing(true) + .build(); + + mNotificationManager.notifyAsUser(fsUuid, PRIVATE_ID, notif, UserHandle.ALL); + } } + } + + private void onDiskScannedInternal(DiskInfo disk, int volumeCount) { + if (volumeCount == 0) { + // No supported volumes found, give user option to format + 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 = new Notification.Builder(mContext) + .setSmallIcon(getSmallIcon(disk, VolumeInfo.STATE_UNMOUNTABLE)) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setContentIntent(buildInitPendingIntent(disk)) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setCategory(Notification.CATEGORY_ERROR) + .build(); - Log.d(TAG, vol.toString()); + mNotificationManager.notifyAsUser(disk.getId(), DISK_ID, notif, UserHandle.ALL); + + } else { + // Yay, we have volumes! + mNotificationManager.cancelAsUser(disk.getId(), DISK_ID, UserHandle.ALL); + } + } + + private void onVolumeStateChangedInternal(VolumeInfo vol) { + switch (vol.getType()) { + case VolumeInfo.TYPE_PRIVATE: + onPrivateVolumeStateChangedInternal(vol); + break; + case VolumeInfo.TYPE_PUBLIC: + onPublicVolumeStateChangedInternal(vol); + break; + } + } + + private void onPrivateVolumeStateChangedInternal(VolumeInfo vol) { + Log.d(TAG, "Notifying about private volume: " + vol.toString()); + + updateMissingPrivateVolumes(); + } + + private void onPublicVolumeStateChangedInternal(VolumeInfo vol) { + Log.d(TAG, "Notifying about public volume: " + vol.toString()); final Notification notif; - switch (newState) { + switch (vol.getState()) { case VolumeInfo.STATE_UNMOUNTED: notif = onVolumeUnmounted(vol); break; @@ -133,9 +274,9 @@ public class StorageNotification extends SystemUI { } if (notif != null) { - mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL); + mNotificationManager.notifyAsUser(vol.getId(), PUBLIC_ID, notif, UserHandle.ALL); } else { - mNotificationManager.cancelAsUser(vol.getId(), NOTIF_ID, UserHandle.ALL); + mNotificationManager.cancelAsUser(vol.getId(), PUBLIC_ID, UserHandle.ALL); } } @@ -159,20 +300,25 @@ public class StorageNotification extends SystemUI { } private Notification onVolumeMounted(VolumeInfo vol) { + final VolumeRecord rec = mStorageManager.findRecordByUuid(vol.getFsUuid()); + // Don't annoy when user dismissed in past - if (vol.isSnoozed()) return null; + if (rec.isSnoozed()) return null; final DiskInfo disk = vol.getDisk(); - if (disk.isAdoptable() && !vol.isInited()) { + if (disk.isAdoptable() && !rec.isInited()) { final CharSequence title = disk.getDescription(); final CharSequence text = mContext.getString( R.string.ext_media_new_notification_message, disk.getDescription()); + final PendingIntent initIntent = buildInitPendingIntent(vol); return buildNotificationBuilder(vol, title, text) - .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), + .addAction(new Action(R.drawable.ic_settings_24dp, + mContext.getString(R.string.ext_media_init_action), initIntent)) + .addAction(new Action(R.drawable.ic_eject_24dp, + mContext.getString(R.string.ext_media_unmount_action), buildUnmountPendingIntent(vol))) + .setContentIntent(initIntent) .setDeleteIntent(buildSnoozeIntent(vol)) .setCategory(Notification.CATEGORY_SYSTEM) .build(); @@ -182,11 +328,15 @@ public class StorageNotification extends SystemUI { final CharSequence text = mContext.getString( R.string.ext_media_ready_notification_message, disk.getDescription()); + final PendingIntent browseIntent = buildBrowsePendingIntent(vol); return buildNotificationBuilder(vol, title, text) - .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), + .addAction(new Action(R.drawable.ic_folder_24dp, + mContext.getString(R.string.ext_media_browse_action), + browseIntent)) + .addAction(new Action(R.drawable.ic_eject_24dp, + mContext.getString(R.string.ext_media_unmount_action), buildUnmountPendingIntent(vol))) + .setContentIntent(browseIntent) .setDeleteIntent(buildSnoozeIntent(vol)) .setCategory(Notification.CATEGORY_SYSTEM) .setPriority(Notification.PRIORITY_LOW) @@ -221,7 +371,7 @@ public class StorageNotification extends SystemUI { R.string.ext_media_unmountable_notification_message, disk.getDescription()); return buildNotificationBuilder(vol, title, text) - .setContentIntent(buildDetailsPendingIntent(vol)) + .setContentIntent(buildVolumeSettingsPendingIntent(vol)) .setCategory(Notification.CATEGORY_ERROR) .build(); } @@ -260,26 +410,112 @@ public class StorageNotification extends SystemUI { .build(); } - private int getSmallIcon(VolumeInfo vol) { - if (vol.disk.isSd()) { - switch (vol.getState()) { + private void onMoveProgress(MoveInfo move, int status, long estMillis) { + final CharSequence title; + if (!TextUtils.isEmpty(move.label)) { + title = mContext.getString(R.string.ext_media_move_specific_title, move.label); + } else { + title = mContext.getString(R.string.ext_media_move_title); + } + + final CharSequence text; + if (estMillis < 0) { + text = null; + } else { + text = DateUtils.formatDuration(estMillis); + } + + final PendingIntent intent; + if (move.packageName != null) { + intent = buildWizardMovePendingIntent(move); + } else { + intent = buildWizardMigratePendingIntent(move); + } + + final Notification notif = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.ic_sd_card_48dp) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setContentIntent(intent) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setCategory(Notification.CATEGORY_PROGRESS) + .setPriority(Notification.PRIORITY_LOW) + .setProgress(100, status, false) + .setOngoing(true) + .build(); + + mNotificationManager.notifyAsUser(move.packageName, MOVE_ID, notif, UserHandle.ALL); + } + + private void onMoveFinished(MoveInfo move, int status) { + if (move.packageName != null) { + // We currently ignore finished app moves; just clear the last + // published progress + mNotificationManager.cancelAsUser(move.packageName, MOVE_ID, UserHandle.ALL); + return; + } + + final VolumeInfo privateVol = mContext.getPackageManager().getPrimaryStorageCurrentVolume(); + final String descrip = mStorageManager.getBestVolumeDescription(privateVol); + + final CharSequence title; + final CharSequence text; + if (status == PackageManager.MOVE_SUCCEEDED) { + title = mContext.getString(R.string.ext_media_move_success_title); + text = mContext.getString(R.string.ext_media_move_success_message, descrip); + } else { + title = mContext.getString(R.string.ext_media_move_failure_title); + text = mContext.getString(R.string.ext_media_move_failure_message); + } + + // Jump back into the wizard flow if we moved to a real disk + final PendingIntent intent; + if (privateVol != null && privateVol.getDisk() != null) { + intent = buildWizardReadyPendingIntent(privateVol.getDisk()); + } else { + intent = buildVolumeSettingsPendingIntent(privateVol); + } + + final Notification notif = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.ic_sd_card_48dp) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setContentIntent(intent) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setCategory(Notification.CATEGORY_SYSTEM) + .setPriority(Notification.PRIORITY_LOW) + .setAutoCancel(true) + .build(); + + mNotificationManager.notifyAsUser(move.packageName, MOVE_ID, notif, UserHandle.ALL); + } + + private int getSmallIcon(DiskInfo disk, int state) { + if (disk.isSd()) { + switch (state) { case VolumeInfo.STATE_CHECKING: case VolumeInfo.STATE_EJECTING: - return R.drawable.stat_notify_sdcard_prepare; + return R.drawable.ic_sd_card_48dp; default: - return R.drawable.stat_notify_sdcard; + return R.drawable.ic_sd_card_48dp; } - } else if (vol.disk.isUsb()) { - return R.drawable.stat_sys_data_usb; + } else if (disk.isUsb()) { + return R.drawable.ic_usb_48dp; } else { - return R.drawable.stat_notify_sdcard; + return R.drawable.ic_sd_card_48dp; } } private Notification.Builder buildNotificationBuilder(VolumeInfo vol, CharSequence title, CharSequence text) { return new Notification.Builder(mContext) - .setSmallIcon(getSmallIcon(vol)) + .setSmallIcon(getSmallIcon(vol.getDisk(), vol.getState())) .setColor(mContext.getColor(R.color.system_notification_accent_color)) .setContentTitle(title) .setContentText(text) @@ -288,6 +524,17 @@ public class StorageNotification extends SystemUI { .setLocalOnly(true); } + private PendingIntent buildInitPendingIntent(DiskInfo disk) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardInit"); + intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId()); + + final int requestKey = disk.getId().hashCode(); + return PendingIntent.getActivityAsUser(mContext, requestKey, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } + private PendingIntent buildInitPendingIntent(VolumeInfo vol) { final Intent intent = new Intent(); intent.setClassName("com.android.settings", @@ -318,10 +565,20 @@ public class StorageNotification extends SystemUI { PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); } - private PendingIntent buildDetailsPendingIntent(VolumeInfo vol) { + private PendingIntent buildVolumeSettingsPendingIntent(VolumeInfo vol) { final Intent intent = new Intent(); - intent.setClassName("com.android.settings", - "com.android.settings.Settings$StorageVolumeSettingsActivity"); + switch (vol.getType()) { + case VolumeInfo.TYPE_PRIVATE: + intent.setClassName("com.android.settings", + "com.android.settings.Settings$PrivateVolumeSettingsActivity"); + break; + case VolumeInfo.TYPE_PUBLIC: + intent.setClassName("com.android.settings", + "com.android.settings.Settings$PublicVolumeSettingsActivity"); + break; + default: + return null; + } intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); final int requestKey = vol.getId().hashCode(); @@ -331,10 +588,55 @@ public class StorageNotification extends SystemUI { private PendingIntent buildSnoozeIntent(VolumeInfo vol) { final Intent intent = new Intent(ACTION_SNOOZE_VOLUME); - intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); + intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.getFsUuid()); final int requestKey = vol.getId().hashCode(); return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent, PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT); } + + private PendingIntent buildForgetPendingIntent(VolumeRecord rec) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.Settings$PrivateVolumeForgetActivity"); + intent.putExtra(VolumeRecord.EXTRA_FS_UUID, rec.getFsUuid()); + + final int requestKey = rec.getFsUuid().hashCode(); + return PendingIntent.getActivityAsUser(mContext, requestKey, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } + + private PendingIntent buildWizardMigratePendingIntent(MoveInfo move) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardMigrateProgress"); + intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId); + + final VolumeInfo vol = mStorageManager.findVolumeByQualifiedUuid(move.volumeUuid); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); + + return PendingIntent.getActivityAsUser(mContext, move.moveId, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } + + private PendingIntent buildWizardMovePendingIntent(MoveInfo move) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardMoveProgress"); + intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId); + + return PendingIntent.getActivityAsUser(mContext, move.moveId, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } + + private PendingIntent buildWizardReadyPendingIntent(DiskInfo disk) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardReady"); + intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId()); + + final int requestKey = disk.getId().hashCode(); + return PendingIntent.getActivityAsUser(mContext, requestKey, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } } diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java index 23a65e8..ca32567 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java @@ -93,7 +93,7 @@ public class UsbResolverActivity extends ResolverActivity { } @Override - protected void onTargetSelected(TargetInfo target, boolean alwaysCheck) { + protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { final ResolveInfo ri = target.getResolveInfo(); try { IBinder b = ServiceManager.getService(USB_SERVICE); @@ -129,5 +129,6 @@ public class UsbResolverActivity extends ResolverActivity { } catch (RemoteException e) { Log.e(TAG, "onIntentSelected failed", e); } + return true; } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java index 4f20ac7..f7cb9fe 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java +++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java @@ -17,6 +17,7 @@ package com.android.systemui.volume; import android.content.Context; +import android.graphics.Typeface; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -30,6 +31,8 @@ import java.util.Objects; public class SegmentedButtons extends LinearLayout { private static final int LABEL_RES_KEY = R.id.label; + private static final Typeface REGULAR = Typeface.create("sans-serif", Typeface.NORMAL); + private static final Typeface MEDIUM = Typeface.create("sans-serif-medium", Typeface.NORMAL); private final Context mContext; private final LayoutInflater mInflater; @@ -60,6 +63,7 @@ public class SegmentedButtons extends LinearLayout { final Object tag = c.getTag(); final boolean selected = Objects.equals(mSelectedValue, tag); c.setSelected(selected); + c.setTypeface(selected ? MEDIUM : REGULAR); } fireOnSelected(); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/Util.java b/packages/SystemUI/src/com/android/systemui/volume/Util.java index 216a4da..4214091 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/Util.java +++ b/packages/SystemUI/src/com/android/systemui/volume/Util.java @@ -144,9 +144,14 @@ class Util { return HMMAA.format(new Date(millis)); } - public static void setText(TextView tv, CharSequence text) { - if (Objects.equals(tv.getText(), text)) return; + private static CharSequence emptyToNull(CharSequence str) { + return str == null || str.length() == 0 ? null : str; + } + + public static boolean setText(TextView tv, CharSequence text) { + if (Objects.equals(emptyToNull(tv.getText()), emptyToNull(text))) return false; tv.setText(text); + return true; } public static final void setVisOrGone(View v, boolean vis) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java index bb4aa61..6e0ca3c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java @@ -16,6 +16,9 @@ package com.android.systemui.volume; +import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; + +import android.accessibilityservice.AccessibilityServiceInfo; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -23,6 +26,7 @@ import android.annotation.SuppressLint; import android.app.Dialog; import android.app.KeyguardManager; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Color; import android.graphics.PixelFormat; @@ -32,18 +36,20 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.AudioSystem; +import android.os.Debug; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.provider.Settings.Global; -import android.service.notification.ZenModeConfig; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseBooleanArray; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; +import android.view.View.AccessibilityDelegate; +import android.view.View.OnAttachStateChangeListener; import android.view.View.OnClickListener; import android.view.View.OnLayoutChangeListener; import android.view.View.OnTouchListener; @@ -51,8 +57,10 @@ import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; import android.view.Window; import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; import android.view.animation.DecelerateInterpolator; -import android.widget.Button; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.SeekBar; @@ -90,26 +98,23 @@ public class VolumeDialog { private final ViewGroup mDialogView; private final ViewGroup mDialogContentView; private final ImageButton mExpandButton; - private final TextView mFootlineText; - private final Button mFootlineAction; private final View mSettingsButton; - private final View mFooter; private final List<VolumeRow> mRows = new ArrayList<VolumeRow>(); private final SpTexts mSpTexts; private final SparseBooleanArray mDynamic = new SparseBooleanArray(); private final KeyguardManager mKeyguard; private final int mExpandButtonAnimationDuration; - private final View mTextFooter; private final ZenFooter mZenFooter; private final LayoutTransition mLayoutTransition; private final Object mSafetyWarningLock = new Object(); + private final Accessibility mAccessibility = new Accessibility(); + private final ColorStateList mActiveSliderTint; + private final ColorStateList mInactiveSliderTint; private boolean mShowing; private boolean mExpanded; private int mActiveStream; private boolean mShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS; - private boolean mShowFooter = VolumePrefs.DEFAULT_SHOW_FOOTER; - private boolean mShowZenFooter = VolumePrefs.DEFAULT_ZEN_FOOTER; private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE; private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE; private State mState; @@ -118,7 +123,7 @@ public class VolumeDialog { private SafetyWarningDialog mSafetyWarning; private Callback mCallback; - public VolumeDialog(Context context, VolumeDialogController controller, + public VolumeDialog(Context context, int windowType, VolumeDialogController controller, ZenModeController zenModeController, Callback callback) { mContext = context; mController = controller; @@ -141,7 +146,7 @@ public class VolumeDialog { mDialog.setCanceledOnTouchOutside(true); final Resources res = mContext.getResources(); final WindowManager.LayoutParams lp = window.getAttributes(); - lp.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; + lp.type = windowType; lp.format = PixelFormat.TRANSLUCENT; lp.setTitle(VolumeDialog.class.getSimpleName()); lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; @@ -150,6 +155,8 @@ public class VolumeDialog { lp.gravity = Gravity.TOP; window.setAttributes(lp); + mActiveSliderTint = loadColorStateList(R.color.system_accent_color); + mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive); mDialog.setContentView(R.layout.volume_dialog); mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog); mDialogContentView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog_content); @@ -176,22 +183,22 @@ public class VolumeDialog { addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system, R.drawable.ic_volume_system_mute, false); - mTextFooter = mDialog.findViewById(R.id.volume_text_footer); - mFootlineText = (TextView) mDialog.findViewById(R.id.volume_footline_text); - mSpTexts.add(mFootlineText); - mFootlineAction = (Button) mDialog.findViewById(R.id.volume_footline_action_button); - mSpTexts.add(mFootlineAction); - mFooter = mDialog.findViewById(R.id.volume_footer); mSettingsButton = mDialog.findViewById(R.id.volume_settings_button); mSettingsButton.setOnClickListener(mClickSettings); mExpandButtonAnimationDuration = res.getInteger(R.integer.volume_expand_animation_duration); mZenFooter = (ZenFooter) mDialog.findViewById(R.id.volume_zen_footer); - mZenFooter.init(zenModeController, mZenFooterCallback); + mZenFooter.init(zenModeController); + + mAccessibility.init(); controller.addCallback(mControllerCallbackH, mHandler); controller.getState(); } + private ColorStateList loadColorStateList(int colorResId) { + return ColorStateList.valueOf(mContext.getColor(colorResId)); + } + private void updateWindowWidthH() { final ViewGroup.LayoutParams lp = mDialogView.getLayoutParams(); final DisplayMetrics dm = mContext.getResources().getDisplayMetrics(); @@ -217,18 +224,6 @@ public class VolumeDialog { mHandler.sendEmptyMessage(H.RECHECK_ALL); } - public void setShowFooter(boolean show) { - if (mShowFooter == show) return; - mShowFooter = show; - mHandler.sendEmptyMessage(H.RECHECK_ALL); - } - - public void setZenFooter(boolean zen) { - if (mShowZenFooter == zen) return; - mShowZenFooter = zen; - mHandler.sendEmptyMessage(H.RECHECK_ALL); - } - public void setAutomute(boolean automute) { if (mAutomute == automute) return; mAutomute = automute; @@ -315,7 +310,6 @@ public class VolumeDialog { writer.print(" mActiveStream: "); writer.println(mActiveStream); writer.print(" mDynamic: "); writer.println(mDynamic); writer.print(" mShowHeaders: "); writer.println(mShowHeaders); - writer.print(" mShowFooter: "); writer.println(mShowFooter); writer.print(" mAutomute: "); writer.println(mAutomute); writer.print(" mSilentMode: "); writer.println(mSilentMode); } @@ -436,15 +430,17 @@ public class VolumeDialog { protected void rescheduleTimeoutH() { mHandler.removeMessages(H.DISMISS); - final int timeout = computeTimeoutH(); - if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout); - mHandler.sendMessageDelayed(mHandler - .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout); + int timeout = -1; + if (!mAccessibility.mFeedbackEnabled) { + timeout = computeTimeoutH(); + mHandler.sendMessageDelayed(mHandler + .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout); + } + if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller()); mController.userActivity(); } private int computeTimeoutH() { - if (mZenFooter != null && mZenFooter.isFooterExpanded()) return 10000; if (mSafetyWarning != null) return 5000; if (mExpanded || mExpanding) return 5000; if (mActiveStream == AudioManager.STREAM_MUSIC) return 1500; @@ -503,6 +499,8 @@ public class VolumeDialog { if (res == mExpandButtonRes) return; mExpandButtonRes = res; mExpandButton.setImageResource(res); + mExpandButton.setContentDescription(mContext.getString(mExpanded ? + R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand)); } private boolean isVisibleH(VolumeRow row, boolean isActive) { @@ -515,18 +513,9 @@ public class VolumeDialog { final VolumeRow activeRow = getActiveRow(); updateFooterH(); updateExpandButtonH(); - final boolean footerVisible = mFooter.getVisibility() == View.VISIBLE; if (!mShowing) { trimObsoleteH(); } - // first, find the last visible row - VolumeRow lastVisible = null; - for (VolumeRow row : mRows) { - final boolean isActive = row == activeRow; - if (isVisibleH(row, isActive)) { - lastVisible = row; - } - } // apply changes to all rows for (VolumeRow row : mRows) { final boolean isActive = row == activeRow; @@ -542,9 +531,9 @@ public class VolumeDialog { row.settingsButton.setImageResource(expandButtonRes); } } - Util.setVisOrInvis(row.settingsButton, - mExpanded && (!footerVisible && row == lastVisible)); + Util.setVisOrInvis(row.settingsButton, false); row.header.setAlpha(mExpanded && isActive ? 1 : 0.5f); + updateVolumeRowSliderTintH(row, isActive); } } @@ -585,51 +574,9 @@ public class VolumeDialog { updateFooterH(); } - private void updateTextFooterH() { - final boolean zen = mState.zenMode != Global.ZEN_MODE_OFF; - final boolean wasVisible = mFooter.getVisibility() == View.VISIBLE; - Util.setVisOrGone(mTextFooter, mExpanded && mShowFooter && (zen || mShowing && wasVisible)); - if (mTextFooter.getVisibility() == View.VISIBLE) { - String text = null; - String action = null; - if (mState.exitCondition != null) { - final long countdown = ZenModeConfig.tryParseCountdownConditionId(mState - .exitCondition.id); - if (countdown != 0) { - text = mContext.getString(R.string.volume_dnd_ends_at, - Util.getShortTime(countdown)); - action = mContext.getString(R.string.volume_end_now); - } - } - if (text == null) { - text = mContext.getString(R.string.volume_dnd_is_on); - } - if (action == null) { - action = mContext.getString(R.string.volume_turn_off); - } - Util.setText(mFootlineText, text); - Util.setText(mFootlineAction, action); - mFootlineAction.setOnClickListener(mTurnOffDnd); - } - Util.setVisOrGone(mFootlineText, zen); - Util.setVisOrGone(mFootlineAction, zen); - } - private void updateFooterH() { - if (!mShowFooter) { - Util.setVisOrGone(mFooter, false); - return; - } - if (mShowZenFooter) { - Util.setVisOrGone(mTextFooter, false); - final boolean ringActive = mActiveStream == AudioManager.STREAM_RING; - Util.setVisOrGone(mZenFooter, mZenFooter.isZen() && ringActive - || mShowing && (mExpanded || mZenFooter.getVisibility() == View.VISIBLE)); - mZenFooter.update(); - } else { - Util.setVisOrGone(mZenFooter, false); - updateTextFooterH(); - } + Util.setVisOrGone(mZenFooter, mState.zenMode != Global.ZEN_MODE_OFF); + mZenFooter.update(); } private void updateVolumeRowH(VolumeRow row) { @@ -642,12 +589,20 @@ public class VolumeDialog { } final boolean isRingStream = row.stream == AudioManager.STREAM_RING; final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM; + final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM; + final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC; final boolean isRingVibrate = isRingStream && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE; - final boolean isNoned = (isRingStream || isSystemStream) - && mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS; - final boolean isLimited = isRingStream - && mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + final boolean isRingSilent = isRingStream + && mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT; + final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS; + final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS; + final boolean isZenPriority = mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + final boolean isRingZenNone = (isRingStream || isSystemStream) && isZenNone; + final boolean isRingLimited = isRingStream && isZenPriority; + final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream) + : isZenNone ? (isRingStream || isSystemStream || isAlarmStream || isMusicStream) + : false; // update slider max final int max = ss.levelMax * 100; @@ -663,15 +618,15 @@ public class VolumeDialog { // update header text final String text; - if (isNoned) { + if (isRingZenNone) { text = mContext.getString(R.string.volume_stream_muted_dnd, ss.name); - } else if (isRingVibrate && isLimited) { + } else if (isRingVibrate && isRingLimited) { text = mContext.getString(R.string.volume_stream_vibrate_dnd, ss.name); } else if (isRingVibrate) { text = mContext.getString(R.string.volume_stream_vibrate, ss.name); } else if (ss.muted || mAutomute && ss.level == 0) { text = mContext.getString(R.string.volume_stream_muted, ss.name); - } else if (isLimited) { + } else if (isRingLimited) { text = mContext.getString(R.string.volume_stream_limited_dnd, ss.name); } else { text = ss.name; @@ -679,11 +634,12 @@ public class VolumeDialog { Util.setText(row.header, text); // update icon - final boolean iconEnabled = mAutomute || ss.muteSupported; + final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted; row.icon.setEnabled(iconEnabled); row.icon.setAlpha(iconEnabled ? 1 : 0.5f); final int iconRes = isRingVibrate ? R.drawable.ic_volume_ringer_vibrate + : isRingSilent || zenMuted ? row.cachedIconRes : ss.routedToBluetooth ? (ss.muted ? R.drawable.ic_volume_media_bt_mute : R.drawable.ic_volume_media_bt) @@ -703,12 +659,24 @@ public class VolumeDialog { : (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes) ? Events.ICON_STATE_UNMUTE : Events.ICON_STATE_UNKNOWN; + row.icon.setContentDescription(ss.name); // update slider - updateVolumeRowSliderH(row); + updateVolumeRowSliderH(row, zenMuted); + } + + private void updateVolumeRowSliderTintH(VolumeRow row, boolean isActive) { + final ColorStateList tint = isActive && row.slider.isEnabled() ? mActiveSliderTint + : mInactiveSliderTint; + if (tint == row.cachedSliderTint) return; + row.cachedSliderTint = tint; + row.slider.setProgressTintList(tint); + row.slider.setThumbTintList(tint); } - private void updateVolumeRowSliderH(VolumeRow row) { + private void updateVolumeRowSliderH(VolumeRow row, boolean zenMuted) { + row.slider.setEnabled(!zenMuted); + updateVolumeRowSliderTintH(row, row.stream == mActiveStream); if (row.tracking) { return; // don't update if user is sliding } @@ -840,6 +808,7 @@ public class VolumeDialog { public void onConfigurationChanged() { updateWindowWidthH(); mSpTexts.update(); + mZenFooter.onConfigurationChanged(); } @Override @@ -887,46 +856,6 @@ public class VolumeDialog { } }; - private final View.OnClickListener mTurnOffDnd = new View.OnClickListener() { - @Override - public void onClick(View v) { - mSettingsButton.postDelayed(new Runnable() { - @Override - public void run() { - mController.setZenMode(Global.ZEN_MODE_OFF); - } - }, WAIT_FOR_RIPPLE); - } - }; - - private final ZenFooter.Callback mZenFooterCallback = new ZenFooter.Callback() { - @Override - public void onFooterExpanded() { - mHandler.sendEmptyMessage(H.RESCHEDULE_TIMEOUT); - } - - @Override - public void onSettingsClicked() { - dismiss(Events.DISMISS_REASON_SETTINGS_CLICKED); - if (mCallback != null) { - mCallback.onZenSettingsClicked(); - } - } - - @Override - public void onDoneClicked() { - dismiss(Events.DISMISS_REASON_DONE_CLICKED); - } - - @Override - public void onPrioritySettingsClicked() { - dismiss(Events.DISMISS_REASON_SETTINGS_CLICKED); - if (mCallback != null) { - mCallback.onZenPrioritySettingsClicked(); - } - } - }; - private final class H extends Handler { private static final int SHOW = 1; private static final int DISMISS = 2; @@ -1041,6 +970,56 @@ public class VolumeDialog { } } + private final class Accessibility { + private AccessibilityManager mMgr; + private boolean mFeedbackEnabled; + + public void init() { + mMgr = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); + mDialogView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { + @Override + public void onViewDetachedFromWindow(View v) { + // noop + } + + @Override + public void onViewAttachedToWindow(View v) { + updateFeedbackEnabled(); + } + }); + mDialogView.setAccessibilityDelegate(new AccessibilityDelegate() { + @Override + public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, + AccessibilityEvent event) { + rescheduleTimeoutH(); + return super.onRequestSendAccessibilityEvent(host, child, event); + } + }); + mMgr.addAccessibilityStateChangeListener(new AccessibilityStateChangeListener() { + @Override + public void onAccessibilityStateChanged(boolean enabled) { + updateFeedbackEnabled(); + } + }); + updateFeedbackEnabled(); + } + + private void updateFeedbackEnabled() { + mFeedbackEnabled = computeFeedbackEnabled(); + } + + private boolean computeFeedbackEnabled() { + final List<AccessibilityServiceInfo> services = + mMgr.getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK); + for (AccessibilityServiceInfo asi : services) { + if ((asi.feedbackType & FEEDBACK_ALL_MASK) != 0) { + return true; + } + } + return false; + } + } + private static class VolumeRow { private View view; private View space; @@ -1057,6 +1036,7 @@ public class VolumeDialog { private int iconMuteRes; private boolean important; private int cachedIconRes; + private ColorStateList cachedSliderTint; private int iconState; // from Events private boolean cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS; private int cachedExpandButtonRes; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java index 86abfcc..1083f40 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java @@ -24,6 +24,7 @@ import android.media.VolumePolicy; import android.os.Bundle; import android.os.Handler; import android.provider.Settings; +import android.view.WindowManager; import com.android.systemui.SystemUI; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -61,7 +62,8 @@ public class VolumeDialogComponent implements VolumeComponent { } }; mZenModeController = zen; - mDialog = new VolumeDialog(context, mController, zen, mVolumeDialogCallback); + mDialog = new VolumeDialog(context, WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY, + mController, zen, mVolumeDialogCallback); applyConfiguration(); } @@ -76,12 +78,10 @@ public class VolumeDialogComponent implements VolumeComponent { mDialog.setStreamImportant(AudioManager.STREAM_ALARM, true); mDialog.setStreamImportant(AudioManager.STREAM_SYSTEM, false); mDialog.setShowHeaders(false); - mDialog.setShowFooter(true); - mDialog.setZenFooter(true); mDialog.setAutomute(true); mDialog.setSilentMode(false); mController.setVolumePolicy(mVolumePolicy); - mController.showDndTile(false); + mController.showDndTile(true); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java index 012eb41..3a8081f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java @@ -100,7 +100,7 @@ public class VolumeDialogController { private boolean mEnabled; private boolean mDestroyed; private VolumePolicy mVolumePolicy; - private boolean mShowDndTile = false; + private boolean mShowDndTile = true; public VolumeDialogController(Context context, ComponentName component) { mContext = context.getApplicationContext(); @@ -125,6 +125,10 @@ public class VolumeDialogController { return mAudio; } + public ZenModeConfig getZenModeConfig() { + return mNoMan.getZenModeConfig(); + } + public void dismiss() { mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER); } @@ -342,7 +346,7 @@ public class VolumeDialogController { updateRingerModeExternalW(mAudio.getRingerMode()); updateZenModeW(); updateEffectsSuppressorW(mNoMan.getEffectsSuppressor()); - updateExitConditionW(); + updateZenModeConfigW(); mCallbacks.onStateChanged(mState); } @@ -395,17 +399,10 @@ public class VolumeDialogController { return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION; } - private Condition getExitCondition() { - final ZenModeConfig config = mNoMan.getZenModeConfig(); - return config == null ? null - : config.manualRule == null ? null - : config.manualRule.condition; - } - - private boolean updateExitConditionW() { - final Condition exitCondition = getExitCondition(); - if (Objects.equals(mState.exitCondition, exitCondition)) return false; - mState.exitCondition = exitCondition; + private boolean updateZenModeConfigW() { + final ZenModeConfig zenModeConfig = getZenModeConfig(); + if (Objects.equals(mState.zenModeConfig, zenModeConfig)) return false; + mState.zenModeConfig = zenModeConfig; return true; } @@ -750,7 +747,7 @@ public class VolumeDialogController { changed = updateZenModeW(); } if (ZEN_MODE_CONFIG_URI.equals(uri)) { - changed = updateExitConditionW(); + changed = updateZenModeConfigW(); } if (changed) { mCallbacks.onStateChanged(mState); @@ -943,7 +940,7 @@ public class VolumeDialogController { public int zenMode; public ComponentName effectsSuppressor; public String effectsSuppressorName; - public Condition exitCondition; + public ZenModeConfig zenModeConfig; public int activeStream = NO_ACTIVE_STREAM; public State copy() { @@ -956,7 +953,7 @@ public class VolumeDialogController { rt.zenMode = zenMode; if (effectsSuppressor != null) rt.effectsSuppressor = effectsSuppressor.clone(); rt.effectsSuppressorName = effectsSuppressorName; - if (exitCondition != null) rt.exitCondition = exitCondition.copy(); + if (zenModeConfig != null) rt.zenModeConfig = zenModeConfig.copy(); rt.activeStream = activeStream; return rt; } @@ -977,10 +974,15 @@ public class VolumeDialogController { sb.append(",zenMode:").append(zenMode); sb.append(",effectsSuppressor:").append(effectsSuppressor); sb.append(",effectsSuppressorName:").append(effectsSuppressorName); - sb.append(",exitCondition:").append(exitCondition); + sb.append(",zenModeConfig:").append(zenModeConfig); sb.append(",activeStream:").append(activeStream); return sb.append('}').toString(); } + + public Condition getManualExitCondition() { + return zenModeConfig != null && zenModeConfig.manualRule != null + ? zenModeConfig.manualRule.condition : null; + } } public interface Callbacks { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java index 915e998..04339eb 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java @@ -32,8 +32,6 @@ public class VolumePrefs { public static final String PREF_SHOW_HEADERS = "pref_show_headers"; public static final String PREF_SHOW_FAKE_REMOTE_1 = "pref_show_fake_remote_1"; public static final String PREF_SHOW_FAKE_REMOTE_2 = "pref_show_fake_remote_2"; - public static final String PREF_SHOW_FOOTER = "pref_show_footer"; - public static final String PREF_ZEN_FOOTER = "pref_zen_footer"; public static final String PREF_ENABLE_AUTOMUTE = "pref_enable_automute"; public static final String PREF_ENABLE_SILENT_MODE = "pref_enable_silent_mode"; public static final String PREF_DEBUG_LOGGING = "pref_debug_logging"; @@ -46,10 +44,8 @@ public class VolumePrefs { public static final String PREF_ADJUST_NOTIFICATION = "pref_adjust_notification"; public static final boolean DEFAULT_SHOW_HEADERS = true; - public static final boolean DEFAULT_SHOW_FOOTER = true; public static final boolean DEFAULT_ENABLE_AUTOMUTE = true; public static final boolean DEFAULT_ENABLE_SILENT_MODE = true; - public static final boolean DEFAULT_ZEN_FOOTER = true; public static void unregisterCallbacks(Context c, OnSharedPreferenceChangeListener listener) { prefs(c).unregisterOnSharedPreferenceChangeListener(listener); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index 5f04aaf..2688813 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -103,7 +103,7 @@ public class VolumeUI extends SystemUI { private void setDefaultVolumeController(boolean register) { if (register) { - DndTile.setVisible(mContext, false); + DndTile.setVisible(mContext, true); if (LOGD) Log.d(TAG, "Registering default volume controller"); getVolumeComponent().register(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java index 775c87d..6d67d11 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java @@ -16,20 +16,13 @@ package com.android.systemui.volume; import android.animation.LayoutTransition; -import android.animation.ValueAnimator; -import android.app.ActivityManager; import android.content.Context; -import android.content.res.Resources; import android.provider.Settings.Global; import android.service.notification.ZenModeConfig; import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; import android.view.View; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.Switch; import android.widget.TextView; import com.android.systemui.R; @@ -38,70 +31,43 @@ import com.android.systemui.statusbar.policy.ZenModeController; import java.util.Objects; /** - * Switch bar + zen mode panel (conditions) attached to the bottom of the volume dialog. + * Zen mode information (and end button) attached to the bottom of the volume dialog. */ public class ZenFooter extends LinearLayout { private static final String TAG = Util.logTag(ZenFooter.class); private final Context mContext; - private final float mSecondaryAlpha; - private final LayoutTransition mLayoutTransition; + private final SpTexts mSpTexts; - private ZenModeController mController; - private Switch mSwitch; - private ZenModePanel mZenModePanel; - private View mZenModePanelButtons; - private View mZenModePanelMoreButton; - private View mZenModePanelDoneButton; - private View mSwitchBar; - private View mSwitchBarIcon; - private View mSummary; + private ImageView mIcon; private TextView mSummaryLine1; private TextView mSummaryLine2; - private boolean mFooterExpanded; + private TextView mEndNowButton; private int mZen = -1; private ZenModeConfig mConfig; - private Callback mCallback; + private ZenModeController mController; public ZenFooter(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; - mSecondaryAlpha = getFloat(context.getResources(), R.dimen.volume_secondary_alpha); - mLayoutTransition = new LayoutTransition(); - mLayoutTransition.setDuration(new ValueAnimator().getDuration() / 2); - mLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING); - mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); - } - - private static float getFloat(Resources r, int resId) { - final TypedValue tv = new TypedValue(); - r.getValue(resId, tv, true); - return tv.getFloat(); + mSpTexts = new SpTexts(mContext); + setLayoutTransition(new LayoutTransition()); } @Override protected void onFinishInflate() { super.onFinishInflate(); - mSwitchBar = findViewById(R.id.volume_zen_switch_bar); - mSwitchBarIcon = findViewById(R.id.volume_zen_switch_bar_icon); - mSwitch = (Switch) findViewById(R.id.volume_zen_switch); - mZenModePanel = (ZenModePanel) findViewById(R.id.zen_mode_panel); - mZenModePanelButtons = findViewById(R.id.volume_zen_mode_panel_buttons); - mZenModePanelMoreButton = findViewById(R.id.volume_zen_mode_panel_more); - mZenModePanelDoneButton = findViewById(R.id.volume_zen_mode_panel_done); - mSummary = findViewById(R.id.volume_zen_panel_summary); - mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_panel_summary_line_1); - mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_panel_summary_line_2); + mIcon = (ImageView) findViewById(R.id.volume_zen_icon); + mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_summary_line_1); + mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_summary_line_2); + mEndNowButton = (TextView) findViewById(R.id.volume_zen_end_now); + mSpTexts.add(mSummaryLine1); + mSpTexts.add(mSummaryLine2); + mSpTexts.add(mEndNowButton); } - public void init(ZenModeController controller, Callback callback) { - mCallback = callback; - mController = controller; - mZenModePanel.init(controller); - mZenModePanel.setEmbedded(true); - mZenModePanel.setCallback(mZenModePanelCallback); - mSwitch.setOnCheckedChangeListener(mCheckedListener); - mController.addCallback(new ZenModeController.Callback() { + public void init(final ZenModeController controller) { + controller.addCallback(new ZenModeController.Callback() { @Override public void onZenChanged(int zen) { setZen(zen); @@ -111,30 +77,15 @@ public class ZenFooter extends LinearLayout { setConfig(config); } }); - mSwitchBar.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mSwitch.setChecked(!mSwitch.isChecked()); - } - }); - mZenModePanelMoreButton.setOnClickListener(new OnClickListener() { + mEndNowButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - if (mCallback != null) { - mCallback.onSettingsClicked(); - } + controller.setZen(Global.ZEN_MODE_OFF, null, TAG); } }); - mZenModePanelDoneButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (mCallback != null) { - mCallback.onDoneClicked(); - } - } - }); - mZen = mController.getZen(); - mConfig = mController.getConfig(); + mZen = controller.getZen(); + mConfig = controller.getConfig(); + mController = controller; update(); } @@ -166,96 +117,22 @@ public class ZenFooter extends LinearLayout { return mZen == Global.ZEN_MODE_NO_INTERRUPTIONS; } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - setLayoutTransition(null); - setFooterExpanded(false); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - setLayoutTransition(mLayoutTransition); - } - - private boolean setFooterExpanded(boolean expanded) { - if (mFooterExpanded == expanded) return false; - mFooterExpanded = expanded; - update(); - if (mCallback != null) { - mCallback.onFooterExpanded(); - } - return true; - } - - public boolean isFooterExpanded() { - return mFooterExpanded; - } - public void update() { - final boolean isZen = isZen(); - mSwitch.setOnCheckedChangeListener(null); - mSwitch.setChecked(isZen); - mSwitch.setOnCheckedChangeListener(mCheckedListener); - Util.setVisOrGone(mZenModePanel, isZen && mFooterExpanded); - Util.setVisOrGone(mZenModePanelButtons, isZen && mFooterExpanded); - Util.setVisOrGone(mSummary, isZen && !mFooterExpanded); - mSwitchBarIcon.setAlpha(isZen ? 1 : mSecondaryAlpha); + mIcon.setImageResource(isZenNone() ? R.drawable.ic_dnd_total_silence : R.drawable.ic_dnd); final String line1 = isZenPriority() ? mContext.getString(R.string.interruption_level_priority) : isZenAlarms() ? mContext.getString(R.string.interruption_level_alarms) : isZenNone() ? mContext.getString(R.string.interruption_level_none) : null; Util.setText(mSummaryLine1, line1); + final String line2 = ZenModeConfig.getConditionSummary(mContext, mConfig, - ActivityManager.getCurrentUser()); + mController.getCurrentUser(), true /*shortVersion*/); Util.setText(mSummaryLine2, line2); } - private final ZenModePanel.Callback mZenModePanelCallback = new ZenModePanel.Callback() { - @Override - public void onMoreSettings() { - if (mCallback != null) { - mCallback.onSettingsClicked(); - } - } - - @Override - public void onPrioritySettings() { - if (mCallback != null) { - mCallback.onPrioritySettingsClicked(); - } - } - - @Override - public void onInteraction() { - // noop - } - - @Override - public void onExpanded(boolean expanded) { - // noop - } - }; - - private final OnCheckedChangeListener mCheckedListener = new OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (D.BUG) Log.d(TAG, "onCheckedChanged " + isChecked); - if (isChecked != isZen()) { - final int newZen = isChecked ? Global.ZEN_MODE_ALARMS : Global.ZEN_MODE_OFF; - mZen = newZen; // this one's optimistic - setFooterExpanded(isChecked); - mController.setZen(newZen, null, TAG); - } - } - }; - - public interface Callback { - void onFooterExpanded(); - void onSettingsClicked(); - void onDoneClicked(); - void onPrioritySettingsClicked(); + public void onConfigurationChanged() { + mSpTexts.update(); } + } diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java index 1b563dc..8dfa9b0 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java @@ -41,8 +41,6 @@ import android.util.MathUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ImageView; @@ -50,6 +48,7 @@ import android.widget.LinearLayout; import android.widget.RadioButton; import android.widget.TextView; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.statusbar.policy.ZenModeController; @@ -85,22 +84,14 @@ public class ZenModePanel extends LinearLayout { private final H mHandler = new H(); private final ZenPrefs mPrefs; private final IconPulser mIconPulser; - private final int mSubheadWarningColor; - private final int mSubheadColor; - private final Interpolator mInterpolator; private final TransitionHelper mTransitionHelper = new TransitionHelper(); private final Uri mForeverId; 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 View mZenIntroduction; + private TextView mZenIntroductionMessage; private View mZenIntroductionConfirm; private View mZenIntroductionCustomize; private LinearLayout mZenConditions; @@ -113,7 +104,6 @@ public class ZenModePanel extends LinearLayout { private int mFirstConditionIndex; private boolean mRequestingConditions; private Condition mExitCondition; - private String mExitConditionText; private int mBucketIndex = -1; private boolean mExpanded; private boolean mHidden; @@ -123,7 +113,6 @@ 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); @@ -131,10 +120,6 @@ public class ZenModePanel extends LinearLayout { mPrefs = new ZenPrefs(); mInflater = LayoutInflater.from(mContext.getApplicationContext()); mIconPulser = new IconPulser(mContext); - mSubheadWarningColor = context.getColor(R.color.system_warning_color); - mSubheadColor = context.getColor(R.color.qs_subhead); - mInterpolator = AnimationUtils.loadInterpolator(mContext, - com.android.internal.R.interpolator.fast_out_slow_in); mForeverId = Condition.newId(mContext).appendPath("forever").build(); if (DEBUG) Log.d(mTag, "new ZenModePanel"); } @@ -149,25 +134,13 @@ 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); + pw.print(" mConfirmedPriorityIntroduction="); + pw.println(mPrefs.mConfirmedPriorityIntroduction); + pw.print(" mConfirmedSilenceIntroduction="); + pw.println(mPrefs.mConfirmedSilenceIntroduction); mTransitionHelper.dump(fd, pw, args); } - public void setEmbedded(boolean embedded) { - if (mEmbedded == embedded) return; - mEmbedded = embedded; - mZenButtonsContainer.setLayoutTransition(mEmbedded ? null : newLayoutTransition(null)); - setLayoutTransition(mEmbedded ? null : newLayoutTransition(null)); - if (mEmbedded) { - mZenButtonsContainer.setBackground(null); - } else { - mZenButtonsContainer.setBackgroundResource(R.drawable.qs_background_secondary); - } - mZenButtons.getChildAt(3).setVisibility(mEmbedded ? GONE : VISIBLE); - mZenEmbeddedDivider.setVisibility(mEmbedded ? VISIBLE : GONE); - updateWidgets(); - } - @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -179,37 +152,10 @@ public class ZenModePanel extends LinearLayout { Global.ZEN_MODE_ALARMS); mZenButtons.addButton(R.string.interruption_level_priority_twoline, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); - mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF); mZenButtons.setCallback(mZenButtonsCallback); - 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() { - @Override - public void onClick(View v) { - setExpanded(true); - } - }); - Interaction.register(mZenSubheadCollapsed, mInteractionCallback); - - mZenSubheadExpanded = (TextView) findViewById(R.id.zen_subhead_expanded); - Interaction.register(mZenSubheadExpanded, mInteractionCallback); - - mMoreSettings = findViewById(R.id.zen_more_settings); - mMoreSettings.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - fireMoreSettings(); - } - }); - Interaction.register(mMoreSettings, mInteractionCallback); - mZenIntroduction = findViewById(R.id.zen_introduction); + mZenIntroductionMessage = (TextView) findViewById(R.id.zen_introduction_message); mZenIntroductionConfirm = findViewById(R.id.zen_introduction_confirm); mZenIntroductionConfirm.setOnClickListener(new OnClickListener() { @Override @@ -230,25 +176,25 @@ public class ZenModePanel extends LinearLayout { mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions); - setLayoutTransition(newLayoutTransition(mTransitionHelper)); } private void confirmZenIntroduction() { - if (DEBUG) Log.d(TAG, "confirmZenIntroduction"); - Prefs.putBoolean(mContext, Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION, true); + final String prefKey = prefKeyForConfirmation(getSelectedZen(Global.ZEN_MODE_OFF)); + if (prefKey == null) return; + if (DEBUG) Log.d(TAG, "confirmZenIntroduction " + prefKey); + Prefs.putBoolean(mContext, prefKey, true); mHandler.sendEmptyMessage(H.UPDATE_WIDGETS); } - private LayoutTransition newLayoutTransition(TransitionListener listener) { - final LayoutTransition transition = new LayoutTransition(); - transition.disableTransitionType(LayoutTransition.DISAPPEARING); - transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); - transition.disableTransitionType(LayoutTransition.APPEARING); - transition.setInterpolator(LayoutTransition.CHANGE_APPEARING, mInterpolator); - if (listener != null) { - transition.addTransitionListener(listener); + private static String prefKeyForConfirmation(int zen) { + switch (zen) { + case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: + return Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION; + case Global.ZEN_MODE_NO_INTERRUPTIONS: + return Prefs.Key.DND_CONFIRMED_SILENCE_INTRODUCTION; + default: + return null; } - return transition; } @Override @@ -260,7 +206,6 @@ public class ZenModePanel extends LinearLayout { mSessionZen = mAttachedZen; mTransitionHelper.clear(); setSessionExitCondition(copy(mExitCondition)); - refreshExitConditionText(); updateWidgets(); setRequestingConditions(!mHidden); } @@ -274,9 +219,6 @@ public class ZenModePanel extends LinearLayout { mAttachedZen = -1; mSessionZen = -1; setSessionExitCondition(null); - if (!mEmbedded) { - setExpanded(false); - } setRequestingConditions(false); mTransitionHelper.clear(); } @@ -359,7 +301,6 @@ public class ZenModePanel extends LinearLayout { for (int i = 0; i < mMaxConditions; i++) { mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false)); } - refreshExitConditionText(); mSessionZen = getSelectedZen(-1); handleUpdateManualRule(mController.getManualRule()); if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition); @@ -375,7 +316,6 @@ public class ZenModePanel extends LinearLayout { if (Objects.equals(mExitCondition, exitCondition)) return; mExitCondition = exitCondition; if (DEBUG) Log.d(mTag, "mExitCondition=" + getConditionId(mExitCondition)); - refreshExitConditionText(); updateWidgets(); } @@ -395,10 +335,6 @@ public class ZenModePanel extends LinearLayout { return condition == null ? null : condition.copy(); } - private void refreshExitConditionText() { - mExitConditionText = getExitConditionText(mContext, mExitCondition); - } - public static String getExitConditionText(Context context, Condition exitCondition) { if (exitCondition == null) { return foreverSummary(context); @@ -430,7 +366,7 @@ public class ZenModePanel extends LinearLayout { private void handleUpdateZen(int zen) { if (mSessionZen != -1 && mSessionZen != zen) { - setExpanded(mEmbedded && isShown() || !mEmbedded && zen != Global.ZEN_MODE_OFF); + setExpanded(isShown()); mSessionZen = zen; } mZenButtons.setSelectedValue(zen); @@ -480,30 +416,18 @@ public class ZenModePanel extends LinearLayout { return; } final int zen = getSelectedZen(Global.ZEN_MODE_OFF); - final boolean zenOff = zen == Global.ZEN_MODE_OFF; final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS; - final boolean expanded = !mHidden && mExpanded; - final boolean conditions = mEmbedded || !zenOff && expanded; - final boolean introduction = conditions && zenImportant && !mPrefs.mConfirmedIntroduction; + final boolean introduction = (zenImportant && !mPrefs.mConfirmedPriorityIntroduction + || zenNone && !mPrefs.mConfirmedSilenceIntroduction); mZenButtons.setVisibility(mHidden ? GONE : VISIBLE); - mZenSubhead.setVisibility(!mHidden && !zenOff && !mEmbedded ? VISIBLE : GONE); - mZenSubheadExpanded.setVisibility(expanded ? VISIBLE : GONE); - mZenSubheadCollapsed.setVisibility(!expanded ? VISIBLE : GONE); - mMoreSettings.setVisibility(zenImportant && expanded ? VISIBLE : GONE); - mZenConditions.setVisibility(conditions ? VISIBLE : GONE); - - if (zenNone) { - mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning); - mZenSubheadCollapsed.setText(mExitConditionText); - } else if (zenImportant) { - mZenSubheadExpanded.setText(R.string.zen_important_interruptions); - mZenSubheadCollapsed.setText(mExitConditionText); - } - mZenSubheadExpanded.setTextColor(zenNone && mPrefs.isNoneDangerous() - ? mSubheadWarningColor : mSubheadColor); mZenIntroduction.setVisibility(introduction ? VISIBLE : GONE); + if (introduction) { + mZenIntroductionMessage.setText(zenImportant ? R.string.zen_priority_introduction + : R.string.zen_silence_introduction); + mZenIntroductionCustomize.setVisibility(zenImportant ? VISIBLE : GONE); + } } private static Condition parseExistingTimeCondition(Context context, Condition condition) { @@ -514,7 +438,8 @@ public class ZenModePanel extends LinearLayout { final long span = time - now; if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null; return ZenModeConfig.toTimeCondition(context, - time, Math.round(span / (float) MINUTES_MS), now, ActivityManager.getCurrentUser()); + time, Math.round(span / (float) MINUTES_MS), now, ActivityManager.getCurrentUser(), + false /*shortVersion*/); } private void handleUpdateConditions(Condition[] conditions) { @@ -675,6 +600,7 @@ public class ZenModePanel extends LinearLayout { if (childTag == null || childTag == tag) continue; childTag.rb.setChecked(false); } + MetricsLogger.action(mContext, MetricsLogger.QS_DND_CONDITION_SELECT); select(tag.condition); announceConditionSelection(tag); } @@ -761,13 +687,13 @@ public class ZenModePanel extends LinearLayout { String modeText; switch(zen) { case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: - modeText = mContext.getString(R.string.zen_important_interruptions); + modeText = mContext.getString(R.string.interruption_level_priority); break; case Global.ZEN_MODE_NO_INTERRUPTIONS: - modeText = mContext.getString(R.string.zen_no_interruptions); + modeText = mContext.getString(R.string.interruption_level_none); break; case Global.ZEN_MODE_ALARMS: - modeText = mContext.getString(R.string.zen_alarms); + modeText = mContext.getString(R.string.interruption_level_alarms); break; default: return; @@ -777,6 +703,7 @@ public class ZenModePanel extends LinearLayout { } private void onClickTimeButton(View row, ConditionTag tag, boolean up) { + MetricsLogger.action(mContext, MetricsLogger.QS_DND_TIME, up); Condition newCondition = null; final int N = MINUTE_BUCKETS.length; if (mBucketIndex == -1) { @@ -791,7 +718,8 @@ public class ZenModePanel extends LinearLayout { if (up && bucketTime > time || !up && bucketTime < time) { mBucketIndex = j; newCondition = ZenModeConfig.toTimeCondition(mContext, - bucketTime, bucketMinutes, now, ActivityManager.getCurrentUser()); + bucketTime, bucketMinutes, now, ActivityManager.getCurrentUser(), + false /*shortVersion*/); break; } } @@ -837,12 +765,6 @@ public class ZenModePanel extends LinearLayout { setSessionExitCondition(copy(condition)); } - private void fireMoreSettings() { - if (mCallback != null) { - mCallback.onMoreSettings(); - } - } - private void fireInteraction() { if (mCallback != null) { mCallback.onInteraction(); @@ -887,7 +809,6 @@ public class ZenModePanel extends LinearLayout { } public interface Callback { - void onMoreSettings(); void onPrioritySettings(); void onInteraction(); void onExpanded(boolean expanded); @@ -907,7 +828,8 @@ public class ZenModePanel extends LinearLayout { private int mMinuteIndex; private int mNoneSelected; - private boolean mConfirmedIntroduction; + private boolean mConfirmedPriorityIntroduction; + private boolean mConfirmedSilenceIntroduction; private ZenPrefs() { mNoneDangerousThreshold = mContext.getResources() @@ -915,11 +837,8 @@ public class ZenModePanel extends LinearLayout { Prefs.registerListener(mContext, this); updateMinuteIndex(); updateNoneSelected(); - updateConfirmedIntroduction(); - } - - public boolean isNoneDangerous() { - return mNoneSelected < mNoneDangerousThreshold; + updateConfirmedPriorityIntroduction(); + updateConfirmedSilenceIntroduction(); } public void trackNoneSelected() { @@ -945,7 +864,8 @@ public class ZenModePanel extends LinearLayout { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { updateMinuteIndex(); updateNoneSelected(); - updateConfirmedIntroduction(); + updateConfirmedPriorityIntroduction(); + updateConfirmedSilenceIntroduction(); } private void updateMinuteIndex() { @@ -968,12 +888,22 @@ public class ZenModePanel extends LinearLayout { return MathUtils.constrain(noneSelected, 0, Integer.MAX_VALUE); } - private void updateConfirmedIntroduction() { + private void updateConfirmedPriorityIntroduction() { final boolean confirmed = Prefs.getBoolean(mContext, Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION, false); - if (confirmed == mConfirmedIntroduction) return; - mConfirmedIntroduction = confirmed; - if (DEBUG) Log.d(mTag, "Confirmed introduction: " + mConfirmedIntroduction); + if (confirmed == mConfirmedPriorityIntroduction) return; + mConfirmedPriorityIntroduction = confirmed; + if (DEBUG) Log.d(mTag, "Confirmed priority introduction: " + + mConfirmedPriorityIntroduction); + } + + private void updateConfirmedSilenceIntroduction() { + final boolean confirmed = Prefs.getBoolean(mContext, + Prefs.Key.DND_CONFIRMED_SILENCE_INTRODUCTION, false); + if (confirmed == mConfirmedSilenceIntroduction) return; + mConfirmedSilenceIntroduction = confirmed; + if (DEBUG) Log.d(mTag, "Confirmed silence introduction: " + + mConfirmedSilenceIntroduction); } } @@ -981,12 +911,17 @@ public class ZenModePanel extends LinearLayout { @Override public void onSelected(final Object value) { if (value != null && mZenButtons.isShown() && isAttachedToWindow()) { - if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value); + final int zen = (Integer) value; + MetricsLogger.action(mContext, MetricsLogger.QS_DND_ZEN_SELECT, zen); + if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + zen); final Uri realConditionId = getRealConditionId(mSessionExitCondition); AsyncTask.execute(new Runnable() { @Override public void run() { - mController.setZen((Integer) value, realConditionId, TAG + ".selectZen"); + mController.setZen(zen, realConditionId, TAG + ".selectZen"); + if (zen != Global.ZEN_MODE_OFF) { + Prefs.putInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, zen); + } } }); } |