diff options
Diffstat (limited to 'packages/SystemUI/src/com')
53 files changed, 1123 insertions, 465 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java index 099dd5d82a10..87e853cf64d7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java @@ -18,6 +18,7 @@ package com.android.keyguard; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Handler; @@ -30,6 +31,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.internal.policy.SystemBarUtils; import com.android.settingslib.Utils; import com.android.systemui.R; @@ -57,8 +60,14 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR); private boolean mBouncerVisible; private boolean mAltBouncerShowing; + /** + * Container that wraps the KeyguardMessageArea - may be null if current view hierarchy doesn't + * contain {@link R.id.keyguard_message_area_container}. + */ + @Nullable private ViewGroup mContainer; - private int mTopMargin; + private int mContainerTopMargin; + private int mLastOrientation = -1; public KeyguardMessageArea(Context context, AttributeSet attrs) { super(context, attrs); @@ -74,16 +83,31 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp mContainer = getRootView().findViewById(R.id.keyguard_message_area_container); } - void onConfigChanged() { - final int newTopMargin = SystemBarUtils.getStatusBarHeight(getContext()); - if (mTopMargin == newTopMargin) { + void onConfigChanged(Configuration newConfig) { + if (mContainer == null) { return; } - mTopMargin = newTopMargin; - ViewGroup.MarginLayoutParams lp = + final int newTopMargin = SystemBarUtils.getStatusBarHeight(getContext()); + if (mContainerTopMargin != newTopMargin) { + mContainerTopMargin = newTopMargin; + ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mContainer.getLayoutParams(); - lp.topMargin = mTopMargin; - mContainer.setLayoutParams(lp); + lp.topMargin = mContainerTopMargin; + mContainer.setLayoutParams(lp); + } + + if (mLastOrientation != newConfig.orientation) { + mLastOrientation = newConfig.orientation; + int messageAreaTopMargin = 0; + if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { + messageAreaTopMargin = mContext.getResources().getDimensionPixelSize( + R.dimen.keyguard_lock_padding); + } + + ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams(); + lp.topMargin = messageAreaTopMargin; + setLayoutParams(lp); + } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java index 05318bb0df78..659aadd69614 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java @@ -50,7 +50,7 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag private ConfigurationListener mConfigurationListener = new ConfigurationListener() { @Override public void onConfigChanged(Configuration newConfig) { - mView.onConfigChanged(); + mView.onConfigChanged(newConfig); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index 1efda7edee2f..5b4f7a21f8d0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -21,10 +21,13 @@ import static com.android.systemui.statusbar.policy.DevicePostureController.DEVI import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; import android.util.AttributeSet; import android.view.View; +import android.view.ViewGroup; import android.view.animation.AnimationUtils; +import androidx.constraintlayout.helper.widget.Flow; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; @@ -87,48 +90,45 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { } private void updateMargins() { + Resources res = mContext.getResources(); + // Re-apply everything to the keys... - int bottomMargin = mContext.getResources().getDimensionPixelSize( - R.dimen.num_pad_entry_row_margin_bottom); - int rightMargin = mContext.getResources().getDimensionPixelSize( - R.dimen.num_pad_key_margin_end); - String ratio = mContext.getResources().getString(R.string.num_pad_key_ratio); - - // mView contains all Views that make up the PIN pad; row0 = the entry test field, then - // rows 1-4 contain the buttons. Iterate over all views that make up the buttons in the pad, - // and re-set all the margins. - for (int row = 1; row < 5; row++) { - for (int column = 0; column < 3; column++) { - View key = mViews[row][column]; - - ConstraintLayout.LayoutParams lp = - (ConstraintLayout.LayoutParams) key.getLayoutParams(); - - lp.dimensionRatio = ratio; - - // Don't set any margins on the last row of buttons. - if (row != 4) { - lp.bottomMargin = bottomMargin; - } - - // Don't set margins on the rightmost buttons. - if (column != 2) { - lp.rightMargin = rightMargin; - } - - key.setLayoutParams(lp); - } - } + int verticalMargin = res.getDimensionPixelSize(R.dimen.num_pad_entry_row_margin_bottom); + int horizontalMargin = res.getDimensionPixelSize(R.dimen.num_pad_key_margin_end); + String ratio = res.getString(R.string.num_pad_key_ratio); + + Flow flow = (Flow) mContainer.findViewById(R.id.flow1); + flow.setHorizontalGap(horizontalMargin); + flow.setVerticalGap(verticalMargin); // Update the guideline based on the device posture... - float halfOpenPercentage = - mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio); + float halfOpenPercentage = res.getFloat(R.dimen.half_opened_bouncer_height_ratio); ConstraintSet cs = new ConstraintSet(); cs.clone(mContainer); cs.setGuidelinePercent(R.id.pin_pad_top_guideline, mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f); cs.applyTo(mContainer); + + // Password entry area + int passwordHeight = res.getDimensionPixelSize(R.dimen.keyguard_password_height); + View pinEntry = findViewById(getPasswordTextViewId()); + ViewGroup.LayoutParams lp = pinEntry.getLayoutParams(); + lp.height = passwordHeight; + pinEntry.setLayoutParams(lp); + + // Below row0 + View row0 = findViewById(R.id.row0); + row0.setPadding(0, 0, 0, verticalMargin); + + // Above the emergency contact area + int marginTop = res.getDimensionPixelSize(R.dimen.keyguard_eca_top_margin); + View eca = findViewById(R.id.keyguard_selector_fade_container); + if (eca != null) { + ViewGroup.MarginLayoutParams mLp = (ViewGroup.MarginLayoutParams) eca.getLayoutParams(); + mLp.topMargin = marginTop; + eca.setLayoutParams(mLp); + } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 450949881aa4..b54d11d1f811 100755 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1164,6 +1164,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return fingerprintAllowed || faceAllowed; } + /** + * Returns whether the user is unlocked with a biometric that is currently bypassing + * the lock screen. + */ + public boolean getUserUnlockedWithBiometricAndIsBypassing(int userId) { + BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId); + BiometricAuthenticated face = mUserFaceAuthenticated.get(userId); + // fingerprint always bypasses + boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated + && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric); + boolean faceAllowed = face != null && face.mAuthenticated + && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric); + return fingerprintAllowed || faceAllowed && mKeyguardBypassController.canBypass(); + } + public boolean getUserTrustIsManaged(int userId) { return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt index b7404dfeb1cc..dfbe348c6ede 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt @@ -37,7 +37,7 @@ class BiometricDisplayListener( private val onChanged: () -> Unit ) : DisplayManager.DisplayListener { - private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0 + private var lastRotation = Surface.ROTATION_0 override fun onDisplayAdded(displayId: Int) {} override fun onDisplayRemoved(displayId: Int) {} @@ -63,6 +63,7 @@ class BiometricDisplayListener( /** Listen for changes. */ fun enable() { + lastRotation = context.display?.rotation ?: Surface.ROTATION_0 displayManager.registerDisplayListener(this, handler) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 44674ec297ba..f81706d35748 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -482,8 +482,33 @@ public class UdfpsController implements DozeReceiver { final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime; if (!isIlluminationRequested && !mGoodCaptureReceived && !exceedsVelocityThreshold) { - onFingerDown((int) event.getRawX(), (int) event.getRawY(), minor, - major); + final int rawX = (int) event.getRawX(); + final int rawY = (int) event.getRawY(); + // Default coordinates assume portrait mode. + int x = rawX; + int y = rawY; + + // Gets the size based on the current rotation of the display. + Point p = new Point(); + mContext.getDisplay().getRealSize(p); + + // Transform x, y to portrait mode if the device is in landscape mode. + switch (mContext.getDisplay().getRotation()) { + case Surface.ROTATION_90: + x = p.y - rawY; + y = rawX; + break; + + case Surface.ROTATION_270: + x = rawY; + y = p.x - rawX; + break; + + default: + // Do nothing to stay in portrait mode. + } + + onFingerDown(x, y, minor, major); Log.v(TAG, "onTouch | finger down: " + touchInfo); mTouchLogTime = mSystemClock.elapsedRealtime(); mPowerManager.userActivity(mSystemClock.uptimeMillis(), diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java index ac38b13cc4dd..9137dca6cc71 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java @@ -97,6 +97,8 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintWidthUpdateListener; @NonNull private final Animator.AnimatorListener mEdgeHintPulseListener; + private boolean mShowingNewUdfpsEnroll = false; + UdfpsEnrollDrawable(@NonNull Context context) { super(context); @@ -211,6 +213,8 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { @Override public void onAnimationRepeat(Animator animation) {} }; + mShowingNewUdfpsEnroll = context.getResources().getBoolean( + com.android.internal.R.bool.config_udfpsSupportsNewUi); } void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { @@ -292,6 +296,10 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { } mShouldShowTipHint = shouldShow; + if (mShowingNewUdfpsEnroll) { + return; + } + if (mTipHintWidthAnimator != null && mTipHintWidthAnimator.isRunning()) { mTipHintWidthAnimator.cancel(); } @@ -306,6 +314,7 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { } else { mTipHintWidthAnimator.start(); } + } private void updateEdgeHintVisibility() { @@ -315,6 +324,10 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { } mShouldShowEdgeHint = shouldShow; + if (mShowingNewUdfpsEnroll) { + return; + } + if (mEdgeHintWidthAnimator != null && mEdgeHintWidthAnimator.isRunning()) { mEdgeHintWidthAnimator.cancel(); } @@ -333,6 +346,10 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { } private void startTipHintPulseAnimation() { + if (mShowingNewUdfpsEnroll) { + return; + } + mHandler.removeCallbacksAndMessages(null); if (mTipHintAnimatorSet != null && mTipHintAnimatorSet.isRunning()) { mTipHintAnimatorSet.cancel(); @@ -353,6 +370,10 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { } private void startEdgeHintPulseAnimation() { + if (mShowingNewUdfpsEnroll) { + return; + } + mHandler.removeCallbacksAndMessages(null); if (mEdgeHintAnimatorSet != null && mEdgeHintAnimatorSet.isRunning()) { mEdgeHintAnimatorSet.cancel(); @@ -409,6 +430,10 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { mSensorOutlinePaint.setAlpha(mAlpha); } + if (mShowingNewUdfpsEnroll) { + return; + } + // Draw the finger tip or edges hint. if (isTipHintVisible() || isEdgeHintVisible()) { canvas.save(); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index befb648152d4..789ad6223e79 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -118,9 +118,11 @@ public class DozeMachine { switch (this) { case UNINITIALIZED: case INITIALIZED: - case DOZE_REQUEST_PULSE: return parameters.shouldControlScreenOff() ? Display.STATE_ON : Display.STATE_OFF; + case DOZE_REQUEST_PULSE: + return parameters.getDisplayNeedsBlanking() ? Display.STATE_OFF + : Display.STATE_ON; case DOZE_AOD_PAUSED: case DOZE: return Display.STATE_OFF; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index 36319380ad50..52db1bd00c8e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -105,17 +105,7 @@ public class DozeScreenState implements DozeMachine.Part { updateUdfpsController(); if (mUdfpsController == null) { - mAuthController.addCallback(new AuthController.Callback() { - @Override - public void onAllAuthenticatorsRegistered() { - updateUdfpsController(); - } - - @Override - public void onEnrollmentsChanged() { - updateUdfpsController(); - } - }); + mAuthController.addCallback(mAuthControllerCallback); } } @@ -128,6 +118,11 @@ public class DozeScreenState implements DozeMachine.Part { } @Override + public void destroy() { + mAuthController.removeCallback(mAuthControllerCallback); + } + + @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { int screenState = newState.screenState(mParameters); mDozeHost.cancelGentleSleep(); @@ -222,4 +217,16 @@ public class DozeScreenState implements DozeMachine.Part { mWakeLock.setAcquired(false); } } + + private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { + @Override + public void onAllAuthenticatorsRegistered() { + updateUdfpsController(); + } + + @Override + public void onEnrollmentsChanged() { + updateUdfpsController(); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index fabe92d2532d..94d5ee2b923c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator import android.content.Context import android.graphics.Matrix +import android.util.Log import android.view.RemoteAnimationTarget import android.view.SyncRtSurfaceTransactionApplier import android.view.View @@ -38,6 +39,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Lazy import javax.inject.Inject +const val TAG = "KeyguardUnlock" + /** * Starting scale factor for the app/launcher surface behind the keyguard, when it's animating * in during keyguard exit. @@ -254,7 +257,12 @@ class KeyguardUnlockAnimationController @Inject constructor( } fun hideKeyguardViewAfterRemoteAnimation() { - keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 350) + if (keyguardViewController.isShowing) { + keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 350) + } else { + Log.e(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " + + "showing. Ignoring...") + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 862358f02900..f30e2832c1aa 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -113,6 +113,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; @@ -210,7 +211,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, private static final int KEYGUARD_DONE_PENDING_TIMEOUT = 13; private static final int NOTIFY_STARTED_WAKING_UP = 14; private static final int NOTIFY_SCREEN_TURNED_ON = 15; - private static final int NOTIFY_SCREEN_TURNED_OFF = 16; private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 17; private static final int SYSTEM_READY = 18; private static final int CANCEL_KEYGUARD_EXIT_ANIM = 19; @@ -325,6 +325,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, // the properties of the keyguard private final KeyguardUpdateMonitor mUpdateMonitor; + private final Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy; /** * Last SIM state reported by the telephony system. @@ -436,7 +437,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, private boolean mInGestureNavigationMode; private boolean mWakeAndUnlocking; - private IKeyguardDrawnCallback mDrawnCallback; private CharSequence mCustomMessage; /** @@ -849,7 +849,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, KeyguardStateController keyguardStateController, Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationControllerLazy, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, - Lazy<NotificationShadeDepthController> notificationShadeDepthController) { + Lazy<NotificationShadeDepthController> notificationShadeDepthController, + Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy) { super(context); mFalsingCollector = falsingCollector; mLockPatternUtils = lockPatternUtils; @@ -865,6 +866,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, mKeyguardDisplayManager = keyguardDisplayManager; dumpManager.registerDumpable(getClass().getName(), this); mDeviceConfig = deviceConfig; + mNotificationShadeWindowControllerLazy = notificationShadeWindowControllerLazy; mShowHomeOverLockscreen = mDeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, @@ -1263,7 +1265,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, } public void onScreenTurnedOff() { - notifyScreenTurnedOff(); mUpdateMonitor.dispatchScreenTurnedOff(); } @@ -1491,7 +1492,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, public void doKeyguardTimeout(Bundle options) { mHandler.removeMessages(KEYGUARD_TIMEOUT); Message msg = mHandler.obtainMessage(KEYGUARD_TIMEOUT, options); - mHandler.sendMessage(msg); + // Treat these messages with priority - A call to timeout means the device should lock + // as soon as possible and not wait for other messages on the thread to process first. + mHandler.sendMessageAtFrontOfQueue(msg); } /** @@ -1669,23 +1672,20 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, mHandler.sendMessage(msg); } - private void notifyScreenTurnedOff() { - if (DEBUG) Log.d(TAG, "notifyScreenTurnedOff"); - Message msg = mHandler.obtainMessage(NOTIFY_SCREEN_TURNED_OFF); - mHandler.sendMessage(msg); - } - /** * Send message to keyguard telling it to show itself * @see #handleShow */ private void showLocked(Bundle options) { - Trace.beginSection("KeyguardViewMediator#showLocked aqcuiring mShowKeyguardWakeLock"); + Trace.beginSection("KeyguardViewMediator#showLocked acquiring mShowKeyguardWakeLock"); if (DEBUG) Log.d(TAG, "showLocked"); // ensure we stay awake until we are finished displaying the keyguard mShowKeyguardWakeLock.acquire(); Message msg = mHandler.obtainMessage(SHOW, options); - mHandler.sendMessage(msg); + // Treat these messages with priority - This call can originate from #doKeyguardTimeout, + // meaning the device should lock as soon as possible and not wait for other messages on + // the thread to process first. + mHandler.sendMessageAtFrontOfQueue(msg); Trace.endSection(); } @@ -1859,9 +1859,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, handleNotifyScreenTurnedOn(); Trace.endSection(); break; - case NOTIFY_SCREEN_TURNED_OFF: - handleNotifyScreenTurnedOff(); - break; case NOTIFY_STARTED_WAKING_UP: Trace.beginSection( "KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP"); @@ -1886,6 +1883,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, case KEYGUARD_TIMEOUT: synchronized (KeyguardViewMediator.this) { doKeyguardLocked((Bundle) msg.obj); + notifyDefaultDisplayCallbacks(mShowing); } break; case DISMISS: @@ -1896,10 +1894,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, Trace.beginSection( "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM"); StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj; - handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration, - params.mApps, params.mWallpapers, params.mNonApps, - params.mFinishedCallback); - mFalsingCollector.onSuccessfulUnlock(); + mNotificationShadeWindowControllerLazy.get().batchApplyWindowLayoutParams( + () -> { + handleStartKeyguardExitAnimation(params.startTime, + params.fadeoutDuration, params.mApps, params.mWallpapers, + params.mNonApps, params.mFinishedCallback); + mFalsingCollector.onSuccessfulUnlock(); + }); Trace.endSection(); break; case CANCEL_KEYGUARD_EXIT_ANIM: @@ -2197,10 +2198,12 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, mKeyguardGoingAwayRunnable.run(); } else { // TODO(bc-unlock): Fill parameters - handleStartKeyguardExitAnimation( - SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(), - mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */, - null /* nonApps */, null /* finishedCallback */); + mNotificationShadeWindowControllerLazy.get().batchApplyWindowLayoutParams(() -> { + handleStartKeyguardExitAnimation( + SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(), + mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */, + null /* nonApps */, null /* finishedCallback */); + }); } } Trace.endSection(); @@ -2237,16 +2240,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, IRemoteAnimationRunner runner = mKeyguardExitAnimationRunner; mKeyguardExitAnimationRunner = null; - if (mWakeAndUnlocking && mDrawnCallback != null) { - - // Hack level over 9000: To speed up wake-and-unlock sequence, force it to report - // the next draw from here so we don't have to wait for window manager to signal - // this to our ViewRootImpl. - mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw(); - notifyDrawn(mDrawnCallback); - mDrawnCallback = null; - } - LatencyTracker.getInstance(mContext) .onActionEnd(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK); @@ -2602,11 +2595,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, mKeyguardViewControllerLazy.get().onScreenTurningOn(); if (callback != null) { - if (mWakeAndUnlocking) { - mDrawnCallback = callback; - } else { - notifyDrawn(callback); - } + notifyDrawn(callback); } } Trace.endSection(); @@ -2621,13 +2610,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, Trace.endSection(); } - private void handleNotifyScreenTurnedOff() { - synchronized (this) { - if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff"); - mDrawnCallback = null; - } - } - private void notifyDrawn(final IKeyguardDrawnCallback callback) { Trace.beginSection("KeyguardViewMediator#notifyDrawn"); if (mPendingDrawnTasks.decrementAndGet() == 0) { @@ -2796,7 +2778,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, pw.print(" mPendingLock: "); pw.println(mPendingLock); pw.print(" mPendingDrawnTasks: "); pw.println(mPendingDrawnTasks.get()); pw.print(" mWakeAndUnlocking: "); pw.println(mWakeAndUnlocking); - pw.print(" mDrawnCallback: "); pw.println(mDrawnCallback); } /** @@ -2895,7 +2876,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, for (int i = size - 1; i >= 0; i--) { IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i); try { - callback.onShowingStateChanged(showing); + callback.onShowingStateChanged(showing, KeyguardUpdateMonitor.getCurrentUser()); } catch (RemoteException e) { Slog.w(TAG, "Failed to call onShowingStateChanged", e); if (e instanceof DeadObjectException) { @@ -2929,7 +2910,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, mKeyguardStateCallbacks.add(callback); try { callback.onSimSecureStateChanged(mUpdateMonitor.isSimPinSecure()); - callback.onShowingStateChanged(mShowing); + callback.onShowingStateChanged(mShowing, KeyguardUpdateMonitor.getCurrentUser()); callback.onInputRestrictedStateChanged(mInputRestricted); callback.onTrustedChanged(mUpdateMonitor.getUserHasTrust( KeyguardUpdateMonitor.getCurrentUser())); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index cae9feeb62eb..88dcf6d35075 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -42,6 +42,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardLiftController; @@ -97,7 +98,8 @@ public class KeyguardModule { KeyguardStateController keyguardStateController, Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, - Lazy<NotificationShadeDepthController> notificationShadeDepthController) { + Lazy<NotificationShadeDepthController> notificationShadeDepthController, + Lazy<NotificationShadeWindowController> notificationShadeWindowController) { return new KeyguardViewMediator( context, falsingCollector, @@ -120,7 +122,8 @@ public class KeyguardModule { keyguardStateController, keyguardUnlockAnimationController, unlockedScreenOffAnimationController, - notificationShadeDepthController + notificationShadeDepthController, + notificationShadeWindowController ); } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index b85f10726040..70f89e499ed5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -209,17 +209,17 @@ class MediaCarouselController @Inject constructor( oldKey: String?, data: MediaData, immediately: Boolean, - receivedSmartspaceCardLatency: Int + receivedSmartspaceCardLatency: Int, + isSsReactivated: Boolean ) { - if (addOrUpdatePlayer(key, oldKey, data)) { + if (addOrUpdatePlayer(key, oldKey, data, isSsReactivated)) { // Log card received if a new resumable media card is added MediaPlayerData.getMediaPlayer(key)?.let { /* ktlint-disable max-line-length */ logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED it.mInstanceId, it.mUid, - /* isRecommendationCard */ false, - intArrayOf( + surfaces = intArrayOf( SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE, SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN), rank = MediaPlayerData.getMediaPlayerIndex(key)) @@ -242,8 +242,7 @@ class MediaCarouselController @Inject constructor( logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED it.mInstanceId, it.mUid, - /* isRecommendationCard */ false, - intArrayOf( + surfaces = intArrayOf( SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE, SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN), rank = index, @@ -277,12 +276,17 @@ class MediaCarouselController @Inject constructor( override fun onSmartspaceMediaDataLoaded( key: String, data: SmartspaceMediaData, - shouldPrioritize: Boolean, - isSsReactivated: Boolean + shouldPrioritize: Boolean ) { if (DEBUG) Log.d(TAG, "Loading Smartspace media update") + // Log the case where the hidden media carousel with the existed inactive resume + // media is shown by the Smartspace signal. if (data.isActive) { - if (isSsReactivated && shouldPrioritize) { + val hasActivatedExistedResumeMedia = + !mediaManager.hasActiveMedia() && + mediaManager.hasAnyMedia() && + shouldPrioritize + if (hasActivatedExistedResumeMedia) { // Log resume card received if resumable media card is reactivated and // recommendation card is valid and ranked first MediaPlayerData.players().forEachIndexed { index, it -> @@ -294,8 +298,7 @@ class MediaCarouselController @Inject constructor( logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED it.mInstanceId, it.mUid, - /* isRecommendationCard */ false, - intArrayOf( + surfaces = intArrayOf( SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE, SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN), rank = index, @@ -310,8 +313,7 @@ class MediaCarouselController @Inject constructor( logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED it.mInstanceId, it.mUid, - /* isRecommendationCard */ true, - intArrayOf( + surfaces = intArrayOf( SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE, SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN), rank = MediaPlayerData.getMediaPlayerIndex(key), @@ -408,7 +410,12 @@ class MediaCarouselController @Inject constructor( } // Returns true if new player is added - private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData): Boolean { + private fun addOrUpdatePlayer( + key: String, + oldKey: String?, + data: MediaData, + isSsReactivated: Boolean + ): Boolean { val dataCopy = data.copy(backgroundColor = bgColor) MediaPlayerData.moveIfExists(oldKey, key) val existingPlayer = MediaPlayerData.getMediaPlayer(key) @@ -424,12 +431,13 @@ class MediaCarouselController @Inject constructor( newPlayer.playerViewHolder?.player?.setLayoutParams(lp) newPlayer.bindPlayer(dataCopy, key) newPlayer.setListening(currentlyExpanded) - MediaPlayerData.addMediaPlayer(key, dataCopy, newPlayer, systemClock) + MediaPlayerData.addMediaPlayer(key, dataCopy, newPlayer, systemClock, isSsReactivated) updatePlayerToState(newPlayer, noAnimation = true) reorderAllPlayers(curVisibleMediaKey) } else { existingPlayer.bindPlayer(dataCopy, key) - MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer, systemClock) + MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer, systemClock, + isSsReactivated) if (visualStabilityManager.isReorderingAllowed || shouldScrollToActivePlayer) { reorderAllPlayers(curVisibleMediaKey) } else { @@ -523,8 +531,10 @@ class MediaCarouselController @Inject constructor( it.targetId, it, MediaPlayerData.shouldPrioritizeSs) } } else { + val isSsReactivated = MediaPlayerData.isSsReactivated(key) removePlayer(key, dismissMediaData = false, dismissRecommendation = false) - addOrUpdatePlayer(key = key, oldKey = null, data = data) + addOrUpdatePlayer( + key = key, oldKey = null, data = data, isSsReactivated = isSsReactivated) } } } @@ -682,7 +692,8 @@ class MediaCarouselController @Inject constructor( this.desiredHostState = it currentlyExpanded = it.expansion > 0 - val shouldCloseGuts = !currentlyExpanded && !mediaManager.hasActiveMedia() && + val shouldCloseGuts = !currentlyExpanded && + !mediaManager.hasActiveMediaOrRecommendation() && desiredHostState.showsOnlyActiveMedia for (mediaPlayer in MediaPlayerData.players()) { @@ -747,7 +758,6 @@ class MediaCarouselController @Inject constructor( val mediaControlPanel = MediaPlayerData.players().elementAt(visibleMediaIndex) val hasActiveMediaOrRecommendationCard = MediaPlayerData.hasActiveMediaOrRecommendationCard() - val isRecommendationCard = mediaControlPanel.recommendationViewHolder != null if (!hasActiveMediaOrRecommendationCard && !qsExpanded) { // Skip logging if on LS or QQS, and there is no active media card return @@ -755,7 +765,6 @@ class MediaCarouselController @Inject constructor( logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN mediaControlPanel.mInstanceId, mediaControlPanel.mUid, - isRecommendationCard, intArrayOf(mediaControlPanel.surfaceForSmartspaceLogging)) mediaControlPanel.mIsImpressed = true } @@ -769,7 +778,6 @@ class MediaCarouselController @Inject constructor( * @param instanceId id to uniquely identify a card, e.g. each headphone generates a new * instanceId * @param uid uid for the application that media comes from - * @param isRecommendationCard whether the card is media recommendation * @param surfaces list of display surfaces the media card is on (e.g. lockscreen, shade) when * the event happened * @param interactedSubcardRank the rank for interacted media item for recommendation card, -1 @@ -779,21 +787,27 @@ class MediaCarouselController @Inject constructor( * @param rank the rank for media card in the media carousel, starting from 0 * @param receivedLatencyMillis latency in milliseconds for card received events. E.g. latency * between headphone connection to sysUI displays media recommendation card + * @param isSwipeToDismiss whether is to log swipe-to-dismiss event * */ fun logSmartspaceCardReported( eventId: Int, instanceId: Int, uid: Int, - isRecommendationCard: Boolean, surfaces: IntArray, interactedSubcardRank: Int = 0, interactedSubcardCardinality: Int = 0, rank: Int = mediaCarouselScrollHandler.visibleMediaIndex, - receivedLatencyMillis: Int = 0 + receivedLatencyMillis: Int = 0, + isSwipeToDismiss: Boolean = false ) { + if (MediaPlayerData.players().size <= rank) { + return + } + + val mediaControlKey = MediaPlayerData.playerKeys().elementAt(rank) // Only log media resume card when Smartspace data is available - if (!isRecommendationCard && + if (!mediaControlKey.isSsMediaRec && !mediaManager.smartspaceMediaData.isActive && MediaPlayerData.smartspaceMediaData == null) { return @@ -809,22 +823,28 @@ class MediaCarouselController @Inject constructor( // card type for each new feature. SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD, surface, - rank, + // Use -1 as rank value to indicate user swipe to dismiss the card + if (isSwipeToDismiss) -1 else rank, cardinality, - if (isRecommendationCard) + if (mediaControlKey.isSsMediaRec) 15 // MEDIA_RECOMMENDATION + else if (mediaControlKey.isSsReactivated) + 43 // MEDIA_RESUME_SS_ACTIVATED else 31, // MEDIA_RESUME uid, interactedSubcardRank, interactedSubcardCardinality, - receivedLatencyMillis + receivedLatencyMillis, + null // Media cards cannot have subcards. ) /* ktlint-disable max-line-length */ if (DEBUG) { Log.d(TAG, "Log Smartspace card event id: $eventId instance id: $instanceId" + " surface: $surface rank: $rank cardinality: $cardinality " + - "isRecommendationCard: $isRecommendationCard uid: $uid " + + "isRecommendationCard: ${mediaControlKey.isSsMediaRec} " + + "isSsReactivated: ${mediaControlKey.isSsReactivated}" + + "uid: $uid " + "interactedSubcardRank: $interactedSubcardRank " + "interactedSubcardCardinality: $interactedSubcardCardinality " + "received_latency_millis: $receivedLatencyMillis") @@ -839,10 +859,9 @@ class MediaCarouselController @Inject constructor( logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS it.mInstanceId, it.mUid, - it.recommendationViewHolder != null, intArrayOf(it.surfaceForSmartspaceLogging), - // Use -1 as rank value to indicate user swipe to dismiss the card - rank = -1) + rank = index, + isSwipeToDismiss = true) // Reset card impressed state when swipe to dismissed it.mIsImpressed = false } @@ -871,29 +890,37 @@ internal object MediaPlayerData { private set data class MediaSortKey( - // Whether the item represents a Smartspace media recommendation. - val isSsMediaRec: Boolean, + val isSsMediaRec: Boolean, // Whether the item represents a Smartspace media recommendation. val data: MediaData, - val updateTime: Long = 0 + val updateTime: Long = 0, + val isSsReactivated: Boolean = false ) private val comparator = compareByDescending<MediaSortKey> { it.data.isPlaying == true && it.data.playbackLocation == MediaData.PLAYBACK_LOCAL } - .thenByDescending { it.data.isPlaying == true && - it.data.playbackLocation == MediaData.PLAYBACK_CAST_LOCAL } - .thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec } - .thenByDescending { !it.data.resumption } - .thenByDescending { it.data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE } - .thenByDescending { it.updateTime } - .thenByDescending { it.data.notificationKey } + .thenByDescending { it.data.isPlaying == true && + it.data.playbackLocation == MediaData.PLAYBACK_CAST_LOCAL + } + .thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec } + .thenByDescending { !it.data.resumption } + .thenByDescending { it.data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE } + .thenByDescending { it.updateTime } + .thenByDescending { it.data.notificationKey } private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator) private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf() - fun addMediaPlayer(key: String, data: MediaData, player: MediaControlPanel, clock: SystemClock) { + fun addMediaPlayer( + key: String, + data: MediaData, + player: MediaControlPanel, + clock: SystemClock, + isSsReactivated: Boolean + ) { removeMediaPlayer(key) - val sortKey = MediaSortKey(isSsMediaRec = false, data, clock.currentTimeMillis()) + val sortKey = MediaSortKey(isSsMediaRec = false, + data, clock.currentTimeMillis(), isSsReactivated = isSsReactivated) mediaData.put(key, sortKey) mediaPlayers.put(sortKey, player) } @@ -907,8 +934,8 @@ internal object MediaPlayerData { ) { shouldPrioritizeSs = shouldPrioritize removeMediaPlayer(key) - val sortKey = MediaSortKey(/* isSsMediaRec= */ true, - EMPTY.copy(isPlaying = false), clock.currentTimeMillis()) + val sortKey = MediaSortKey(isSsMediaRec = true, + EMPTY.copy(isPlaying = false), clock.currentTimeMillis(), isSsReactivated = true) mediaData.put(key, sortKey) mediaPlayers.put(sortKey, player) smartspaceMediaData = data @@ -988,4 +1015,8 @@ internal object MediaPlayerData { } return false } + + fun isSsReactivated(key: String): Boolean = mediaData.get(key)?.let { + it.isSsReactivated + } ?: false }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index f66eb5ba008a..65b06946d3e7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -22,6 +22,7 @@ import android.app.PendingIntent; import android.app.smartspace.SmartspaceAction; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.ColorStateList; @@ -35,6 +36,7 @@ import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Process; import android.text.Layout; +import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -85,6 +87,7 @@ public class MediaControlPanel { private static final int MEDIA_RECOMMENDATION_MAX_NUM = 6; private static final String KEY_SMARTSPACE_ARTIST_NAME = "artist_name"; private static final String KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND"; + private static final String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME"; private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS); @@ -155,8 +158,8 @@ public class MediaControlPanel { mSeekBarViewModel.setLogSmartspaceClick(() -> { logSmartspaceCardReported( - 760, // SMARTSPACE_CARD_CLICK - /* isRecommendationCard */ false); + 760 // SMARTSPACE_CARD_CLICK + ); return Unit.INSTANCE; }); } @@ -323,8 +326,9 @@ public class MediaControlPanel { if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return; if (mMediaViewController.isGutsVisible()) return; - logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK - /* isRecommendationCard */ false); + logSmartspaceCardReported( + 760 // SMARTSPACE_CARD_CLICK + ); mActivityStarter.postStartActivityDismissingKeyguard(clickIntent, buildLaunchAnimatorController(mPlayerViewHolder.getPlayer())); }); @@ -444,8 +448,9 @@ public class MediaControlPanel { button.setEnabled(true); button.setOnClickListener(v -> { if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK - /* isRecommendationCard */ false); + logSmartspaceCardReported( + 760 // SMARTSPACE_CARD_CLICK + ); action.run(); } }); @@ -481,8 +486,9 @@ public class MediaControlPanel { mPlayerViewHolder.getDismiss().setOnClickListener(v -> { if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return; - logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS - /* isRecommendationCard */ false); + logSmartspaceCardReported( + 761 // SMARTSPACE_CARD_DISMISS + ); if (mKey != null) { closeGuts(); @@ -578,18 +584,33 @@ public class MediaControlPanel { icon.setColorFilter(getGrayscaleFilter()); ImageView headerLogoImageView = mRecommendationViewHolder.getCardIcon(); headerLogoImageView.setImageDrawable(icon); + // Set up media source app's label text. - CharSequence appLabel = packageManager.getApplicationLabel(applicationInfo); - if (appLabel.length() != 0) { + CharSequence appName = getAppName(data.getCardAction()); + if (TextUtils.isEmpty(appName)) { + Intent launchIntent = + packageManager.getLaunchIntentForPackage(data.getPackageName()); + if (launchIntent != null) { + ActivityInfo launchActivity = launchIntent.resolveActivityInfo(packageManager, 0); + appName = launchActivity.loadLabel(packageManager); + } else { + Log.w(TAG, "Package " + data.getPackageName() + + " does not have a main launcher activity. Fallback to full app name"); + appName = packageManager.getApplicationLabel(applicationInfo); + } + } + // Set the app name as card's title. + if (!TextUtils.isEmpty(appName)) { TextView headerTitleText = mRecommendationViewHolder.getCardText(); - headerTitleText.setText(appLabel); + headerTitleText.setText(appName); } + // Set up media rec card's tap action if applicable. setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(), /* interactedSubcardRank */ -1); // Set up media rec card's accessibility label. recommendationCard.setContentDescription( - mContext.getString(R.string.controls_media_smartspace_rec_description, appLabel)); + mContext.getString(R.string.controls_media_smartspace_rec_description, appName)); List<ImageView> mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems(); List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers(); @@ -634,12 +655,12 @@ public class MediaControlPanel { mediaCoverImageView.setContentDescription( mContext.getString( R.string.controls_media_smartspace_rec_item_no_artist_description, - recommendation.getTitle(), appLabel)); + recommendation.getTitle(), appName)); } else { mediaCoverImageView.setContentDescription( mContext.getString( R.string.controls_media_smartspace_rec_item_description, - recommendation.getTitle(), artistName, appLabel)); + recommendation.getTitle(), artistName, appName)); } if (uiComponentIndex < MEDIA_RECOMMENDATION_ITEMS_PER_ROW) { @@ -665,8 +686,9 @@ public class MediaControlPanel { mRecommendationViewHolder.getDismiss().setOnClickListener(v -> { if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return; - logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS - /* isRecommendationCard */ true); + logSmartspaceCardReported( + 761 // SMARTSPACE_CARD_DISMISS + ); closeGuts(); mMediaDataManagerLazy.get().dismissSmartspaceRecommendation( data.getTargetId(), MediaViewController.GUTS_ANIMATION_DURATION + 100L); @@ -823,7 +845,6 @@ public class MediaControlPanel { if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return; logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK - /* isRecommendationCard */ true, interactedSubcardRank, getSmartspaceSubCardCardinality()); @@ -844,6 +865,17 @@ public class MediaControlPanel { }); } + /** Returns the upstream app name if available. */ + @Nullable + private String getAppName(SmartspaceAction action) { + if (action == null || action.getIntent() == null + || action.getIntent().getExtras() == null) { + return null; + } + + return action.getIntent().getExtras().getString(KEY_SMARTSPACE_APP_NAME); + } + /** Returns if the Smartspace action will open the activity in foreground. */ private boolean shouldSmartspaceRecItemOpenInForeground(SmartspaceAction action) { if (action == null || action.getIntent() == null @@ -882,18 +914,17 @@ public class MediaControlPanel { return SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DEFAULT_SURFACE; } - private void logSmartspaceCardReported(int eventId, boolean isRecommendationCard) { - logSmartspaceCardReported(eventId, isRecommendationCard, + private void logSmartspaceCardReported(int eventId) { + logSmartspaceCardReported(eventId, /* interactedSubcardRank */ 0, /* interactedSubcardCardinality */ 0); } - private void logSmartspaceCardReported(int eventId, boolean isRecommendationCard, + private void logSmartspaceCardReported(int eventId, int interactedSubcardRank, int interactedSubcardCardinality) { mMediaCarouselController.logSmartspaceCardReported(eventId, mInstanceId, mUid, - isRecommendationCard, new int[]{getSurfaceForSmartspaceLogging()}, interactedSubcardRank, interactedSubcardCardinality); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt index b68f2a7654f0..311973ad5af0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -32,7 +32,8 @@ class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener, oldKey: String?, data: MediaData, immediately: Boolean, - receivedSmartspaceCardLatency: Int + receivedSmartspaceCardLatency: Int, + isSsReactivated: Boolean ) { if (oldKey != null && oldKey != key && entries.contains(oldKey)) { entries[key] = data to entries.remove(oldKey)?.second @@ -46,8 +47,7 @@ class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener, override fun onSmartspaceMediaDataLoaded( key: String, data: SmartspaceMediaData, - shouldPrioritize: Boolean, - isSsReactivated: Boolean + shouldPrioritize: Boolean ) { listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt index ae5c1f2b19a9..919bce2cb360 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt @@ -87,7 +87,8 @@ class MediaDataFilter @Inject constructor( oldKey: String?, data: MediaData, immediately: Boolean, - receivedSmartspaceCardLatency: Int + receivedSmartspaceCardLatency: Int, + isSsReactivated: Boolean ) { if (oldKey != null && oldKey != key) { allEntries.remove(oldKey) @@ -112,8 +113,7 @@ class MediaDataFilter @Inject constructor( override fun onSmartspaceMediaDataLoaded( key: String, data: SmartspaceMediaData, - shouldPrioritize: Boolean, - isSsReactivated: Boolean + shouldPrioritize: Boolean ) { if (!data.isActive) { Log.d(TAG, "Inactive recommendation data. Skip triggering.") @@ -138,13 +138,12 @@ class MediaDataFilter @Inject constructor( } } - val activeMedia = userEntries.filter { (key, value) -> value.active } - var isSsReactivatedMutable = activeMedia.isEmpty() && userEntries.isNotEmpty() + val shouldReactivate = !hasActiveMedia() && hasAnyMedia() if (timeSinceActive < smartspaceMaxAgeMillis) { // It could happen there are existing active media resume cards, then we don't need to // reactivate. - if (isSsReactivatedMutable) { + if (shouldReactivate) { val lastActiveKey = sorted.lastKey() // most recently active // Notify listeners to consider this media active Log.d(TAG, "reactivating $lastActiveKey instead of smartspace") @@ -154,7 +153,7 @@ class MediaDataFilter @Inject constructor( it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData, receivedSmartspaceCardLatency = (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis) - .toInt()) + .toInt(), isSsReactivated = true) } } } else { @@ -166,8 +165,7 @@ class MediaDataFilter @Inject constructor( Log.d(TAG, "Invalid recommendation data. Skip showing the rec card") return } - listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable, - isSsReactivatedMutable) } + listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) } } override fun onMediaDataRemoved(key: String) { @@ -258,14 +256,27 @@ class MediaDataFilter @Inject constructor( } /** - * Are there any media notifications active? + * Are there any media notifications active, including the recommendation? */ - fun hasActiveMedia() = userEntries.any { it.value.active } || smartspaceMediaData.isActive + fun hasActiveMediaOrRecommendation() = + userEntries.any { it.value.active } || + (smartspaceMediaData.isActive && smartspaceMediaData.isValid) /** * Are there any media entries we should display? */ - fun hasAnyMedia() = userEntries.isNotEmpty() || smartspaceMediaData.isActive + fun hasAnyMediaOrRecommendation() = userEntries.isNotEmpty() || + (smartspaceMediaData.isActive && smartspaceMediaData.isValid) + + /** + * Are there any media notifications active (excluding the recommendation)? + */ + fun hasActiveMedia() = userEntries.any { it.value.active } + + /** + * Are there any media entries we should display (excluding the recommendation)? + */ + fun hasAnyMedia() = userEntries.isNotEmpty() /** * Add a listener for filtered [MediaData] changes diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 7c0f7fc2967e..5d2d556a5773 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -828,15 +828,27 @@ class MediaDataManager( fun onSwipeToDismiss() = mediaDataFilter.onSwipeToDismiss() /** - * Are there any media notifications active? + * Are there any media notifications active, including the recommendations? */ - fun hasActiveMedia() = mediaDataFilter.hasActiveMedia() + fun hasActiveMediaOrRecommendation() = mediaDataFilter.hasActiveMediaOrRecommendation() /** - * Are there any media entries we should display? + * Are there any media entries we should display, including the recommendations? * If resumption is enabled, this will include inactive players * If resumption is disabled, we only want to show active players */ + fun hasAnyMediaOrRecommendation() = mediaDataFilter.hasAnyMediaOrRecommendation() + + /** + * Are there any resume media notifications active, excluding the recommendations? + */ + fun hasActiveMedia() = mediaDataFilter.hasActiveMedia() + + /** + * Are there any resume media notifications active, excluding the recommendations? + * If resumption is enabled, this will include inactive players + * If resumption is disabled, we only want to show active players + */ fun hasAnyMedia() = mediaDataFilter.hasAnyMedia() interface Listener { @@ -855,13 +867,17 @@ class MediaDataManager( * @param receivedSmartspaceCardLatency is the latency between headphone connects and sysUI * displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace * signal. + * + * @param isSsReactivated indicates resume media card is reactivated by Smartspace + * recommendation signal */ fun onMediaDataLoaded( key: String, oldKey: String?, data: MediaData, immediately: Boolean = true, - receivedSmartspaceCardLatency: Int = 0 + receivedSmartspaceCardLatency: Int = 0, + isSsReactivated: Boolean = false ) {} /** @@ -870,15 +886,11 @@ class MediaDataManager( * @param shouldPrioritize indicates the sorting priority of the Smartspace card. If true, * it will be prioritized as the first card. Otherwise, it will show up as the last card as * default. - * - * @param isSsReactivated indicates resume media card is reactivated by Smartspace - * recommendation signal */ fun onSmartspaceMediaDataLoaded( key: String, data: SmartspaceMediaData, - shouldPrioritize: Boolean = false, - isSsReactivated: Boolean = false + shouldPrioritize: Boolean = false ) {} /** Called whenever a previously existing Media notification was removed. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index bed254fe8249..085bae8bc25d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -68,7 +68,8 @@ class MediaDeviceManager @Inject constructor( oldKey: String?, data: MediaData, immediately: Boolean, - receivedSmartspaceCardLatency: Int + receivedSmartspaceCardLatency: Int, + isSsReactivated: Boolean ) { if (oldKey != null && oldKey != key) { val oldEntry = entries.remove(oldKey) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index c8cd43287c99..a7640ff951c8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -28,6 +28,7 @@ import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay import androidx.annotation.VisibleForTesting +import com.android.keyguard.KeyguardViewController import com.android.systemui.R import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton @@ -39,7 +40,6 @@ import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.KeyguardBypassController -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.Utils @@ -82,7 +82,7 @@ class MediaHierarchyManager @Inject constructor( private val notifLockscreenUserManager: NotificationLockscreenUserManager, configurationController: ConfigurationController, wakefulnessLifecycle: WakefulnessLifecycle, - private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager + private val keyguardViewController: KeyguardViewController ) { /** @@ -998,7 +998,7 @@ class MediaHierarchyManager @Inject constructor( private fun isLockScreenVisibleToUser(): Boolean { return !statusBarStateController.isDozing && - !statusBarKeyguardViewManager.isBouncerShowing && + !keyguardViewController.isBouncerShowing && statusBarStateController.state == StatusBarState.KEYGUARD && notifLockscreenUserManager.shouldShowLockscreenNotifications() && statusBarStateController.isExpanded && @@ -1007,7 +1007,7 @@ class MediaHierarchyManager @Inject constructor( private fun isLockScreenShadeVisibleToUser(): Boolean { return !statusBarStateController.isDozing && - !statusBarKeyguardViewManager.isBouncerShowing && + !keyguardViewController.isBouncerShowing && (statusBarStateController.state == StatusBarState.SHADE_LOCKED || (statusBarStateController.state == StatusBarState.KEYGUARD && qsExpanded)) } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index 0a4b68b501f7..3ed90fd0c58c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -61,7 +61,8 @@ class MediaHost constructor( oldKey: String?, data: MediaData, immediately: Boolean, - receivedSmartspaceCardLatency: Int + receivedSmartspaceCardLatency: Int, + isSsReactivated: Boolean ) { if (immediately) { updateViewVisibility() @@ -71,8 +72,7 @@ class MediaHost constructor( override fun onSmartspaceMediaDataLoaded( key: String, data: SmartspaceMediaData, - shouldPrioritize: Boolean, - isSsReactivated: Boolean + shouldPrioritize: Boolean ) { updateViewVisibility() } @@ -162,9 +162,9 @@ class MediaHost constructor( private fun updateViewVisibility() { state.visible = if (showsOnlyActiveMedia) { - mediaDataManager.hasActiveMedia() + mediaDataManager.hasActiveMediaOrRecommendation() } else { - mediaDataManager.hasAnyMedia() + mediaDataManager.hasAnyMediaOrRecommendation() } val newVisibility = if (visible) View.VISIBLE else View.GONE if (newVisibility != hostView.visibility) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index 35f95dd27c1f..61d0b41e9bb6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -184,7 +184,8 @@ class MediaResumeListener @Inject constructor( oldKey: String?, data: MediaData, immediately: Boolean, - receivedSmartspaceCardLatency: Int + receivedSmartspaceCardLatency: Int, + isSsReactivated: Boolean ) { if (useMediaResumption) { // If this had been started from a resume state, disconnect now that it's live diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt index 1c448a2ff8c4..31792967899d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt @@ -96,7 +96,8 @@ class MediaSessionBasedFilter @Inject constructor( oldKey: String?, data: MediaData, immediately: Boolean, - receivedSmartspaceCardLatency: Int + receivedSmartspaceCardLatency: Int, + isSsReactivated: Boolean ) { backgroundExecutor.execute { data.token?.let { @@ -143,8 +144,7 @@ class MediaSessionBasedFilter @Inject constructor( override fun onSmartspaceMediaDataLoaded( key: String, data: SmartspaceMediaData, - shouldPrioritize: Boolean, - isSsReactivated: Boolean + shouldPrioritize: Boolean ) { backgroundExecutor.execute { dispatchSmartspaceMediaDataLoaded(key, data) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt index 9581a633a8c5..51755065d4b6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt @@ -63,7 +63,8 @@ class MediaTimeoutListener @Inject constructor( oldKey: String?, data: MediaData, immediately: Boolean, - receivedSmartspaceCardLatency: Int + receivedSmartspaceCardLatency: Int, + isSsReactivated: Boolean ) { var reusedListener: PlaybackStateListener? = null diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index 113ba59cd514..e01916f0abe8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -177,13 +177,9 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mConnectedItem = mContainerLayout; mBottomDivider.setVisibility(View.GONE); mCheckBox.setVisibility(View.GONE); - if (mController.getSelectableMediaDevice().size() > 0) { - mAddIcon.setVisibility(View.VISIBLE); - mAddIcon.setTransitionAlpha(1); - mAddIcon.setOnClickListener(this::onEndItemClick); - } else { - mAddIcon.setVisibility(View.GONE); - } + mAddIcon.setVisibility(View.VISIBLE); + mAddIcon.setTransitionAlpha(1); + mAddIcon.setOnClickListener(this::onEndItemClick); mTitleIcon.setImageDrawable(getSpeakerDrawable()); final CharSequence sessionName = mController.getSessionName(); final CharSequence title = TextUtils.isEmpty(sessionName) @@ -198,7 +194,10 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { if (mController.isTransferring()) { return; } - + if (isCurrentlyConnected(device)) { + Log.d(TAG, "This device is already connected! : " + device.getName()); + return; + } mCurrentActivePosition = -1; playSwitchingAnim(mConnectedItem, view); mController.connectDevice(device); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index f2cb254c3b97..b309c1bb67b9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -150,6 +150,10 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements } void refresh() { + refresh(false); + } + + void refresh(boolean deviceSetChanged) { // Update header icon final int iconRes = getHeaderIconRes(); final IconCompat iconCompat = getHeaderIcon(); @@ -175,7 +179,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements } if (!mAdapter.isDragging() && !mAdapter.isAnimating()) { int currentActivePosition = mAdapter.getCurrentActivePosition(); - if (currentActivePosition >= 0 && currentActivePosition < mAdapter.getItemCount()) { + if (!deviceSetChanged && currentActivePosition >= 0 + && currentActivePosition < mAdapter.getItemCount()) { mAdapter.notifyItemChanged(currentActivePosition); } else { mAdapter.notifyDataSetChanged(); @@ -215,6 +220,11 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements } @Override + public void onDeviceListChanged() { + mMainThreadHandler.post(() -> refresh(true)); + } + + @Override public void dismissDialog() { dismiss(); } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index a1e2c57c5c37..0d368fa5fb7f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -84,7 +84,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { private final SystemUIDialogManager mDialogManager; private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>(); private final boolean mAboveStatusbar; - private final boolean mVolumeAdjustmentForRemoteGroupSessions; private final NotificationEntryManager mNotificationEntryManager; @VisibleForTesting final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); @@ -117,8 +116,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName); mUiEventLogger = uiEventLogger; mDialogLaunchAnimator = dialogLaunchAnimator; - mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions); mDialogManager = dialogManager; } @@ -166,7 +163,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { @Override public void onDeviceListUpdate(List<MediaDevice> devices) { buildMediaDevices(devices); - mCallback.onRouteChanged(); + mCallback.onDeviceListChanged(); } @Override @@ -496,10 +493,15 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { || features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK)); } + private boolean isPlayBackInfoLocal() { + return mMediaController.getPlaybackInfo() != null + && mMediaController.getPlaybackInfo().getPlaybackType() + == MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL; + } + boolean isVolumeControlEnabled(@NonNull MediaDevice device) { - // TODO(b/202500642): Also enable volume control for remote non-group sessions. - return !isActiveRemoteDevice(device) - || mVolumeAdjustmentForRemoteGroupSessions; + return isPlayBackInfoLocal() + || mLocalMediaManager.isMediaSessionAvailableForVolumeControl(); } private final MediaController.Callback mCb = new MediaController.Callback() { @@ -529,11 +531,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { void onMediaStoppedOrPaused(); /** - * Override to handle the device updating. + * Override to handle the device status or attributes updating. */ void onRouteChanged(); /** + * Override to handle the devices set updating. + */ + void onDeviceListChanged(); + + /** * Override to dismiss dialog. */ void dismissDialog(); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java index a201c071bbbe..6c95cc661e7f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java @@ -107,6 +107,8 @@ public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter { initSeekbar(device); final List<MediaDevice> selectedDevices = mController.getSelectedMediaDevice(); if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) { + mSeekBar.setEnabled(false); + mSeekBar.setOnTouchListener((v, event) -> true); mCheckBox.setButtonDrawable(R.drawable.ic_check_box); mCheckBox.setChecked(false); mCheckBox.setEnabled(true); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index e82e9d284bdd..e348f2ca7b31 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -428,7 +428,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca public void setBrightnessMirrorController( BrightnessMirrorController brightnessMirrorController) { mQSPanelController.setBrightnessMirror(brightnessMirrorController); - mQuickQSPanelController.setBrightnessMirror(brightnessMirrorController); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index 001c740e310a..1837b5bf2044 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -182,7 +182,9 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { /** */ public void setListening(boolean listening, boolean expanded) { - setListening(listening && expanded); + // TODO(218268829): checking for split shade is workaround but when proper fix lands + // "|| mShouldUseSplitNotificationShade" should be removed + setListening(listening && (expanded || mShouldUseSplitNotificationShade)); if (mView.isListening()) { refreshAllTiles(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt deleted file mode 100644 index 65889d792769..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.qs - -import androidx.annotation.VisibleForTesting -import com.android.systemui.settings.brightness.BrightnessController -import com.android.systemui.settings.brightness.BrightnessSliderController -import com.android.systemui.settings.brightness.MirroredBrightnessController -import com.android.systemui.statusbar.policy.BrightnessMirrorController -import javax.inject.Inject - -/** - * Controls brightness slider in QQS, which is visible only in split shade. It's responsible for - * showing/hiding it when appropriate and (un)registering listeners - */ -class QuickQSBrightnessController @VisibleForTesting constructor( - private val brightnessControllerFactory: () -> BrightnessController -) : MirroredBrightnessController { - - @Inject constructor( - brightnessControllerFactory: BrightnessController.Factory, - brightnessSliderControllerFactory: BrightnessSliderController.Factory, - quickQSPanel: QuickQSPanel - ) : this(brightnessControllerFactory = { - val slider = brightnessSliderControllerFactory.create(quickQSPanel.context, - quickQSPanel) - slider.init() - quickQSPanel.setBrightnessView(slider.rootView) - brightnessControllerFactory.create(slider) - }) - - private var isListening = false - private var brightnessController: BrightnessController? = null - private var mirrorController: BrightnessMirrorController? = null - - fun init(shouldUseSplitNotificationShade: Boolean) { - refreshVisibility(shouldUseSplitNotificationShade) - } - - /** - * Starts/Stops listening for brightness changing events. - * It's fine to call this function even if slider is not visible (which would be the case for - * all small screen devices), it will just do nothing in that case - */ - fun setListening(listening: Boolean) { - if (listening) { - // controller can be null when slider was never shown - if (!isListening && brightnessController != null) { - brightnessController?.registerCallbacks() - isListening = true - } - } else { - brightnessController?.unregisterCallbacks() - isListening = false - } - } - - fun checkRestrictionAndSetEnabled() { - brightnessController?.checkRestrictionAndSetEnabled() - } - - fun refreshVisibility(shouldUseSplitNotificationShade: Boolean) { - if (shouldUseSplitNotificationShade) { - showBrightnessSlider() - } else { - hideBrightnessSlider() - } - } - - override fun setMirror(controller: BrightnessMirrorController) { - mirrorController = controller - mirrorController?.let { brightnessController?.setMirror(it) } - } - - private fun hideBrightnessSlider() { - brightnessController?.hideSlider() - } - - private fun showBrightnessSlider() { - if (brightnessController == null) { - brightnessController = brightnessControllerFactory() - mirrorController?.also { brightnessController?.setMirror(it) } - brightnessController?.registerCallbacks() - isListening = true - } - brightnessController?.showSlider() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index 92690c7d1202..fdd764ce7127 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -30,8 +30,6 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.logging.QSLogger; -import com.android.systemui.settings.brightness.BrightnessMirrorHandler; -import com.android.systemui.statusbar.policy.BrightnessMirrorController; import java.util.ArrayList; import java.util.List; @@ -51,9 +49,6 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> } }; - // brightness is visible only in split shade - private final QuickQSBrightnessController mBrightnessController; - private final BrightnessMirrorHandler mBrightnessMirrorHandler; private final FooterActionsController mFooterActionsController; @Inject @@ -63,13 +58,10 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> @Named(QUICK_QS_PANEL) MediaHost mediaHost, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, DumpManager dumpManager, - QuickQSBrightnessController quickQSBrightnessController, @Named(QQS_FOOTER) FooterActionsController footerActionsController ) { super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger, uiEventLogger, qsLogger, dumpManager); - mBrightnessController = quickQSBrightnessController; - mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController); mFooterActionsController = footerActionsController; } @@ -79,7 +71,6 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> mMediaHost.setExpansion(0.0f); mMediaHost.setShowsOnlyActiveMedia(true); mMediaHost.init(MediaHierarchyManager.LOCATION_QQS); - mBrightnessController.init(mShouldUseSplitNotificationShade); mFooterActionsController.init(); mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade); } @@ -88,20 +79,17 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> protected void onViewAttached() { super.onViewAttached(); mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener); - mBrightnessMirrorHandler.onQsPanelAttached(); } @Override protected void onViewDetached() { super.onViewDetached(); mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); - mBrightnessMirrorHandler.onQsPanelDettached(); } @Override void setListening(boolean listening) { super.setListening(listening); - mBrightnessController.setListening(listening); mFooterActionsController.setListening(listening); } @@ -115,14 +103,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> } @Override - public void refreshAllTiles() { - mBrightnessController.checkRestrictionAndSetEnabled(); - super.refreshAllTiles(); - } - - @Override protected void onConfigurationChanged() { - mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade); mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade); } @@ -146,8 +127,4 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> public int getNumQuickTiles() { return mView.getNumQuickTiles(); } - - public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) { - mBrightnessMirrorHandler.setController(brightnessMirrorController); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 0e3b5b5c882b..5b378007d570 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -242,13 +242,12 @@ public class NotificationMediaManager implements Dumpable { @Override public void onMediaDataLoaded(@NonNull String key, @Nullable String oldKey, @NonNull MediaData data, boolean immediately, - int receivedSmartspaceCardLatency) { + int receivedSmartspaceCardLatency, boolean isSsReactivated) { } @Override public void onSmartspaceMediaDataLoaded(@NonNull String key, - @NonNull SmartspaceMediaData data, boolean shouldPrioritize, - boolean isSsReactivated) { + @NonNull SmartspaceMediaData data, boolean shouldPrioritize) { } @Override @@ -317,13 +316,12 @@ public class NotificationMediaManager implements Dumpable { @Override public void onMediaDataLoaded(@NonNull String key, @Nullable String oldKey, @NonNull MediaData data, boolean immediately, - int receivedSmartspaceCardLatency) { + int receivedSmartspaceCardLatency, boolean isSsReactivated) { } @Override public void onSmartspaceMediaDataLoaded(@NonNull String key, - @NonNull SmartspaceMediaData data, boolean shouldPrioritize, - boolean isSsReactivated) { + @NonNull SmartspaceMediaData data, boolean shouldPrioritize) { } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java index 6ea79af8b9ad..65ff5583e7d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar; import android.view.ViewGroup; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; @@ -188,6 +189,14 @@ public interface NotificationShadeWindowController extends RemoteInputController default void setLightRevealScrimOpaque(boolean opaque) {} /** + * Defer any application of window {@link WindowManager.LayoutParams} until {@code scope} is + * fully applied. + */ + default void batchApplyWindowLayoutParams(@NonNull Runnable scope) { + scope.run(); + } + + /** * Custom listener to pipe data back to plugins about whether or not the status bar would be * collapsed if not for the plugin. * TODO: Find cleaner way to do this. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 464b2b69c58e..e0e928073336 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -25,6 +25,7 @@ import android.util.Log; import android.view.View; import android.view.ViewGroup; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; @@ -42,6 +43,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.Assert; import com.android.wm.shell.bubbles.Bubbles; @@ -92,6 +94,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle private final KeyguardBypassController mBypassController; private final ForegroundServiceSectionController mFgsSectionController; private AssistantFeedbackController mAssistantFeedbackController; + private final KeyguardStateController mKeyguardStateController; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final Context mContext; private NotificationPresenter mPresenter; @@ -121,7 +125,9 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle ForegroundServiceSectionController fgsSectionController, DynamicChildBindController dynamicChildBindController, LowPriorityInflationHelper lowPriorityInflationHelper, - AssistantFeedbackController assistantFeedbackController) { + AssistantFeedbackController assistantFeedbackController, + KeyguardUpdateMonitor keyguardUpdateMonitor, + KeyguardStateController keyguardStateController) { mContext = context; mHandler = mainHandler; mFeatureFlags = featureFlags; @@ -140,6 +146,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle mDynamicChildBindController = dynamicChildBindController; mLowPriorityInflationHelper = lowPriorityInflationHelper; mAssistantFeedbackController = assistantFeedbackController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mKeyguardStateController = keyguardStateController; } public void setUpWithPresenter(NotificationPresenter presenter, @@ -163,6 +171,11 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle beginUpdate(); + boolean dynamicallyUnlocked = mDynamicPrivacyController.isDynamicallyUnlocked() + && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD + && mKeyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing( + KeyguardUpdateMonitor.getCurrentUser())) + && !mKeyguardStateController.isKeyguardGoingAway(); List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications(); ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size()); final int N = activeNotifications.size(); @@ -181,7 +194,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle boolean devicePublic = mLockscreenUserManager.isLockscreenPublicMode(currentUserId); boolean userPublic = devicePublic || mLockscreenUserManager.isLockscreenPublicMode(userId); - if (userPublic && mDynamicPrivacyController.isDynamicallyUnlocked() + if (userPublic && dynamicallyUnlocked && (userId == currentUserId || userId == UserHandle.USER_ALL || !mLockscreenUserManager.needsSeparateWorkChallenge(userId))) { userPublic = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index da2b85ee0b61..af503a9360ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -28,6 +28,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.os.SystemProperties; +import android.os.Trace; import android.text.format.DateFormat; import android.util.FloatProperty; import android.util.Log; @@ -507,6 +508,7 @@ public class StatusBarStateControllerImpl implements } private void recordHistoricalState(int newState, int lastState, boolean upcoming) { + Trace.traceCounter(Trace.TRACE_TAG_APP, "statusBarState", newState); mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; HistoricalState state = mHistoricalRecords[mHistoryIndex]; state.mNewState = newState; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index f2d926d97108..4c5522fc66c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -23,6 +23,7 @@ import android.os.Handler; import android.service.dreams.IDreamManager; import com.android.internal.statusbar.IStatusBarService; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.SysUISingleton; @@ -69,6 +70,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.tracing.ProtoTracer; @@ -201,7 +203,9 @@ public interface StatusBarDependenciesModule { ForegroundServiceSectionController fgsSectionController, DynamicChildBindController dynamicChildBindController, LowPriorityInflationHelper lowPriorityInflationHelper, - AssistantFeedbackController assistantFeedbackController) { + AssistantFeedbackController assistantFeedbackController, + KeyguardUpdateMonitor keyguardUpdateMonitor, + KeyguardStateController keyguardStateController) { return new NotificationViewHierarchyManager( context, mainHandler, @@ -217,7 +221,9 @@ public interface StatusBarDependenciesModule { fgsSectionController, dynamicChildBindController, lowPriorityInflationHelper, - assistantFeedbackController); + assistantFeedbackController, + keyguardUpdateMonitor, + keyguardStateController); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index fe1cd7b98cf9..43710916628a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -157,6 +157,8 @@ public class KeyguardCoordinator implements Coordinator { } } + // TODO(b/206118999): merge this class with SensitiveContentCoordinator which also depends on + // these same updates private void setupInvalidateNotifListCallbacks() { // register onKeyguardShowing callback mKeyguardStateController.addCallback(mKeyguardCallback); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt index a115e0400de3..9c82cb64a0d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt @@ -17,7 +17,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.os.UserHandle +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.DynamicPrivacyController import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry @@ -26,6 +29,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator +import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Module import dagger.Provides @@ -36,9 +40,13 @@ object SensitiveContentCoordinatorModule { @CoordinatorScope fun provideCoordinator( dynamicPrivacyController: DynamicPrivacyController, - lockscreenUserManager: NotificationLockscreenUserManager + lockscreenUserManager: NotificationLockscreenUserManager, + keyguardUpdateMonitor: KeyguardUpdateMonitor, + statusBarStateController: StatusBarStateController, + keyguardStateController: KeyguardStateController ): SensitiveContentCoordinator = - SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager) + SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager, + keyguardUpdateMonitor, statusBarStateController, keyguardStateController) } /** Coordinates re-inflation and post-processing of sensitive notification content. */ @@ -46,7 +54,10 @@ interface SensitiveContentCoordinator : Coordinator private class SensitiveContentCoordinatorImpl( private val dynamicPrivacyController: DynamicPrivacyController, - private val lockscreenUserManager: NotificationLockscreenUserManager + private val lockscreenUserManager: NotificationLockscreenUserManager, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val statusBarStateController: StatusBarStateController, + private val keyguardStateController: KeyguardStateController ) : Invalidator("SensitiveContentInvalidator"), SensitiveContentCoordinator, DynamicPrivacyController.Listener, @@ -61,6 +72,19 @@ private class SensitiveContentCoordinatorImpl( override fun onDynamicPrivacyChanged(): Unit = invalidateList() override fun onBeforeRenderList(entries: List<ListEntry>) { + if (keyguardStateController.isKeyguardGoingAway() || + statusBarStateController.getState() == StatusBarState.KEYGUARD && + keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing( + KeyguardUpdateMonitor.getCurrentUser())) { + // don't update yet if: + // - the keyguard is currently going away + // - LS is about to be dismissed by a biometric that bypasses LS (avoid notif flash) + + // TODO(b/206118999): merge this class with KeyguardCoordinator which ensures the + // dependent state changes invalidate the pipeline + return + } + val currentUserId = lockscreenUserManager.currentUserId val devicePublic = lockscreenUserManager.isLockscreenPublicMode(currentUserId) val deviceSensitive = devicePublic && diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 98b5dcc25730..2df56bfa3909 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -405,7 +405,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp // During wake and unlock, we need to draw black before waking up to avoid abrupt // brightness changes due to display state transitions. boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn(); - boolean delayWakeUp = mode == MODE_WAKE_AND_UNLOCK && alwaysOnEnabled && mWakeUpDelay > 0; Runnable wakeUp = ()-> { if (!wasDeviceInteractive) { if (DEBUG_BIO_WAKELOCK) { @@ -414,15 +413,12 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, "android.policy:BIOMETRIC"); } - if (delayWakeUp) { - mKeyguardViewMediator.onWakeAndUnlocking(); - } Trace.beginSection("release wake-and-unlock"); releaseBiometricWakeLock(); Trace.endSection(); }; - if (!delayWakeUp && mMode != MODE_NONE) { + if (mMode != MODE_NONE) { wakeUp.run(); } switch (mMode) { @@ -472,11 +468,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mUpdateMonitor.awakenFromDream(); } mNotificationShadeWindowController.setNotificationShadeFocusable(false); - if (delayWakeUp) { - mHandler.postDelayed(wakeUp, mWakeUpDelay); - } else { - mKeyguardViewMediator.onWakeAndUnlocking(); - } + mKeyguardViewMediator.onWakeAndUnlocking(); Trace.endSection(); break; case MODE_ONLY_WAKE: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 0dda75b6bfd9..5f596f8afb7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -147,6 +147,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.RemoteInputController; @@ -669,7 +670,9 @@ public class NotificationPanelViewController extends PanelViewController { NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, - StatusBarStateController statusBarStateController, DozeLog dozeLog, + StatusBarStateController statusBarStateController, + NotificationShadeWindowController notificationShadeWindowController, + DozeLog dozeLog, DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, @@ -722,6 +725,7 @@ public class NotificationPanelViewController extends PanelViewController { dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, + notificationShadeWindowController, vibratorHelper, statusBarKeyguardViewManager, latencyTracker, @@ -1304,9 +1308,11 @@ public class NotificationPanelViewController extends PanelViewController { int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard; boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController - .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); + .getVisibleNotificationCount() != 0 + || mMediaDataManager.hasActiveMediaOrRecommendation(); boolean splitShadeWithActiveMedia = - mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia(); + mShouldUseSplitNotificationShade + && mMediaDataManager.hasActiveMediaOrRecommendation(); if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade) || (splitShadeWithActiveMedia && !mDozing)) { mKeyguardStatusViewController.displayClock(SMALL); @@ -1372,7 +1378,8 @@ public class NotificationPanelViewController extends PanelViewController { private void updateKeyguardStatusViewAlignment(boolean animate) { boolean hasVisibleNotifications = mNotificationStackScrollLayoutController - .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); + .getVisibleNotificationCount() != 0 + || mMediaDataManager.hasActiveMediaOrRecommendation(); boolean shouldBeCentered = !mShouldUseSplitNotificationShade || !hasVisibleNotifications || mDozing; if (mStatusViewCentered != shouldBeCentered) { @@ -2587,7 +2594,7 @@ public class NotificationPanelViewController extends PanelViewController { float endPosition = 0; if (pxAmount > 0.0f) { if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0 - && !mMediaDataManager.hasActiveMedia()) { + && !mMediaDataManager.hasActiveMediaOrRecommendation()) { // No notifications are visible, let's animate to the height of qs instead if (mQs != null) { // Let's interpolate to the header height instead of the top padding, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index 030a8951943d..8c76a1bf4f83 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -107,6 +107,12 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private final SysuiColorExtractor mColorExtractor; private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private float mFaceAuthDisplayBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE; + /** + * Layout params would be aggregated and dispatched all at once if this is > 0. + * + * @see #batchApplyWindowLayoutParams(Runnable) + */ + private int mDeferWindowLayoutParams; @Inject public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager, @@ -433,6 +439,20 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW } } + private void applyWindowLayoutParams() { + if (mDeferWindowLayoutParams == 0 && mLp != null && mLp.copyFrom(mLpChanged) != 0) { + mWindowManager.updateViewLayout(mNotificationShadeView, mLp); + } + } + + @Override + public void batchApplyWindowLayoutParams(Runnable scope) { + mDeferWindowLayoutParams++; + scope.run(); + mDeferWindowLayoutParams--; + applyWindowLayoutParams(); + } + private void apply(State state) { applyKeyguardFlags(state); applyFocusableFlag(state); @@ -447,9 +467,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW applyHasTopUi(state); applyNotTouchable(state); applyStatusBarColorSpaceAgnosticFlag(state); - if (mLp != null && mLp.copyFrom(mLpChanged) != 0) { - mWindowManager.updateViewLayout(mNotificationShadeView, mLp); - } + applyWindowLayoutParams(); + if (mHasTopUi != mHasTopUiChanged) { whitelistIpcs(() -> { try { @@ -722,6 +741,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW pw.println(TAG + ":"); pw.println(" mKeyguardMaxRefreshRate=" + mKeyguardMaxRefreshRate); pw.println(" mKeyguardPreferredRefreshRate=" + mKeyguardPreferredRefreshRate); + pw.println(" mDeferWindowLayoutParams=" + mDeferWindowLayoutParams); pw.println(mCurrentState); if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) { mNotificationShadeView.getViewRootImpl().dump(" ", pw); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index ef6ed66061fe..956ac31b3627 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -54,6 +54,7 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.classifier.Classifier; import com.android.systemui.doze.DozeLog; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; @@ -183,6 +184,7 @@ public abstract class PanelViewController { private boolean mExpandLatencyTracking; private final PanelView mView; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private final NotificationShadeWindowController mNotificationShadeWindowController; protected final Resources mResources; protected final KeyguardStateController mKeyguardStateController; protected final SysuiStatusBarStateController mStatusBarStateController; @@ -221,6 +223,7 @@ public abstract class PanelViewController { DozeLog dozeLog, KeyguardStateController keyguardStateController, SysuiStatusBarStateController statusBarStateController, + NotificationShadeWindowController notificationShadeWindowController, VibratorHelper vibratorHelper, StatusBarKeyguardViewManager statusBarKeyguardViewManager, LatencyTracker latencyTracker, @@ -253,6 +256,7 @@ public abstract class PanelViewController { mResources = mView.getResources(); mKeyguardStateController = keyguardStateController; mStatusBarStateController = statusBarStateController; + mNotificationShadeWindowController = notificationShadeWindowController; mFlingAnimationUtils = flingAnimationUtilsBuilder .reset() .setMaxLengthSeconds(0.6f) @@ -761,34 +765,36 @@ public abstract class PanelViewController { if (isNaN(h)) { Log.wtf(TAG, "ExpandedHeight set to NaN"); } - if (mExpandLatencyTracking && h != 0f) { - DejankUtils.postAfterTraversal( - () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL)); - mExpandLatencyTracking = false; - } - float maxPanelHeight = getMaxPanelHeight(); - if (mHeightAnimator == null) { - if (mTracking) { - float overExpansionPixels = Math.max(0, h - maxPanelHeight); - setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */); + mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> { + if (mExpandLatencyTracking && h != 0f) { + DejankUtils.postAfterTraversal( + () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL)); + mExpandLatencyTracking = false; + } + float maxPanelHeight = getMaxPanelHeight(); + if (mHeightAnimator == null) { + if (mTracking) { + float overExpansionPixels = Math.max(0, h - maxPanelHeight); + setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */); + } + mExpandedHeight = Math.min(h, maxPanelHeight); + } else { + mExpandedHeight = h; } - mExpandedHeight = Math.min(h, maxPanelHeight); - } else { - mExpandedHeight = h; - } - // If we are closing the panel and we are almost there due to a slow decelerating - // interpolator, abort the animation. - if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) { - mExpandedHeight = 0f; - if (mHeightAnimator != null) { - mHeightAnimator.end(); + // If we are closing the panel and we are almost there due to a slow decelerating + // interpolator, abort the animation. + if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) { + mExpandedHeight = 0f; + if (mHeightAnimator != null) { + mHeightAnimator.end(); + } } - } - mExpandedFraction = Math.min(1f, - maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight); - onHeightUpdated(mExpandedHeight); - updatePanelExpansionAndVisibility(); + mExpandedFraction = Math.min(1f, + maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight); + onHeightUpdated(mExpandedHeight); + updatePanelExpansionAndVisibility(); + }); } /** 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 2784cbb2136b..50f5b216bfd6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1351,8 +1351,11 @@ public class StatusBar extends SystemUI implements * keyguard. */ private void dispatchPanelExpansionForKeyguardDismiss(float fraction, boolean trackingTouch) { - // Things that mean we're not dismissing the keyguard, and should ignore this expansion: + // Things that mean we're not swiping to dismiss the keyguard, and should ignore this + // expansion: // - Keyguard isn't even visible. + // - Keyguard is occluded. Expansion changes here are the shade being expanded over the + // occluding activity. // - Keyguard is visible, but can't be dismissed (swiping up will show PIN/password prompt). // - The SIM is locked, you can't swipe to unlock. If the SIM is locked but there is no // device lock set, canDismissLockScreen returns true even though you should not be able @@ -1360,6 +1363,7 @@ public class StatusBar extends SystemUI implements // - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the // keyguard. if (!isKeyguardShowing() + || mIsOccluded || !mKeyguardStateController.canDismissLockScreen() || mKeyguardViewMediator.isAnySimPinSecure() || (mNotificationPanelViewController.isQsExpanded() && trackingTouch)) { @@ -2957,6 +2961,7 @@ public class StatusBar extends SystemUI implements } public void showKeyguardImpl() { + Trace.beginSection("StatusBar#showKeyguard"); mIsKeyguard = true; if (mKeyguardStateController.isLaunchTransitionFadingAway()) { mNotificationPanelViewController.cancelAnimation(); @@ -2969,6 +2974,7 @@ public class StatusBar extends SystemUI implements mStatusBarStateController.setState(StatusBarState.KEYGUARD); } updatePanelExpansionForKeyguard(); + Trace.endSection(); } private void updatePanelExpansionForKeyguard() { @@ -3557,26 +3563,29 @@ public class StatusBar extends SystemUI implements public void onStartedWakingUp() { String tag = "StatusBar#onStartedWakingUp"; DejankUtils.startDetectingBlockingIpcs(tag); - mDeviceInteractive = true; - mWakeUpCoordinator.setWakingUp(true); - if (!mKeyguardBypassController.getBypassEnabled()) { - mHeadsUpManager.releaseAllImmediately(); - } - updateVisibleToUser(); - updateIsKeyguard(); - mDozeServiceHost.stopDozing(); - // This is intentionally below the stopDozing call above, since it avoids that we're - // unnecessarily animating the wakeUp transition. Animations should only be enabled - // once we fully woke up. - updateRevealEffect(true /* wakingUp */); - updateNotificationPanelTouchState(); - - // If we are waking up during the screen off animation, we should undo making the - // expanded visible (we did that so the LightRevealScrim would be visible). - if (mUnlockedScreenOffAnimationController.isScreenOffLightRevealAnimationPlaying()) { - makeExpandedInvisible(); - } + mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> { + mDeviceInteractive = true; + mWakeUpCoordinator.setWakingUp(true); + if (!mKeyguardBypassController.getBypassEnabled()) { + mHeadsUpManager.releaseAllImmediately(); + } + updateVisibleToUser(); + updateIsKeyguard(); + mDozeServiceHost.stopDozing(); + // This is intentionally below the stopDozing call above, since it avoids that we're + // unnecessarily animating the wakeUp transition. Animations should only be enabled + // once we fully woke up. + updateRevealEffect(true /* wakingUp */); + updateNotificationPanelTouchState(); + + // If we are waking up during the screen off animation, we should undo making the + // expanded visible (we did that so the LightRevealScrim would be visible). + if (mUnlockedScreenOffAnimationController + .isScreenOffLightRevealAnimationPlaying()) { + makeExpandedInvisible(); + } + }); DejankUtils.stopDetectingBlockingIpcs(tag); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 48fe77482340..fd435d45934a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; +import com.android.systemui.util.Assert; import java.util.ArrayList; import java.util.List; @@ -65,6 +66,8 @@ public interface StatusBarIconController { void addIconGroup(IconManager iconManager); /** */ void removeIconGroup(IconManager iconManager); + /** Refresh the state of an IconManager by recreating the views */ + void refreshIconGroup(IconManager iconManager); /** */ void setExternalIcon(String slot); /** */ @@ -242,6 +245,7 @@ public interface StatusBarIconController { protected final int mIconSize; // Whether or not these icons show up in dumpsys protected boolean mShouldLog = false; + private StatusBarIconController mController; // Enables SystemUI demo mode to take effect in this group protected boolean mDemoable = true; @@ -266,13 +270,17 @@ public interface StatusBarIconController { mDemoable = demoable; } + void setController(StatusBarIconController controller) { + mController = controller; + } + public void setBlockList(@Nullable List<String> blockList) { + Assert.isMainThread(); mBlockList.clear(); - if (blockList == null || blockList.isEmpty()) { - return; - } - mBlockList.addAll(blockList); + if (mController != null) { + mController.refreshIconGroup(this); + } } public void setShouldLog(boolean should) { 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 88a7dc7bcd75..d6cf80ec70aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -99,6 +99,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } + group.setController(this); mIconGroups.add(group); List<Slot> allSlots = getSlots(); for (int i = 0; i < allSlots.size(); i++) { @@ -114,6 +115,12 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } + @Override + public void refreshIconGroup(IconManager iconManager) { + removeIconGroup(iconManager); + addIconGroup(iconManager); + } + private void refreshIconGroups() { for (int i = mIconGroups.size() - 1; i >= 0; --i) { IconManager group = mIconGroups.get(i); 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 87c693b29a55..f31e7264841d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -29,6 +29,7 @@ import android.content.res.ColorStateList; import android.hardware.biometrics.BiometricSourceType; import android.os.Bundle; import android.os.SystemClock; +import android.os.Trace; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -377,6 +378,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ @Override public void show(Bundle options) { + Trace.beginSection("StatusBarKeyguardViewManager#show"); mShowing = true; mNotificationShadeWindowController.setKeyguardShowing(true); mKeyguardStateController.notifyKeyguardState(mShowing, @@ -384,6 +386,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb reset(true /* hideBouncerWhenShowing */); SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN); + Trace.endSection(); } /** @@ -722,6 +725,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void hide(long startTime, long fadeoutDuration) { + Trace.beginSection("StatusBarKeyguardViewManager#hide"); mShowing = false; mKeyguardStateController.notifyKeyguardState(mShowing, mKeyguardStateController.isOccluded()); @@ -821,6 +825,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN); + Trace.endSection(); } private boolean needsBypassFading() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 14cca13b396b..8750845e57a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -60,6 +60,9 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.settings.SecureSettings; + +import java.util.concurrent.Executor; import javax.inject.Named; @@ -257,7 +260,9 @@ public abstract class StatusBarViewModule { StatusBarStateController statusBarStateController, CommandQueue commandQueue, CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, - OperatorNameViewController.Factory operatorNameViewControllerFactory + OperatorNameViewController.Factory operatorNameViewControllerFactory, + SecureSettings secureSettings, + @Main Executor mainExecutor ) { return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory, ongoingCallController, @@ -274,6 +279,8 @@ public abstract class StatusBarViewModule { statusBarStateController, commandQueue, collapsedStatusBarFragmentLogger, - operatorNameViewControllerFactory); + operatorNameViewControllerFactory, + secureSettings, + mainExecutor); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index d6ba6f3ff97a..1a2f8a8e5db7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -29,8 +29,10 @@ import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Fragment; +import android.database.ContentObserver; import android.os.Bundle; import android.os.Parcelable; +import android.provider.Settings; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -38,8 +40,11 @@ import android.view.ViewGroup; import android.view.ViewStub; import android.widget.LinearLayout; +import androidx.annotation.VisibleForTesting; + import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; @@ -65,11 +70,13 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.EncryptionHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.SecureSettings; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -113,6 +120,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final PanelExpansionStateManager mPanelExpansionStateManager; private final StatusBarIconController mStatusBarIconController; private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; + private final SecureSettings mSecureSettings; + private final Executor mMainExecutor; private List<String> mBlockedIcons = new ArrayList<>(); @@ -148,7 +157,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue StatusBarStateController statusBarStateController, CommandQueue commandQueue, CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, - OperatorNameViewController.Factory operatorNameViewControllerFactory + OperatorNameViewController.Factory operatorNameViewControllerFactory, + SecureSettings secureSettings, + @Main Executor mainExecutor ) { mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory; mOngoingCallController = ongoingCallController; @@ -166,6 +177,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mCommandQueue = commandQueue; mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger; mOperatorNameViewControllerFactory = operatorNameViewControllerFactory; + mSecureSettings = secureSettings; + mMainExecutor = mainExecutor; } @Override @@ -190,10 +203,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags); mDarkIconManager.setShouldLog(true); - mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume)); - mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock)); - mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength)); - mDarkIconManager.setBlockList(mBlockedIcons); + updateBlockedIcons(); mStatusBarIconController.addIconGroup(mDarkIconManager); mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area); mClockView = mStatusBar.findViewById(R.id.clock); @@ -206,6 +216,24 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mAnimationScheduler.addCallback(this); } + @VisibleForTesting + void updateBlockedIcons() { + mBlockedIcons.clear(); + + if (mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0) == 0) { + mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume)); + } + mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock)); + mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength)); + + mMainExecutor.execute(() -> mDarkIconManager.setBlockList(mBlockedIcons)); + } + + @VisibleForTesting + List<String> getBlockedIcons() { + return mBlockedIcons; + } + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); @@ -220,6 +248,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mCommandQueue.addCallback(this); mStatusBarStateController.addCallback(this); initOngoingCallChip(); + + mSecureSettings.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON), + false, + mVolumeSettingObserver); } @Override @@ -228,6 +261,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); mOngoingCallController.removeCallback(mOngoingCallListener); + mSecureSettings.unregisterContentObserver(mVolumeSettingObserver); } @Override @@ -597,6 +631,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mLocationPublisher.updateStatusBarMargin(leftMargin, rightMargin); } + private final ContentObserver mVolumeSettingObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + updateBlockedIcons(); + } + }; + // Listen for view end changes of PhoneStatusBarView and publish that to the privacy dot private View.OnLayoutChangeListener mStatusBarLayoutListener = (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index 12258136c011..67985b95dda4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -21,7 +21,7 @@ import android.app.IActivityManager import android.app.IUidObserver import android.app.Notification import android.app.Notification.CallStyle.CALL_TYPE_ONGOING -import android.content.Intent +import android.app.PendingIntent import android.util.Log import android.view.View import androidx.annotation.VisibleForTesting @@ -98,7 +98,7 @@ class OngoingCallController @Inject constructor( val newOngoingCallInfo = CallNotificationInfo( entry.sbn.key, entry.sbn.notification.`when`, - entry.sbn.notification.contentIntent?.intent, + entry.sbn.notification.contentIntent, entry.sbn.uid, entry.sbn.notification.extras.getInt( Notification.EXTRA_CALL_TYPE, -1) == CALL_TYPE_ONGOING, @@ -230,7 +230,6 @@ class OngoingCallController @Inject constructor( logger.logChipClicked() activityStarter.postStartActivityDismissingKeyguard( intent, - 0, ActivityLaunchAnimator.Controller.fromView( backgroundView, InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP) @@ -351,7 +350,7 @@ class OngoingCallController @Inject constructor( private data class CallNotificationInfo( val key: String, val callStartTime: Long, - val intent: Intent?, + val intent: PendingIntent?, val uid: Int, /** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */ val isOngoing: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java new file mode 100644 index 000000000000..df845e53a36b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.usb; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.util.Log; + +import com.android.systemui.R; + +import java.lang.annotation.Retention; + +/** + * USB Audio devices warning dialog messages help class. + */ +public class UsbAudioWarningDialogMessage { + private static final String TAG = "UsbAudioWarningDialogMessage"; + + @Retention(SOURCE) + @IntDef({TYPE_PERMISSION, TYPE_CONFIRM}) + public @interface DialogType {} + public static final int TYPE_PERMISSION = 0; + public static final int TYPE_CONFIRM = 1; + + private final int mDialogType; + private UsbDialogHelper mDialogHelper; + + public UsbAudioWarningDialogMessage(Context context, Intent intent, @DialogType int type) { + mDialogType = type; + try { + mDialogHelper = new UsbDialogHelper(context, intent); + } catch (IllegalStateException e) { + Log.e(TAG, "Unable to initialize UsbDialogHelper!", e); + } + } + + private boolean hasRecordPermission() { + return mDialogHelper.packageHasAudioRecordingPermission(); + } + + private boolean isUsbAudioDevice() { + return mDialogHelper.isUsbDevice() && (mDialogHelper.deviceHasAudioCapture() + || (mDialogHelper.deviceHasAudioPlayback())); + } + + private boolean hasAudioPlayback() { + return mDialogHelper.deviceHasAudioPlayback(); + } + + private boolean hasAudioCapture() { + return mDialogHelper.deviceHasAudioCapture(); + } + + /** + * According to USB audio warning dialog matrix table to return warning message id. + * @return string resId for USB audio warning dialog message, otherwise {ID_NULL}. + * See usb_audio.md for USB audio Permission and Confirmation warning dialog resource + * string id matrix table. + */ + public int getMessageId() { + if (!mDialogHelper.isUsbDevice()) { + return getUsbAccessoryPromptId(); + } + + if (hasRecordPermission() && isUsbAudioDevice()) { + // case# 1, 2, 3 + return R.string.usb_audio_device_prompt; + } else if (!hasRecordPermission() && isUsbAudioDevice() && hasAudioPlayback() + && !hasAudioCapture()) { + // case# 5 + return R.string.usb_audio_device_prompt; + } + + if (!hasRecordPermission() && isUsbAudioDevice() && hasAudioCapture()) { + // case# 6,7 + return R.string.usb_audio_device_prompt_warn; + } + + Log.w(TAG, "Only shows title with empty content description!"); + return Resources.ID_NULL; + } + + /** + * Gets prompt dialog title. + * @return string id for USB prompt dialog title. + */ + public int getPromptTitleId() { + return (mDialogType == TYPE_PERMISSION) + ? R.string.usb_audio_device_permission_prompt_title + : R.string.usb_audio_device_confirm_prompt_title; + } + + /** + * Gets USB Accessory prompt message id. + * @return string id for USB Accessory prompt message. + */ + public int getUsbAccessoryPromptId() { + return (mDialogType == TYPE_PERMISSION) + ? R.string.usb_accessory_permission_prompt : R.string.usb_accessory_confirm_prompt; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java index 21d700e41a40..a2bee05246ce 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.hardware.usb.IUsbManager; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; @@ -35,7 +36,6 @@ import android.os.UserHandle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.view.Window; import android.view.WindowManager; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -57,6 +57,7 @@ public class UsbConfirmActivity extends AlertActivity private ResolveInfo mResolveInfo; private boolean mPermissionGranted; private UsbDisconnectedReceiver mDisconnectedReceiver; + private UsbAudioWarningDialogMessage mUsbConfirmMessageHandler; @Override public void onCreate(Bundle icicle) { @@ -70,7 +71,9 @@ public class UsbConfirmActivity extends AlertActivity mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); mResolveInfo = (ResolveInfo) intent.getParcelableExtra("rinfo"); String packageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE); - + mUsbConfirmMessageHandler = new UsbAudioWarningDialogMessage( + getApplicationContext(), getIntent(), + UsbAudioWarningDialogMessage.TYPE_CONFIRM); PackageManager packageManager = getPackageManager(); String appName = mResolveInfo.loadLabel(packageManager).toString(); @@ -78,8 +81,8 @@ public class UsbConfirmActivity extends AlertActivity ap.mTitle = appName; boolean useRecordWarning = false; if (mDevice == null) { - ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName, - mAccessory.getDescription()); + final int messageId = mUsbConfirmMessageHandler.getUsbAccessoryPromptId(); + ap.mMessage = getString(messageId, appName, mAccessory.getDescription()); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory); } else { int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); @@ -91,11 +94,11 @@ public class UsbConfirmActivity extends AlertActivity boolean isAudioCaptureDevice = mDevice.getHasAudioCapture(); useRecordWarning = isAudioCaptureDevice && !hasRecordPermission; - int strID = useRecordWarning - ? R.string.usb_device_confirm_prompt_warn - : R.string.usb_device_confirm_prompt; - - ap.mMessage = getString(strID, appName, mDevice.getProductName()); + final int messageId = mUsbConfirmMessageHandler.getMessageId(); + final int titleId = mUsbConfirmMessageHandler.getPromptTitleId(); + ap.mTitle = getString(titleId, appName, mDevice.getProductName()); + ap.mMessage = (messageId != Resources.ID_NULL) ? getString(messageId, appName, + mDevice.getProductName()) : null; mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); } ap.mPositiveButtonText = getString(android.R.string.ok); diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java new file mode 100644 index 000000000000..ab29a9e970c7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.usb; + +import static android.Manifest.permission.RECORD_AUDIO; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.PermissionChecker; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.hardware.usb.IUsbManager; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Log; + +/** + * Helper class to separate model and view for USB permission and confirm dialogs. + */ +public class UsbDialogHelper { + private static final String TAG = UsbDialogHelper.class.getSimpleName(); + private static final String EXTRA_RESOLVE_INFO = "rinfo"; + + private final UsbDevice mDevice; + private final UsbAccessory mAccessory; + private final ResolveInfo mResolveInfo; + private final String mPackageName; + private final CharSequence mAppName; + private final Context mContext; + private final PendingIntent mPendingIntent; + private final IUsbManager mUsbService; + private final int mUid; + private final boolean mCanBeDefault; + + private UsbDisconnectedReceiver mDisconnectedReceiver; + private boolean mIsUsbDevice; + private boolean mResponseSent; + + /** + * @param context The Context of the caller. + * @param intent The intent of the caller. + * @throws IllegalStateException Thrown if both UsbDevice and UsbAccessory are null or if the + * query for the matching ApplicationInfo is unsuccessful. + */ + public UsbDialogHelper(Context context, Intent intent) throws IllegalStateException { + mContext = context; + mDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + mAccessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); + mCanBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false); + if (mDevice == null && mAccessory == null) { + throw new IllegalStateException("Device and accessory are both null."); + } + if (mDevice != null) { + mIsUsbDevice = true; + } + mResolveInfo = intent.getParcelableExtra(EXTRA_RESOLVE_INFO); + PackageManager packageManager = mContext.getPackageManager(); + if (mResolveInfo != null) { + // If a ResolveInfo is provided it will be used to determine the activity to start + mUid = mResolveInfo.activityInfo.applicationInfo.uid; + mPackageName = mResolveInfo.activityInfo.packageName; + mPendingIntent = null; + } else { + mUid = intent.getIntExtra(Intent.EXTRA_UID, -1); + mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE); + mPendingIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT); + } + try { + ApplicationInfo aInfo = packageManager.getApplicationInfo(mPackageName, 0); + mAppName = aInfo.loadLabel(packageManager); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalStateException("unable to look up package name", e); + } + IBinder b = ServiceManager.getService(Context.USB_SERVICE); + mUsbService = IUsbManager.Stub.asInterface(b); + } + + /** + * Registers UsbDisconnectedReceiver to dismiss dialog automatically when device or accessory + * gets disconnected + * @param activity The activity to finish when device / accessory gets disconnected. + */ + public void registerUsbDisconnectedReceiver(Activity activity) { + if (mIsUsbDevice) { + mDisconnectedReceiver = new UsbDisconnectedReceiver(activity, mDevice); + } else { + mDisconnectedReceiver = new UsbDisconnectedReceiver(activity, mAccessory); + } + } + + /** + * Unregisters the UsbDisconnectedReceiver. To be called when the activity is destroyed. + * @param activity The activity registered to finish when device / accessory gets disconnected. + */ + public void unregisterUsbDisconnectedReceiver(Activity activity) { + if (mDisconnectedReceiver != null) { + try { + activity.unregisterReceiver(mDisconnectedReceiver); + } catch (Exception e) { + // pass + } + mDisconnectedReceiver = null; + } + } + + /** + * @return True if the intent contains a UsbDevice which can capture audio. + */ + public boolean deviceHasAudioCapture() { + return mDevice != null && mDevice.getHasAudioCapture(); + } + + /** + * @return True if the intent contains a UsbDevice which can play audio. + */ + public boolean deviceHasAudioPlayback() { + return mDevice != null && mDevice.getHasAudioPlayback(); + } + + /** + * @return True if the package has RECORD_AUDIO permission specified in its manifest. + */ + public boolean packageHasAudioRecordingPermission() { + return PermissionChecker.checkPermissionForPreflight(mContext, RECORD_AUDIO, + PermissionChecker.PID_UNKNOWN, mUid, mPackageName) + == android.content.pm.PackageManager.PERMISSION_GRANTED; + } + + /** + * @return True if the intent contains a UsbDevice. + */ + public boolean isUsbDevice() { + return mIsUsbDevice; + } + + /** + * @return True if the intent contains a UsbAccessory. + */ + public boolean isUsbAccessory() { + return !mIsUsbDevice; + } + + /** + * Grants USB permission to the device / accessory to the calling uid. + */ + public void grantUidAccessPermission() { + try { + if (mIsUsbDevice) { + mUsbService.grantDevicePermission(mDevice, mUid); + } else { + mUsbService.grantAccessoryPermission(mAccessory, mUid); + } + } catch (RemoteException e) { + Log.e(TAG, "IUsbService connection failed", e); + } + } + + /** + * Sets the package as default for the device / accessory. + */ + public void setDefaultPackage() { + final int userId = UserHandle.myUserId(); + try { + if (mIsUsbDevice) { + mUsbService.setDevicePackage(mDevice, mPackageName, userId); + } else { + mUsbService.setAccessoryPackage(mAccessory, mPackageName, userId); + } + } catch (RemoteException e) { + Log.e(TAG, "IUsbService connection failed", e); + } + } + + /** + * Clears the default package of the device / accessory. + */ + public void clearDefaultPackage() { + final int userId = UserHandle.myUserId(); + try { + if (mIsUsbDevice) { + mUsbService.setDevicePackage(mDevice, null, userId); + } else { + mUsbService.setAccessoryPackage(mAccessory, null, userId); + } + } catch (RemoteException e) { + Log.e(TAG, "IUsbService connection failed", e); + } + } + + /** + * Starts the activity which was selected to handle the device / accessory. + */ + public void confirmDialogStartActivity() { + final int userId = UserHandle.myUserId(); + Intent intent; + + if (mIsUsbDevice) { + intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); + intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice); + } else { + intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); + intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory); + } + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setComponent( + new ComponentName(mResolveInfo.activityInfo.packageName, + mResolveInfo.activityInfo.name)); + try { + mContext.startActivityAsUser(intent, new UserHandle(userId)); + } catch (Exception e) { + Log.e(TAG, "Unable to start activity", e); + } + } + + /** + * Sends the result of the permission dialog via the provided PendingIntent. + * + * @param permissionGranted True if the user pressed ok in the permission dialog. + */ + public void sendPermissionDialogResponse(boolean permissionGranted) { + if (!mResponseSent) { + // send response via pending intent + Intent intent = new Intent(); + if (mIsUsbDevice) { + intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice); + } else { + intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory); + } + intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, permissionGranted); + try { + mPendingIntent.send(mContext, 0, intent); + mResponseSent = true; + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "PendingIntent was cancelled"); + } + } + } + + /** + * @return A description of the device / accessory + */ + public String getDeviceDescription() { + String desc; + if (mIsUsbDevice) { + desc = mDevice.getProductName(); + if (desc == null) { + desc = mDevice.getDeviceName(); + } + } else { + // UsbAccessory + desc = mAccessory.getDescription(); + if (desc == null) { + desc = String.format("%s %s", mAccessory.getManufacturer(), mAccessory.getModel()); + } + } + return desc; + } + + /** + * Whether the calling package can set as default handler of the USB device or accessory. + * In case of a UsbAccessory this is the case if the calling package has an intent filter for + * {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} with a usb-accessory filter matching the + * attached accessory. In case of a UsbDevice this is the case if the calling package has an + * intent filter for {@link UsbManager#ACTION_USB_DEVICE_ATTACHED} with a usb-device filter + * matching the attached device. + * + * @return True if the package can be default for the USB device. + */ + public boolean canBeDefault() { + return mCanBeDefault; + } + + /** + * @return The name of the app which requested permission or the name of the app which will be + * opened if the user allows it to handle the USB device. + */ + public CharSequence getAppName() { + return mAppName; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java index bfa50bcee270..b9a37b1918f2 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.PermissionChecker; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.hardware.usb.IUsbManager; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; @@ -59,6 +60,7 @@ public class UsbPermissionActivity extends AlertActivity private int mUid; private boolean mPermissionGranted; private UsbDisconnectedReceiver mDisconnectedReceiver; + private UsbAudioWarningDialogMessage mUsbAudioPermissionMessageHandler; @Override public void onCreate(Bundle icicle) { @@ -73,7 +75,9 @@ public class UsbPermissionActivity extends AlertActivity mUid = intent.getIntExtra(Intent.EXTRA_UID, -1); mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE); boolean canBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false); - + mUsbAudioPermissionMessageHandler = new UsbAudioWarningDialogMessage( + getApplicationContext(), getIntent(), + UsbAudioWarningDialogMessage.TYPE_PERMISSION); PackageManager packageManager = getPackageManager(); ApplicationInfo aInfo; try { @@ -91,8 +95,8 @@ public class UsbPermissionActivity extends AlertActivity if (mDevice == null) { // Accessory Case - ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName, - mAccessory.getDescription()); + final int messageId = mUsbAudioPermissionMessageHandler.getUsbAccessoryPromptId(); + ap.mMessage = getString(messageId, appName, mAccessory.getDescription()); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory); } else { boolean hasRecordPermission = @@ -103,10 +107,11 @@ public class UsbPermissionActivity extends AlertActivity boolean isAudioCaptureDevice = mDevice.getHasAudioCapture(); useRecordWarning = isAudioCaptureDevice && !hasRecordPermission; - int strID = useRecordWarning - ? R.string.usb_device_permission_prompt_warn - : R.string.usb_device_permission_prompt; - ap.mMessage = getString(strID, appName, mDevice.getProductName()); + final int messageId = mUsbAudioPermissionMessageHandler.getMessageId(); + final int titleId = mUsbAudioPermissionMessageHandler.getPromptTitleId(); + ap.mTitle = getString(titleId, appName, mDevice.getProductName()); + ap.mMessage = (messageId != Resources.ID_NULL) ? getString(messageId, appName, + mDevice.getProductName()) : null; mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); } |