diff options
author | Jiyong Park <jiyong@google.com> | 2018-04-11 21:14:26 +0900 |
---|---|---|
committer | Jiyong Park <jiyong@google.com> | 2018-04-11 21:14:26 +0900 |
commit | 1452baeffa18c2760dac56bee26c20fa12a499a3 (patch) | |
tree | e1e6c5922257450926c99e481c50fd42cedba61e /packages/SystemUI/src | |
parent | 5c521b2e9db825519d7b287acc58378912b56b69 (diff) | |
parent | 8dcd862081e335405aecacd1d10e095cd78f7116 (diff) |
Update to PPR1.180411.001
Conflicts:
services/core/java/com/android/server/ConnectivityService.java
services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
services/core/java/com/android/server/om/OverlayManagerSettings.java
Change-Id: I44e49cb1dc1cf3f10e12b49ca84d0cb486925d08
Diffstat (limited to 'packages/SystemUI/src')
70 files changed, 1432 insertions, 837 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index d63ad0840734..00cd5a7b1689 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -265,11 +265,11 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout mPendingLockCheck.cancel(false); mPendingLockCheck = null; } + reset(); } @Override public void onResume(int reason) { - reset(); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index c1cff9e8f735..adb246013d5d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -107,6 +107,13 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { new View[]{ null, mEcaView, null }}; + + View cancelBtn = findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + mCallback.reset(); + }); + } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 75c52d8ead65..7cc37c476e31 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -205,6 +205,13 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } }); + View cancelBtn = findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + mCallback.reset(); + }); + } + // If there's more than one IME, enable the IME switcher button updateSwitchImeButton(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index 651831eea517..174dcaba0759 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -157,6 +157,13 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit if (button != null) { button.setCallback(this); } + + View cancelBtn = findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + mCallback.reset(); + }); + } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java index f0a823e8af34..90e092d57ebd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java @@ -78,6 +78,11 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { } break; } + case READY: { + mRemainingAttempts = -1; + resetState(); + break; + } default: resetState(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index ce16efbb311f..ff3af17bb61d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -216,8 +216,7 @@ public class KeyguardStatusView extends GridLayout { } public void refreshTime() { - mClockView.setFormat12Hour(Patterns.clockView12); - mClockView.setFormat24Hour(Patterns.clockView24); + mClockView.refresh(); } private void refresh() { diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 1ae06d751255..0683514f6f2a 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -81,6 +81,14 @@ public class BatteryMeterView extends LinearLayout implements private float mDarkIntensity; private int mUser; + /** + * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings. + */ + private boolean mUseWallpaperTextColors; + + private int mNonAdaptedForegroundColor; + private int mNonAdaptedBackgroundColor; + public BatteryMeterView(Context context) { this(context, null, 0); } @@ -140,6 +148,29 @@ public class BatteryMeterView extends LinearLayout implements updateShowPercent(); } + /** + * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll + * revert back to dark-mode-based/tinted colors. + * + * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for all + * components + */ + public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) { + if (shouldUseWallpaperTextColor == mUseWallpaperTextColors) { + return; + } + + mUseWallpaperTextColors = shouldUseWallpaperTextColor; + + if (mUseWallpaperTextColors) { + updateColors( + Utils.getColorAttr(mContext, R.attr.wallpaperTextColor), + Utils.getColorAttr(mContext, R.attr.wallpaperTextColorSecondary)); + } else { + updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor); + } + } + public void setColorsFromContext(Context context) { if (context == null) { return; @@ -179,7 +210,8 @@ public class BatteryMeterView extends LinearLayout implements getContext().getContentResolver().registerContentObserver( Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser); updateShowPercent(); - Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST); + Dependency.get(TunerService.class) + .addTunable(this, StatusBarIconController.ICON_BLACKLIST); Dependency.get(ConfigurationController.class).addCallback(this); mUserTracker.startTracking(); } @@ -273,19 +305,23 @@ public class BatteryMeterView extends LinearLayout implements @Override public void onDarkChanged(Rect area, float darkIntensity, int tint) { mDarkIntensity = darkIntensity; + float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0; - int foreground = getColorForDarkIntensity(intensity, mLightModeFillColor, - mDarkModeFillColor); - int background = getColorForDarkIntensity(intensity, mLightModeBackgroundColor, - mDarkModeBackgroundColor); - mDrawable.setColors(foreground, background); - setTextColor(foreground); + mNonAdaptedForegroundColor = getColorForDarkIntensity( + intensity, mLightModeFillColor, mDarkModeFillColor); + mNonAdaptedBackgroundColor = getColorForDarkIntensity( + intensity, mLightModeBackgroundColor,mDarkModeBackgroundColor); + + if (!mUseWallpaperTextColors) { + updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor); + } } - public void setTextColor(int color) { - mTextColor = color; + private void updateColors(int foregroundColor, int backgroundColor) { + mDrawable.setColors(foregroundColor, backgroundColor); + mTextColor = foregroundColor; if (mBatteryPercentView != null) { - mBatteryPercentView.setTextColor(color); + mBatteryPercentView.setTextColor(foregroundColor); } } diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index a4f8d8c1bcd2..b8a57bfe885a 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -444,13 +444,7 @@ public class ImageWallpaper extends WallpaperService { final Surface surface = getSurfaceHolder().getSurface(); surface.hwuiDestroy(); - mLoader = new AsyncTask<Void, Void, Bitmap>() { - @Override - protected Bitmap doInBackground(Void... params) { - mWallpaperManager.forgetLoadedWallpaper(); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + mWallpaperManager.forgetLoadedWallpaper(); } private void scheduleUnloadWallpaper() { diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 8e5984244476..b1020cfb5a84 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -52,6 +52,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP; import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType; @@ -315,7 +316,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } private void updateEnabledState() { - mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent, 0, + mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent, + MATCH_DIRECT_BOOT_UNAWARE, ActivityManagerWrapper.getInstance().getCurrentUserId()) != null; } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 039e7b5a6613..c01cafaaa9b8 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -25,6 +25,7 @@ import android.view.ViewGroup; import com.android.internal.logging.MetricsLogger; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.Dependency.DependencyProvider; import com.android.systemui.classifier.FalsingManager; @@ -44,6 +45,7 @@ import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBouncer; +import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.LockscreenWallpaper; @@ -100,12 +102,13 @@ public class SystemUIFactory { dismissCallbackRegistry, FalsingManager.getInstance(context)); } - public ScrimController createScrimController(LightBarController lightBarController, - ScrimView scrimBehind, ScrimView scrimInFront, LockscreenWallpaper lockscreenWallpaper, + public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront, + LockscreenWallpaper lockscreenWallpaper, Consumer<Float> scrimBehindAlphaListener, + Consumer<GradientColors> scrimInFrontColorListener, Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters, AlarmManager alarmManager) { - return new ScrimController(lightBarController, scrimBehind, scrimInFront, - scrimVisibleListener, dozeParameters, alarmManager); + return new ScrimController(scrimBehind, scrimInFront, scrimBehindAlphaListener, + scrimInFrontColorListener, scrimVisibleListener, dozeParameters, alarmManager); } public NotificationIconAreaController createNotificationIconAreaController(Context context, @@ -142,5 +145,6 @@ public class SystemUIFactory { providers.put(NotificationViewHierarchyManager.class, () -> new NotificationViewHierarchyManager(context)); providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context)); + providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java index 5f6c1b762c82..9ffe783fbded 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java @@ -18,12 +18,11 @@ package com.android.systemui.classifier; public class AnglesVarianceEvaluator { public static float evaluate(float value, int type) { - final boolean secureUnlock = type == Classifier.BOUNCER_UNLOCK; float evaluation = 0.0f; if (value > 0.20) evaluation++; - if (value > 0.40 && !secureUnlock) evaluation++; - if (value > 0.80 && !secureUnlock) evaluation++; - if (value > 1.50 && !secureUnlock) evaluation++; + if (value > 0.40) evaluation++; + if (value > 0.80) evaluation++; + if (value > 1.50) evaluation++; return evaluation; } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 778e63092150..c39076499d08 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -109,7 +109,11 @@ public class DozeUi implements DozeMachine.Part { switch (newState) { case DOZE_AOD: if (oldState == DOZE_AOD_PAUSED) { + // Whenever turning on the display, it's necessary to push a new frame. + // The display buffers will be empty and need to be filled. mHost.dozeTimeTick(); + // The first frame may arrive when the display isn't ready yet. + mHandler.postDelayed(mHost::dozeTimeTick, 100); } scheduleTimeTick(); break; diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java index 3577c0fa6180..c238e54e7cc9 100644 --- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java @@ -18,8 +18,8 @@ package com.android.systemui.fingerprint; import android.content.Context; import android.content.pm.PackageManager; -import android.hardware.biometrics.BiometricDialog; -import android.hardware.biometrics.IBiometricDialogReceiver; +import android.hardware.biometrics.BiometricPrompt; +import android.hardware.biometrics.IBiometricPromptReceiver; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -48,7 +48,7 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call private FingerprintDialogView mDialogView; private WindowManager mWindowManager; - private IBiometricDialogReceiver mReceiver; + private IBiometricPromptReceiver mReceiver; private boolean mDialogShowing; private Handler mHandler = new Handler() { @@ -97,7 +97,7 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call } @Override - public void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) { + public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) { if (DEBUG) Log.d(TAG, "showFingerprintDialog"); // Remove these messages as they are part of the previous client mHandler.removeMessages(MSG_FINGERPRINT_ERROR); @@ -139,7 +139,7 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call Log.w(TAG, "Dialog already showing"); return; } - mReceiver = (IBiometricDialogReceiver) args.arg2; + mReceiver = (IBiometricPromptReceiver) args.arg2; mDialogView.setBundle((Bundle)args.arg1); mWindowManager.addView(mDialogView, mDialogView.getLayoutParams()); mDialogShowing = true; @@ -177,7 +177,7 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call } if (userCanceled) { try { - mReceiver.onDialogDismissed(BiometricDialog.DISMISSED_REASON_USER_CANCEL); + mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); } catch (RemoteException e) { Log.e(TAG, "RemoteException when hiding dialog", e); } @@ -193,7 +193,7 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call return; } try { - mReceiver.onDialogDismissed(BiometricDialog.DISMISSED_REASON_NEGATIVE); + mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE); } catch (RemoteException e) { Log.e(TAG, "Remote exception when handling negative button", e); } @@ -206,7 +206,7 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call return; } try { - mReceiver.onDialogDismissed(BiometricDialog.DISMISSED_REASON_POSITIVE); + mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_POSITIVE); } catch (RemoteException e) { Log.e(TAG, "Remote exception when handling positive button", e); } diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java index 3e1ac022de61..d1d66097d48d 100644 --- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java @@ -22,11 +22,12 @@ import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; -import android.hardware.biometrics.BiometricDialog; +import android.hardware.biometrics.BiometricPrompt; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; @@ -161,29 +162,29 @@ public class FingerprintDialogView extends LinearLayout { mLastState = STATE_NONE; updateFingerprintIcon(STATE_FINGERPRINT); - title.setText(mBundle.getCharSequence(BiometricDialog.KEY_TITLE)); + title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE)); title.setSelected(true); - final CharSequence subtitleText = mBundle.getCharSequence(BiometricDialog.KEY_SUBTITLE); - if (subtitleText == null) { + final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE); + if (TextUtils.isEmpty(subtitleText)) { subtitle.setVisibility(View.GONE); } else { subtitle.setVisibility(View.VISIBLE); subtitle.setText(subtitleText); } - final CharSequence descriptionText = mBundle.getCharSequence(BiometricDialog.KEY_DESCRIPTION); - if (descriptionText == null) { - subtitle.setVisibility(View.VISIBLE); + final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION); + if (TextUtils.isEmpty(descriptionText)) { description.setVisibility(View.GONE); } else { - description.setText(mBundle.getCharSequence(BiometricDialog.KEY_DESCRIPTION)); + description.setVisibility(View.VISIBLE); + description.setText(descriptionText); } - negative.setText(mBundle.getCharSequence(BiometricDialog.KEY_NEGATIVE_TEXT)); + negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT)); final CharSequence positiveText = - mBundle.getCharSequence(BiometricDialog.KEY_POSITIVE_TEXT); + mBundle.getCharSequence(BiometricPrompt.KEY_POSITIVE_TEXT); positive.setText(positiveText); // needs to be set for marquee to work if (positiveText != null) { positive.setVisibility(View.VISIBLE); @@ -269,7 +270,7 @@ public class FingerprintDialogView extends LinearLayout { mErrorText.setTextColor(mErrorColor); mErrorText.setContentDescription(message); mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_CLEAR_MESSAGE), - BiometricDialog.HIDE_DIALOG_DELAY); + BiometricPrompt.HIDE_DIALOG_DELAY); } public void showHelpMessage(String message) { @@ -279,7 +280,7 @@ public class FingerprintDialogView extends LinearLayout { public void showErrorMessage(String error) { showTemporaryMessage(error); mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG, - false /* userCanceled */), BiometricDialog.HIDE_DIALOG_DELAY); + false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY); } private void updateFingerprintIcon(int newState) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index d6e59c77af9c..426f71409095 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1184,6 +1184,10 @@ public class KeyguardViewMediator extends SystemUI { Trace.endSection(); } + public boolean isHiding() { + return mHiding; + } + /** * Handles SET_OCCLUDED message sent by setOccluded() */ diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index a0bdcd00d43a..1805f96c2cf5 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -295,6 +295,26 @@ public class PipTouchHandler { final Rect toAdjustedBounds = mMenuState == MENU_STATE_FULL ? expandedAdjustedBounds : normalAdjustedBounds; + final Rect toMovementBounds = mMenuState == MENU_STATE_FULL + ? expandedMovementBounds + : normalMovementBounds; + + // If the PIP window needs to shift to right above shelf/IME and it's already above + // that, don't move the PIP window. + if (toAdjustedBounds.bottom < mMovementBounds.bottom + && animatingBounds.top < toAdjustedBounds.bottom) { + return; + } + + // If the PIP window needs to shift down due to dismissal of shelf/IME but it's way + // above the position as if shelf/IME shows, don't move the PIP window. + int movementBoundsAdjustment = toMovementBounds.bottom - mMovementBounds.bottom; + int offsetAdjustment = fromImeAdjustment ? mImeOffset : mShelfHeight; + if (toAdjustedBounds.bottom >= mMovementBounds.bottom + && animatingBounds.top + < toAdjustedBounds.bottom - movementBoundsAdjustment - offsetAdjustment) { + return; + } animateToOffset(animatingBounds, toAdjustedBounds); } @@ -320,10 +340,6 @@ public class PipTouchHandler { private void animateToOffset(Rect animatingBounds, Rect toAdjustedBounds) { final Rect bounds = new Rect(animatingBounds); - if (toAdjustedBounds.bottom < mMovementBounds.bottom - && bounds.top < toAdjustedBounds.bottom) { - return; - } bounds.offset(0, toAdjustedBounds.bottom - bounds.top); // In landscape mode, PIP window can go offset while launching IME. We want to align the // the top of the PIP window with the top of the movement bounds in that case. diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index a70b3587f195..40ce69b8e580 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -514,7 +514,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { autoTriggerThreshold = 15; } - BatterySaverUtils.scheduleAutoBatterySaver(mContext, autoTriggerThreshold); + BatterySaverUtils.ensureAutoBatterySaver(mContext, autoTriggerThreshold); showAutoSaverEnabledConfirmation(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index bfbfbf6fe813..a9455f23e29d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -16,19 +16,19 @@ package com.android.systemui.qs; +import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; + import android.content.Context; import android.content.res.Configuration; -import android.graphics.Canvas; -import android.graphics.Path; import android.graphics.Point; import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; -import com.android.settingslib.Utils; import com.android.systemui.R; +import com.android.systemui.SysUiServiceProvider; import com.android.systemui.qs.customize.QSCustomizer; -import com.android.systemui.statusbar.ExpandableOutlineView; +import com.android.systemui.statusbar.CommandQueue; /** * Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader} @@ -44,8 +44,13 @@ public class QSContainerImpl extends FrameLayout { protected float mQsExpansion; private QSCustomizer mQSCustomizer; private View mQSFooter; + private View mBackground; + private View mBackgroundGradient; + private View mStatusBarBackground; + private int mSideMargins; + private boolean mQsDisabled; public QSContainerImpl(Context context, AttributeSet attrs) { super(context, attrs); @@ -60,6 +65,8 @@ public class QSContainerImpl extends FrameLayout { mQSCustomizer = findViewById(R.id.qs_customize); mQSFooter = findViewById(R.id.qs_footer); mBackground = findViewById(R.id.quick_settings_background); + mStatusBarBackground = findViewById(R.id.quick_settings_status_bar_background); + mBackgroundGradient = findViewById(R.id.quick_settings_gradient_view); mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); setClickable(true); @@ -68,6 +75,23 @@ public class QSContainerImpl extends FrameLayout { } @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + // Hide the backgrounds when in landscape mode. + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + mBackgroundGradient.setVisibility(View.INVISIBLE); + mStatusBarBackground.setVisibility(View.INVISIBLE); + } else { + mBackgroundGradient.setVisibility(View.VISIBLE); + mStatusBarBackground.setVisibility(View.VISIBLE); + } + + updateResources(); + mSizePoint.set(0, 0); // Will be retrieved on next measure pass. + } + + @Override public boolean performClick() { // Want to receive clicks so missing QQS tiles doesn't cause collapse, but // don't want to do anything with them. @@ -76,6 +100,16 @@ public class QSContainerImpl extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mQsDisabled) { + // Only show the status bar contents in QQS header when QS is disabled. + mHeader.measure(widthMeasureSpec, heightMeasureSpec); + LayoutParams layoutParams = (LayoutParams) mHeader.getLayoutParams(); + int height = layoutParams.topMargin + layoutParams.bottomMargin + + mHeader.getMeasuredHeight(); + super.onMeasure( + widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + return; + } // Since we control our own bottom, be whatever size we want. // Otherwise the QSPanel ends up with 0 height when the window is only the // size of the status bar. @@ -90,9 +124,8 @@ public class QSContainerImpl extends FrameLayout { // QSCustomizer will always be the height of the screen, but do this after // other measuring to avoid changing the height of the QS. - getDisplay().getRealSize(mSizePoint); mQSCustomizer.measure(widthMeasureSpec, - MeasureSpec.makeMeasureSpec(mSizePoint.y, MeasureSpec.EXACTLY)); + MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY)); } @Override @@ -101,6 +134,23 @@ public class QSContainerImpl extends FrameLayout { updateExpansion(); } + public void disable(int state1, int state2, boolean animate) { + final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0; + if (disabled == mQsDisabled) return; + mQsDisabled = disabled; + mBackgroundGradient.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); + mQSPanel.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); + mQSFooter.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); + } + + private void updateResources() { + LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams(); + layoutParams.topMargin = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.quick_qs_offset_height); + + mQSPanel.setLayoutParams(layoutParams); + } + /** * Overrides the height of this view (post-layout), so that the content is clipped to that * height and the background is set to that height. @@ -147,4 +197,11 @@ public class QSContainerImpl extends FrameLayout { lp.rightMargin = mSideMargins; lp.leftMargin = mSideMargins; } + + private int getDisplayHeight() { + if (mSizePoint.y == 0) { + getDisplay().getRealSize(mSizePoint); + } + return mSizePoint.y; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index 3f3cea2eaa17..6c7eda7c89d1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -69,4 +69,6 @@ public interface QSFooter { */ @Nullable View getExpandView(); + + default void disable(int state1, int state2, boolean animate) {} } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 51c359a023a8..458e6480db24 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -47,10 +47,8 @@ import com.android.settingslib.graph.SignalDrawable; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.R.dimen; -import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.TouchAnimator.Builder; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.MultiUserSwitch; import com.android.systemui.statusbar.phone.SettingsButton; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -62,8 +60,7 @@ import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChange import com.android.systemui.tuner.TunerService; public class QSFooterImpl extends FrameLayout implements QSFooter, - OnClickListener, OnUserInfoChangedListener, EmergencyListener, - SignalCallback, CommandQueue.Callbacks { + OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback { private ActivityStarter mActivityStarter; private UserInfoController mUserInfoController; @@ -211,16 +208,9 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, } @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this); - } - - @Override @VisibleForTesting public void onDetachedFromWindow() { setListening(false); - SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this); super.onDetachedFromWindow(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 018a63560429..cb068e3b5372 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -14,9 +14,12 @@ package com.android.systemui.qs; +import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.Fragment; +import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; @@ -35,12 +38,14 @@ import android.widget.FrameLayout.LayoutParams; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.R.id; +import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.customize.QSCustomizer; +import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; import com.android.systemui.statusbar.stack.StackStateAnimator; -public class QSFragment extends Fragment implements QS { +public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { private static final String TAG = "QS"; private static final boolean DEBUG = false; private static final String EXTRA_EXPANDED = "expanded"; @@ -65,6 +70,7 @@ public class QSFragment extends Fragment implements QS { private int mLayoutDirection; private QSFooter mFooter; private float mLastQSExpansion = -1; + private boolean mQsDisabled; @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @@ -176,6 +182,17 @@ public class QSFragment extends Fragment implements QS { } } + @Override + public void disable(int state1, int state2, boolean animate) { + final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0; + if (disabled == mQsDisabled) return; + mQsDisabled = disabled; + mContainer.disable(state1, state2, animate); + mHeader.disable(state1, state2, animate); + mFooter.disable(state1, state2, animate); + updateQsState(); + } + private void updateQsState() { final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating; @@ -189,6 +206,9 @@ public class QSFragment extends Fragment implements QS { mFooter.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating) ? View.VISIBLE : View.INVISIBLE); + if (mQsDisabled) { + mFooter.setVisibility(View.GONE); + } mFooter.setExpanded((mKeyguardShowing && !mHeaderAnimating) || (mQsExpanded && !mStackScrollerOverscrolling)); mQSPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE); @@ -258,6 +278,12 @@ public class QSFragment extends Fragment implements QS { mHeader.setListening(listening); mFooter.setListening(listening); mQSPanel.setListening(mListening && mQsExpanded); + if (listening) { + SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this); + } else { + SysUiServiceProvider.getComponent(getContext(), CommandQueue.class) + .removeCallbacks(this); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 224c367b9c95..e2af90d6bbce 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; import android.media.AudioManager; @@ -54,8 +55,10 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager; +import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.NextAlarmController; import java.util.Locale; @@ -65,7 +68,7 @@ import java.util.Locale; * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner * contents. */ -public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks, +public class QuickStatusBarHeader extends RelativeLayout implements View.OnClickListener, NextAlarmController.NextAlarmChangeCallback { private static final String TAG = "QuickStatusBarHeader"; private static final boolean DEBUG = false; @@ -90,6 +93,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue private TouchAnimator mStatusIconsAlphaAnimator; private TouchAnimator mHeaderTextContainerAlphaAnimator; + private View mSystemIconsView; private View mQuickQsStatusIcons; private View mDate; private View mHeaderTextContainerView; @@ -107,6 +111,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue private View mStatusSeparator; private ImageView mRingerModeIcon; private TextView mRingerModeTextView; + private BatteryMeterView mBatteryMeterView; + private Clock mClockView; + private DateView mDateView; private NextAlarmController mAlarmController; /** Counts how many times the long press tooltip has been shown to the user. */ @@ -138,6 +145,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue mHeaderQsPanel = findViewById(R.id.quick_qs_panel); mDate = findViewById(R.id.date); mDate.setOnClickListener(this); + mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons); mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons); mIconManager = new TintedIconManager(findViewById(R.id.statusIcons)); @@ -164,8 +172,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue // Set the correct tint for the status icons so they contrast mIconManager.setTint(fillColor); - BatteryMeterView battery = findViewById(R.id.battery); - battery.setForceShowPercent(true); + mBatteryMeterView = findViewById(R.id.battery); + mBatteryMeterView.setForceShowPercent(true); + mClockView = findViewById(R.id.clock); + mDateView = findViewById(R.id.date); } private void updateStatusText() { @@ -212,6 +222,13 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); updateResources(); + + // Update color schemes in landscape to use wallpaperTextColor + boolean shouldUseWallpaperTextColor = + newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE; + mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor); + mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor); + mDateView.useWallpaperTextColor(shouldUseWallpaperTextColor); } @Override @@ -221,11 +238,22 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue } private void updateResources() { - // Update height, especially due to landscape mode restricting space. + Resources resources = mContext.getResources(); + + // Update height for a few views, especially due to landscape mode restricting space. mHeaderTextContainerView.getLayoutParams().height = - mContext.getResources().getDimensionPixelSize(R.dimen.qs_header_tooltip_height); + resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height); mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams()); + mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize( + com.android.internal.R.dimen.quick_qs_offset_height); + mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams()); + + getLayoutParams().height = resources.getDimensionPixelSize(mQsDisabled + ? com.android.internal.R.dimen.quick_qs_offset_height + : com.android.internal.R.dimen.quick_qs_total_height); + setLayoutParams(getLayoutParams()); + updateStatusIconAlphaAnimator(); updateHeaderTextContainerAlphaAnimator(); } @@ -293,20 +321,18 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue TOOLTIP_NOT_YET_SHOWN_COUNT); } - @Override public void disable(int state1, int state2, boolean animate) { final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0; if (disabled == mQsDisabled) return; mQsDisabled = disabled; mHeaderQsPanel.setDisabledByPolicy(disabled); - final int rawHeight = (int) getResources().getDimension( - com.android.internal.R.dimen.quick_qs_total_height); - getLayoutParams().height = disabled ? (rawHeight - mHeaderQsPanel.getHeight()) : rawHeight; + mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); + mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); + updateResources(); } @Override public void onAttachedToWindow() { - SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this); Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager); requestApplyInsets(); } @@ -327,7 +353,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue @VisibleForTesting public void onDetachedFromWindow() { setListening(false); - SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this); Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager); super.onDetachedFromWindow(); } @@ -497,9 +522,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue mHeaderQsPanel.setHost(host, null /* No customization in header */); // Use SystemUI context to get battery meter colors, and let it use the default tint (white) - BatteryMeterView battery = findViewById(R.id.battery); - battery.setColorsFromContext(mHost.getContext()); - battery.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT); + mBatteryMeterView.setColorsFromContext(mHost.getContext()); + mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT); } public void setCallback(Callback qsPanelCallback) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java index 23d3ebbbfe80..24b5a34075d0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java @@ -29,7 +29,6 @@ import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSFooter; import com.android.systemui.qs.QSPanel; -import com.android.systemui.statusbar.car.UserGridView; import com.android.systemui.statusbar.phone.MultiUserSwitch; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.UserInfoController; diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java index 0ee6d1fb6664..da21aa5017d3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java @@ -20,21 +20,20 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.Fragment; +import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; +import android.support.v7.widget.GridLayoutManager; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.QSFooter; -import com.android.systemui.statusbar.car.PageIndicator; -import com.android.systemui.statusbar.car.UserGridView; -import com.android.systemui.statusbar.policy.UserSwitcherController; +import com.android.systemui.statusbar.car.UserGridRecyclerView; import java.util.ArrayList; import java.util.List; @@ -45,14 +44,12 @@ import java.util.List; * status bar, and a static row with access to the user switcher and settings. */ public class CarQSFragment extends Fragment implements QS { - private ViewGroup mPanel; private View mHeader; private View mUserSwitcherContainer; private CarQSFooter mFooter; private View mFooterUserName; private View mFooterExpandIcon; - private UserGridView mUserGridView; - private PageIndicator mPageIndicator; + private UserGridRecyclerView mUserGridView; private AnimatorSet mAnimatorSet; private UserSwitchCallback mUserSwitchCallback; @@ -65,7 +62,6 @@ public class CarQSFragment extends Fragment implements QS { @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - mPanel = (ViewGroup) view; mHeader = view.findViewById(R.id.header); mFooter = view.findViewById(R.id.qs_footer); mFooterUserName = mFooter.findViewById(R.id.user_name); @@ -75,16 +71,15 @@ public class CarQSFragment extends Fragment implements QS { updateUserSwitcherHeight(0); - mUserGridView = view.findViewById(R.id.user_grid); - mUserGridView.init(null, Dependency.get(UserSwitcherController.class), - false /* overrideAlpha */); - - mPageIndicator = view.findViewById(R.id.user_switcher_page_indicator); - mPageIndicator.setupWithViewPager(mUserGridView); + Context context = getContext(); + mUserGridView = mUserSwitcherContainer.findViewById(R.id.user_grid); + GridLayoutManager layoutManager = new GridLayoutManager(context, + context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col)); + mUserGridView.setLayoutManager(layoutManager); + mUserGridView.buildAdapter(); mUserSwitchCallback = new UserSwitchCallback(); mFooter.setUserSwitchCallback(mUserSwitchCallback); - mUserGridView.setUserSwitchCallback(mUserSwitchCallback); } @Override @@ -111,13 +106,11 @@ public class CarQSFragment extends Fragment implements QS { @Override public void setHeaderListening(boolean listening) { mFooter.setListening(listening); - mUserGridView.setListening(listening); } @Override public void setListening(boolean listening) { mFooter.setListening(listening); - mUserGridView.setListening(listening); } @Override @@ -219,24 +212,6 @@ public class CarQSFragment extends Fragment implements QS { mShowing = false; animateHeightChange(false /* opening */); } - - public void resetShowing() { - if (mShowing) { - for (int i = 0; i < mUserGridView.getChildCount(); i++) { - ViewGroup podContainer = (ViewGroup) mUserGridView.getChildAt(i); - // Need to bring the last child to the front to maintain the order in the pod - // container. Why? ¯\_(ツ)_/¯ - if (podContainer.getChildCount() > 0) { - podContainer.getChildAt(podContainer.getChildCount() - 1).bringToFront(); - } - // The alpha values are default to 0, so if the pods have been refreshed, they - // need to be set to 1 when showing. - for (int j = 0; j < podContainer.getChildCount(); j++) { - podContainer.getChildAt(j).setAlpha(1f); - } - } - } - } } private void updateUserSwitcherHeight(int height) { @@ -260,27 +235,6 @@ public class CarQSFragment extends Fragment implements QS { }); allAnimators.add(heightAnimator); - // The user grid contains pod containers that each contain a number of pods. Animate - // all pods to avoid any discrepancy/race conditions with possible changes during the - // animation. - int cascadeDelay = getResources().getInteger( - R.integer.car_user_switcher_anim_cascade_delay_ms); - for (int i = 0; i < mUserGridView.getChildCount(); i++) { - ViewGroup podContainer = (ViewGroup) mUserGridView.getChildAt(i); - for (int j = 0; j < podContainer.getChildCount(); j++) { - View pod = podContainer.getChildAt(j); - Animator podAnimator = AnimatorInflater.loadAnimator(getContext(), - opening ? R.anim.car_user_switcher_open_pod_animation - : R.anim.car_user_switcher_close_pod_animation); - // Add the cascading delay between pods - if (opening) { - podAnimator.setStartDelay(podAnimator.getStartDelay() + j * cascadeDelay); - } - podAnimator.setTarget(pod); - allAnimators.add(podAnimator); - } - } - Animator nameAnimator = AnimatorInflater.loadAnimator(getContext(), opening ? R.anim.car_user_switcher_open_name_animation : R.anim.car_user_switcher_close_name_animation); @@ -293,12 +247,6 @@ public class CarQSFragment extends Fragment implements QS { iconAnimator.setTarget(mFooterExpandIcon); allAnimators.add(iconAnimator); - Animator pageAnimator = AnimatorInflater.loadAnimator(getContext(), - opening ? R.anim.car_user_switcher_open_pages_animation - : R.anim.car_user_switcher_close_pages_animation); - pageAnimator.setTarget(mPageIndicator); - allAnimators.add(pageAnimator); - mAnimatorSet = new AnimatorSet(); mAnimatorSet.addListener(new AnimatorListenerAdapter() { @Override 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 7dcf5c0c3b35..4b312f533024 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -209,7 +209,7 @@ public class DndTile extends QSTileImpl<BooleanState> { state.slash.isSlashed = !state.value; state.label = getTileLabel(); state.secondaryLabel = ZenModeConfig.getDescription(mContext,zen != Global.ZEN_MODE_OFF, - mController.getConfig()); + mController.getConfig(), false); state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on); checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME); switch (zen) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 0f85c5b37f65..8bb3c0231a76 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS; import android.app.ActivityManager; +import android.app.trust.TrustManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -51,6 +52,7 @@ import com.android.systemui.EventLogTags; import com.android.systemui.OverviewProxyService; import com.android.systemui.R; import com.android.systemui.RecentsComponent; +import com.android.systemui.SystemUIApplication; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.SystemUI; import com.android.systemui.recents.events.EventBus; @@ -70,6 +72,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.phone.StatusBar; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -107,6 +110,7 @@ public class Recents extends SystemUI private Handler mHandler; private RecentsImpl mImpl; + private TrustManager mTrustManager; private int mDraggingInRecentsCurrentUser; // Only For system user, this is the callbacks instance we return to each secondary user @@ -235,6 +239,8 @@ public class Recents extends SystemUI registerWithSystemUser(); } putComponent(Recents.class, this); + + mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); } @Override @@ -342,12 +348,28 @@ public class Recents extends SystemUI // If connected to launcher service, let it handle the toggle logic IOverviewProxy overviewProxy = mOverviewProxyService.getProxy(); if (overviewProxy != null) { - try { - overviewProxy.onOverviewToggle(); - return; - } catch (RemoteException e) { - Log.e(TAG, "Cannot send toggle recents through proxy service.", e); + final Runnable toggleRecents = () -> { + try { + if (mOverviewProxyService.getProxy() != null) { + mOverviewProxyService.getProxy().onOverviewToggle(); + } + } catch (RemoteException e) { + Log.e(TAG, "Cannot send toggle recents through proxy service.", e); + } + }; + // Preload only if device for current user is unlocked + final StatusBar statusBar = getComponent(StatusBar.class); + if (statusBar != null && statusBar.isKeyguardShowing()) { + statusBar.executeRunnableDismissingKeyguard(() -> { + // Flush trustmanager before checking device locked per user + mTrustManager.reportKeyguardShowingChanged(); + mHandler.post(toggleRecents); + }, null, true /* dismissShade */, false /* afterKeyguardGone */, + true /* deferred */); + } else { + toggleRecents.run(); } + return; } int growTarget = getComponent(Divider.class).getView().growsRecents(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java index 75bc95588244..30e9afd8ea04 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java @@ -21,7 +21,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import android.annotation.TargetApi; import android.app.ActivityManager; -import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -62,6 +61,7 @@ public class RecentsOnboarding { private static final String TAG = "RecentsOnboarding"; private static final boolean RESET_PREFS_FOR_DEBUG = false; + private static final boolean ONBOARDING_ENABLED = false; private static final long SHOW_DELAY_MS = 500; private static final long SHOW_HIDE_DURATION_MS = 300; // Don't show the onboarding until the user has launched this number of apps. @@ -184,6 +184,9 @@ public class RecentsOnboarding { } public void onConnectedToLauncher() { + if (!ONBOARDING_ENABLED) { + return; + } boolean alreadySeenRecentsOnboarding = Prefs.getBoolean(mContext, Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false); if (!mTaskListenerRegistered && !alreadySeenRecentsOnboarding) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index 3dd6e353c924..bfbba7c1128a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -107,7 +107,7 @@ public class ScreenPinningRequest implements View.OnClickListener { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 65037f99ab30..6fd0aa6330f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -18,7 +18,7 @@ package com.android.systemui.statusbar; import android.content.ComponentName; import android.graphics.Rect; -import android.hardware.biometrics.IBiometricDialogReceiver; +import android.hardware.biometrics.IBiometricPromptReceiver; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -160,7 +160,7 @@ public class CommandQueue extends IStatusBar.Stub { default void onRotationProposal(int rotation, boolean isValid) { } - default void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) { } + default void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) { } default void onFingerprintAuthenticated() { } default void onFingerprintHelp(String message) { } default void onFingerprintError(String error) { } @@ -513,7 +513,7 @@ public class CommandQueue extends IStatusBar.Stub { } @Override - public void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) { + public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) { synchronized (mLock) { SomeArgs args = SomeArgs.obtain(); args.arg1 = bundle; @@ -759,7 +759,7 @@ public class CommandQueue extends IStatusBar.Stub { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).showFingerprintDialog( (Bundle)((SomeArgs)msg.obj).arg1, - (IBiometricDialogReceiver)((SomeArgs)msg.obj).arg2); + (IBiometricPromptReceiver)((SomeArgs)msg.obj).arg2); } break; case MSG_FINGERPRINT_AUTHENTICATED: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 3ece2f958100..87e6608a576a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -35,6 +35,7 @@ import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.service.notification.StatusBarNotification; @@ -100,13 +101,17 @@ import java.util.function.Consumer; public class ExpandableNotificationRow extends ActivatableNotificationView implements PluginListener<NotificationMenuRowPlugin> { + private static final boolean DEBUG = false; private static final int DEFAULT_DIVIDER_ALPHA = 0x29; private static final int COLORED_DIVIDER_ALPHA = 0x7B; private static final int MENU_VIEW_INDEX = 0; private static final String TAG = "ExpandableNotifRow"; + /** + * Listener for when {@link ExpandableNotificationRow} is laid out. + */ public interface LayoutListener { - public void onLayout(); + void onLayout(); } private LayoutListener mLayoutListener; @@ -174,8 +179,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationGuts mGuts; private NotificationData.Entry mEntry; private StatusBarNotification mStatusBarNotification; - private PackageManager mCachedPackageManager; - private PackageInfo mCachedPackageInfo; + /** + * Whether or not this row represents a system notification. Note that if this is {@code null}, + * that means we were either unable to retrieve the info or have yet to retrieve the info. + */ + private Boolean mIsSystemNotification; private String mAppName; private boolean mIsHeadsUp; private boolean mLastChronometerRunning = true; @@ -271,7 +279,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public Float get(ExpandableNotificationRow object) { return object.getTranslation(); } - }; + }; private OnClickListener mOnClickListener; private boolean mHeadsupDisappearRunning; private View mChildAfterViewWhenDismissed; @@ -292,6 +300,33 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private int mNotificationColorAmbient; private NotificationViewState mNotificationViewState; + private SystemNotificationAsyncTask mSystemNotificationAsyncTask = + new SystemNotificationAsyncTask(); + + /** + * Returns whether the given {@code statusBarNotification} is a system notification. + * <b>Note</b>, this should be run in the background thread if possible as it makes multiple IPC + * calls. + */ + private static Boolean isSystemNotification( + Context context, StatusBarNotification statusBarNotification) { + PackageManager packageManager = StatusBar.getPackageManagerForUser( + context, statusBarNotification.getUser().getIdentifier()); + Boolean isSystemNotification = null; + + try { + PackageInfo packageInfo = packageManager.getPackageInfo( + statusBarNotification.getPackageName(), PackageManager.GET_SIGNATURES); + + isSystemNotification = + com.android.settingslib.Utils.isSystemPackage( + context.getResources(), packageManager, packageInfo); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "cacheIsSystemNotification: Could not find package info"); + } + return isSystemNotification; + } + @Override public boolean isGroupExpansionChanging() { if (isChildInGroup()) { @@ -383,45 +418,43 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mStatusBarNotification = entry.notification; mNotificationInflater.inflateNotificationViews(); - perhapsCachePackageInfo(); + cacheIsSystemNotification(); } /** - * Caches the package manager and info objects which are expensive to obtain. + * Caches whether or not this row contains a system notification. Note, this is only cached + * once per notification as the packageInfo can't technically change for a notification row. */ - private void perhapsCachePackageInfo() { - if (mCachedPackageInfo == null) { - mCachedPackageManager = StatusBar.getPackageManagerForUser( - mContext, mStatusBarNotification.getUser().getIdentifier()); - try { - mCachedPackageInfo = mCachedPackageManager.getPackageInfo( - mStatusBarNotification.getPackageName(), PackageManager.GET_SIGNATURES); - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "perhapsCachePackageInfo: Could not find package info"); + private void cacheIsSystemNotification() { + if (mIsSystemNotification == null) { + if (mSystemNotificationAsyncTask.getStatus() == AsyncTask.Status.PENDING) { + // Run async task once, only if it hasn't already been executed. Note this is + // executed in serial - no need to parallelize this small task. + mSystemNotificationAsyncTask.execute(); } } } /** - * Returns whether this row is considered non-blockable (e.g. it's a non-blockable system notif, - * covers multiple channels, or is in a whitelist). + * Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif + * or is in a whitelist). */ public boolean getIsNonblockable() { - boolean isNonblockable; - - isNonblockable = Dependency.get(NotificationBlockingHelperManager.class) + boolean isNonblockable = Dependency.get(NotificationBlockingHelperManager.class) .isNonblockablePackage(mStatusBarNotification.getPackageName()); - // Only bother with going through the children if the row is still blockable based on the - // number of unique channels. - if (!isNonblockable) { - isNonblockable = getNumUniqueChannels() > 1; + // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once + // again, but in-place on the main thread this time. This should rarely ever get called. + if (mIsSystemNotification == null) { + if (DEBUG) { + Log.d(TAG, "Retrieving isSystemNotification on main thread"); + } + mSystemNotificationAsyncTask.cancel(true /* mayInterruptIfRunning */); + mIsSystemNotification = isSystemNotification(mContext, mStatusBarNotification); } - // Only bother with IPC if the package is still blockable. - if (!isNonblockable && mCachedPackageManager != null && mCachedPackageInfo != null) { - if (com.android.settingslib.Utils.isSystemPackage( - mContext.getResources(), mCachedPackageManager, mCachedPackageInfo)) { + if (!isNonblockable && mIsSystemNotification != null) { + if (mIsSystemNotification) { if (mEntry.channel != null && !mEntry.channel.isBlockableSystem()) { isNonblockable = true; @@ -2828,4 +2861,21 @@ public class ExpandableNotificationRow extends ActivatableNotificationView */ boolean onClick(View v, int x, int y, MenuItem item); } + + /** + * Background task for executing IPCs to check if the notification is a system notification. The + * output is used for both the blocking helper and the notification info. + */ + private class SystemNotificationAsyncTask extends AsyncTask<Void, Void, Boolean> { + + @Override + protected Boolean doInBackground(Void... voids) { + return isSystemNotification(mContext, mStatusBarNotification); + } + + @Override + protected void onPostExecute(Boolean result) { + mIsSystemNotification = result; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index b81e9af4f692..29c2edc22f4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -26,6 +26,7 @@ import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; +import android.view.MotionEvent; import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; @@ -1631,6 +1632,42 @@ public class NotificationContentView extends FrameLayout { return null; } + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + float y = ev.getY(); + // We still want to distribute touch events to the remote input even if it's outside the + // view boundary. We're therefore manually dispatching these events to the remote view + RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType)); + if (riv != null && riv.getVisibility() == VISIBLE) { + int inputStart = mUnrestrictedContentHeight - riv.getHeight(); + if (y <= mUnrestrictedContentHeight && y >= inputStart) { + ev.offsetLocation(0, -inputStart); + return riv.dispatchTouchEvent(ev); + } + } + return super.dispatchTouchEvent(ev); + } + + /** + * Overridden to make sure touches to the reply action bar actually go through to this view + */ + @Override + public boolean pointInView(float localX, float localY, float slop) { + float top = mClipTopAmount; + float bottom = mUnrestrictedContentHeight; + return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) && + localY < (bottom + slop); + } + + private RemoteInputView getRemoteInputForView(View child) { + if (child == mExpandedChild) { + return mExpandedRemoteInput; + } else if (child == mHeadsUpChild) { + return mHeadsUpRemoteInput; + } + return null; + } + public int getExpandHeight() { int viewType = VISIBLE_TYPE_EXPANDED; if (mExpandedChild == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index a93be00ba080..81dd9e8c3d87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -23,6 +23,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.annotation.Nullable; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; @@ -34,10 +35,12 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; +import android.os.Handler; import android.os.RemoteException; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; @@ -63,10 +66,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private INotificationManager mINotificationManager; private PackageManager mPm; - private String mPkg; + private String mPackageName; private String mAppName; private int mAppUid; - private int mNumNotificationChannels; + private int mNumUniqueChannelsInRow; private NotificationChannel mSingleNotificationChannel; private int mStartingUserImportance; private int mChosenImportance; @@ -87,7 +90,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private OnClickListener mOnKeepShowing = this::closeControls; - private OnClickListener mOnStopMinNotifications = v -> { + private OnClickListener mOnStopOrMinimizeNotifications = v -> { swapContent(false); }; @@ -120,16 +123,16 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G final INotificationManager iNotificationManager, final String pkg, final NotificationChannel notificationChannel, - final int numChannels, + final int numUniqueChannelsInRow, final StatusBarNotification sbn, final CheckSaveListener checkSaveListener, final OnSettingsClickListener onSettingsClick, final OnAppSettingsClickListener onAppSettingsClick, boolean isNonblockable) throws RemoteException { - bindNotification(pm, iNotificationManager, pkg, notificationChannel, numChannels, sbn, - checkSaveListener, onSettingsClick, onAppSettingsClick, isNonblockable, - false /* isBlockingHelper */, + bindNotification(pm, iNotificationManager, pkg, notificationChannel, + numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick, + onAppSettingsClick, isNonblockable, false /* isBlockingHelper */, false /* isUserSentimentNegative */); } @@ -138,7 +141,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G INotificationManager iNotificationManager, String pkg, NotificationChannel notificationChannel, - int numChannels, + int numUniqueChannelsInRow, StatusBarNotification sbn, CheckSaveListener checkSaveListener, OnSettingsClickListener onSettingsClick, @@ -148,12 +151,12 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G boolean isUserSentimentNegative) throws RemoteException { mINotificationManager = iNotificationManager; - mPkg = pkg; - mNumNotificationChannels = numChannels; + mPackageName = pkg; + mNumUniqueChannelsInRow = numUniqueChannelsInRow; mSbn = sbn; mPm = pm; mAppSettingsClickListener = onAppSettingsClick; - mAppName = mPkg; + mAppName = mPackageName; mCheckSaveListener = checkSaveListener; mOnSettingsClickListener = onSettingsClick; mSingleNotificationChannel = notificationChannel; @@ -167,11 +170,11 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); - if (mNumNotificationChannels == 0) { + if (mNumUniqueChannelsInRow == 0) { throw new IllegalArgumentException("bindNotification requires at least one channel"); } else { // Special behavior for the Default channel if no other channels have been defined. - mIsSingleDefaultChannel = mNumNotificationChannels == 1 + mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1 && mSingleNotificationChannel.getId().equals( NotificationChannel.DEFAULT_CHANNEL_ID) && numTotalChannels == 1; @@ -187,7 +190,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G Drawable pkgicon = null; ApplicationInfo info; try { - info = mPm.getApplicationInfo(mPkg, + info = mPm.getApplicationInfo( + mPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_UNAWARE @@ -208,7 +212,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) { final NotificationChannelGroup notificationChannelGroup = mINotificationManager.getNotificationChannelGroupForPackage( - mSingleNotificationChannel.getGroup(), mPkg, mAppUid); + mSingleNotificationChannel.getGroup(), mPackageName, mAppUid); if (notificationChannelGroup != null) { groupName = notificationChannelGroup.getName(); } @@ -232,7 +236,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G settingsButton.setOnClickListener( (View view) -> { mOnSettingsClickListener.onClick(view, - mNumNotificationChannels > 1 ? null : mSingleNotificationChannel, + mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel, appUidF); }); } else { @@ -248,7 +252,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } else { if (mNegativeUserSentiment) { blockPrompt.setText(R.string.inline_blocking_helper); - } else if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) { + } else if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) { blockPrompt.setText(R.string.inline_keep_showing_app); } else { blockPrompt.setText(R.string.inline_keep_showing); @@ -258,7 +262,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void bindName() { final TextView channelName = findViewById(R.id.channel_name); - if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) { + if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) { channelName.setVisibility(View.GONE); } else { channelName.setText(mSingleNotificationChannel.getName()); @@ -270,19 +274,26 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private void saveImportance() { - if (mIsNonblockable) { - return; + if (!mIsNonblockable) { + if (mCheckSaveListener != null) { + mCheckSaveListener.checkSave(this::updateImportance, mSbn); + } else { + updateImportance(); + } } + } + + /** + * Commits the updated importance values on the background thread. + */ + private void updateImportance() { MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, mChosenImportance - mStartingUserImportance); - mSingleNotificationChannel.setImportance(mChosenImportance); - mSingleNotificationChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); - try { - mINotificationManager.updateNotificationChannelForPackage( - mPkg, mAppUid, mSingleNotificationChannel); - } catch (RemoteException e) { - // :( - } + + Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); + bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, + mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null, + mStartingUserImportance, mChosenImportance)); } private void bindButtons() { @@ -292,9 +303,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G View minimize = findViewById(R.id.minimize); findViewById(R.id.undo).setOnClickListener(mOnUndo); - block.setOnClickListener(mOnStopMinNotifications); + block.setOnClickListener(mOnStopOrMinimizeNotifications); keep.setOnClickListener(mOnKeepShowing); - minimize.setOnClickListener(mOnStopMinNotifications); + minimize.setOnClickListener(mOnStopOrMinimizeNotifications); if (mIsNonblockable) { keep.setText(R.string.notification_done); @@ -308,15 +319,15 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G minimize.setVisibility(GONE); } - // Set up app settings link + // Set up app settings link (i.e. Customize) TextView settingsLinkView = findViewById(R.id.app_settings); - Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel, + Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, mSingleNotificationChannel, mSbn.getId(), mSbn.getTag()); - if (settingsIntent != null + if (!mIsForBlockingHelper + && settingsIntent != null && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { settingsLinkView.setVisibility(VISIBLE); - settingsLinkView.setText(mContext.getString(R.string.notification_app_settings, - mSbn.getNotification().getSettingsText())); + settingsLinkView.setText(mContext.getString(R.string.notification_app_settings)); settingsLinkView.setOnClickListener((View view) -> { mAppSettingsClickListener.onClick(view, settingsIntent); }); @@ -415,12 +426,25 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G return intent; } + /** + * Closes the controls and commits the updated importance values (indirectly). If this view is + * being used to show the blocking helper, this will immediately dismiss the blocking helper and + * commit the updated importance. + * + * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the + * user does not have the ability to undo the action anymore. See {@link #swapContent(boolean)} + * for where undo is handled. + */ @VisibleForTesting void closeControls(View v) { if (mIsForBlockingHelper) { NotificationBlockingHelperManager manager = Dependency.get(NotificationBlockingHelperManager.class); manager.dismissCurrentBlockingHelper(); + + // Since this won't get a callback via gutsContainer.closeControls, save the new + // importance values immediately. + saveImportance(); } else { int[] parentLoc = new int[2]; int[] targetLoc = new int[2]; @@ -454,11 +478,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G // Save regardless of the importance so we can lock the importance field if the user wants // to keep getting notifications if (save) { - if (mCheckSaveListener != null) { - mCheckSaveListener.checkSave(this::saveImportance, mSbn); - } else { - saveImportance(); - } + saveImportance(); } return false; } @@ -467,4 +487,48 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G public int getActualHeight() { return getHeight(); } + + /** + * Runnable to either update the given channel (with a new importance value) or, if no channel + * is provided, update notifications enabled state for the package. + */ + private static class UpdateImportanceRunnable implements Runnable { + private final INotificationManager mINotificationManager; + private final String mPackageName; + private final int mAppUid; + private final @Nullable NotificationChannel mChannelToUpdate; + private final int mCurrentImportance; + private final int mNewImportance; + + + public UpdateImportanceRunnable(INotificationManager notificationManager, + String packageName, int appUid, @Nullable NotificationChannel channelToUpdate, + int currentImportance, int newImportance) { + mINotificationManager = notificationManager; + mPackageName = packageName; + mAppUid = appUid; + mChannelToUpdate = channelToUpdate; + mCurrentImportance = currentImportance; + mNewImportance = newImportance; + } + + @Override + public void run() { + try { + if (mChannelToUpdate != null) { + mChannelToUpdate.setImportance(mNewImportance); + mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); + mINotificationManager.updateNotificationChannelForPackage( + mPackageName, mAppUid, mChannelToUpdate); + } else { + // For notifications with more than one channel, update notification enabled + // state. If the importance was lowered, we disable notifications. + mINotificationManager.setNotificationsEnabledForPackage( + mPackageName, mAppUid, mNewImportance >= mCurrentImportance); + } + } catch (RemoteException e) { + Log.e(TAG, "Unable to update notification importance", e); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index ccabb79e229b..e24bf6762b4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -47,7 +47,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.List; /** * Handles keeping track of the current user, profiles, and various things related to hiding @@ -352,7 +351,8 @@ public class NotificationLockscreenUserManager implements Dumpable { final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); - final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle); + final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, + DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); final boolean allowed = allowedByUser && allowedByDpm; mUsersAllowingPrivateNotifications.append(userHandle, allowed); return allowed; @@ -361,13 +361,13 @@ public class NotificationLockscreenUserManager implements Dumpable { return mUsersAllowingPrivateNotifications.get(userHandle); } - private boolean adminAllowsUnredactedNotifications(int userHandle) { + private boolean adminAllowsKeyguardFeature(int userHandle, int feature) { if (userHandle == UserHandle.USER_ALL) { return true; } - final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, - userHandle); - return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0; + final int dpmFlags = + mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, userHandle); + return (dpmFlags & feature) == 0; } /** @@ -389,14 +389,17 @@ public class NotificationLockscreenUserManager implements Dumpable { * "public" (secure & locked) mode? */ private boolean userAllowsNotificationsInPublic(int userHandle) { - if (isCurrentProfile(userHandle)) { + if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) { return true; } if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { - final boolean allowed = 0 != Settings.Secure.getIntForUser( + final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); + final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, + DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); + final boolean allowed = allowedByUser && allowedByDpm; mUsersAllowingNotifications.append(userHandle, allowed); return allowed; } @@ -428,7 +431,6 @@ public class NotificationLockscreenUserManager implements Dumpable { Notification.VISIBILITY_PRIVATE; } - private void updateCurrentProfilesCache() { synchronized (mCurrentProfiles) { mCurrentProfiles.clear(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 852239a2143b..abc261e6bbf6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -172,6 +172,14 @@ public class NotificationMediaManager implements Dumpable { } } + if (mediaNotification != null) { + mMediaNotificationKey = mediaNotification.notification.getKey(); + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" + + mMediaNotificationKey + " controller=" + mMediaController); + } + } + if (controller != null && !sameSessions(mMediaController, controller)) { // We have a new media session clearCurrentMediaNotification(); @@ -183,13 +191,6 @@ public class NotificationMediaManager implements Dumpable { + mMediaMetadata); } - if (mediaNotification != null) { - mMediaNotificationKey = mediaNotification.notification.getKey(); - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" - + mMediaNotificationKey + " controller=" + mMediaController); - } - } metaDataChanged = true; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 0112661cf9fc..6364f5b63e88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar; import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState.NO_VALUE; -import static com.android.systemui.statusbar.phone.NotificationIconContainer.OVERFLOW_EARLY_AMOUNT; import android.content.Context; import android.content.res.Configuration; @@ -26,6 +25,7 @@ import android.graphics.Rect; import android.os.SystemProperties; import android.util.AttributeSet; import android.util.Log; +import android.util.MathUtils; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -58,6 +58,8 @@ public class NotificationShelf extends ActivatableNotificationView implements = SystemProperties.getBoolean("debug.icon_scroll_animations", true); private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag; private static final String TAG = "NotificationShelf"; + private static final long SHELF_IN_TRANSLATION_DURATION = 220; + private ViewInvertHelper mViewInvertHelper; private boolean mDark; private NotificationIconContainer mShelfIcons; @@ -65,6 +67,7 @@ public class NotificationShelf extends ActivatableNotificationView implements private int[] mTmp = new int[2]; private boolean mHideBackground; private int mIconAppearTopPadding; + private int mShelfAppearTranslation; private int mStatusBarHeight; private int mStatusBarPaddingStart; private AmbientState mAmbientState; @@ -120,6 +123,7 @@ public class NotificationShelf extends ActivatableNotificationView implements mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height); mStatusBarPaddingStart = res.getDimensionPixelOffset(R.dimen.status_bar_padding_start); mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height); + mShelfAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation); ViewGroup.LayoutParams layoutParams = getLayoutParams(); layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height); @@ -151,6 +155,18 @@ public class NotificationShelf extends ActivatableNotificationView implements updateInteractiveness(); } + public void fadeInTranslating() { + float translation = mShelfIcons.getTranslationY(); + mShelfIcons.setTranslationY(translation + mShelfAppearTranslation); + mShelfIcons.setAlpha(0); + mShelfIcons.animate() + .alpha(1) + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) + .translationY(translation) + .setDuration(SHELF_IN_TRANSLATION_DURATION) + .start(); + } + @Override protected View getContentView() { return mShelfIcons; @@ -175,12 +191,14 @@ public class NotificationShelf extends ActivatableNotificationView implements float viewEnd = lastViewState.yTranslation + lastViewState.height; mShelfState.copyFrom(lastViewState); mShelfState.height = getIntrinsicHeight(); - mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height, + + float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height, getFullyClosedTranslation()); + float darkTranslation = mAmbientState.getDarkTopPadding(); + float yRatio = mAmbientState.hasPulsingNotifications() ? + 0 : mAmbientState.getDarkAmount(); + mShelfState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio); mShelfState.zTranslation = ambientState.getBaseZHeight(); - if (mAmbientState.isDark() && !mAmbientState.hasPulsingNotifications()) { - mShelfState.yTranslation = mAmbientState.getDarkTopPadding(); - } float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation()) / (getIntrinsicHeight() * 2); openedAmount = Math.min(1.0f, openedAmount); @@ -555,7 +573,9 @@ public class NotificationShelf extends ActivatableNotificationView implements iconState.translateContent = false; } float transitionAmount; - if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount + if (mAmbientState.getDarkAmount() > 0 && !row.isInShelf()) { + transitionAmount = mAmbientState.isFullyDark() ? 1 : 0; + } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount || iconState.useLinearTransitionAmount) { transitionAmount = iconTransitionAmount; } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index fd3a9d5e2bd4..1637849d79ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -323,8 +323,7 @@ public class NotificationViewHierarchyManager { boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry .notification); if (suppressedSummary - || (mLockscreenUserManager.isLockscreenPublicMode(userId) - && !mLockscreenUserManager.shouldShowLockscreenNotifications()) + || mLockscreenUserManager.shouldHideNotifications(userId) || (isLocked && !showOnKeyguard)) { entry.row.setVisibility(View.GONE); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index b7620f30d742..51b42395e369 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar; import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; +import static com.android.systemui.statusbar.policy.DarkIconDispatcher.isInArea; import android.content.Context; import android.content.res.ColorStateList; @@ -141,12 +142,14 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D if (mState.strengthId != state.strengthId) { mMobileDrawable.setLevel(state.strengthId); } - if (mState.typeId != state.typeId && state.typeId != 0) { - mMobileType.setContentDescription(state.typeContentDescription); - mMobileType.setImageResource(state.typeId); - mMobileType.setVisibility(View.VISIBLE); - } else { - mMobileType.setVisibility(View.GONE); + if (mState.typeId != state.typeId) { + if (state.typeId != 0) { + mMobileType.setContentDescription(state.typeContentDescription); + mMobileType.setImageResource(state.typeId); + mMobileType.setVisibility(View.VISIBLE); + } else { + mMobileType.setVisibility(View.GONE); + } } mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE); @@ -161,6 +164,9 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D @Override public void onDarkChanged(Rect area, float darkIntensity, int tint) { + if (!isInArea(area, this)) { + return; + } mMobileDrawable.setDarkIntensity(darkIntensity); ColorStateList color = ColorStateList.valueOf(getTint(area, this, tint)); mIn.setImageTintList(color); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java index afd373ed6321..62cd16fca3bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar; import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; +import static com.android.systemui.statusbar.policy.DarkIconDispatcher.isInArea; import android.content.Context; import android.content.res.ColorStateList; @@ -175,6 +176,9 @@ public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements Dar @Override public void onDarkChanged(Rect area, float darkIntensity, int tint) { + if (!isInArea(area, this)) { + return; + } mDarkIntensity = darkIntensity; Drawable d = mWifiIcon.getDrawable(); if (d instanceof NeutralGoodDrawable) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java index 5f3e2e358306..7285db659f3e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java @@ -42,6 +42,11 @@ public class CarFacetButton extends LinearLayout { /** App packages that are allowed to be used with this widget */ private String[] mFacetPackages; private int mIconResourceId; + /** + * If defined in the xml this will be the icon that's rendered when the button is marked as + * selected + */ + private int mSelectedIconResourceId; private boolean mUseMoreIcon = true; private float mSelectedAlpha = 1f; private float mUnselectedAlpha = 1f; @@ -112,10 +117,9 @@ public class CarFacetButton extends LinearLayout { mIcon.setClickable(false); mIcon.setAlpha(mUnselectedAlpha); mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0); - if (mIconResourceId == 0) { - throw new RuntimeException("specified icon resource was not found and is required"); - } mIcon.setImageResource(mIconResourceId); + mSelectedIconResourceId = styledAttributes.getResourceId( + R.styleable.CarFacetButton_selectedIcon, mIconResourceId); mMoreIcon = findViewById(R.id.car_nav_button_more_icon); mMoreIcon.setClickable(false); @@ -161,22 +165,10 @@ public class CarFacetButton extends LinearLayout { */ public void setSelected(boolean selected, boolean showMoreIcon) { mSelected = selected; - if (selected) { - if (mUseMoreIcon) { - mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE); - } - mIcon.setAlpha(mSelectedAlpha); - } else { - mMoreIcon.setVisibility(GONE); - mIcon.setAlpha(mUnselectedAlpha); - } - } - - public void setIcon(Drawable d) { - if (d != null) { - mIcon.setImageDrawable(d); - } else { - mIcon.setImageResource(mIconResourceId); + mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha); + mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId); + if (mUseMoreIcon) { + mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java index e73b1736f33a..b2cef16277f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java @@ -25,7 +25,9 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.keyguard.AlphaOptimizedImageButton; +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.statusbar.phone.StatusBarIconController; /** * A custom navigation bar for the automotive use case. @@ -52,6 +54,17 @@ class CarNavigationBarView extends LinearLayout { if (mNotificationsButton != null) { mNotificationsButton.setOnClickListener(this::onNotificationsClick); } + View mStatusIcons = findViewById(R.id.statusIcons); + if (mStatusIcons != null) { + // Attach the controllers for Status icons such as wifi and bluetooth if the standard + // container is in the view. + StatusBarIconController.DarkIconManager mDarkIconManager = + new StatusBarIconController.DarkIconManager( + mStatusIcons.findViewById(R.id.statusIcons)); + mDarkIconManager.setShouldLog(true); + Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); + } + } void setStatusBar(CarStatusBar carStatusBar) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java index 0cdaec1432c7..ec243fe98710 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.util.AttributeSet; +import android.util.Log; import android.widget.ImageView; import com.android.systemui.R; @@ -17,23 +18,34 @@ import java.net.URISyntaxException; */ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton { - private static final float SELECTED_ALPHA = 1; - private static final float UNSELECTED_ALPHA = 0.7f; - + private static final String TAG = "CarNavigationButton"; private Context mContext; - private String mIntent = null; - private String mLongIntent = null; - private boolean mBroadcastIntent = false; + private String mIntent; + private String mLongIntent; + private boolean mBroadcastIntent; private boolean mSelected = false; + private float mSelectedAlpha = 1f; + private float mUnselectedAlpha = 1f; + private int mSelectedIconResourceId; + private int mIconResourceId; public CarNavigationButton(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarNavigationButton); + TypedArray typedArray = context.obtainStyledAttributes( + attrs, R.styleable.CarNavigationButton); mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent); mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent); mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false); + mSelectedAlpha = typedArray.getFloat( + R.styleable.CarNavigationButton_selectedAlpha, mSelectedAlpha); + mUnselectedAlpha = typedArray.getFloat( + R.styleable.CarNavigationButton_unselectedAlpha, mUnselectedAlpha); + mIconResourceId = typedArray.getResourceId( + com.android.internal.R.styleable.ImageView_src, 0); + mSelectedIconResourceId = typedArray.getResourceId( + R.styleable.CarNavigationButton_selectedIcon, mIconResourceId); } @@ -45,17 +57,20 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag public void onFinishInflate() { super.onFinishInflate(); setScaleType(ImageView.ScaleType.CENTER); - setAlpha(UNSELECTED_ALPHA); + setAlpha(mUnselectedAlpha); try { if (mIntent != null) { final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); setOnClickListener(v -> { - if (mBroadcastIntent) { - mContext.sendBroadcast(intent); - return; + try { + if (mBroadcastIntent) { + mContext.sendBroadcast(intent); + return; + } + mContext.startActivity(intent); + } catch (Exception e) { + Log.e(TAG, "Failed to launch intent", e); } - mContext.startActivity(intent); }); } } catch (URISyntaxException e) { @@ -65,9 +80,13 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag try { if (mLongIntent != null) { final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); setOnLongClickListener(v -> { - mContext.startActivity(intent); + try { + mContext.startActivity(intent); + } catch (Exception e) { + Log.e(TAG, "Failed to launch intent", e); + } + // consume event either way return true; }); } @@ -82,6 +101,7 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag public void setSelected(boolean selected) { super.setSelected(selected); mSelected = selected; - setAlpha(mSelected ? SELECTED_ALPHA : UNSELECTED_ALPHA); + setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha); + setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 3fb11376f6f4..008794c1c05a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -418,8 +418,7 @@ public class CarStatusBar extends StatusBar implements Dependency.get(UserSwitcherController.class); if (userSwitcherController.useFullscreenUserSwitcher()) { mFullscreenUserSwitcher = new FullscreenUserSwitcher(this, - userSwitcherController, - mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub)); + mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext); } else { super.createUserSwitcher(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java index bc353f2dc0f9..fb525f736f26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java @@ -18,14 +18,15 @@ package com.android.systemui.statusbar.car; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.content.res.Resources; +import android.content.Context; import android.view.View; import android.view.ViewStub; import android.widget.ProgressBar; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; import com.android.systemui.R; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.UserSwitcherController; /** * Manages the fullscreen user switcher. @@ -33,36 +34,25 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; public class FullscreenUserSwitcher { private final View mContainer; private final View mParent; - private final UserGridView mUserGridView; - private final UserSwitcherController mUserSwitcherController; + private final UserGridRecyclerView mUserGridView; private final ProgressBar mSwitchingUsers; private final int mShortAnimDuration; private boolean mShowing; - public FullscreenUserSwitcher(StatusBar statusBar, - UserSwitcherController userSwitcherController, - ViewStub containerStub) { - mUserSwitcherController = userSwitcherController; + public FullscreenUserSwitcher(StatusBar statusBar, ViewStub containerStub, Context context) { mParent = containerStub.inflate(); mContainer = mParent.findViewById(R.id.container); mUserGridView = mContainer.findViewById(R.id.user_grid); - mUserGridView.init(statusBar, mUserSwitcherController, true /* overrideAlpha */); - mUserGridView.setUserSelectionListener(record -> { - if (!record.isCurrent) { - toggleSwitchInProgress(true); - } - }); + mUserGridView.setStatusBar(statusBar); + GridLayoutManager layoutManager = new GridLayoutManager(context, + context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col)); + mUserGridView.setLayoutManager(layoutManager); + mUserGridView.buildAdapter(); + mUserGridView.setUserSelectionListener(record -> toggleSwitchInProgress(true)); - PageIndicator pageIndicator = mContainer.findViewById(R.id.user_switcher_page_indicator); - pageIndicator.setupWithViewPager(mUserGridView); - - Resources res = mContainer.getResources(); - mShortAnimDuration = res.getInteger(android.R.integer.config_shortAnimTime); - - mContainer.findViewById(R.id.start_driving).setOnClickListener(v -> { - automaticallySelectUser(); - }); + mShortAnimDuration = mContainer.getResources() + .getInteger(android.R.integer.config_shortAnimTime); mSwitchingUsers = mParent.findViewById(R.id.switching_users); } @@ -115,10 +105,4 @@ public class FullscreenUserSwitcher { toggleSwitchInProgress(false); mParent.setVisibility(View.GONE); } - - private void automaticallySelectUser() { - // TODO: Switch according to some policy. This implementation just tries to drop the - // keyguard for the current user. - mUserGridView.showOfflineAuthUi(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java new file mode 100644 index 000000000000..e09a36030cd7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2018 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.car; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.drawable.GradientDrawable; +import android.os.AsyncTask; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.settingslib.users.UserManagerHelper; +import com.android.systemui.R; +import com.android.systemui.qs.car.CarQSFragment; +import com.android.systemui.statusbar.phone.StatusBar; + +import java.util.ArrayList; +import java.util.List; + +/** + * Displays a GridLayout with icons for the users in the system to allow switching between users. + * One of the uses of this is for the lock screen in auto. + */ +public class UserGridRecyclerView extends RecyclerView implements + UserManagerHelper.OnUsersUpdateListener { + + private StatusBar mStatusBar; + private UserSelectionListener mUserSelectionListener; + private UserAdapter mAdapter; + private UserManagerHelper mUserManagerHelper; + private Context mContext; + + public UserGridRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + super.setHasFixedSize(true); + mContext = context; + mUserManagerHelper = new UserManagerHelper(mContext); + } + + /** + * Register listener for any update to the users + */ + @Override + public void onFinishInflate() { + mUserManagerHelper.registerOnUsersUpdateListener(this); + } + + /** + * Unregisters listener checking for any change to the users + */ + @Override + public void onDetachedFromWindow() { + mUserManagerHelper.unregisterOnUsersUpdateListener(); + } + + /** + * Initializes the adapter that populates the grid layout + * + * @return the adapter + */ + public void buildAdapter() { + List<UserRecord> userRecords = createUserRecords(mUserManagerHelper + .getAllUsers()); + mAdapter = new UserAdapter(mContext, userRecords); + super.setAdapter(mAdapter); + } + + public void setStatusBar(@Nullable StatusBar statusBar) { + mStatusBar = statusBar; + } + + private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) { + List<UserRecord> userRecords = new ArrayList<>(); + for (UserInfo userInfo : userInfoList) { + boolean isCurrent = false; + if (ActivityManager.getCurrentUser() == userInfo.id) { + isCurrent = true; + } + UserRecord record = new UserRecord(userInfo, false /* isGuest */, + false /* isAddUser */, isCurrent); + userRecords.add(record); + } + + // Add guest user record if the current user is not a guest + if (!mUserManagerHelper.isGuestUser()) { + userRecords.add(addGuestUserRecord()); + } + + // Add add user record if the current user can add users + if (mUserManagerHelper.canAddUsers()) { + userRecords.add(addUserRecord()); + } + + return userRecords; + } + + /** + * Create guest user record + */ + private UserRecord addGuestUserRecord() { + UserInfo userInfo = new UserInfo(); + userInfo.name = mContext.getString(R.string.car_guest); + return new UserRecord(userInfo, true /* isGuest */, + false /* isAddUser */, false /* isCurrent */); + } + + /** + * Create add user record + */ + private UserRecord addUserRecord() { + UserInfo userInfo = new UserInfo(); + userInfo.name = mContext.getString(R.string.car_add_user); + return new UserRecord(userInfo, false /* isGuest */, + true /* isAddUser */, false /* isCurrent */); + } + + public void onUserSwitched(int newUserId) { + // Bring up security view after user switch is completed. + post(this::showOfflineAuthUi); + } + + public void setUserSelectionListener(UserSelectionListener userSelectionListener) { + mUserSelectionListener = userSelectionListener; + } + + void showOfflineAuthUi() { + // TODO: Show keyguard UI in-place. + if (mStatusBar != null) { + mStatusBar.executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */, + true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */); + } + } + + @Override + public void onUsersUpdate() { + mAdapter.clearUsers(); + mAdapter.updateUsers(createUserRecords(mUserManagerHelper.getAllUsers())); + mAdapter.notifyDataSetChanged(); + } + + /** + * Adapter to populate the grid layout with the available user profiles + */ + public final class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserAdapterViewHolder> { + + private final Context mContext; + private List<UserRecord> mUsers; + private final int mPodImageAvatarWidth; + private final int mPodImageAvatarHeight; + private final Resources mRes; + private final String mGuestName; + private final String mNewUserName; + + public UserAdapter(Context context, List<UserRecord> users) { + mRes = context.getResources(); + mContext = context; + updateUsers(users); + mPodImageAvatarWidth = mRes.getDimensionPixelSize( + R.dimen.car_fullscreen_user_pod_image_avatar_width); + mPodImageAvatarHeight = mRes.getDimensionPixelSize( + R.dimen.car_fullscreen_user_pod_image_avatar_height); + mGuestName = mRes.getString(R.string.car_guest); + mNewUserName = mRes.getString(R.string.car_new_user); + } + + public void clearUsers() { + mUsers.clear(); + } + + public void updateUsers(List<UserRecord> users) { + mUsers = users; + } + + @Override + public UserAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(mContext) + .inflate(R.layout.car_fullscreen_user_pod, parent, false); + view.setAlpha(1f); + view.bringToFront(); + return new UserAdapterViewHolder(view); + } + + @Override + public void onBindViewHolder(UserAdapterViewHolder holder, int position) { + UserRecord userRecord = mUsers.get(position); + holder.mUserAvatarImageView.setImageBitmap(getDefaultUserIcon(userRecord)); + holder.mUserNameTextView.setText(userRecord.mInfo.name); + holder.mView.setOnClickListener(v -> { + if (userRecord == null) { + return; + } + + // Notify the listener which user was selected + if (mUserSelectionListener != null) { + mUserSelectionListener.onUserSelected(userRecord); + } + + // If the user selects Guest, switch to Guest profile + if (userRecord.mIsGuest) { + mUserManagerHelper.switchToGuest(mGuestName); + return; + } + + // If the user wants to add a user, start task to add new user + if (userRecord.mIsAddUser) { + new AddNewUserTask().execute(mNewUserName); + return; + } + + // If the user doesn't want to be a guest or add a user, switch to the user selected + mUserManagerHelper.switchToUser(userRecord.mInfo); + }); + + } + + private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> { + + @Override + protected UserInfo doInBackground(String... userNames) { + return mUserManagerHelper.createNewUser(userNames[0]); + } + + @Override + protected void onPreExecute() { + } + + @Override + protected void onPostExecute(UserInfo user) { + if (user != null) { + mUserManagerHelper.switchToUser(user); + } + } + } + + @Override + public int getItemCount() { + return mUsers.size(); + } + + /** + * Returns the default user icon. This icon is a circle with a letter in it. The letter is + * the first character in the username. + * + * @param record the profile of the user for which the icon should be created + */ + private Bitmap getDefaultUserIcon(UserRecord record) { + CharSequence displayText; + boolean isAddUserText = false; + if (record.mIsAddUser) { + displayText = "+"; + isAddUserText = true; + } else { + displayText = record.mInfo.name.subSequence(0, 1); + } + Bitmap out = Bitmap.createBitmap(mPodImageAvatarWidth, mPodImageAvatarHeight, + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(out); + + // Draw the circle background. + GradientDrawable shape = new GradientDrawable(); + shape.setShape(GradientDrawable.RADIAL_GRADIENT); + shape.setGradientRadius(1.0f); + shape.setColor(mContext.getColor(R.color.car_user_switcher_no_user_image_bgcolor)); + shape.setBounds(0, 0, mPodImageAvatarWidth, mPodImageAvatarHeight); + shape.draw(canvas); + + // Draw the letter in the center. + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(mContext.getColor(R.color.car_user_switcher_no_user_image_fgcolor)); + paint.setTextAlign(Align.CENTER); + if (isAddUserText) { + paint.setTextSize(mRes.getDimensionPixelSize( + R.dimen.car_touch_target_size)); + } else { + paint.setTextSize(mRes.getDimensionPixelSize( + R.dimen.car_fullscreen_user_pod_icon_text_size)); + } + + Paint.FontMetricsInt metrics = paint.getFontMetricsInt(); + // The Y coordinate is measured by taking half the height of the pod, but that would + // draw the character putting the bottom of the font in the middle of the pod. To + // correct this, half the difference between the top and bottom distance metrics of the + // font gives the offset of the font. Bottom is a positive value, top is negative, so + // the different is actually a sum. The "half" operation is then factored out. + canvas.drawText(displayText.toString(), mPodImageAvatarWidth / 2, + (mPodImageAvatarHeight - (metrics.bottom + metrics.top)) / 2, paint); + + return out; + } + + public class UserAdapterViewHolder extends RecyclerView.ViewHolder { + + public ImageView mUserAvatarImageView; + public TextView mUserNameTextView; + public View mView; + + public UserAdapterViewHolder(View view) { + super(view); + mView = view; + mUserAvatarImageView = (ImageView) view.findViewById(R.id.user_avatar); + mUserNameTextView = (TextView) view.findViewById(R.id.user_name); + } + } + } + + /** + * Object wrapper class for the userInfo. Use it to distinguish if a profile is a + * guest profile, add user profile, or a current user. + */ + public static final class UserRecord { + + public final UserInfo mInfo; + public final boolean mIsGuest; + public final boolean mIsAddUser; + public final boolean mIsCurrent; + + public UserRecord(UserInfo userInfo, boolean isGuest, boolean isAddUser, + boolean isCurrent) { + mInfo = userInfo; + mIsGuest = isGuest; + mIsAddUser = isAddUser; + mIsCurrent = isCurrent; + } + } + + /** + * Listener used to notify when a user has been selected + */ + interface UserSelectionListener { + + void onUserSelected(UserRecord record); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java deleted file mode 100644 index 1bd820db3f7c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * 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.car; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.systemui.Dependency; -import com.android.systemui.R; -import com.android.systemui.qs.car.CarQSFragment; -import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.UserInfoController; -import com.android.systemui.statusbar.policy.UserSwitcherController; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Vector; - -/** - * Displays a ViewPager with icons for the users in the system to allow switching between users. - * One of the uses of this is for the lock screen in auto. - */ -public class UserGridView extends ViewPager implements - UserInfoController.OnUserInfoChangedListener { - private StatusBar mStatusBar; - private UserSwitcherController mUserSwitcherController; - private Adapter mAdapter; - private UserSelectionListener mUserSelectionListener; - private UserInfoController mUserInfoController; - private Vector mUserContainers; - private int mContainerWidth; - private boolean mOverrideAlpha; - private CarQSFragment.UserSwitchCallback mUserSwitchCallback; - - public UserGridView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public void init(StatusBar statusBar, UserSwitcherController userSwitcherController, - boolean overrideAlpha) { - mStatusBar = statusBar; - mUserSwitcherController = userSwitcherController; - mAdapter = new Adapter(mUserSwitcherController); - mUserInfoController = Dependency.get(UserInfoController.class); - mOverrideAlpha = overrideAlpha; - // Whenever the container width changes, the containers must be refreshed. Instead of - // doing an initial refreshContainers() to populate the containers, this listener will - // refresh them on layout change because that affects how the users are split into - // containers. Furthermore, at this point, the container width is unknown, so - // refreshContainers() cannot populate any containers. - addOnLayoutChangeListener( - (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - int newWidth = Math.max(left - right, right - left); - if (mContainerWidth != newWidth) { - mContainerWidth = newWidth; - refreshContainers(); - } - }); - } - - private void refreshContainers() { - mUserContainers = new Vector(); - - Context context = getContext(); - LayoutInflater inflater = LayoutInflater.from(context); - - for (int i = 0; i < mAdapter.getCount(); i++) { - ViewGroup pods = (ViewGroup) inflater.inflate( - R.layout.car_fullscreen_user_pod_container, null); - - int iconsPerPage = mAdapter.getIconsPerPage(); - int limit = Math.min(mUserSwitcherController.getUsers().size(), (i + 1) * iconsPerPage); - for (int j = i * iconsPerPage; j < limit; j++) { - View v = mAdapter.makeUserPod(inflater, context, j, pods); - if (mOverrideAlpha) { - v.setAlpha(1f); - } - pods.addView(v); - // This is hacky, but the dividers on the pod container LinearLayout don't seem - // to work for whatever reason. Instead, set a right margin on the pod if it's not - // the right-most pod and there is more than one pod in the container. - if (i < limit - 1 && limit > 1) { - ViewGroup.MarginLayoutParams params = - (ViewGroup.MarginLayoutParams) v.getLayoutParams(); - params.setMargins(0, 0, getResources().getDimensionPixelSize( - R.dimen.car_fullscreen_user_pod_margin_between), 0); - v.setLayoutParams(params); - } - } - mUserContainers.add(pods); - } - - mAdapter = new Adapter(mUserSwitcherController); - setAdapter(mAdapter); - } - - @Override - public void onUserInfoChanged(String name, Drawable picture, String userAccount) { - refreshContainers(); - } - - public void setUserSwitchCallback(CarQSFragment.UserSwitchCallback callback) { - mUserSwitchCallback = callback; - } - - public void onUserSwitched(int newUserId) { - // Bring up security view after user switch is completed. - post(this::showOfflineAuthUi); - } - - public void setUserSelectionListener(UserSelectionListener userSelectionListener) { - mUserSelectionListener = userSelectionListener; - } - - public void setListening(boolean listening) { - if (listening) { - mUserInfoController.addCallback(this); - } else { - mUserInfoController.removeCallback(this); - } - } - - void showOfflineAuthUi() { - // TODO: Show keyguard UI in-place. - mStatusBar.executeRunnableDismissingKeyguard(null, null, true, true, true); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // Wrap content doesn't work in ViewPagers, so simulate the behavior in code. - int height = 0; - if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { - height = MeasureSpec.getSize(heightMeasureSpec); - } else { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - child.measure(widthMeasureSpec, - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - height = Math.max(child.getMeasuredHeight(), height); - } - - // Respect the AT_MOST request from parent. - if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { - height = Math.min(MeasureSpec.getSize(heightMeasureSpec), height); - } - } - heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - /** - * This is a ViewPager.PagerAdapter which deletegates the work to a - * UserSwitcherController.BaseUserAdapter. Java doesn't support multiple inheritance so we have - * to use composition instead to achieve the same goal since both the base classes are abstract - * classes and not interfaces. - */ - private final class Adapter extends PagerAdapter { - private final int mPodWidth; - private final int mPodMarginBetween; - private final int mPodImageAvatarWidth; - private final int mPodImageAvatarHeight; - - private final WrappedBaseUserAdapter mUserAdapter; - - public Adapter(UserSwitcherController controller) { - super(); - mUserAdapter = new WrappedBaseUserAdapter(controller, this); - - Resources res = getResources(); - mPodWidth = res.getDimensionPixelSize(R.dimen.car_fullscreen_user_pod_width); - mPodMarginBetween = res.getDimensionPixelSize( - R.dimen.car_fullscreen_user_pod_margin_between); - mPodImageAvatarWidth = res.getDimensionPixelSize( - R.dimen.car_fullscreen_user_pod_image_avatar_width); - mPodImageAvatarHeight = res.getDimensionPixelSize( - R.dimen.car_fullscreen_user_pod_image_avatar_height); - } - - @Override - public void destroyItem(ViewGroup container, int position, Object object) { - container.removeView((View) object); - } - - private int getIconsPerPage() { - // We need to know how many pods we need in this page. Each pod has its own width and - // a margin between them. We can then divide the measured width of the parent by the - // sum of pod width and margin to get the number of pods that will completely fit. - // There is one less margin than the number of pods (eg. for 5 pods, there are 4 - // margins), so need to add the margin to the measured width to account for that. - return (mContainerWidth + mPodMarginBetween) / - (mPodWidth + mPodMarginBetween); - } - - @Override - public void finishUpdate(ViewGroup container) { - if (mUserSwitchCallback != null) { - mUserSwitchCallback.resetShowing(); - } - } - - @Override - public Object instantiateItem(ViewGroup container, int position) { - if (position < mUserContainers.size()) { - container.addView((View) mUserContainers.get(position)); - return mUserContainers.get(position); - } else { - return null; - } - } - - /** - * Returns the default user icon. This icon is a circle with a letter in it. The letter is - * the first character in the username. - * - * @param userName the username of the user for which the icon is to be created - */ - private Bitmap getDefaultUserIcon(CharSequence userName) { - CharSequence displayText = userName.subSequence(0, 1); - Bitmap out = Bitmap.createBitmap(mPodImageAvatarWidth, mPodImageAvatarHeight, - Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(out); - - // Draw the circle background. - GradientDrawable shape = new GradientDrawable(); - shape.setShape(GradientDrawable.RADIAL_GRADIENT); - shape.setGradientRadius(1.0f); - shape.setColor(getContext().getColor(R.color.car_user_switcher_no_user_image_bgcolor)); - shape.setBounds(0, 0, mPodImageAvatarWidth, mPodImageAvatarHeight); - shape.draw(canvas); - - // Draw the letter in the center. - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - paint.setColor(getContext().getColor(R.color.car_user_switcher_no_user_image_fgcolor)); - paint.setTextAlign(Align.CENTER); - paint.setTextSize(getResources().getDimensionPixelSize( - R.dimen.car_fullscreen_user_pod_icon_text_size)); - Paint.FontMetricsInt metrics = paint.getFontMetricsInt(); - // The Y coordinate is measured by taking half the height of the pod, but that would - // draw the character putting the bottom of the font in the middle of the pod. To - // correct this, half the difference between the top and bottom distance metrics of the - // font gives the offset of the font. Bottom is a positive value, top is negative, so - // the different is actually a sum. The "half" operation is then factored out. - canvas.drawText(displayText.toString(), mPodImageAvatarWidth / 2, - (mPodImageAvatarHeight - (metrics.bottom + metrics.top)) / 2, paint); - - return out; - } - - private View makeUserPod(LayoutInflater inflater, Context context, - int position, ViewGroup parent) { - final UserSwitcherController.UserRecord record = mUserAdapter.getItem(position); - View view = inflater.inflate(R.layout.car_fullscreen_user_pod, parent, false); - - TextView nameView = view.findViewById(R.id.user_name); - if (record != null) { - nameView.setText(mUserAdapter.getName(context, record)); - view.setActivated(record.isCurrent); - } else { - nameView.setText(context.getString(R.string.unknown_user_label)); - } - - ImageView iconView = (ImageView) view.findViewById(R.id.user_avatar); - if (record == null || (record.picture == null && !record.isAddUser)) { - iconView.setImageBitmap(getDefaultUserIcon(nameView.getText())); - } else if (record.isAddUser) { - Drawable icon = context.getDrawable(R.drawable.ic_add_circle_qs); - icon.setTint(context.getColor(R.color.car_user_switcher_no_user_image_bgcolor)); - iconView.setImageDrawable(icon); - } else { - iconView.setImageBitmap(record.picture); - } - - iconView.setOnClickListener(v -> { - if (record == null) { - return; - } - - if (mUserSelectionListener != null) { - mUserSelectionListener.onUserSelected(record); - } - - if (record.isCurrent) { - showOfflineAuthUi(); - } else { - mUserSwitcherController.switchTo(record); - } - }); - - return view; - } - - @Override - public int getCount() { - int iconsPerPage = getIconsPerPage(); - if (iconsPerPage == 0) { - return 0; - } - return (int) Math.ceil((double) mUserAdapter.getCount() / getIconsPerPage()); - } - - public void refresh() { - mUserAdapter.refresh(); - } - - @Override - public boolean isViewFromObject(View view, Object object) { - return view == object; - } - } - - private final class WrappedBaseUserAdapter extends UserSwitcherController.BaseUserAdapter { - private final Adapter mContainer; - - public WrappedBaseUserAdapter(UserSwitcherController controller, Adapter container) { - super(controller); - mContainer = container; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - throw new UnsupportedOperationException("unused"); - } - - @Override - public void notifyDataSetChanged() { - super.notifyDataSetChanged(); - mContainer.notifyDataSetChanged(); - } - } - - interface UserSelectionListener { - void onUserSelected(UserSwitcherController.UserRecord record); - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index 3bbfe3c1062c..b8bce95190f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -238,6 +238,7 @@ public class ActivityLaunchAnimator { t.deferTransactionUntilSurface(app.leash, systemUiSurface, systemUiSurface.getNextFrameNumber()); } + t.setEarlyWakeup(); t.apply(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index df2b817101ab..60a3474be684 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -159,6 +159,11 @@ public class KeyguardBouncer { */ public void onFullyShown() { mFalsingManager.onBouncerShown(); + if (mKeyguardView == null) { + Log.wtf(TAG, "onFullyShown when view was null"); + } else { + mKeyguardView.onResume(); + } } /** @@ -180,7 +185,6 @@ public class KeyguardBouncer { @Override public void run() { mRoot.setVisibility(View.VISIBLE); - mKeyguardView.onResume(); showPromptReason(mBouncerPromptReason); final CharSequence customMessage = mCallback.consumeCustomMessage(); if (customMessage != null) { @@ -296,7 +300,7 @@ public class KeyguardBouncer { public boolean isShowing() { return (mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE)) - && mExpansion == 0; + && mExpansion == 0 && !isAnimatingAway(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 3d7067d1d799..1fb1ddd5e70a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -99,6 +99,11 @@ public class KeyguardClockPositionAlgorithm { private int mBurnInPreventionOffsetY; /** + * Clock vertical padding when pulsing. + */ + private int mPulsingPadding; + + /** * Doze/AOD transition amount. */ private float mDarkAmount; @@ -109,9 +114,9 @@ public class KeyguardClockPositionAlgorithm { private boolean mCurrentlySecure; /** - * If notification panel view currently has a touch. + * Dozing and receiving a notification (AOD notification.) */ - private boolean mTracking; + private boolean mPulsing; /** * Distance in pixels between the top of the screen and the first view of the bouncer. @@ -130,11 +135,13 @@ public class KeyguardClockPositionAlgorithm { R.dimen.burn_in_prevention_offset_x); mBurnInPreventionOffsetY = res.getDimensionPixelSize( R.dimen.burn_in_prevention_offset_y); + mPulsingPadding = res.getDimensionPixelSize( + R.dimen.widget_pulsing_bottom_padding); } public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight, float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight, - float dark, boolean secure, boolean tracking, int bouncerTop) { + float dark, boolean secure, boolean pulsing, int bouncerTop) { mMinTopMargin = minTopMargin + mContainerTopPadding; mMaxShadeBottom = maxShadeBottom; mNotificationStackHeight = notificationStackHeight; @@ -144,7 +151,7 @@ public class KeyguardClockPositionAlgorithm { mKeyguardStatusHeight = keyguardStatusHeight; mDarkAmount = dark; mCurrentlySecure = secure; - mTracking = tracking; + mPulsing = pulsing; mBouncerTop = bouncerTop; } @@ -152,7 +159,7 @@ public class KeyguardClockPositionAlgorithm { final int y = getClockY(); result.clockY = y; result.clockAlpha = getClockAlpha(y); - result.stackScrollerPadding = y + mKeyguardStatusHeight; + result.stackScrollerPadding = y + (mPulsing ? 0 : mKeyguardStatusHeight); result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount); } @@ -194,9 +201,13 @@ public class KeyguardClockPositionAlgorithm { private int getClockY() { // Dark: Align the bottom edge of the clock at about half of the screen: - final float clockYDark = getMaxClockY() + burnInPreventionOffsetY(); - final float clockYRegular = getExpandedClockPosition(); - final boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop; + float clockYDark = getMaxClockY() + burnInPreventionOffsetY(); + if (mPulsing) { + clockYDark -= mPulsingPadding; + } + + float clockYRegular = getExpandedClockPosition(); + boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop; float clockYTarget = mCurrentlySecure && hasEnoughSpace ? mMinTopMargin : -mKeyguardStatusHeight; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java new file mode 100644 index 000000000000..759a0d173cdd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 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.Nullable; + +import com.android.keyguard.KeyguardHostView.OnDismissAction; + + +/** Executes actions that require the screen to be unlocked. */ +public interface KeyguardDismissHandler { + /** Executes an action that requres the screen to be unlocked. */ + void dismissKeyguardThenExecute( + OnDismissAction action, @Nullable Runnable cancelAction, boolean afterKeyguardGone); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java new file mode 100644 index 000000000000..c38b0b63190d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 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.util.Log; + +import com.android.keyguard.KeyguardHostView.OnDismissAction; + +/** + * Executes actions that require the screen to be unlocked. Delegates the actual handling to an + * implementation passed via {@link #setDismissHandler}. + */ +public class KeyguardDismissUtil implements KeyguardDismissHandler { + private static final String TAG = "KeyguardDismissUtil"; + + private volatile KeyguardDismissHandler mDismissHandler; + + /** Sets the actual {@link DismissHandler} implementation. */ + public void setDismissHandler(KeyguardDismissHandler dismissHandler) { + mDismissHandler = dismissHandler; + } + + /** + * Executes an action that requres the screen to be unlocked. + * + * <p>Must be called after {@link #setDismissHandler}. + */ + @Override + public void dismissKeyguardThenExecute( + OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) { + KeyguardDismissHandler dismissHandler = mDismissHandler; + if (dismissHandler == null) { + Log.wtf(TAG, "KeyguardDismissHandler not set."); + action.onDismiss(); + return; + } + dismissHandler.dismissKeyguardThenExecute(action, cancelAction, afterKeyguardGone); + } +} 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 4c4eb60e4412..db2139da6b76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -39,7 +39,6 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.os.SystemProperties; -import android.os.VibrationEffect; import android.support.annotation.ColorInt; import android.util.AttributeSet; import android.util.Log; @@ -69,7 +68,6 @@ import com.android.systemui.recents.RecentsOnboarding; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.NavigationBarCompat; import com.android.systemui.stackdivider.Divider; -import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.DeadZone; import com.android.systemui.statusbar.policy.KeyButtonDrawable; import com.android.systemui.statusbar.policy.TintedKeyButtonDrawable; @@ -150,7 +148,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private Divider mDivider; private RecentsOnboarding mRecentsOnboarding; private NotificationPanelView mPanelView; - private final VibratorHelper mVibratorHelper; private int mRotateBtnStyle = R.style.RotateButtonCCWStart90; @@ -246,7 +243,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mOverviewProxyService = Dependency.get(OverviewProxyService.class); mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService); - mVibratorHelper = Dependency.get(VibratorHelper.class); mConfiguration = new Configuration(); mConfiguration.updateFrom(context.getResources().getConfiguration()); @@ -314,9 +310,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } else if (mRecentsButtonBounds.contains(x, y)) { mDownHitTarget = HIT_TARGET_OVERVIEW; } - - // Vibrate tick whenever down occurs on navigation bar - mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); break; } return mGestureHelper.onInterceptTouchEvent(event); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index b6a11f71f251..6bc19ea69be7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -125,7 +125,14 @@ public class NotificationIconAreaController implements DarkReceiver { } else { mTintArea.set(tintArea); } - mIconTint = iconTint; + if (mNotificationIconArea != null) { + if (DarkIconDispatcher.isInArea(tintArea, mNotificationIconArea)) { + mIconTint = iconTint; + } + } else { + mIconTint = iconTint; + } + applyNotificationIconsTint(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 55174349cc53..8c257fe255a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -151,6 +151,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons; // Keep track of the last visible icon so collapsed container can report on its location private IconState mLastVisibleIconState; + private IconState mFirstVisibleIconState; private float mVisualOverflowStart; // Keep track of overflow in range [0, 3] private int mNumDots; @@ -159,7 +160,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private int[] mAbsolutePosition = new int[2]; private View mIsolatedIconForAnimation; - public NotificationIconContainer(Context context, AttributeSet attrs) { super(context, attrs); initDimens(); @@ -192,10 +192,15 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { paint.setColor(Color.BLUE); canvas.drawLine(end, 0, end, height, paint); - paint.setColor(Color.BLACK); + paint.setColor(Color.GREEN); int lastIcon = (int) mLastVisibleIconState.xTranslation; canvas.drawLine(lastIcon, 0, lastIcon, height, paint); + if (mFirstVisibleIconState != null) { + int firstIcon = (int) mFirstVisibleIconState.xTranslation; + canvas.drawLine(firstIcon, 0, firstIcon, height, paint); + } + paint.setColor(Color.RED); canvas.drawLine(mVisualOverflowStart, 0, mVisualOverflowStart, height, paint); @@ -210,6 +215,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { super.onConfigurationChanged(newConfig); initDimens(); } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { float centerY = getHeight() / 2.0f; @@ -364,11 +370,15 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { float layoutEnd = getLayoutEnd(); float overflowStart = getMaxOverflowStart(); mVisualOverflowStart = 0; + mFirstVisibleIconState = null; boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount(); for (int i = 0; i < childCount; i++) { View view = getChildAt(i); IconState iconState = mIconStates.get(view); iconState.xTranslation = translationX; + if (mFirstVisibleIconState == null) { + mFirstVisibleIconState = iconState; + } boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons; boolean noOverflowAfter = i == childCount - 1; @@ -417,10 +427,16 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } else if (childCount > 0) { View lastChild = getChildAt(childCount - 1); mLastVisibleIconState = mIconStates.get(lastChild); + mFirstVisibleIconState = mIconStates.get(getChildAt(0)); } boolean center = mDark; if (center && translationX < getLayoutEnd()) { - float delta = (getLayoutEnd() - translationX) / 2; + float initialTranslation = + mFirstVisibleIconState == null ? 0 : mFirstVisibleIconState.xTranslation; + float contentWidth = getFinalTranslationX() - initialTranslation; + float availableSpace = getLayoutEnd() - getActualPaddingStart(); + float delta = (availableSpace - contentWidth) / 2; + if (firstOverflowIndex != -1) { // If we have an overflow, only count those half for centering because the dots // don't have a lot of visual weight. 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 27ca0d11ee28..351633b590c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -238,6 +238,7 @@ public class NotificationPanelView extends PanelView implements private boolean mIsFullWidth; private float mDarkAmount; private float mDarkAmountTarget; + private boolean mPulsing; private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); private boolean mNoVisibleNotifications = true; private ValueAnimator mDarkAnimator; @@ -477,7 +478,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusView.getHeight(), mDarkAmount, mStatusBar.isKeyguardCurrentlySecure(), - mTracking, + mPulsing, mBouncerTop); mClockPositionAlgorithm.run(mClockPositionResult); if (animate || mClockAnimator != null) { @@ -659,6 +660,14 @@ public class NotificationPanelView extends PanelView implements expand(true /* animate */); } + public void expandWithoutQs() { + if (isQsExpanded()) { + flingSettings(0 /* velocity */, false /* expand */); + } else { + expand(true /* animate */); + } + } + @Override public void fling(float vel, boolean expand) { GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder(); @@ -2681,14 +2690,8 @@ public class NotificationPanelView extends PanelView implements positionClockAndNotifications(); } - public void setNoVisibleNotifications(boolean noNotifications) { - mNoVisibleNotifications = noNotifications; - if (mQs != null) { - mQs.setHasNotifications(!noNotifications); - } - } - public void setPulsing(boolean pulsing) { + mPulsing = pulsing; mKeyguardStatusView.setPulsing(pulsing); positionClockAndNotifications(); mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1] 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 28f31621d2d1..cc143bb848cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -68,8 +68,14 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private static final String TAG = "ScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + /** + * General scrim animation duration. + */ public static final long ANIMATION_DURATION = 220; - + /** + * Longer duration, currently only used when going to AOD. + */ + public static final long ANIMATION_DURATION_LONG = 1000; /** * When both scrims have 0 alpha. */ @@ -85,7 +91,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo /** * Default alpha value for most scrims. */ - public static final float GRADIENT_SCRIM_ALPHA = 0.45f; + public static final float GRADIENT_SCRIM_ALPHA = 0.70f; /** * A scrim varies its opacity based on a busyness factor, for example * how many notifications are currently visible. @@ -105,7 +111,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private final Context mContext; protected final ScrimView mScrimBehind; protected final ScrimView mScrimInFront; - private final LightBarController mLightBarController; private final UnlockMethodCache mUnlockMethodCache; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final DozeParameters mDozeParameters; @@ -139,6 +144,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private int mCurrentBehindTint; private boolean mWallpaperVisibilityTimedOut; private int mScrimsVisibility; + private final Consumer<GradientColors> mScrimInFrontColorListener; + private final Consumer<Float> mScrimBehindAlphaListener; private final Consumer<Integer> mScrimVisibleListener; private boolean mBlankScreen; private boolean mScreenBlankingCallbackCalled; @@ -155,17 +162,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private boolean mWakeLockHeld; private boolean mKeyguardOccluded; - public ScrimController(LightBarController lightBarController, ScrimView scrimBehind, - ScrimView scrimInFront, Consumer<Integer> scrimVisibleListener, - DozeParameters dozeParameters, AlarmManager alarmManager) { + public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, + Consumer<Float> scrimBehindAlphaListener, + Consumer<GradientColors> scrimInFrontColorListener, + Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters, + AlarmManager alarmManager) { mScrimBehind = scrimBehind; mScrimInFront = scrimInFront; + mScrimBehindAlphaListener = scrimBehindAlphaListener; + mScrimInFrontColorListener = scrimInFrontColorListener; mScrimVisibleListener = scrimVisibleListener; mContext = scrimBehind.getContext(); mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer(); mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); - mLightBarController = lightBarController; mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha); mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout, "hide_aod_wallpaper", new Handler()); @@ -190,6 +200,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } mState = ScrimState.UNINITIALIZED; + mScrimBehind.setDefaultFocusHighlightEnabled(false); + mScrimInFront.setDefaultFocusHighlightEnabled(false); + updateScrims(); } @@ -361,6 +374,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo setOrAdaptCurrentAnimation(mScrimBehind); setOrAdaptCurrentAnimation(mScrimInFront); + + mScrimBehindAlphaListener.accept(mScrimBehind.getViewAlpha()); } } @@ -389,7 +404,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // Darken scrim as you pull down the shade when unlocked float behindFraction = getInterpolatedFraction(); behindFraction = (float) Math.pow(behindFraction, 0.8f); - mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard; + mCurrentBehindAlpha = behindFraction * GRADIENT_SCRIM_ALPHA_BUSY; mCurrentInFrontAlpha = 0; } else if (mState == ScrimState.KEYGUARD) { // Either darken of make the scrim transparent when you @@ -469,7 +484,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor, 4.5f /* minimumContrast */) / 255f; mScrimBehindAlpha = Math.max(mScrimBehindAlphaResValue, minOpacity); - mLightBarController.setScrimColor(mScrimInFront.getColors()); + mScrimInFrontColorListener.accept(mScrimInFront.getColors()); } // We want to override the back scrim opacity for the AOD state @@ -528,8 +543,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo if (alpha == 0f) { scrim.setClickable(false); } else { - // Eat touch events (unless dozing). - scrim.setClickable(!mState.isLowPowerState()); + // Eat touch events (unless dozing or pulsing). + scrim.setClickable(mState != ScrimState.AOD && mState != ScrimState.PULSING); } updateScrim(scrim, alpha); } @@ -696,9 +711,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } } - // TODO factor mLightBarController out of this class if (scrim == mScrimBehind) { - mLightBarController.setScrimAlpha(alpha); + mScrimBehindAlphaListener.accept(alpha); } final boolean wantsAlphaUpdate = alpha != currentAlpha; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index f4b6c38c4f6e..bbdaa9993bec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -111,9 +111,10 @@ public enum ScrimState { mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f; mCurrentInFrontTint = Color.BLACK; mCurrentBehindTint = Color.BLACK; - // DisplayPowerManager will blank the screen for us, we just need - // to set our state. - mAnimateChange = !mDisplayRequiresBlanking; + mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG; + // DisplayPowerManager may blank the screen for us, + // in this case we just need to set our state. + mAnimateChange = mDozeParameters.shouldControlScreenOff(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index f7a97be3b0f2..4b2bc45bf417 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -213,6 +213,7 @@ import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; +import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import com.android.systemui.statusbar.policy.BrightnessMirrorController; @@ -915,8 +916,14 @@ public class StatusBar extends SystemUI implements DemoMode, ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind); ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front); - mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController, + mScrimController = SystemUIFactory.getInstance().createScrimController( scrimBehind, scrimInFront, mLockscreenWallpaper, + scrimBehindAlpha -> { + mLightBarController.setScrimAlpha(scrimBehindAlpha); + }, + scrimInFrontColor -> { + mLightBarController.setScrimColor(scrimInFrontColor); + }, scrimsVisible -> { if (mStatusBarWindowManager != null) { mStatusBarWindowManager.setScrimsVisibility(scrimsVisible); @@ -1300,6 +1307,8 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController); + Dependency.get(KeyguardDismissUtil.class).setDismissHandler( + this::dismissKeyguardThenExecute); Trace.endSection(); } @@ -1425,13 +1434,13 @@ public class StatusBar extends SystemUI implements DemoMode, } public void addQsTile(ComponentName tile) { - if (mQSPanel.getHost() != null) { + if (mQSPanel != null && mQSPanel.getHost() != null) { mQSPanel.getHost().addTile(tile); } } public void remQsTile(ComponentName tile) { - if (mQSPanel.getHost() != null) { + if (mQSPanel != null && mQSPanel.getHost() != null) { mQSPanel.getHost().removeTile(tile); } } @@ -1443,7 +1452,8 @@ public class StatusBar extends SystemUI implements DemoMode, @VisibleForTesting protected void updateFooter() { boolean showFooterView = mState != StatusBarState.KEYGUARD - && mEntryManager.getNotificationData().getActiveNotifications().size() != 0; + && mEntryManager.getNotificationData().getActiveNotifications().size() != 0 + && !mRemoteInputManager.getController().isRemoteInputActive(); boolean showDismissView = mClearAllEnabled && mState != StatusBarState.KEYGUARD && hasActiveClearableNotifications(); @@ -2047,11 +2057,19 @@ public class StatusBar extends SystemUI implements DemoMode, } /** + * Decides if the status bar (clock + notifications + signal cluster) should be visible + * or not when showing the bouncer. + * + * We want to hide it when: + * • User swipes up on the keyguard + * • Locked activity that doesn't show a status bar requests the bouncer + * * @param animate should the change of the icons be animated. */ private void updateHideIconsForBouncer(boolean animate) { - boolean shouldHideIconsForBouncer = !mPanelExpanded && mTopHidesStatusBar && mIsOccluded - && (mBouncerShowing || mStatusBarWindowHidden); + boolean hideBecauseApp = mTopHidesStatusBar && mIsOccluded; + boolean hideBecauseKeyguard = !mPanelExpanded && !mIsOccluded && mBouncerShowing; + boolean shouldHideIconsForBouncer = hideBecauseApp || hideBecauseKeyguard; if (mHideIconsForBouncer != shouldHideIconsForBouncer) { mHideIconsForBouncer = shouldHideIconsForBouncer; if (!shouldHideIconsForBouncer && mBouncerWasShowingWhenHidden) { @@ -2290,7 +2308,7 @@ public class StatusBar extends SystemUI implements DemoMode, return ; } - mNotificationPanel.expand(true /* animate */); + mNotificationPanel.expandWithoutQs(); if (false) postStartTracing(); } @@ -2815,6 +2833,7 @@ public class StatusBar extends SystemUI implements DemoMode, boolean remoteInputActive) { mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive); entry.row.notifyHeightChanged(true /* needsAnimation */); + updateFooter(); } public void lockScrollTo(NotificationData.Entry entry) { mStackScroller.lockScrollTo(entry.row); @@ -3888,7 +3907,11 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean onBackPressed() { - if (mStatusBarKeyguardViewManager.onBackPressed()) { + boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED; + if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) { + if (!isScrimmedBouncer) { + mNotificationPanel.expandWithoutQs(); + } return true; } if (mNotificationPanel.isQsExpanded()) { @@ -3919,7 +3942,8 @@ public class StatusBar extends SystemUI implements DemoMode, } private void showBouncerIfKeyguard() { - if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { + if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) + && !mKeyguardViewMediator.isHiding()) { showBouncer(true /* animated */); } } @@ -4983,6 +5007,14 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) { + RemoteInputController controller = mRemoteInputManager.getController(); + if (controller.isRemoteInputActive(row.getEntry()) + && !TextUtils.isEmpty(row.getActiveRemoteInputText())) { + // We have an active remote input typed and the user clicked on the notification. + // this was probably unintentional, so we're closing the edit text instead. + controller.closeRemoteInputs(); + return; + } Notification notification = sbn.getNotification(); final PendingIntent intent = notification.contentIntent != null ? notification.contentIntent @@ -5046,12 +5078,7 @@ public class StatusBar extends SystemUI implements DemoMode, Intent fillInIntent = null; Entry entry = row.getEntry(); CharSequence remoteInputText = null; - RemoteInputController controller = mRemoteInputManager.getController(); - if (controller.isRemoteInputActive(entry)) { - remoteInputText = row.getActiveRemoteInputText(); - } - if (TextUtils.isEmpty(remoteInputText) - && !TextUtils.isEmpty(entry.remoteInputText)) { + if (!TextUtils.isEmpty(entry.remoteInputText)) { remoteInputText = entry.remoteInputText; } if (!TextUtils.isEmpty(remoteInputText) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 510af03e6f28..b4e7575d1480 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -306,17 +306,6 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder)); } - /** - * For mobile essentially (an array of holders in one slot) - */ - private void handleSet(int slotIndex, List<StatusBarIconHolder> holders) { - for (StatusBarIconHolder holder : holders) { - int viewIndex = getViewIndex(slotIndex, holder.getTag()); - mIconLogger.onIconVisibility(getSlotName(slotIndex), holder.isVisible()); - mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder)); - } - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(TAG + " state:"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 6b6ea10bfa63..670c68f3eb22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -89,7 +89,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb protected boolean mFirstUpdate = true; protected boolean mLastShowing; protected boolean mLastOccluded; - private boolean mLastTracking; private boolean mLastBouncerShowing; private boolean mLastBouncerDismissible; protected boolean mLastRemoteInputActive; @@ -152,28 +151,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // • The user quickly taps on the display and we show "swipe up to unlock." // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY // • Full-screen user switcher is displayed. - final boolean noLongerTracking = mLastTracking != tracking && !tracking; if (mOccluded || mNotificationPanelView.isUnlockHintRunning() || mBouncer.willDismissWithAction() || mStatusBar.isFullScreenUserSwitcherState()) { mBouncer.setExpansion(0); } else if (mShowing && mStatusBar.isKeyguardCurrentlySecure() && !mDozing) { mBouncer.setExpansion(expansion); - if (expansion == 1) { - mBouncer.onFullyHidden(); - } else if (!mBouncer.isShowing() && !mBouncer.isAnimatingAway()) { + if (expansion != 1 && tracking && !mBouncer.isShowing() + && !mBouncer.isAnimatingAway()) { mBouncer.show(false /* resetSecuritySelection */, false /* animated */); - } else if (noLongerTracking) { - // Notify that falsing manager should stop its session when user stops touching, - // even before the animation ends, to guarantee that we're not recording sensitive - // data. - mBouncer.onFullyShown(); - } - if (expansion == 0 || expansion == 1) { + } else if (expansion == 0 || expansion == 1) { updateStates(); } } - mLastTracking = tracking; } /** @@ -522,12 +512,15 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb /** * Notifies this manager that the back button has been pressed. * + * @param hideImmediately Hide bouncer when {@code true}, keep it around otherwise. + * Non-scrimmed bouncers have a special animation tied to the expansion + * of the notification panel. * @return whether the back press has been handled */ - public boolean onBackPressed() { + public boolean onBackPressed(boolean hideImmediately) { if (mBouncer.isShowing()) { mStatusBar.endAffordanceLaunch(); - reset(true /* hideBouncerWhenShowing */); + reset(hideImmediately); return true; } return false; @@ -595,6 +588,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) { mStatusBarWindowManager.setBouncerShowing(bouncerShowing); mStatusBar.setBouncerShowing(bouncerShowing); + if (bouncerShowing) { + mBouncer.onFullyShown(); + } else { + mBouncer.onFullyHidden(); + } } KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index 1d9f01dd7adc..127fdfe8a931 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -415,7 +415,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba @Override public String toString() { return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming=" - + roaming + ", visible=" + visible + ")"; + + roaming + ", typeId=" + typeId + ", visible=" + visible + ")"; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 4c92d01eae4c..9aa804484716 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.policy; -import libcore.icu.LocaleData; - -import android.app.ActivityManager; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -40,11 +37,13 @@ import android.view.Display; import android.view.View; import android.widget.TextView; +import com.android.settingslib.Utils; import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -52,6 +51,8 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import libcore.icu.LocaleData; + import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; @@ -65,6 +66,9 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C public static final String CLOCK_SECONDS = "clock_seconds"; + private final CurrentUserTracker mCurrentUserTracker; + private int mCurrentUserId; + private boolean mClockVisibleByPolicy = true; private boolean mClockVisibleByUser = true; @@ -84,6 +88,17 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C private boolean mShowSeconds; private Handler mSecondsHandler; + /** + * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings + * for text. + */ + private boolean mUseWallpaperTextColor; + + /** + * Color to be set on this {@link TextView}, when wallpaperTextColor is <b>not</b> utilized. + */ + private int mNonAdaptedColor; + public Clock(Context context) { this(context, null); } @@ -101,9 +116,16 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C try { mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE); mShowDark = a.getBoolean(R.styleable.Clock_showDark, true); + mNonAdaptedColor = getCurrentTextColor(); } finally { a.recycle(); } + mCurrentUserTracker = new CurrentUserTracker(context) { + @Override + public void onUserSwitched(int newUserId) { + mCurrentUserId = newUserId; + } + }; } @Override @@ -128,6 +150,8 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C if (mShowDark) { Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this); } + mCurrentUserTracker.startTracking(); + mCurrentUserId = mCurrentUserTracker.getCurrentUserId(); } // NOTE: It's safe to do these after registering the receiver since the receiver always runs @@ -153,6 +177,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C if (mShowDark) { Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this); } + mCurrentUserTracker.stopTracking(); } } @@ -227,7 +252,10 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C @Override public void onDarkChanged(Rect area, float darkIntensity, int tint) { - setTextColor(DarkIconDispatcher.getTint(area, this, tint)); + mNonAdaptedColor = DarkIconDispatcher.getTint(area, this, tint); + if (!mUseWallpaperTextColor) { + setTextColor(mNonAdaptedColor); + } } @Override @@ -242,6 +270,25 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C 0); } + /** + * Sets whether the clock uses the wallpaperTextColor. If we're not using it, we'll revert back + * to dark-mode-based/tinted colors. + * + * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for text color + */ + public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) { + if (shouldUseWallpaperTextColor == mUseWallpaperTextColor) { + return; + } + mUseWallpaperTextColor = shouldUseWallpaperTextColor; + + if (mUseWallpaperTextColor) { + setTextColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor)); + } else { + setTextColor(mNonAdaptedColor); + } + } + private void updateShowSeconds() { if (mShowSeconds) { // Wait until we have a display to start trying to show seconds. @@ -267,7 +314,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C private final CharSequence getSmallTime() { Context context = getContext(); - boolean is24 = DateFormat.is24HourFormat(context, ActivityManager.getCurrentUser()); + boolean is24 = DateFormat.is24HourFormat(context, mCurrentUserId); LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale); final char MAGIC1 = '\uEF00'; @@ -357,8 +404,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C } else if (hhmm != null && hhmm.length() == 4) { int hh = Integer.parseInt(hhmm.substring(0, 2)); int mm = Integer.parseInt(hhmm.substring(2)); - boolean is24 = DateFormat.is24HourFormat( - getContext(), ActivityManager.getCurrentUser()); + boolean is24 = DateFormat.is24HourFormat(getContext(), mCurrentUserId); if (is24) { mCalendar.set(Calendar.HOUR_OF_DAY, hh); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java index 74a30fa8094f..ef630c7205e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java @@ -27,6 +27,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.widget.TextView; +import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -42,6 +43,17 @@ public class DateView extends TextView { private String mLastText; private String mDatePattern; + /** + * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings + * for text. + */ + private boolean mUseWallpaperTextColor; + + /** + * Color to be set on this {@link TextView}, when wallpaperTextColor is <b>not</b> utilized. + */ + private int mNonAdaptedTextColor; + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -62,6 +74,7 @@ public class DateView extends TextView { public DateView(Context context, AttributeSet attrs) { super(context, attrs); + mNonAdaptedTextColor = getCurrentTextColor(); TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.DateView, @@ -117,6 +130,25 @@ public class DateView extends TextView { } } + /** + * Sets whether the date view uses the wallpaperTextColor. If we're not using it, we'll revert + * back to dark-mode-based/tinted colors. + * + * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for text color + */ + public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) { + if (shouldUseWallpaperTextColor == mUseWallpaperTextColor) { + return; + } + mUseWallpaperTextColor = shouldUseWallpaperTextColor; + + if (mUseWallpaperTextColor) { + setTextColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor)); + } else { + setTextColor(mNonAdaptedTextColor); + } + } + public void setDatePattern(String pattern) { if (TextUtils.equals(pattern, mDatePattern)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index 2fed3fca24e5..1b02e152ec11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -28,7 +28,6 @@ import android.metrics.LogMaker; import android.os.AsyncTask; import android.os.Bundle; import android.os.SystemClock; -import android.os.VibrationEffect; import android.util.AttributeSet; import android.util.TypedValue; import android.view.HapticFeedbackConstants; @@ -50,7 +49,6 @@ import com.android.systemui.OverviewProxyService; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.statusbar.VibratorHelper; import static android.view.KeyEvent.KEYCODE_HOME; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; @@ -75,7 +73,6 @@ public class KeyButtonView extends ImageView implements ButtonInterface { private OnClickListener mOnClickListener; private final KeyButtonRipple mRipple; private final OverviewProxyService mOverviewProxyService; - private final VibratorHelper mVibratorHelper; private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private final Runnable mCheckLongPress = new Runnable() { @@ -121,7 +118,6 @@ public class KeyButtonView extends ImageView implements ButtonInterface { mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mRipple = new KeyButtonRipple(context, this); - mVibratorHelper = Dependency.get(VibratorHelper.class); mOverviewProxyService = Dependency.get(OverviewProxyService.class); setBackground(mRipple); } @@ -262,9 +258,8 @@ public class KeyButtonView extends ImageView implements ButtonInterface { final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150; if (showSwipeUI) { if (doIt) { - if (doHapticFeedback) { - mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); - } + // Apply haptic feedback on touch up since there is none on touch down + performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); playSoundEffect(SoundEffectConstants.CLICK); } } else if (doHapticFeedback && !mLongClicked) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 790135fc03ca..74b39268fc2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -20,8 +20,10 @@ import android.view.ViewGroup; import android.widget.Button; import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import java.text.BreakIterator; import java.util.Comparator; @@ -42,6 +44,7 @@ public class SmartReplyView extends ViewGroup { private static final int SQUEEZE_FAILED = -1; private final SmartReplyConstants mConstants; + private final KeyguardDismissUtil mKeyguardDismissUtil; /** Spacing to be applied between views. */ private final int mSpacing; @@ -62,6 +65,7 @@ public class SmartReplyView extends ViewGroup { public SmartReplyView(Context context, AttributeSet attrs) { super(context, attrs); mConstants = Dependency.get(SmartReplyConstants.class); + mKeyguardDismissUtil = Dependency.get(KeyguardDismissUtil.class); int spacing = 0; int singleLineButtonPaddingHorizontal = 0; @@ -126,12 +130,13 @@ public class SmartReplyView extends ViewGroup { } @VisibleForTesting - static Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice, + Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice, RemoteInput remoteInput, PendingIntent pendingIntent) { Button b = (Button) LayoutInflater.from(context).inflate( R.layout.smart_reply_button, root, false); b.setText(choice); - b.setOnClickListener(view -> { + + OnDismissAction action = () -> { Bundle results = new Bundle(); results.putString(remoteInput.getResultKey(), choice.toString()); Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); @@ -142,6 +147,12 @@ public class SmartReplyView extends ViewGroup { } catch (PendingIntent.CanceledException e) { Log.w(TAG, "Unable to send smart reply", e); } + return false; // do not defer + }; + + b.setOnClickListener(view -> { + mKeyguardDismissUtil.dismissKeyguardThenExecute( + action, null /* cancelAction */, false /* afterKeyguardGone */); }); return b; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index 7c1c566a57bf..91a4b07c2109 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -70,8 +70,8 @@ public class AmbientState { private int mIntrinsicPadding; private int mExpandAnimationTopChange; private ExpandableNotificationRow mExpandingNotification; - private boolean mFullyDark; private int mDarkTopPadding; + private float mDarkAmount; public AmbientState(Context context) { reload(context); @@ -149,6 +149,16 @@ public class AmbientState { mDark = dark; } + /** Dark ratio of the status bar **/ + public void setDarkAmount(float darkAmount) { + mDarkAmount = darkAmount; + } + + /** Returns the dark ratio of the status bar */ + public float getDarkAmount() { + return mDarkAmount; + } + public void setHideSensitive(boolean hideSensitive) { mHideSensitive = hideSensitive; } @@ -413,17 +423,10 @@ public class AmbientState { } /** - * {@see isFullyDark} - */ - public void setFullyDark(boolean fullyDark) { - mFullyDark = fullyDark; - } - - /** * @return {@code true } when shade is completely dark: in AOD or ambient display. */ public boolean isFullyDark() { - return mFullyDark; + return mDarkAmount == 1; } public void setDarkTopPadding(int darkTopPadding) { 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 375e8606258f..bc5a848f9f2a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -96,6 +96,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.VisibilityLocationProvider; +import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ScrimController; @@ -269,8 +270,6 @@ public class NotificationStackScrollLayout extends ViewGroup */ private boolean mOnlyScrollingInThisMotion; private boolean mDisallowDismissInThisMotion; - private boolean mInterceptDelegateEnabled; - private boolean mDelegateToScrollView; private boolean mDisallowScrollingInThisMotion; private long mGoToFullShadeDelay; private ViewTreeObserver.OnPreDrawListener mChildrenUpdater @@ -562,17 +561,17 @@ public class NotificationStackScrollLayout extends ViewGroup return; } - final int color; - if (mAmbientState.isDark()) { - color = Color.WHITE; - } else { - float alpha = - BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount); - alpha *= 1f - mDarkAmount; - // We need to manually blend in the background color - int scrimColor = mScrimController.getBackgroundColor(); - color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha); - } + float alpha = + BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount); + alpha *= 1f - mDarkAmount; + // We need to manually blend in the background color. + int scrimColor = mScrimController.getBackgroundColor(); + int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha); + + // Interpolate between semi-transparent notification panel background color + // and white AOD separator. + float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(mDarkAmount); + int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation); if (mCachedBackgroundColor != color) { mCachedBackgroundColor = color; @@ -3023,6 +3022,11 @@ public class NotificationStackScrollLayout extends ViewGroup public void setAnimationsEnabled(boolean animationsEnabled) { mAnimationsEnabled = animationsEnabled; updateNotificationAnimationStates(); + if (!animationsEnabled) { + mSwipedOutViews.clear(); + mChildrenToRemoveAnimated.clear(); + clearTemporaryViewsInGroup(this); + } } private void updateNotificationAnimationStates() { @@ -3090,6 +3094,21 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public void changeViewPosition(View child, int newIndex) { int currentIndex = indexOfChild(child); + + if (currentIndex == -1) { + boolean isTransient = false; + if (child instanceof ExpandableNotificationRow + && ((ExpandableNotificationRow)child).getTransientContainer() != null) { + isTransient = true; + } + Log.e(TAG, "Attempting to re-position " + + (isTransient ? "transient" : "") + + " view {" + + child + + "}"); + return; + } + if (child != null && child.getParent() == this && currentIndex != newIndex) { mChangePositionInProgress = true; ((ExpandableView)child).setChangingPosition(true); @@ -3569,17 +3588,17 @@ public class NotificationStackScrollLayout extends ViewGroup private void clearTemporaryViews() { // lets make sure nothing is in the overlay / transient anymore - clearTemporaryViews(this); + clearTemporaryViewsInGroup(this); for (int i = 0; i < getChildCount(); i++) { ExpandableView child = (ExpandableView) getChildAt(i); if (child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; - clearTemporaryViews(row.getChildrenContainer()); + clearTemporaryViewsInGroup(row.getChildrenContainer()); } } } - private void clearTemporaryViews(ViewGroup viewGroup) { + private void clearTemporaryViewsInGroup(ViewGroup viewGroup) { while (viewGroup != null && viewGroup.getTransientViewCount() != 0) { viewGroup.removeTransientView(viewGroup.getTransientView(0)); } @@ -3922,12 +3941,11 @@ public class NotificationStackScrollLayout extends ViewGroup requestChildrenUpdate(); applyCurrentBackgroundBounds(); updateWillNotDraw(); - updateAntiBurnInTranslation(); notifyHeightChangeListener(mShelf); } private void updateAntiBurnInTranslation() { - setTranslationX(mAmbientState.isDark() ? mAntiBurnInOffsetX : 0); + setTranslationX(mAntiBurnInOffsetX * mDarkAmount); } /** @@ -3942,12 +3960,18 @@ public class NotificationStackScrollLayout extends ViewGroup private void setDarkAmount(float darkAmount) { mDarkAmount = darkAmount; - final boolean fullyDark = darkAmount == 1; - if (mAmbientState.isFullyDark() != fullyDark) { - mAmbientState.setFullyDark(fullyDark); + boolean wasFullyDark = mAmbientState.isFullyDark(); + mAmbientState.setDarkAmount(darkAmount); + if (mAmbientState.isFullyDark() != wasFullyDark) { updateContentHeight(); + DozeParameters dozeParameters = DozeParameters.getInstance(mContext); + if (mAmbientState.isFullyDark() && dozeParameters.shouldControlScreenOff()) { + mShelf.fadeInTranslating(); + } } updateBackgroundDimming(); + updateAntiBurnInTranslation(); + requestChildrenUpdate(); } public float getDarkAmount() { 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 a8d2d98b6f2b..f4d7f8d48a66 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -276,8 +276,6 @@ public class StackScrollAlgorithm { if (i >= firstHiddenIndex) { // we need normal padding now, to be in sync with what the stack calculates lastView = null; - ExpandableViewState viewState = resultState.getViewStateForView(v); - viewState.hidden = true; } notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v); float increasedPadding = v.getIncreasedPaddingAmount(); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 5a4478f072e0..639e49b1a17f 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -58,7 +58,7 @@ public class TunerServiceImpl extends TunerService { private static final String TUNER_VERSION = "sysui_tuner_version"; - private static final int CURRENT_TUNER_VERSION = 2; + private static final int CURRENT_TUNER_VERSION = 3; private final Observer mObserver = new Observer(); // Map of Uris we listen on to their settings keys. @@ -119,6 +119,10 @@ public class TunerServiceImpl extends TunerService { if (oldVersion < 2) { setTunerEnabled(mContext, false); } + if (oldVersion < 3) { + // Delay this so that we can wait for everything to be registered first. + new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(() -> clearAll(), 5000); + } setValue(TUNER_VERSION, newVersion); } diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java index e18140984f0e..a901e88219a5 100644 --- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java +++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java @@ -23,7 +23,7 @@ import android.os.Handler; */ public class DelayedWakeLock implements WakeLock { - private static final long RELEASE_DELAY_MS = 240; + private static final long RELEASE_DELAY_MS = 140; private final Handler mHandler; private final WakeLock mInner; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 13157fe19c30..9036a8ad620e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -212,7 +212,6 @@ public class VolumeDialogImpl implements VolumeDialog { .setDuration(300) .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator()) .withEndAction(() -> { - mWindow.getDecorView().requestAccessibilityFocus(); if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) { mRingerIcon.postOnAnimationDelayed(mSinglePress, 1500); } @@ -302,15 +301,8 @@ public class VolumeDialogImpl implements VolumeDialog { if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream); VolumeRow row = new VolumeRow(); initRow(row, stream, iconRes, iconMuteRes, important, defaultStream); - if (dynamic && mRows.size() > 2) { - // Dynamic Streams should be the first in the list, so they're shown to start of - // everything except a11y - mDialogRowsView.addView(row.view, 1); - mRows.add(1, row); - } else { - mDialogRowsView.addView(row.view); - mRows.add(row); - } + mDialogRowsView.addView(row.view); + mRows.add(row); } private void addExistingRows() { @@ -604,7 +596,8 @@ public class VolumeDialogImpl implements VolumeDialog { return activeRow.stream == STREAM_RING || activeRow.stream == STREAM_ALARM || activeRow.stream == STREAM_VOICE_CALL - || activeRow.stream == STREAM_ACCESSIBILITY; + || activeRow.stream == STREAM_ACCESSIBILITY + || mDynamic.get(activeRow.stream); } return false; |