diff options
Diffstat (limited to 'packages/SystemUI/src')
282 files changed, 10965 insertions, 4433 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index 3410883efac7..7cc16cf014fd 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -35,6 +35,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.telephony.IccCardConstants; @@ -59,6 +60,7 @@ public class CarrierTextController { private static final String TAG = "CarrierTextController"; private final boolean mIsEmergencyCallCapable; + private final Handler mMainHandler; private boolean mTelephonyCapable; private boolean mShowMissingSim; private boolean mShowAirplaneMode; @@ -67,6 +69,7 @@ public class CarrierTextController { private WifiManager mWifiManager; private boolean[] mSimErrorState; private final int mSimSlotsNumber; + @Nullable // Check for nullability before dispatching private CarrierTextCallback mCarrierTextCallback; private Context mContext; private CharSequence mSeparator; @@ -76,12 +79,12 @@ public class CarrierTextController { new WakefulnessLifecycle.Observer() { @Override public void onFinishedWakingUp() { - mCarrierTextCallback.finishedWakingUp(); + if (mCarrierTextCallback != null) mCarrierTextCallback.finishedWakingUp(); } @Override public void onStartedGoingToSleep() { - mCarrierTextCallback.startedGoingToSleep(); + if (mCarrierTextCallback != null) mCarrierTextCallback.startedGoingToSleep(); } }; @@ -170,8 +173,9 @@ public class CarrierTextController { mSeparator = separator; mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); mSimSlotsNumber = ((TelephonyManager) context.getSystemService( - Context.TELEPHONY_SERVICE)).getPhoneCount(); + Context.TELEPHONY_SERVICE)).getSupportedModemCount(); mSimErrorState = new boolean[mSimSlotsNumber]; + mMainHandler = Dependency.get(Dependency.MAIN_HANDLER); } /** @@ -230,7 +234,12 @@ public class CarrierTextController { if (whitelistIpcs(() -> ConnectivityManager.from(mContext).isNetworkSupported( ConnectivityManager.TYPE_MOBILE))) { mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - mKeyguardUpdateMonitor.registerCallback(mCallback); + // Keyguard update monitor expects callbacks from main thread + mMainHandler.post(() -> { + if (mKeyguardUpdateMonitor != null) { + mKeyguardUpdateMonitor.registerCallback(mCallback); + } + }); mWakefulnessLifecycle.addObserver(mWakefulnessObserver); telephonyManager.listen(mPhoneStateListener, LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); @@ -242,7 +251,12 @@ public class CarrierTextController { } else { mCarrierTextCallback = null; if (mKeyguardUpdateMonitor != null) { - mKeyguardUpdateMonitor.removeCallback(mCallback); + // Keyguard update monitor expects callbacks from main thread + mMainHandler.post(() -> { + if (mKeyguardUpdateMonitor != null) { + mKeyguardUpdateMonitor.removeCallback(mCallback); + } + }); mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); } telephonyManager.listen(mPhoneStateListener, LISTEN_NONE); @@ -374,10 +388,9 @@ public class CarrierTextController { @VisibleForTesting protected void postToCallback(CarrierTextCallbackInfo info) { - Handler handler = Dependency.get(Dependency.MAIN_HANDLER); final CarrierTextCallback callback = mCarrierTextCallback; if (callback != null) { - handler.post(() -> callback.updateCarrierInfo(info)); + mMainHandler.post(() -> callback.updateCarrierInfo(info)); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index 6cc881dc5320..6f955f936c9a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -33,10 +33,9 @@ import android.widget.LinearLayout; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.systemui.R; -import java.util.Arrays; - /** * Base class for PIN and password unlock screens. */ @@ -132,19 +131,19 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout protected void verifyPasswordAndUnlock() { if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. - final byte[] entry = getPasswordText(); + final LockscreenCredential password = getEnteredCredential(); setPasswordEntryInputEnabled(false); if (mPendingLockCheck != null) { mPendingLockCheck.cancel(false); } final int userId = KeyguardUpdateMonitor.getCurrentUser(); - if (entry.length <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { + if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { // to avoid accidental lockout, only count attempts that are long enough to be a // real password. This may require some tweaking. setPasswordEntryInputEnabled(true); onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */); - Arrays.fill(entry, (byte) 0); + password.zeroize(); return; } @@ -152,9 +151,9 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); } - mPendingLockCheck = LockPatternChecker.checkPassword( + mPendingLockCheck = LockPatternChecker.checkCredential( mLockPatternUtils, - entry, + password, userId, new LockPatternChecker.OnCheckCallback() { @@ -166,7 +165,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, true /* isValidPassword */); - Arrays.fill(entry, (byte) 0); + password.zeroize(); } @Override @@ -181,7 +180,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout onPasswordChecked(userId, false /* matched */, timeoutMs, true /* isValidPassword */); } - Arrays.fill(entry, (byte) 0); + password.zeroize(); } @Override @@ -192,7 +191,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout LatencyTracker.getInstance(mContext).onActionEnd( ACTION_CHECK_CREDENTIAL_UNLOCKED); } - Arrays.fill(entry, (byte) 0); + password.zeroize(); } }); } @@ -224,7 +223,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } protected abstract void resetPasswordText(boolean animate, boolean announce); - protected abstract byte[] getPasswordText(); + protected abstract LockscreenCredential getEnteredCredential(); protected abstract void setPasswordEntryEnabled(boolean enabled); protected abstract void setPasswordEntryInputEnabled(boolean enabled); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index eaaa3ed78654..f8f3dc8d6ecd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -36,6 +36,7 @@ import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; +import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.TextViewInputDisabler; import com.android.systemui.R; @@ -86,13 +87,12 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView mSecurityMessageDisplay.setMessage(""); } final boolean wasDisabled = mPasswordEntry.isEnabled(); - // Don't set enabled password entry & showSoftInput when PasswordEntry is invisible or in - // pausing stage. + setPasswordEntryEnabled(true); + setPasswordEntryInputEnabled(true); + // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage. if (!mResumed || !mPasswordEntry.isVisibleToUser()) { return; } - setPasswordEntryEnabled(true); - setPasswordEntryInputEnabled(true); if (wasDisabled) { mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); } @@ -244,8 +244,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @Override - protected byte[] getPasswordText() { - return charSequenceToByteArray(mPasswordEntry.getText()); + protected LockscreenCredential getEnteredCredential() { + return LockscreenCredential.createPasswordOrNone(mPasswordEntry.getText()); } @Override @@ -380,18 +380,4 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView return getContext().getString( com.android.internal.R.string.keyguard_accessibility_password_unlock); } - - /* - * This method avoids creating a new string when getting a byte array from EditView#getText(). - */ - private static byte[] charSequenceToByteArray(CharSequence chars) { - if (chars == null) { - return null; - } - byte[] bytes = new byte[chars.length()]; - for (int i = 0; i < chars.length(); i++) { - bytes[i] = (byte) chars.charAt(i); - } - return bytes; - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index 9c0688e20aa0..ef48a530e914 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -39,6 +39,7 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockscreenCredential; import com.android.settingslib.animation.AppearAnimationCreator; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; @@ -297,9 +298,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); } - mPendingLockCheck = LockPatternChecker.checkPattern( + mPendingLockCheck = LockPatternChecker.checkCredential( mLockPatternUtils, - pattern, + LockscreenCredential.createPattern(pattern), userId, new LockPatternChecker.OnCheckCallback() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 274f739d8c29..c67deccb1f62 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -23,6 +23,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import com.android.internal.widget.LockscreenCredential; import com.android.systemui.R; /** @@ -167,8 +168,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView } @Override - protected byte[] getPasswordText() { - return charSequenceToByteArray(mPasswordEntry.getText()); + protected LockscreenCredential getEnteredCredential() { + return LockscreenCredential.createPinOrNone(mPasswordEntry.getText()); } @Override @@ -266,18 +267,4 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView return getContext().getString( com.android.internal.R.string.keyguard_accessibility_pin_unlock); } - - /* - * This method avoids creating a new string when getting a byte array from EditView#getText(). - */ - private static byte[] charSequenceToByteArray(CharSequence chars) { - if (chars == null) { - return null; - } - byte[] bytes = new byte[chars.length()]; - for (int i = 0; i < chars.length(); i++) { - bytes[i] = (byte) chars.charAt(i); - } - return bytes; - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 35eb272bdaef..b9fe9334d14c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -49,7 +49,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; -import com.android.systemui.statusbar.phone.UnlockMethodCache; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.InjectionInflationController; public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView { @@ -76,7 +76,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe // How much you need to drag the bouncer to trigger an auth retry (in dps.) private static final float MIN_DRAG_SIZE = 10; // How much to scale the default slop by, to avoid accidental drags. - private static final float SLOP_SCALE = 2f; + private static final float SLOP_SCALE = 4f; private KeyguardSecurityModel mSecurityModel; private LockPatternUtils mLockPatternUtils; @@ -94,7 +94,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe private final SpringAnimation mSpringAnimation; private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); private final KeyguardUpdateMonitor mUpdateMonitor; - private final UnlockMethodCache mUnlockMethodCache; + private final KeyguardStateController mKeyguardStateController; private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private float mLastTouchY = -1; @@ -128,14 +128,14 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mSecurityModel = new KeyguardSecurityModel(context); + mSecurityModel = Dependency.get(KeyguardSecurityModel.class); mLockPatternUtils = new LockPatternUtils(context); mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); mInjectionInflationController = new InjectionInflationController( SystemUIFactory.getInstance().getRootComponent()); - mUnlockMethodCache = UnlockMethodCache.getInstance(context); mViewConfiguration = ViewConfiguration.get(context); + mKeyguardStateController = Dependency.get(KeyguardStateController.class); } public void setSecurityCallback(SecurityCallback callback) { @@ -272,7 +272,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe */ private void updateBiometricRetry() { SecurityMode securityMode = getSecurityMode(); - mSwipeUpToRetry = mUnlockMethodCache.isFaceAuthEnabled() + mSwipeUpToRetry = mKeyguardStateController.isFaceAuthEnabled() && securityMode != SecurityMode.SimPin && securityMode != SecurityMode.SimPuk && securityMode != SecurityMode.None; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java index bb89959b7a11..1395fd9e3482 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java @@ -25,6 +25,10 @@ import com.android.internal.telephony.IccCardConstants; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Dependency; +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton public class KeyguardSecurityModel { /** @@ -46,6 +50,7 @@ public class KeyguardSecurityModel { private LockPatternUtils mLockPatternUtils; + @Inject KeyguardSecurityModel(Context context) { mContext = context; mLockPatternUtils = new LockPatternUtils(context); @@ -57,7 +62,7 @@ public class KeyguardSecurityModel { mLockPatternUtils = utils; } - SecurityMode getSecurityMode(int userId) { + public SecurityMode getSecurityMode(int userId) { KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class); if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index af4e61b3f6bc..5d35169cf926 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -31,6 +31,7 @@ import android.app.PendingIntent; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.Drawable; +import android.graphics.text.LineBreaker; import android.net.Uri; import android.os.Trace; import android.provider.Settings; @@ -152,6 +153,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize( R.dimen.header_row_font_size); mTitle.setOnClickListener(this); + mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 63da5339efe7..5a1c9976f021 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -22,7 +22,6 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.os.Handler; -import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; @@ -135,7 +134,7 @@ public class KeyguardStatusView extends GridLayout implements super(context, attrs, defStyle); mIActivityManager = ActivityManager.getService(); mLockPatternUtils = new LockPatternUtils(getContext()); - mHandler = new Handler(Looper.myLooper()); + mHandler = new Handler(); onDensityOrFontScaleChanged(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index f2a359e67e29..e73a27bb4eb2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -39,7 +39,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.systemui.DejankUtils.whitelistIpcs; -import static com.android.systemui.Dependency.MAIN_LOOPER_NAME; import android.annotation.AnyThread; import android.annotation.MainThread; @@ -58,6 +57,7 @@ import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; import android.database.ContentObserver; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; @@ -98,7 +98,9 @@ import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.widget.LockPatternUtils; import com.android.settingslib.WirelessUtils; +import com.android.systemui.DejankUtils; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.MainLooper; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -116,13 +118,14 @@ import java.util.TimeZone; import java.util.function.Consumer; import javax.inject.Inject; -import javax.inject.Named; +import javax.inject.Singleton; /** * Watches for updates that may be interesting to the keyguard, and provides * the up to date information as well as a registration for callbacks that care * to be updated. */ +@Singleton public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final String TAG = "KeyguardUpdateMonitor"; @@ -219,7 +222,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private final Context mContext; private final boolean mIsPrimaryUser; - HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>(); + HashMap<Integer, SimData> mSimDatas = new HashMap<>(); HashMap<Integer, ServiceState> mServiceStates = new HashMap<Integer, ServiceState>(); private int mRingMode; @@ -329,6 +332,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private SparseBooleanArray mUserIsUnlocked = new SparseBooleanArray(); private SparseBooleanArray mUserHasTrust = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray(); + private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray(); private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray(); private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray(); private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray(); @@ -472,6 +476,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { public void onTrustManagedChanged(boolean managed, int userId) { checkIsHandlerThread(); mUserTrustIsManaged.put(userId, managed); + mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId)); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -925,6 +930,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId); } + /** + * Cached version of {@link TrustManager#isTrustUsuallyManaged(int)}. + */ + public boolean isTrustUsuallyManaged(int userId) { + checkIsHandlerThread(); + return mUserTrustIsUsuallyManaged.get(userId); + } + public boolean isUnlockingWithBiometricAllowed() { return mStrongAuthTracker.isUnlockingWithBiometricAllowed(); } @@ -1424,6 +1437,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } private void handleScreenTurnedOff() { + final String tag = "KeyguardUpdateMonitor#handleScreenTurnedOff"; + DejankUtils.startDetectingBlockingIpcs(tag); checkIsHandlerThread(); mHardwareFingerprintUnavailableRetryCount = 0; mHardwareFaceUnavailableRetryCount = 0; @@ -1433,6 +1448,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { cb.onScreenTurnedOff(); } } + DejankUtils.stopDetectingBlockingIpcs(tag); } private void handleDreamingStateChanged(int dreamStart) { @@ -1474,14 +1490,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { mUserIsUnlocked.put(userId, mUserManager.isUserUnlocked(userId)); } - private void handleUserRemoved(int userId) { + @VisibleForTesting + void handleUserRemoved(int userId) { checkIsHandlerThread(); mUserIsUnlocked.delete(userId); + mUserTrustIsUsuallyManaged.delete(userId); } @VisibleForTesting @Inject - protected KeyguardUpdateMonitor(Context context, @Named(MAIN_LOOPER_NAME) Looper mainLooper) { + protected KeyguardUpdateMonitor(Context context, @MainLooper Looper mainLooper) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); @@ -1662,7 +1680,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { e.rethrowAsRuntimeException(); } - mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE); + mTrustManager = context.getSystemService(TrustManager.class); mTrustManager.registerTrustListener(this); mLockPatternUtils = new LockPatternUtils(context); mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); @@ -1697,6 +1715,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user)); mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled(); + List<UserInfo> allUsers = mUserManager.getUsers(); + for (UserInfo userInfo : allUsers) { + mUserTrustIsUsuallyManaged.put(userInfo.id, + mTrustManager.isTrustUsuallyManaged(userInfo.id)); + } updateAirplaneModeState(); TelephonyManager telephony = @@ -2048,6 +2071,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { */ private void handleUserSwitching(int userId, IRemoteCallback reply) { checkIsHandlerThread(); + mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId)); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -2522,8 +2546,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { @MainThread public void reportSimUnlocked(int subId) { if (DEBUG_SIM_STATES) Log.v(TAG, "reportSimUnlocked(subId=" + subId + ")"); - int slotId = SubscriptionManager.getSlotIndex(subId); - handleSimStateChange(subId, slotId, State.READY); + handleSimStateChange(subId, getSlotId(subId), State.READY); } /** @@ -2596,6 +2619,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } + private int getSlotId(int subId) { + if (!mSimDatas.containsKey(subId)) { + refreshSimState(subId, SubscriptionManager.getSlotIndex(subId)); + } + return mSimDatas.get(subId).slotId; + } + private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { @Override @@ -2750,7 +2780,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { for (int i = 0; i < list.size(); i++) { final SubscriptionInfo info = list.get(i); final int id = info.getSubscriptionId(); - int slotId = SubscriptionManager.getSlotIndex(id); + int slotId = getSlotId(id); if (state == getSimState(id) && bestSlotId > slotId) { resultId = id; bestSlotId = slotId; @@ -2792,7 +2822,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private void checkIsHandlerThread() { if (!mHandler.getLooper().isCurrentThread()) { - Log.wtf(TAG, "must call on mHandler's thread " + Log.wtfStack(TAG, "must call on mHandler's thread " + mHandler.getLooper().getThread() + ", not " + Thread.currentThread()); } } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 7771f8655128..486d02c207db 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -17,6 +17,7 @@ package com.android.systemui; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.INotificationManager; +import android.app.IWallpaperManager; import android.content.res.Configuration; import android.hardware.SensorPrivacyManager; import android.hardware.display.NightDisplayListener; @@ -30,6 +31,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.Preconditions; +import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.clock.ClockManager; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -38,6 +40,10 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.MainLooper; import com.android.systemui.dock.DockManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.ScreenLifecycle; @@ -76,6 +82,7 @@ import com.android.systemui.statusbar.notification.row.ChannelEditorDialogContro import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.AutoHideController; +import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; @@ -97,7 +104,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.HotspotController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NextAlarmController; @@ -145,16 +152,16 @@ public class Dependency { /** * Key for getting a the main looper. */ - public static final String MAIN_LOOPER_NAME = "main_looper"; + private static final String MAIN_LOOPER_NAME = "main_looper"; /** * Key for getting a background Looper for background work. */ - public static final String BG_LOOPER_NAME = "background_looper"; + private static final String BG_LOOPER_NAME = "background_looper"; /** * Key for getting a background Handler for background work. */ - public static final String BG_HANDLER_NAME = "background_handler"; + private static final String BG_HANDLER_NAME = "background_handler"; /** * Key for getting a Handler for receiving time tick broadcasts on. */ @@ -162,7 +169,7 @@ public class Dependency { /** * Generic handler on the main thread. */ - public static final String MAIN_HANDLER_NAME = "main_handler"; + private static final String MAIN_HANDLER_NAME = "main_handler"; /** * An email address to send memory leak reports to by default. @@ -221,7 +228,7 @@ public class Dependency { @Inject Lazy<FlashlightController> mFlashlightController; @Inject Lazy<UserSwitcherController> mUserSwitcherController; @Inject Lazy<UserInfoController> mUserInfoController; - @Inject Lazy<KeyguardMonitor> mKeyguardMonitor; + @Inject Lazy<KeyguardStateController> mKeyguardMonitor; @Inject Lazy<KeyguardUpdateMonitor> mKeyguardUpdateMonitor; @Inject Lazy<BatteryController> mBatteryController; @Inject Lazy<NightDisplayListener> mNightDisplayListener; @@ -297,10 +304,10 @@ public class Dependency { @Inject Lazy<AutoHideController> mAutoHideController; @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener; @Inject Lazy<PrivacyItemController> mPrivacyItemController; - @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper; - @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler; - @Inject @Named(MAIN_LOOPER_NAME) Lazy<Looper> mMainLooper; - @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler; + @Inject @BgLooper Lazy<Looper> mBgLooper; + @Inject @BgHandler Lazy<Handler> mBgHandler; + @Inject @MainLooper Lazy<Looper> mMainLooper; + @Inject @MainHandler Lazy<Handler> mMainHandler; @Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler; @Nullable @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail; @@ -316,6 +323,9 @@ public class Dependency { @Inject Lazy<FalsingManager> mFalsingManager; @Inject Lazy<SysUiState> mSysUiStateFlagsContainer; @Inject Lazy<AlarmManager> mAlarmManager; + @Inject Lazy<KeyguardSecurityModel> mKeyguardSecurityModel; + @Inject Lazy<DozeParameters> mDozeParameters; + @Inject Lazy<IWallpaperManager> mWallpaperManager; @Inject public Dependency() { @@ -355,7 +365,7 @@ public class Dependency { mProviders.put(FlashlightController.class, mFlashlightController::get); - mProviders.put(KeyguardMonitor.class, mKeyguardMonitor::get); + mProviders.put(KeyguardStateController.class, mKeyguardMonitor::get); mProviders.put(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor::get); @@ -501,6 +511,9 @@ public class Dependency { mProviders.put(FalsingManager.class, mFalsingManager::get); mProviders.put(SysUiState.class, mSysUiStateFlagsContainer::get); mProviders.put(AlarmManager.class, mAlarmManager::get); + mProviders.put(KeyguardSecurityModel.class, mKeyguardSecurityModel::get); + mProviders.put(DozeParameters.class, mDozeParameters::get); + mProviders.put(IWallpaperManager.class, mWallpaperManager::get); // TODO(b/118592525): to support multi-display , we start to add something which is // per-display, while others may be global. I think it's time to add diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java index 0e079e36a175..362014f51e36 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java @@ -50,13 +50,13 @@ public class ForegroundServiceLifetimeExtender implements NotificationLifetimeEx @Override public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) { - if ((entry.notification.getNotification().flags + if ((entry.getSbn().getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) == 0) { return false; } long currentTime = System.currentTimeMillis(); - return currentTime - entry.notification.getPostTime() < MIN_FGS_TIME_MS; + return currentTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS; } @Override @@ -79,11 +79,13 @@ public class ForegroundServiceLifetimeExtender implements NotificationLifetimeEx if (mManagedEntries.contains(entry)) { mManagedEntries.remove(entry); if (mNotificationSafeToRemoveCallback != null) { - mNotificationSafeToRemoveCallback.onSafeToRemove(entry.key); + mNotificationSafeToRemoveCallback.onSafeToRemove(entry.getKey()); } } }; - mHandler.postDelayed(r, MIN_FGS_TIME_MS); + long delayAmt = MIN_FGS_TIME_MS + - (System.currentTimeMillis() - entry.getSbn().getPostTime()); + mHandler.postDelayed(r, delayAmt); } } diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java index f9d877142d21..4a3b6df6ac90 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java @@ -50,12 +50,12 @@ public class ForegroundServiceNotificationListener { notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override public void onPendingEntryAdded(NotificationEntry entry) { - addNotification(entry.notification, entry.getImportance()); + addNotification(entry.getSbn(), entry.getImportance()); } @Override public void onPostEntryUpdated(NotificationEntry entry) { - updateNotification(entry.notification, entry.getImportance()); + updateNotification(entry.getSbn(), entry.getImportance()); } @Override @@ -63,7 +63,7 @@ public class ForegroundServiceNotificationListener { NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { - removeNotification(entry.notification); + removeNotification(entry.getSbn()); } }); diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index bd91333100bd..29a7167394ae 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -20,6 +20,7 @@ import android.app.ActivityManager; import android.content.Context; import android.graphics.Rect; import android.os.HandlerThread; +import android.os.Trace; import android.service.wallpaper.WallpaperService; import android.util.Log; import android.util.Size; @@ -37,6 +38,8 @@ import com.android.systemui.statusbar.phone.DozeParameters; import java.io.FileDescriptor; import java.io.PrintWriter; +import javax.inject.Inject; + /** * Default built-in wallpaper that simply shows a static image. */ @@ -48,8 +51,16 @@ public class ImageWallpaper extends WallpaperService { private static final int DELAY_FINISH_RENDERING = 1000; private static final int INTERVAL_WAIT_FOR_RENDERING = 100; private static final int PATIENCE_WAIT_FOR_RENDERING = 10; + private static final boolean DEBUG = true; + private final DozeParameters mDozeParameters; private HandlerThread mWorker; + @Inject + public ImageWallpaper(DozeParameters dozeParameters) { + super(); + mDozeParameters = dozeParameters; + } + @Override public void onCreate() { super.onCreate(); @@ -59,7 +70,7 @@ public class ImageWallpaper extends WallpaperService { @Override public Engine onCreateEngine() { - return new GLEngine(this); + return new GLEngine(this, mDozeParameters); } @Override @@ -87,9 +98,9 @@ public class ImageWallpaper extends WallpaperService { // This variable can only be accessed in synchronized block. private boolean mWaitingForRendering; - GLEngine(Context context) { + GLEngine(Context context, DozeParameters dozeParameters) { mNeedTransition = ActivityManager.isHighEndGfx() - && !DozeParameters.getInstance(context).getDisplayNeedsBlanking(); + && !dozeParameters.getDisplayNeedsBlanking(); // We will preserve EGL context when we are in lock screen or aod // to avoid janking in following transition, we need to release when back to home. @@ -125,6 +136,10 @@ public class ImageWallpaper extends WallpaperService { @Override public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { if (!mNeedTransition) return; + if (DEBUG) { + Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode + + ", duration=" + animationDuration); + } mWorker.getThreadHandler().post( () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration)); if (inAmbientMode && animationDuration == 0) { @@ -184,17 +199,32 @@ public class ImageWallpaper extends WallpaperService { @Override public void onSurfaceRedrawNeeded(SurfaceHolder holder) { + if (DEBUG) { + Log.d(TAG, "onSurfaceRedrawNeeded: mNeedRedraw=" + mNeedRedraw); + } + mWorker.getThreadHandler().post(() -> { if (mNeedRedraw) { - preRender(); - requestRender(); - postRender(); + drawFrame(); mNeedRedraw = false; } }); } @Override + public void onVisibilityChanged(boolean visible) { + if (DEBUG) { + Log.d(TAG, "wallpaper visibility changes: " + visible); + } + } + + private void drawFrame() { + preRender(); + requestRender(); + postRender(); + } + + @Override public void onStatePostChange() { // When back to home, we try to release EGL, which is preserved in lock screen or aod. if (mController.getState() == StatusBarState.SHADE) { @@ -205,7 +235,9 @@ public class ImageWallpaper extends WallpaperService { @Override public void preRender() { // This method should only be invoked from worker thread. + Trace.beginSection("ImageWallpaper#preRender"); preRenderInternal(); + Trace.endSection(); } private void preRenderInternal() { @@ -240,7 +272,9 @@ public class ImageWallpaper extends WallpaperService { @Override public void requestRender() { // This method should only be invoked from worker thread. + Trace.beginSection("ImageWallpaper#requestRender"); requestRenderInternal(); + Trace.endSection(); } private void requestRenderInternal() { @@ -263,8 +297,10 @@ public class ImageWallpaper extends WallpaperService { @Override public void postRender() { // This method should only be invoked from worker thread. + Trace.beginSection("ImageWallpaper#postRender"); notifyWaitingThread(); scheduleFinishRendering(); + Trace.endSection(); } private void notifyWaitingThread() { @@ -289,12 +325,14 @@ public class ImageWallpaper extends WallpaperService { } private void finishRendering() { + Trace.beginSection("ImageWallpaper#finishRendering"); if (mEglHelper != null) { mEglHelper.destroyEglSurface(); if (!needPreserveEglContext()) { mEglHelper.destroyEglContext(); } } + Trace.endSection(); } private boolean needPreserveEglContext() { @@ -310,9 +348,9 @@ public class ImageWallpaper extends WallpaperService { boolean isHighEndGfx = ActivityManager.isHighEndGfx(); out.print(prefix); out.print("isHighEndGfx="); out.println(isHighEndGfx); - DozeParameters dozeParameters = DozeParameters.getInstance(getApplicationContext()); out.print(prefix); out.print("displayNeedsBlanking="); - out.println(dozeParameters != null ? dozeParameters.getDisplayNeedsBlanking() : "null"); + out.println( + mDozeParameters != null ? mDozeParameters.getDisplayNeedsBlanking() : "null"); out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition); out.print(prefix); out.print("StatusBarState="); diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java index 50f1b44b05b1..30a60abfcd86 100644 --- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java +++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java @@ -16,6 +16,8 @@ package com.android.systemui; +import static android.os.PowerManager.WAKE_REASON_UNKNOWN; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -28,18 +30,33 @@ import android.os.SystemClock; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.statusbar.phone.BiometricUnlockController; -import com.android.systemui.statusbar.phone.StatusBar; + +import javax.inject.Inject; +import javax.inject.Singleton; /** * Class that only runs on debuggable builds that listens to broadcasts that simulate actions in the * system that are used for testing the latency. */ +@Singleton public class LatencyTester extends SystemUI { - private static final String ACTION_FINGERPRINT_WAKE = + private static final String + ACTION_FINGERPRINT_WAKE = "com.android.systemui.latency.ACTION_FINGERPRINT_WAKE"; - private static final String ACTION_TURN_ON_SCREEN = + private static final String + ACTION_TURN_ON_SCREEN = "com.android.systemui.latency.ACTION_TURN_ON_SCREEN"; + private final BiometricUnlockController mBiometricUnlockController; + private final PowerManager mPowerManager; + + @Inject + public LatencyTester(Context context, BiometricUnlockController biometricUnlockController, + PowerManager powerManager) { + super(context); + mBiometricUnlockController = biometricUnlockController; + mPowerManager = powerManager; + } @Override public void start() { @@ -64,19 +81,17 @@ public class LatencyTester extends SystemUI { } private void fakeTurnOnScreen() { - PowerManager powerManager = mContext.getSystemService(PowerManager.class); if (LatencyTracker.isEnabled(mContext)) { LatencyTracker.getInstance(mContext).onActionStart( LatencyTracker.ACTION_TURN_ON_SCREEN); } - powerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:LATENCY_TESTS"); + mPowerManager.wakeUp( + SystemClock.uptimeMillis(), WAKE_REASON_UNKNOWN, "android.policy:LATENCY_TESTS"); } private void fakeWakeAndUnlock() { - BiometricUnlockController biometricUnlockController = getComponent(StatusBar.class) - .getBiometricUnlockController(); - biometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT); - biometricUnlockController.onBiometricAuthenticated( + mBiometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT); + mBiometricUnlockController.onBiometricAuthenticated( KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT); } } diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index f38b4f259c88..ad209861d273 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -25,9 +25,6 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static com.android.systemui.tuner.TunablePadding.FLAG_END; import static com.android.systemui.tuner.TunablePadding.FLAG_START; -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.annotation.Dimension; import android.app.ActivityManager; import android.app.Fragment; @@ -52,7 +49,6 @@ import android.os.SystemProperties; import android.provider.Settings.Secure; import android.util.DisplayMetrics; import android.util.Log; -import android.util.MathUtils; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; @@ -64,9 +60,6 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.WindowManager; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; @@ -78,10 +71,7 @@ import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.SecureSetting; -import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; -import com.android.systemui.statusbar.phone.NavigationBarTransitions; -import com.android.systemui.statusbar.phone.NavigationModeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.tuner.TunablePadding; import com.android.systemui.tuner.TunerService; @@ -95,8 +85,7 @@ import java.util.List; * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout) * for antialiasing and emulation purposes. */ -public class ScreenDecorations extends SystemUI implements Tunable, - NavigationBarTransitions.DarkIntensityListener { +public class ScreenDecorations extends SystemUI implements Tunable { private static final boolean DEBUG = false; private static final String TAG = "ScreenDecorations"; @@ -120,15 +109,11 @@ public class ScreenDecorations extends SystemUI implements Tunable, private float mDensity; private WindowManager mWindowManager; private int mRotation; - private boolean mAssistHintVisible; private DisplayCutoutView mCutoutTop; private DisplayCutoutView mCutoutBottom; private SecureSetting mColorInversionSetting; private boolean mPendingRotationChange; private Handler mHandler; - private boolean mAssistHintBlocked = false; - private boolean mIsReceivingNavBarColor = false; - private boolean mInGesturalMode; /** * Converts a set of {@link Rect}s into a {@link Region} @@ -147,166 +132,15 @@ public class ScreenDecorations extends SystemUI implements Tunable, return result; } + public ScreenDecorations(Context context) { + super(context); + } + @Override public void start() { mHandler = startHandlerThread(); mHandler.post(this::startOnScreenDecorationsThread); setupStatusBarPaddingIfNeeded(); - putComponent(ScreenDecorations.class, this); - mInGesturalMode = QuickStepContract.isGesturalMode( - Dependency.get(NavigationModeController.class) - .addListener(this::handleNavigationModeChange)); - } - - @VisibleForTesting - void handleNavigationModeChange(int navigationMode) { - if (!mHandler.getLooper().isCurrentThread()) { - mHandler.post(() -> handleNavigationModeChange(navigationMode)); - return; - } - boolean inGesturalMode = QuickStepContract.isGesturalMode(navigationMode); - if (mInGesturalMode != inGesturalMode) { - mInGesturalMode = inGesturalMode; - - if (mInGesturalMode && mOverlay == null) { - setupDecorations(); - if (mOverlay != null) { - updateLayoutParams(); - } - } - } - } - - /** - * Returns an animator that animates the given view from start to end over durationMs. Start and - * end represent total animation progress: 0 is the start, 1 is the end, 1.1 would be an - * overshoot. - */ - Animator getHandleAnimator(View view, float start, float end, boolean isLeft, long durationMs, - Interpolator interpolator) { - // Note that lerp does allow overshoot, in cases where start and end are outside of [0,1]. - float scaleStart = MathUtils.lerp(2f, 1f, start); - float scaleEnd = MathUtils.lerp(2f, 1f, end); - Animator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, scaleStart, scaleEnd); - Animator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, scaleStart, scaleEnd); - float translationStart = MathUtils.lerp(0.2f, 0f, start); - float translationEnd = MathUtils.lerp(0.2f, 0f, end); - int xDirection = isLeft ? -1 : 1; - Animator translateX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, - xDirection * translationStart * view.getWidth(), - xDirection * translationEnd * view.getWidth()); - Animator translateY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, - translationStart * view.getHeight(), translationEnd * view.getHeight()); - - AnimatorSet set = new AnimatorSet(); - set.play(scaleX).with(scaleY); - set.play(scaleX).with(translateX); - set.play(scaleX).with(translateY); - set.setDuration(durationMs); - set.setInterpolator(interpolator); - return set; - } - - private void fade(View view, boolean fadeIn, boolean isLeft) { - if (fadeIn) { - view.animate().cancel(); - view.setAlpha(1f); - view.setVisibility(View.VISIBLE); - - // A piecewise spring-like interpolation. - // End value in one animator call must match the start value in the next, otherwise - // there will be a discontinuity. - AnimatorSet anim = new AnimatorSet(); - Animator first = getHandleAnimator(view, 0, 1.1f, isLeft, 750, - new PathInterpolator(0, 0.45f, .67f, 1f)); - Interpolator secondInterpolator = new PathInterpolator(0.33f, 0, 0.67f, 1f); - Animator second = getHandleAnimator(view, 1.1f, 0.97f, isLeft, 400, - secondInterpolator); - Animator third = getHandleAnimator(view, 0.97f, 1.02f, isLeft, 400, - secondInterpolator); - Animator fourth = getHandleAnimator(view, 1.02f, 1f, isLeft, 400, - secondInterpolator); - anim.play(first).before(second); - anim.play(second).before(third); - anim.play(third).before(fourth); - anim.start(); - } else { - view.animate().cancel(); - view.animate() - .setInterpolator(new AccelerateInterpolator(1.5f)) - .setDuration(250) - .alpha(0f); - } - - } - - /** - * Controls the visibility of the assist gesture handles. - * - * @param visible whether the handles should be shown - */ - public void setAssistHintVisible(boolean visible) { - if (!mHandler.getLooper().isCurrentThread()) { - mHandler.post(() -> setAssistHintVisible(visible)); - return; - } - - if (mAssistHintBlocked && visible) { - if (VERBOSE) { - Log.v(TAG, "Assist hint blocked, cannot make it visible"); - } - return; - } - - if (mOverlay == null || mBottomOverlay == null) { - return; - } - - if (mAssistHintVisible != visible) { - mAssistHintVisible = visible; - - CornerHandleView assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left); - CornerHandleView assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right); - CornerHandleView assistHintBottomLeft = mBottomOverlay.findViewById( - R.id.assist_hint_left); - CornerHandleView assistHintBottomRight = mBottomOverlay.findViewById( - R.id.assist_hint_right); - - switch (mRotation) { - case RotationUtils.ROTATION_NONE: - fade(assistHintBottomLeft, mAssistHintVisible, /* isLeft = */ true); - fade(assistHintBottomRight, mAssistHintVisible, /* isLeft = */ false); - break; - case RotationUtils.ROTATION_LANDSCAPE: - fade(assistHintTopRight, mAssistHintVisible, /* isLeft = */ true); - fade(assistHintBottomRight, mAssistHintVisible, /* isLeft = */ false); - break; - case RotationUtils.ROTATION_SEASCAPE: - fade(assistHintTopLeft, mAssistHintVisible, /* isLeft = */ false); - fade(assistHintBottomLeft, mAssistHintVisible, /* isLeft = */ true); - break; - case RotationUtils.ROTATION_UPSIDE_DOWN: - fade(assistHintTopLeft, mAssistHintVisible, /* isLeft = */ false); - fade(assistHintTopRight, mAssistHintVisible, /* isLeft = */ true); - break; - } - } - updateWindowVisibilities(); - } - - /** - * Prevents the assist hint from becoming visible even if `mAssistHintVisible` is true. - */ - public void setAssistHintBlocked(boolean blocked) { - if (!mHandler.getLooper().isCurrentThread()) { - mHandler.post(() -> setAssistHintBlocked(blocked)); - return; - } - - mAssistHintBlocked = blocked; - if (mAssistHintVisible && mAssistHintBlocked) { - hideAssistHandles(); - } } @VisibleForTesting @@ -316,15 +150,11 @@ public class ScreenDecorations extends SystemUI implements Tunable, return thread.getThreadHandler(); } - private boolean shouldHostHandles() { - return mInGesturalMode; - } - private void startOnScreenDecorationsThread() { mRotation = RotationUtils.getExactRotation(mContext); mWindowManager = mContext.getSystemService(WindowManager.class); updateRoundedCornerRadii(); - if (hasRoundedCorners() || shouldDrawCutout() || shouldHostHandles()) { + if (hasRoundedCorners() || shouldDrawCutout()) { setupDecorations(); } @@ -501,26 +331,10 @@ public class ScreenDecorations extends SystemUI implements Tunable, if (mOverlay != null) { updateLayoutParams(); updateViews(); - if (mAssistHintVisible) { - // If assist handles are visible, hide them without animation and then make them - // show once again (with corrected rotation). - hideAssistHandles(); - setAssistHintVisible(true); - } } } } - private void hideAssistHandles() { - if (mOverlay != null && mBottomOverlay != null) { - mOverlay.findViewById(R.id.assist_hint_left).setVisibility(View.GONE); - mOverlay.findViewById(R.id.assist_hint_right).setVisibility(View.GONE); - mBottomOverlay.findViewById(R.id.assist_hint_left).setVisibility(View.GONE); - mBottomOverlay.findViewById(R.id.assist_hint_right).setVisibility(View.GONE); - mAssistHintVisible = false; - } - } - private void updateRoundedCornerRadii() { final int newRoundedDefault = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.rounded_corner_radius); @@ -569,52 +383,12 @@ public class ScreenDecorations extends SystemUI implements Tunable, updateView(bottomRight, Gravity.TOP | Gravity.LEFT, 0); } - updateAssistantHandleViews(); mCutoutTop.setRotation(mRotation); mCutoutBottom.setRotation(mRotation); updateWindowVisibilities(); } - private void updateAssistantHandleViews() { - View assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left); - View assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right); - View assistHintBottomLeft = mBottomOverlay.findViewById(R.id.assist_hint_left); - View assistHintBottomRight = mBottomOverlay.findViewById(R.id.assist_hint_right); - - final int assistHintVisibility = mAssistHintVisible ? View.VISIBLE : View.INVISIBLE; - - if (mRotation == RotationUtils.ROTATION_NONE) { - assistHintTopLeft.setVisibility(View.GONE); - assistHintTopRight.setVisibility(View.GONE); - assistHintBottomLeft.setVisibility(assistHintVisibility); - assistHintBottomRight.setVisibility(assistHintVisibility); - updateView(assistHintBottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270); - updateView(assistHintBottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180); - } else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) { - assistHintTopLeft.setVisibility(View.GONE); - assistHintTopRight.setVisibility(assistHintVisibility); - assistHintBottomLeft.setVisibility(View.GONE); - assistHintBottomRight.setVisibility(assistHintVisibility); - updateView(assistHintTopRight, Gravity.BOTTOM | Gravity.LEFT, 270); - updateView(assistHintBottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180); - } else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) { - assistHintTopLeft.setVisibility(assistHintVisibility); - assistHintTopRight.setVisibility(assistHintVisibility); - assistHintBottomLeft.setVisibility(View.GONE); - assistHintBottomRight.setVisibility(View.GONE); - updateView(assistHintTopLeft, Gravity.BOTTOM | Gravity.LEFT, 270); - updateView(assistHintTopRight, Gravity.BOTTOM | Gravity.RIGHT, 180); - } else if (mRotation == RotationUtils.ROTATION_SEASCAPE) { - assistHintTopLeft.setVisibility(assistHintVisibility); - assistHintTopRight.setVisibility(View.GONE); - assistHintBottomLeft.setVisibility(assistHintVisibility); - assistHintBottomRight.setVisibility(View.GONE); - updateView(assistHintTopLeft, Gravity.BOTTOM | Gravity.RIGHT, 180); - updateView(assistHintBottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270); - } - } - private void updateView(View v, int gravity, int rotation) { ((FrameLayout.LayoutParams) v.getLayoutParams()).gravity = gravity; v.setRotation(rotation); @@ -629,10 +403,7 @@ public class ScreenDecorations extends SystemUI implements Tunable, boolean visibleForCutout = shouldDrawCutout() && overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE; boolean visibleForRoundedCorners = hasRoundedCorners(); - boolean visibleForHandles = overlay.findViewById(R.id.assist_hint_left).getVisibility() - == View.VISIBLE || overlay.findViewById(R.id.assist_hint_right).getVisibility() - == View.VISIBLE; - overlay.setVisibility(visibleForCutout || visibleForRoundedCorners || visibleForHandles + overlay.setVisibility(visibleForCutout || visibleForRoundedCorners ? View.VISIBLE : View.GONE); } @@ -689,7 +460,7 @@ public class ScreenDecorations extends SystemUI implements Tunable, | WindowManager.LayoutParams.FLAG_SLIPPERY | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) { @@ -766,31 +537,6 @@ public class ScreenDecorations extends SystemUI implements Tunable, view.setLayoutParams(params); } - @Override - public void onDarkIntensity(float darkIntensity) { - if (!mHandler.getLooper().isCurrentThread()) { - mHandler.post(() -> onDarkIntensity(darkIntensity)); - return; - } - if (mOverlay != null) { - CornerHandleView assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left); - CornerHandleView assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right); - - assistHintTopLeft.updateDarkness(darkIntensity); - assistHintTopRight.updateDarkness(darkIntensity); - } - - if (mBottomOverlay != null) { - CornerHandleView assistHintBottomLeft = mBottomOverlay.findViewById( - R.id.assist_hint_left); - CornerHandleView assistHintBottomRight = mBottomOverlay.findViewById( - R.id.assist_hint_right); - - assistHintBottomLeft.updateDarkness(darkIntensity); - assistHintBottomRight.updateDarkness(darkIntensity); - } - } - @VisibleForTesting static class TunablePaddingTagListener implements FragmentListener { diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java index c54f6306ddb1..10009f5f5582 100644 --- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java @@ -59,12 +59,13 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman /** Only show once automatically in the process life. */ private boolean mHasShownHint; - public SizeCompatModeActivityController() { - this(ActivityManagerWrapper.getInstance()); + public SizeCompatModeActivityController(Context context) { + this(context, ActivityManagerWrapper.getInstance()); } @VisibleForTesting - SizeCompatModeActivityController(ActivityManagerWrapper am) { + SizeCompatModeActivityController(Context context, ActivityManagerWrapper am) { + super(context); am.registerTaskStackListener(new TaskStackChangeListener() { @Override public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { @@ -202,7 +203,7 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; mWinParams.format = PixelFormat.TRANSLUCENT; - mWinParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; mWinParams.setTitle(SizeCompatModeActivityController.class.getSimpleName() + context.getDisplayId()); } diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java index b3fc69e8a49d..92fbd259b471 100644 --- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java +++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java @@ -39,6 +39,10 @@ public class SliceBroadcastRelayHandler extends SystemUI { private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>(); + public SliceBroadcastRelayHandler(Context context) { + super(context); + } + @Override public void start() { if (DEBUG) Log.d(TAG, "Start"); diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java index 8bcf0571b2d0..0f7f1bebae57 100644 --- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java @@ -31,7 +31,7 @@ public class SysUIToast { public static Toast makeText(Context context, CharSequence text, @Duration int duration) { Toast toast = Toast.makeText(context, text, duration); toast.getWindowParams().privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; return toast; } diff --git a/packages/SystemUI/src/com/android/systemui/SystemBars.java b/packages/SystemUI/src/com/android/systemui/SystemBars.java deleted file mode 100644 index c4c0fd6da124..000000000000 --- a/packages/SystemUI/src/com/android/systemui/SystemBars.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2017 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; - -import android.util.Log; - -import com.android.systemui.statusbar.phone.StatusBar; - -import java.io.FileDescriptor; -import java.io.PrintWriter; - -/** - * Ensure a single status bar service implementation is running at all times, using the in-process - * implementation according to the product config. - */ -public class SystemBars extends SystemUI { - private static final String TAG = "SystemBars"; - private static final boolean DEBUG = false; - private static final int WAIT_FOR_BARS_TO_DIE = 500; - - // in-process fallback implementation, per the product config - private SystemUI mStatusBar; - - @Override - public void start() { - if (DEBUG) Log.d(TAG, "start"); - createStatusBarFromConfig(); - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mStatusBar != null) { - mStatusBar.dump(fd, pw, args); - } - } - - @Override - public void onBootCompleted() { - if (mStatusBar != null) { - mStatusBar.onBootCompleted(); - } - } - - private void createStatusBarFromConfig() { - if (DEBUG) Log.d(TAG, "createStatusBarFromConfig"); - final String clsName = mContext.getString(R.string.config_statusBarComponent); - if (clsName == null || clsName.length() == 0) { - throw andLog("No status bar component configured", null); - } - Class<?> cls = null; - try { - cls = mContext.getClassLoader().loadClass(clsName); - } catch (Throwable t) { - throw andLog("Error loading status bar component: " + clsName, t); - } - try { - mStatusBar = (SystemUI) cls.newInstance(); - } catch (Throwable t) { - throw andLog("Error creating status bar component: " + clsName, t); - } - mStatusBar.mContext = mContext; - mStatusBar.mComponents = mComponents; - if (mStatusBar instanceof StatusBar) { - SystemUIFactory.getInstance().getRootComponent() - .getStatusBarInjector() - .createStatusBar((StatusBar) mStatusBar); - } - mStatusBar.start(); - if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName()); - } - - private RuntimeException andLog(String msg, Throwable t) { - Log.w(TAG, msg, t); - throw new RuntimeException(msg, t); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java index 30fbef6cbefb..75700379caca 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUI.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java @@ -26,9 +26,13 @@ import java.io.PrintWriter; import java.util.Map; public abstract class SystemUI implements SysUiServiceProvider { - public Context mContext; + protected final Context mContext; public Map<Class<?>, Object> mComponents; + public SystemUI(Context context) { + mContext = context; + } + public abstract void start(); protected void onConfigurationChanged(Configuration newConfig) { diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java index 2c8324cafca0..746515a816b3 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java @@ -27,6 +27,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.AppComponentFactory; +import com.android.systemui.dagger.ContextComponentHelper; + import javax.inject.Inject; /** @@ -92,6 +94,12 @@ public class SystemUIAppComponentFactory extends AppComponentFactory { public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className, @Nullable Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { + if (mComponentHelper == null) { + // This shouldn't happen, but is seen on occasion. + // Bug filed against framework to take a look: http://b/141008541 + SystemUIFactory.getInstance().getRootComponent().inject( + SystemUIAppComponentFactory.this); + } Activity activity = mComponentHelper.resolveActivity(className); if (activity != null) { return activity; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 56b5d080acc0..022bf06838a6 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -34,6 +34,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.TimingsTraceLog; +import com.android.systemui.dagger.ContextComponentHelper; import com.android.systemui.plugins.OverlayPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; @@ -42,6 +43,8 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.util.NotificationChannels; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; @@ -193,18 +196,18 @@ public class SystemUIApplication extends Application implements SysUiServiceProv try { SystemUI obj = mComponentHelper.resolveSystemUI(clsName); if (obj == null) { - obj = (SystemUI) Class.forName(clsName).newInstance(); + Constructor constructor = Class.forName(clsName).getConstructor(Context.class); + obj = (SystemUI) constructor.newInstance(this); } mServices[i] = obj; - } catch (ClassNotFoundException ex) { - throw new RuntimeException(ex); - } catch (IllegalAccessException ex) { - throw new RuntimeException(ex); - } catch (InstantiationException ex) { + } catch (ClassNotFoundException + | NoSuchMethodException + | IllegalAccessException + | InstantiationException + | InvocationTargetException ex) { throw new RuntimeException(ex); } - mServices[i].mContext = this; mServices[i].mComponents = mComponents; if (DEBUG) Log.d(TAG, "running: " + mServices[i]); mServices[i].start(); @@ -235,7 +238,7 @@ public class SystemUIApplication extends Application implements SysUiServiceProv if (statusBar != null) { plugin.setup(statusBar.getStatusBarWindow(), statusBar.getNavigationBarView(), new Callback(plugin), - DozeParameters.getInstance(getBaseContext())); + Dependency.get(DozeParameters.class)); } } }); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 8e693185ab7f..0a547b6bf051 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -17,7 +17,6 @@ package com.android.systemui; import android.annotation.NonNull; -import android.app.AlarmManager; import android.content.Context; import android.os.Handler; import android.os.Looper; @@ -25,33 +24,26 @@ import android.util.Log; import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.colorextraction.ColorExtractor.GradientColors; -import com.android.internal.util.function.TriConsumer; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; +import com.android.systemui.dagger.DaggerSystemUIRootComponent; +import com.android.systemui.dagger.DependencyProvider; +import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationMediaManager; -import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockIcon; -import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.NotificationIconAreaController; -import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.statusbar.phone.ScrimState; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.UnlockMethodCache; -import com.android.systemui.statusbar.policy.KeyguardMonitor; -import com.android.systemui.volume.VolumeDialogComponent; - -import java.util.function.Consumer; +import com.android.systemui.statusbar.policy.KeyguardStateController; import dagger.Module; import dagger.Provides; @@ -118,7 +110,7 @@ public class SystemUIFactory { protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { return DaggerSystemUIRootComponent.builder() - .dependencyProvider(new com.android.systemui.DependencyProvider()) + .dependencyProvider(new DependencyProvider()) .contextHolder(new ContextHolder(context)) .build(); } @@ -136,24 +128,15 @@ public class SystemUIFactory { LockPatternUtils lockPatternUtils, ViewGroup container, DismissCallbackRegistry dismissCallbackRegistry, KeyguardBouncer.BouncerExpansionCallback expansionCallback, - FalsingManager falsingManager, KeyguardBypassController bypassController) { + KeyguardStateController keyguardStateController, FalsingManager falsingManager, + KeyguardBypassController bypassController) { return new KeyguardBouncer(context, callback, lockPatternUtils, container, dismissCallbackRegistry, falsingManager, - expansionCallback, UnlockMethodCache.getInstance(context), + expansionCallback, keyguardStateController, Dependency.get(KeyguardUpdateMonitor.class), bypassController, new Handler(Looper.getMainLooper())); } - public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront, - ScrimView scrimForBubble, - LockscreenWallpaper lockscreenWallpaper, - TriConsumer<ScrimState, Float, GradientColors> scrimStateListener, - Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters, - AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) { - return new ScrimController(scrimBehind, scrimInFront, scrimForBubble, scrimStateListener, - scrimVisibleListener, dozeParameters, alarmManager, keyguardMonitor); - } - public NotificationIconAreaController createNotificationIconAreaController(Context context, StatusBar statusBar, NotificationWakeUpCoordinator wakeUpCoordinator, @@ -161,7 +144,8 @@ public class SystemUIFactory { StatusBarStateController statusBarStateController) { return new NotificationIconAreaController(context, statusBar, statusBarStateController, wakeUpCoordinator, keyguardBypassController, - Dependency.get(NotificationMediaManager.class)); + Dependency.get(NotificationMediaManager.class), + Dependency.get(DozeParameters.class)); } public KeyguardIndicationController createKeyguardIndicationController(Context context, @@ -169,10 +153,6 @@ public class SystemUIFactory { return new KeyguardIndicationController(context, indicationArea, lockIcon); } - public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) { - return new VolumeDialogComponent(systemUi, context); - } - @Module public static class ContextHolder { private Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java index 8f1fcae8e0f7..3f56ff07b24d 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -16,6 +16,7 @@ package com.android.systemui; +import android.annotation.NonNull; import android.app.Service; import android.content.Intent; import android.os.Build; @@ -66,7 +67,13 @@ public class SystemUIService extends Service { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (args != null && args.length > 0 && args[0].equals("--config")) { + dumpConfig(pw); + return; + } + dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args); + dumpConfig(pw); } static void dumpServices( @@ -95,5 +102,29 @@ public class SystemUIService extends Service { } } } + + private void dumpConfig(@NonNull PrintWriter pw) { + pw.println("SystemUiServiceComponents configuration:"); + + pw.print("vendor component: "); + pw.println(getResources().getString(R.string.config_systemUIVendorServiceComponent)); + + dumpConfig(pw, "global", R.array.config_systemUIServiceComponents); + dumpConfig(pw, "per-user", R.array.config_systemUIServiceComponentsPerUser); + } + + private void dumpConfig(@NonNull PrintWriter pw, @NonNull String type, int resId) { + final String[] services = getResources().getStringArray(resId); + pw.print(type); pw.print(": "); + if (services == null) { + pw.println("N/A"); + return; + } + pw.print(services.length); + pw.println(" services"); + for (int i = 0; i < services.length; i++) { + pw.print(" "); pw.print(i); pw.print(": "); pw.println(services[i]); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/VendorServices.java b/packages/SystemUI/src/com/android/systemui/VendorServices.java index 0be6b12fa365..13d847bf93c4 100644 --- a/packages/SystemUI/src/com/android/systemui/VendorServices.java +++ b/packages/SystemUI/src/com/android/systemui/VendorServices.java @@ -16,11 +16,17 @@ package com.android.systemui; +import android.content.Context; + /** * Placeholder for any vendor-specific services. */ public class VendorServices extends SystemUI { + public VendorServices(Context context) { + super(context); + } + @Override public void start() { // no-op diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index ef171d305d28..f616d57e90aa 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -16,8 +16,6 @@ package com.android.systemui.appops; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; - import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; @@ -31,6 +29,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.BgLooper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -39,7 +38,6 @@ import java.util.List; import java.util.Set; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -79,7 +77,7 @@ public class AppOpsControllerImpl implements AppOpsController, }; @Inject - public AppOpsControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) { + public AppOpsControllerImpl(Context context, @BgLooper Looper bgLooper) { this(context, bgLooper, new PermissionFlagsCache(context)); } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java index 9bdfa03408aa..4516996345b9 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java @@ -32,7 +32,6 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; -import com.android.systemui.ScreenDecorations; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.NavigationModeController; @@ -71,7 +70,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac private final Handler mHandler; private final Runnable mHideHandles = this::hideHandles; private final Runnable mShowAndGo = this::showAndGoInternal; - private final Provider<ScreenDecorations> mScreenDecorations; + private final Provider<AssistHandleViewController> mAssistHandleViewController; private final PhenotypeHelper mPhenotypeHelper; private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap; @@ -90,7 +89,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac Context context, AssistUtils assistUtils, @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler, - Provider<ScreenDecorations> screenDecorations, + Provider<AssistHandleViewController> assistHandleViewController, PhenotypeHelper phenotypeHelper, Map<AssistHandleBehavior, BehaviorController> behaviorMap, NavigationModeController navigationModeController, @@ -98,7 +97,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac mContext = context; mAssistUtils = assistUtils; mHandler = handler; - mScreenDecorations = screenDecorations; + mAssistHandleViewController = assistHandleViewController; mPhenotypeHelper = phenotypeHelper; mBehaviorMap = behaviorMap; @@ -193,7 +192,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac try { setBehavior(AssistHandleBehavior.valueOf(behavior)); } catch (IllegalArgumentException | NullPointerException e) { - Log.e(TAG, "Invalid behavior: " + behavior, e); + Log.e(TAG, "Invalid behavior: " + behavior); } } @@ -229,12 +228,13 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac } if (handlesUnblocked(ignoreThreshold)) { - ScreenDecorations screenDecorations = mScreenDecorations.get(); - if (screenDecorations == null) { - Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable"); + mHandlesShowing = true; + AssistHandleViewController assistHandleViewController = + mAssistHandleViewController.get(); + if (assistHandleViewController == null) { + Log.w(TAG, "Couldn't show handles, AssistHandleViewController unavailable"); } else { - mHandlesShowing = true; - screenDecorations.setAssistHintVisible(true); + assistHandleViewController.setAssistHintVisible(true); } } } @@ -244,13 +244,14 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac return; } - ScreenDecorations screenDecorations = mScreenDecorations.get(); - if (screenDecorations == null) { - Log.w(TAG, "Couldn't hide handles, ScreenDecorations unavailable"); + mHandlesShowing = false; + mHandlesLastHiddenAt = SystemClock.elapsedRealtime(); + AssistHandleViewController assistHandleViewController = + mAssistHandleViewController.get(); + if (assistHandleViewController == null) { + Log.w(TAG, "Couldn't show handles, AssistHandleViewController unavailable"); } else { - mHandlesShowing = false; - mHandlesLastHiddenAt = SystemClock.elapsedRealtime(); - screenDecorations.setAssistHintVisible(false); + assistHandleViewController.setAssistHintVisible(false); } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java new file mode 100644 index 000000000000..5010f319f8b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2019 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.assist; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.os.Handler; +import android.util.Log; +import android.util.MathUtils; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.CornerHandleView; +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.NavigationBarTransitions; + +/** + * A class for managing Assistant handle show, hide and animation. + */ +public class AssistHandleViewController implements NavigationBarTransitions.DarkIntensityListener { + + private static final boolean DEBUG = false; + private static final String TAG = "AssistHandleViewController"; + + private Handler mHandler; + private CornerHandleView mAssistHintLeft; + private CornerHandleView mAssistHintRight; + private int mBottomOffset; + + @VisibleForTesting + boolean mAssistHintVisible; + @VisibleForTesting + boolean mAssistHintBlocked = false; + + public AssistHandleViewController(Handler handler, View navBar) { + mHandler = handler; + mAssistHintLeft = navBar.findViewById(R.id.assist_hint_left); + mAssistHintRight = navBar.findViewById(R.id.assist_hint_right); + } + + @Override + public void onDarkIntensity(float darkIntensity) { + mAssistHintLeft.updateDarkness(darkIntensity); + mAssistHintRight.updateDarkness(darkIntensity); + } + + /** + * Set the bottom offset. + * + * @param bottomOffset the bottom offset to translate. + */ + public void setBottomOffset(int bottomOffset) { + if (mBottomOffset != bottomOffset) { + mBottomOffset = bottomOffset; + if (mAssistHintVisible) { + // If assist handles are visible, hide them without animation and then make them + // show once again (with corrected bottom offset). + hideAssistHandles(); + setAssistHintVisible(true); + } + } + } + + /** + * Controls the visibility of the assist gesture handles. + * + * @param visible whether the handles should be shown + */ + public void setAssistHintVisible(boolean visible) { + if (!mHandler.getLooper().isCurrentThread()) { + mHandler.post(() -> setAssistHintVisible(visible)); + return; + } + + if (mAssistHintBlocked && visible) { + if (DEBUG) { + Log.v(TAG, "Assist hint blocked, cannot make it visible"); + } + return; + } + + if (mAssistHintVisible != visible) { + mAssistHintVisible = visible; + fade(mAssistHintLeft, mAssistHintVisible, /* isLeft = */ true); + fade(mAssistHintRight, mAssistHintVisible, /* isLeft = */ false); + } + } + + /** + * Prevents the assist hint from becoming visible even if `mAssistHintVisible` is true. + */ + public void setAssistHintBlocked(boolean blocked) { + if (!mHandler.getLooper().isCurrentThread()) { + mHandler.post(() -> setAssistHintBlocked(blocked)); + return; + } + + mAssistHintBlocked = blocked; + if (mAssistHintVisible && mAssistHintBlocked) { + hideAssistHandles(); + } + } + + private void hideAssistHandles() { + mAssistHintLeft.setVisibility(View.GONE); + mAssistHintRight.setVisibility(View.GONE); + mAssistHintVisible = false; + } + + /** + * Returns an animator that animates the given view from start to end over durationMs. Start and + * end represent total animation progress: 0 is the start, 1 is the end, 1.1 would be an + * overshoot. + */ + Animator getHandleAnimator(View view, float start, float end, boolean isLeft, long durationMs, + Interpolator interpolator) { + // Note that lerp does allow overshoot, in cases where start and end are outside of [0,1]. + float scaleStart = MathUtils.lerp(2f, 1f, start); + float scaleEnd = MathUtils.lerp(2f, 1f, end); + Animator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, scaleStart, scaleEnd); + Animator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, scaleStart, scaleEnd); + float translationStart = MathUtils.lerp(0.2f, 0f, start); + float translationEnd = MathUtils.lerp(0.2f, 0f, end); + int xDirection = isLeft ? -1 : 1; + Animator translateX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, + xDirection * translationStart * view.getWidth(), + xDirection * translationEnd * view.getWidth()); + Animator translateY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, + translationStart * view.getHeight() + mBottomOffset, + translationEnd * view.getHeight() + mBottomOffset); + + AnimatorSet set = new AnimatorSet(); + set.play(scaleX).with(scaleY); + set.play(scaleX).with(translateX); + set.play(scaleX).with(translateY); + set.setDuration(durationMs); + set.setInterpolator(interpolator); + return set; + } + + private void fade(View view, boolean fadeIn, boolean isLeft) { + if (fadeIn) { + view.animate().cancel(); + view.setAlpha(1f); + view.setVisibility(View.VISIBLE); + + // A piecewise spring-like interpolation. + // End value in one animator call must match the start value in the next, otherwise + // there will be a discontinuity. + AnimatorSet anim = new AnimatorSet(); + Animator first = getHandleAnimator(view, 0, 1.1f, isLeft, 750, + new PathInterpolator(0, 0.45f, .67f, 1f)); + Interpolator secondInterpolator = new PathInterpolator(0.33f, 0, 0.67f, 1f); + Animator second = getHandleAnimator(view, 1.1f, 0.97f, isLeft, 400, + secondInterpolator); + Animator third = getHandleAnimator(view, 0.97f, 1.02f, isLeft, 400, + secondInterpolator); + Animator fourth = getHandleAnimator(view, 1.02f, 1f, isLeft, 400, + secondInterpolator); + anim.play(first).before(second); + anim.play(second).before(third); + anim.play(third).before(fourth); + anim.start(); + } else { + view.animate().cancel(); + view.animate() + .setInterpolator(new AccelerateInterpolator(1.5f)) + .setDuration(250) + .alpha(0f); + } + + } +} diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java index 2a82d215e44a..6f5a17dca432 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java @@ -21,11 +21,11 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; +import androidx.annotation.Nullable; import androidx.slice.Clock; import com.android.internal.app.AssistUtils; -import com.android.systemui.ScreenDecorations; -import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.statusbar.NavigationBarController; import java.util.EnumMap; import java.util.Map; @@ -69,8 +69,10 @@ public abstract class AssistModule { } @Provides - static ScreenDecorations provideScreenDecorations(Context context) { - return SysUiServiceProvider.getComponent(context, ScreenDecorations.class); + @Nullable + static AssistHandleViewController provideAssistHandleViewController( + NavigationBarController navigationBarController) { + return navigationBarController.getAssistHandlerViewController(); } @Provides diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java index 0c4f05123c79..4cb1708468ea 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java @@ -17,6 +17,7 @@ package com.android.systemui.assist.ui; import static com.android.systemui.assist.AssistManager.DISMISS_REASON_INVOCATION_CANCELLED; +import static com.android.systemui.assist.AssistManager.INVOCATION_TYPE_GESTURE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -24,6 +25,7 @@ import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PixelFormat; import android.metrics.LogMaker; +import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.Gravity; @@ -36,9 +38,11 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.ScreenDecorations; -import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.assist.AssistHandleViewController; import com.android.systemui.assist.AssistManager; +import com.android.systemui.statusbar.NavigationBarController; + +import java.util.Locale; /** * Default UiController implementation. Shows white edge lights along the bottom of the phone, @@ -50,6 +54,9 @@ public class DefaultUiController implements AssistManager.UiController { private static final long ANIM_DURATION_MS = 200; + private static final boolean VERBOSE = Build.TYPE.toLowerCase(Locale.ROOT).contains("debug") + || Build.TYPE.toLowerCase(Locale.ROOT).equals("eng"); + protected final FrameLayout mRoot; protected InvocationLightsView mInvocationLightsView; @@ -114,6 +121,7 @@ public class DefaultUiController implements AssistManager.UiController { @Override // AssistManager.UiController public void onGestureCompletion(float velocity) { animateInvocationCompletion(AssistManager.INVOCATION_TYPE_GESTURE, velocity); + logInvocationProgressMetrics(INVOCATION_TYPE_GESTURE, 1, mInvocationInProgress); } @Override // AssistManager.UiController @@ -127,16 +135,28 @@ public class DefaultUiController implements AssistManager.UiController { updateAssistHandleVisibility(); } - protected static void logInvocationProgressMetrics( + protected void logInvocationProgressMetrics( int type, float progress, boolean invocationWasInProgress) { // Logs assistant invocation start. + if (progress == 1f) { + if (VERBOSE) { + Log.v(TAG, "Invocation complete: type=" + type); + } + } if (!invocationWasInProgress && progress > 0.f) { + if (VERBOSE) { + Log.v(TAG, "Invocation started: type=" + type); + } MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT) .setType(MetricsEvent.TYPE_ACTION) .setSubtype(Dependency.get(AssistManager.class).toLoggingSubType(type))); } // Logs assistant invocation cancelled. - if (invocationWasInProgress && progress == 0f) { + if ((mInvocationAnimator == null || !mInvocationAnimator.isRunning()) + && invocationWasInProgress && progress == 0f) { + if (VERBOSE) { + Log.v(TAG, "Invocation cancelled: type=" + type); + } MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT) .setType(MetricsEvent.TYPE_DISMISS) .setSubtype(DISMISS_REASON_INVOCATION_CANCELLED)); @@ -144,9 +164,14 @@ public class DefaultUiController implements AssistManager.UiController { } private void updateAssistHandleVisibility() { - ScreenDecorations decorations = SysUiServiceProvider.getComponent(mRoot.getContext(), - ScreenDecorations.class); - decorations.setAssistHintBlocked(mInvocationInProgress); + NavigationBarController navigationBarController = + Dependency.get(NavigationBarController.class); + AssistHandleViewController controller = + navigationBarController == null + ? null : navigationBarController.getAssistHandlerViewController(); + if (controller != null) { + controller.setAssistHintBlocked(mInvocationInProgress); + } } private void attach() { diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/EdgeLight.java b/packages/SystemUI/src/com/android/systemui/assist/ui/EdgeLight.java index 9ae02c5e3104..baa3a4a938c1 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/ui/EdgeLight.java +++ b/packages/SystemUI/src/com/android/systemui/assist/ui/EdgeLight.java @@ -16,6 +16,8 @@ package com.android.systemui.assist.ui; +import android.util.Log; + import androidx.annotation.ColorInt; /** @@ -29,9 +31,12 @@ import androidx.annotation.ColorInt; * counter-clockwise. */ public final class EdgeLight { + + private static final String TAG = "EdgeLight"; + @ColorInt private int mColor; - private float mOffset; + private float mStart; private float mLength; /** Copies a list of EdgeLights. */ @@ -45,13 +50,13 @@ public final class EdgeLight { public EdgeLight(@ColorInt int color, float offset, float length) { mColor = color; - mOffset = offset; + mStart = offset; mLength = length; } public EdgeLight(EdgeLight sourceLight) { mColor = sourceLight.getColor(); - mOffset = sourceLight.getOffset(); + mStart = sourceLight.getStart(); mLength = sourceLight.getLength(); } @@ -77,23 +82,41 @@ public final class EdgeLight { } /** - * Returns the current offset, in units of the total device perimeter and measured from the - * bottom-left corner (see class description). + * Sets the endpoints of the edge light, both measured from the bottom-left corner (see class + * description). This is a convenience method to avoid separate setStart and setLength calls. */ - public float getOffset() { - return mOffset; + public void setEndpoints(float start, float end) { + if (start > end) { + Log.e(TAG, String.format("Endpoint must be >= start (add 1 if necessary). Got [%f, %f]", + start, end)); + return; + } + mStart = start; + mLength = end - start; + } + + /** + * Returns the current starting position, in units of the total device perimeter and measured + * from the bottom-left corner (see class description). + */ + public float getStart() { + return mStart; } /** * Sets the current offset, in units of the total device perimeter and measured from the * bottom-left corner (see class description). */ - public void setOffset(float offset) { - mOffset = offset; + public void setStart(float start) { + mStart = start; + } + + public float getEnd() { + return mStart + mLength; } /** Returns the center, measured from the bottom-left corner (see class description). */ public float getCenter() { - return mOffset + (mLength / 2.f); + return mStart + (mLength / 2.f); } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java index bb3bd781df66..570b911cd400 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java +++ b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java @@ -140,10 +140,10 @@ public class InvocationLightsView extends View float rightStart = mGuide.getRegionWidth(PerimeterPathGuide.Region.BOTTOM) + (cornerLengthNormalized - arcOffsetNormalized) * (1 - progress); - setLight(0, leftStart, lightLength); - setLight(1, leftStart + lightLength, lightLength); - setLight(2, rightStart - (lightLength * 2), lightLength); - setLight(3, rightStart - lightLength, lightLength); + setLight(0, leftStart, leftStart + lightLength); + setLight(1, leftStart + lightLength, leftStart + lightLength * 2); + setLight(2, rightStart - (lightLength * 2), rightStart - lightLength); + setLight(3, rightStart - lightLength, rightStart); setVisibility(View.VISIBLE); } invalidate(); @@ -155,7 +155,7 @@ public class InvocationLightsView extends View public void hide() { setVisibility(GONE); for (EdgeLight light : mAssistInvocationLights) { - light.setLength(0); + light.setEndpoints(0, 0); } attemptUnregisterNavBarListener(); } @@ -235,12 +235,11 @@ public class InvocationLightsView extends View } } - protected void setLight(int index, float offset, float length) { + protected void setLight(int index, float start, float end) { if (index < 0 || index >= 4) { Log.w(TAG, "invalid invocation light index: " + index); } - mAssistInvocationLights.get(index).setOffset(offset); - mAssistInvocationLights.get(index).setLength(length); + mAssistInvocationLights.get(index).setEndpoints(start, end); } /** @@ -268,9 +267,11 @@ public class InvocationLightsView extends View } private void renderLight(EdgeLight light, Canvas canvas) { - mGuide.strokeSegment(mPath, light.getOffset(), light.getOffset() + light.getLength()); - mPaint.setColor(light.getColor()); - canvas.drawPath(mPath, mPaint); + if (light.getLength() > 0) { + mGuide.strokeSegment(mPath, light.getStart(), light.getStart() + light.getLength()); + mPaint.setColor(light.getColor()); + canvas.drawPath(mPath, mPaint); + } } private void attemptRegisterNavBarListener() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index 73bbce9c5b35..d20cd72f0712 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -16,8 +16,6 @@ package com.android.systemui.biometrics; -import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -25,6 +23,7 @@ import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.admin.DevicePolicyManager; import android.content.Context; import android.hardware.biometrics.BiometricPrompt; import android.os.Bundle; @@ -34,7 +33,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.View; -import android.view.accessibility.AccessibilityEvent; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; import android.widget.Button; import android.widget.ImageView; @@ -42,10 +41,13 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** * Contains the Biometric views (title, subtitle, icon, buttons, etc) and its controllers. @@ -97,6 +99,7 @@ public abstract class AuthBiometricView extends LinearLayout { int ACTION_BUTTON_NEGATIVE = 3; int ACTION_BUTTON_TRY_AGAIN = 4; int ACTION_ERROR = 5; + int ACTION_USE_DEVICE_CREDENTIAL = 6; /** * When an action has occurred. The caller will only invoke this when the callback should @@ -145,6 +148,14 @@ public abstract class AuthBiometricView extends LinearLayout { public int getDelayAfterError() { return BiometricPrompt.HIDE_DIALOG_DELAY; } + + public int getMediumToLargeAnimationDurationMs() { + return AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS; + } + + public int getAnimateCredentialStartDelayMs() { + return AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS; + } } private final Injector mInjector; @@ -154,8 +165,9 @@ public abstract class AuthBiometricView extends LinearLayout { private final int mTextColorHint; private AuthPanelController mPanelController; - private Bundle mBundle; + private Bundle mBiometricPromptBundle; private boolean mRequireConfirmation; + private int mUserId; @AuthDialog.DialogSize int mSize = AuthDialog.SIZE_UNKNOWN; private TextView mTitleView; @@ -212,6 +224,9 @@ public abstract class AuthBiometricView extends LinearLayout { } else if (mSize == AuthDialog.SIZE_SMALL) { Log.w(TAG, "Ignoring background click during small dialog"); return; + } else if (mSize == AuthDialog.SIZE_LARGE) { + Log.w(TAG, "Ignoring background click during large dialog"); + return; } mCallback.onAction(Callback.ACTION_USER_CANCELED); }; @@ -256,7 +271,7 @@ public abstract class AuthBiometricView extends LinearLayout { } public void setBiometricPromptBundle(Bundle bundle) { - mBundle = bundle; + mBiometricPromptBundle = bundle; } public void setCallback(Callback callback) { @@ -267,6 +282,10 @@ public abstract class AuthBiometricView extends LinearLayout { backgroundView.setOnClickListener(mBackgroundClickListener); } + public void setUserId(int userId) { + mUserId = userId; + } + public void setRequireConfirmation(boolean requireConfirmation) { mRequireConfirmation = requireConfirmation; } @@ -287,7 +306,7 @@ public abstract class AuthBiometricView extends LinearLayout { final int newHeight = mIconView.getHeight() + 2 * (int) iconPadding; mPanelController.updateForContentDimensions(mMediumWidth, newHeight, - false /* animate */); + 0 /* animateDurationMs */); mSize = newSize; } else if (mSize == AuthDialog.SIZE_SMALL && newSize == AuthDialog.SIZE_MEDIUM) { @@ -305,10 +324,8 @@ public abstract class AuthBiometricView extends LinearLayout { // Animate the text final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0, 1); - opacityAnimator.setDuration(AuthDialog.ANIMATE_DURATION_MS); opacityAnimator.addUpdateListener((animation) -> { final float opacity = (float) animation.getAnimatedValue(); - mTitleView.setAlpha(opacity); mIndicatorView.setAlpha(opacity); mNegativeButton.setAlpha(opacity); @@ -324,7 +341,7 @@ public abstract class AuthBiometricView extends LinearLayout { // Choreograph together final AnimatorSet as = new AnimatorSet(); - as.setDuration(AuthDialog.ANIMATE_DURATION_MS); + as.setDuration(AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS); as.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { @@ -355,11 +372,73 @@ public abstract class AuthBiometricView extends LinearLayout { as.start(); // Animate the panel mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight, - true /* animate */); + AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS); } else if (newSize == AuthDialog.SIZE_MEDIUM) { mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight, - false /* animate */); + 0 /* animateDurationMs */); mSize = newSize; + } else if (newSize == AuthDialog.SIZE_LARGE) { + final boolean isManagedProfile = Utils.isManagedProfile(mContext, mUserId); + + // If it's a managed profile, animate the contents and panel down, since the credential + // contents will be shown on the same "layer" as the background. If it's not a managed + // profile, animate the contents up and expand the panel to full-screen - the credential + // contents will be shown on the same "layer" as the panel. + final float translationY = isManagedProfile ? + -getResources().getDimension( + R.dimen.biometric_dialog_animation_translation_offset) + : getResources().getDimension( + R.dimen.biometric_dialog_medium_to_large_translation_offset); + final AuthBiometricView biometricView = this; + + // Translate at full duration + final ValueAnimator translationAnimator = ValueAnimator.ofFloat( + biometricView.getY(), biometricView.getY() - translationY); + translationAnimator.setDuration(mInjector.getMediumToLargeAnimationDurationMs()); + translationAnimator.addUpdateListener((animation) -> { + final float translation = (float) animation.getAnimatedValue(); + biometricView.setTranslationY(translation); + }); + translationAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (biometricView.getParent() != null) { + ((ViewGroup) biometricView.getParent()).removeView(biometricView); + } + mSize = newSize; + } + }); + + // Opacity to 0 in half duration + final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(1, 0); + opacityAnimator.setDuration(mInjector.getMediumToLargeAnimationDurationMs() / 2); + opacityAnimator.addUpdateListener((animation) -> { + final float opacity = (float) animation.getAnimatedValue(); + biometricView.setAlpha(opacity); + }); + + if (!isManagedProfile) { + mPanelController.setUseFullScreen(true); + mPanelController.updateForContentDimensions( + mPanelController.getContainerWidth(), + mPanelController.getContainerHeight(), + mInjector.getMediumToLargeAnimationDurationMs()); + } + + // Start the animations together + AnimatorSet as = new AnimatorSet(); + List<Animator> animators = new ArrayList<>(); + animators.add(translationAnimator); + animators.add(opacityAnimator); + if (isManagedProfile) { + animators.add(mPanelController.getTranslationAnimator(translationY)); + animators.add(mPanelController.getAlphaAnimator(0)); + } + as.playTogether(animators); + as.setDuration(isManagedProfile ? mInjector.getMediumToLargeAnimationDurationMs() + : mInjector.getMediumToLargeAnimationDurationMs() * 2 / 3); + as.start(); } else { Log.e(TAG, "Unknown transition from: " + mSize + " to: " + newSize); } @@ -528,7 +607,11 @@ public abstract class AuthBiometricView extends LinearLayout { if (mState == STATE_PENDING_CONFIRMATION) { mCallback.onAction(Callback.ACTION_USER_CANCELED); } else { - mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE); + if (isDeviceCredentialAllowed()) { + startTransitionToCredentialUI(); + } else { + mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE); + } } }); @@ -544,6 +627,16 @@ public abstract class AuthBiometricView extends LinearLayout { }); } + /** + * Kicks off the animation process and invokes the callback. + */ + void startTransitionToCredentialUI() { + updateSize(AuthDialog.SIZE_LARGE); + mHandler.postDelayed(() -> { + mCallback.onAction(Callback.ACTION_USE_DEVICE_CREDENTIAL); + }, mInjector.getAnimateCredentialStartDelayMs()); + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -556,11 +649,37 @@ public abstract class AuthBiometricView extends LinearLayout { */ @VisibleForTesting void onAttachedToWindowInternal() { - setText(mTitleView, mBundle.getString(BiometricPrompt.KEY_TITLE)); - setText(mNegativeButton, mBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT)); + setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE)); + + final String negativeText; + if (isDeviceCredentialAllowed()) { + + final @Utils.CredentialType int credentialType = + Utils.getCredentialType(mContext, mUserId); + switch(credentialType) { + case Utils.CREDENTIAL_PIN: + negativeText = getResources().getString(R.string.biometric_dialog_use_pin); + break; + case Utils.CREDENTIAL_PATTERN: + negativeText = getResources().getString(R.string.biometric_dialog_use_pattern); + break; + case Utils.CREDENTIAL_PASSWORD: + negativeText = getResources().getString(R.string.biometric_dialog_use_password); + break; + default: + negativeText = getResources().getString(R.string.biometric_dialog_use_password); + break; + } - setTextOrHide(mSubtitleView, mBundle.getString(BiometricPrompt.KEY_SUBTITLE)); - setTextOrHide(mDescriptionView, mBundle.getString(BiometricPrompt.KEY_DESCRIPTION)); + } else { + negativeText = mBiometricPromptBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT); + } + setText(mNegativeButton, negativeText); + + setTextOrHide(mSubtitleView, + mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE)); + setTextOrHide(mDescriptionView, + mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION)); if (mSavedState == null) { updateState(STATE_AUTHENTICATING_ANIMATING_IN); @@ -655,4 +774,8 @@ public abstract class AuthBiometricView extends LinearLayout { } } } + + private boolean isDeviceCredentialAllowed() { + return Utils.isDeviceCredentialAllowed(mBiometricPromptBundle); + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 6555c75f677a..f1abdb31b5f8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -24,7 +24,9 @@ import android.content.Context; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.hardware.biometrics.Authenticator; import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricPrompt; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -36,6 +38,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.Interpolator; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ScrollView; @@ -72,17 +75,20 @@ public class AuthContainerView extends LinearLayout @interface ContainerState {} final Config mConfig; + private final Injector mInjector; private final IBinder mWindowToken = new Binder(); private final WindowManager mWindowManager; private final AuthPanelController mPanelController; private final Interpolator mLinearOutSlowIn; @VisibleForTesting final BiometricCallback mBiometricCallback; + private final CredentialCallback mCredentialCallback; - private final ViewGroup mContainerView; - private final AuthBiometricView mBiometricView; + @VisibleForTesting final FrameLayout mFrameLayout; + @VisibleForTesting @Nullable AuthBiometricView mBiometricView; + @VisibleForTesting @Nullable AuthCredentialView mCredentialView; private final ImageView mBackgroundView; - private final ScrollView mScrollView; + @VisibleForTesting final ScrollView mBiometricScrollView; private final View mPanelView; private final float mTranslationY; @@ -145,7 +151,31 @@ public class AuthContainerView extends LinearLayout public AuthContainerView build(int modalityMask) { mConfig.mModalityMask = modalityMask; - return new AuthContainerView(mConfig); + return new AuthContainerView(mConfig, new Injector()); + } + } + + public static class Injector { + ScrollView getBiometricScrollView(FrameLayout parent) { + return parent.findViewById(R.id.biometric_scrollview); + } + + FrameLayout inflateContainerView(LayoutInflater factory, ViewGroup root) { + return (FrameLayout) factory.inflate( + R.layout.auth_container_view, root, false /* attachToRoot */); + } + + AuthPanelController getPanelController(Context context, View panelView, + boolean isManagedProfile) { + return new AuthPanelController(context, panelView, isManagedProfile); + } + + ImageView getBackgroundView(FrameLayout parent) { + return parent.findViewById(R.id.background); + } + + View getPanelView(FrameLayout parent) { + return parent.findViewById(R.id.panel); } } @@ -155,7 +185,7 @@ public class AuthContainerView extends LinearLayout public void onAction(int action) { switch (action) { case AuthBiometricView.Callback.ACTION_AUTHENTICATED: - animateAway(AuthDialogCallback.DISMISSED_AUTHENTICATED); + animateAway(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED); break; case AuthBiometricView.Callback.ACTION_USER_CANCELED: animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); @@ -169,17 +199,30 @@ public class AuthContainerView extends LinearLayout case AuthBiometricView.Callback.ACTION_ERROR: animateAway(AuthDialogCallback.DISMISSED_ERROR); break; + case AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL: + mConfig.mCallback.onDeviceCredentialPressed(); + addCredentialView(false /* animatePanel */, true /* animateContents */); + break; default: Log.e(TAG, "Unhandled action: " + action); } } } + final class CredentialCallback implements AuthCredentialView.Callback { + @Override + public void onCredentialMatched() { + animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED); + } + } + @VisibleForTesting - AuthContainerView(Config config) { + AuthContainerView(Config config, Injector injector) { super(config.mContext); mConfig = config; + mInjector = injector; + mWindowManager = mContext.getSystemService(WindowManager.class); mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); @@ -187,51 +230,48 @@ public class AuthContainerView extends LinearLayout .getDimension(R.dimen.biometric_dialog_animation_translation_offset); mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN; mBiometricCallback = new BiometricCallback(); + mCredentialCallback = new CredentialCallback(); final LayoutInflater factory = LayoutInflater.from(mContext); - mContainerView = (ViewGroup) factory.inflate( - R.layout.auth_container_view, this, false /* attachToRoot */); - - mPanelView = mContainerView.findViewById(R.id.panel); - mPanelController = new AuthPanelController(mContext, mPanelView); - - // TODO: Update with new controllers if multi-modal authentication can occur simultaneously - if (config.mModalityMask == BiometricAuthenticator.TYPE_FINGERPRINT) { - mBiometricView = (AuthBiometricFingerprintView) - factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false); - } else if (config.mModalityMask == BiometricAuthenticator.TYPE_FACE) { - mBiometricView = (AuthBiometricFaceView) - factory.inflate(R.layout.auth_biometric_face_view, null, false); - } else { - Log.e(TAG, "Unsupported modality mask: " + config.mModalityMask); - mBiometricView = null; - mBackgroundView = null; - mScrollView = null; - return; + mFrameLayout = mInjector.inflateContainerView(factory, this); + + final boolean isManagedProfile = Utils.isManagedProfile(mContext, mConfig.mUserId); + + mPanelView = mInjector.getPanelView(mFrameLayout); + mPanelController = mInjector.getPanelController(mContext, mPanelView, isManagedProfile); + + // Inflate biometric view only if necessary. + if (Utils.isBiometricAllowed(mConfig.mBiometricPromptBundle)) { + if (config.mModalityMask == BiometricAuthenticator.TYPE_FINGERPRINT) { + mBiometricView = (AuthBiometricFingerprintView) + factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false); + } else if (config.mModalityMask == BiometricAuthenticator.TYPE_FACE) { + mBiometricView = (AuthBiometricFaceView) + factory.inflate(R.layout.auth_biometric_face_view, null, false); + } else { + Log.e(TAG, "Unsupported biometric modality: " + config.mModalityMask); + mBiometricView = null; + mBackgroundView = null; + mBiometricScrollView = null; + return; + } } - mBackgroundView = mContainerView.findViewById(R.id.background); + mBiometricScrollView = mInjector.getBiometricScrollView(mFrameLayout); + mBackgroundView = mInjector.getBackgroundView(mFrameLayout); + - UserManager userManager = mContext.getSystemService(UserManager.class); - DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); - if (userManager.isManagedProfile(mConfig.mUserId)) { + if (isManagedProfile) { final Drawable image = getResources().getDrawable(R.drawable.work_challenge_background, mContext.getTheme()); + final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); image.setColorFilter(dpm.getOrganizationColorForUser(mConfig.mUserId), PorterDuff.Mode.DARKEN); mBackgroundView.setScaleType(ImageView.ScaleType.CENTER_CROP); mBackgroundView.setImageDrawable(image); } - mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation); - mBiometricView.setPanelController(mPanelController); - mBiometricView.setBiometricPromptBundle(config.mBiometricPromptBundle); - mBiometricView.setCallback(mBiometricCallback); - mBiometricView.setBackgroundView(mBackgroundView); - - mScrollView = mContainerView.findViewById(R.id.scrollview); - mScrollView.addView(mBiometricView); - addView(mContainerView); + addView(mFrameLayout); setOnKeyListener((v, keyCode, event) -> { if (keyCode != KeyEvent.KEYCODE_BACK) { @@ -248,6 +288,53 @@ public class AuthContainerView extends LinearLayout } @Override + public boolean isAllowDeviceCredentials() { + return Utils.isDeviceCredentialAllowed(mConfig.mBiometricPromptBundle); + } + + private void addBiometricView() { + mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation); + mBiometricView.setPanelController(mPanelController); + mBiometricView.setBiometricPromptBundle(mConfig.mBiometricPromptBundle); + mBiometricView.setCallback(mBiometricCallback); + mBiometricView.setBackgroundView(mBackgroundView); + mBiometricView.setUserId(mConfig.mUserId); + mBiometricScrollView.addView(mBiometricView); + } + + /** + * Adds the credential view. When going from biometric to credential view, the biometric + * view starts the panel expansion animation. If the credential view is being shown first, + * it should own the panel expansion. + * @param animatePanel if the credential view needs to own the panel expansion animation + */ + private void addCredentialView(boolean animatePanel, boolean animateContents) { + final LayoutInflater factory = LayoutInflater.from(mContext); + final int credentialType = Utils.getCredentialType(mContext, mConfig.mUserId); + switch (credentialType) { + case Utils.CREDENTIAL_PATTERN: + mCredentialView = (AuthCredentialView) factory.inflate( + R.layout.auth_credential_pattern_view, null, false); + break; + case Utils.CREDENTIAL_PIN: + case Utils.CREDENTIAL_PASSWORD: + mCredentialView = (AuthCredentialView) factory.inflate( + R.layout.auth_credential_password_view, null, false); + break; + default: + throw new IllegalStateException("Unknown credential type: " + credentialType); + } + + mCredentialView.setContainerView(this); + mCredentialView.setUser(mConfig.mUserId); + mCredentialView.setCallback(mCredentialCallback); + mCredentialView.setBiometricPromptBundle(mConfig.mBiometricPromptBundle); + mCredentialView.setPanelController(mPanelController, animatePanel); + mCredentialView.setShouldAnimateContents(animateContents); + mFrameLayout.addView(mCredentialView); + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mPanelController.setContainerDimensions(getMeasuredWidth(), getMeasuredHeight()); @@ -256,8 +343,22 @@ public class AuthContainerView extends LinearLayout @Override public void onAttachedToWindow() { super.onAttachedToWindow(); + onAttachedToWindowInternal(); + } + + @VisibleForTesting + void onAttachedToWindowInternal() { mWakefulnessLifecycle.addObserver(this); + if (Utils.isBiometricAllowed(mConfig.mBiometricPromptBundle)) { + addBiometricView(); + } else if (Utils.isDeviceCredentialAllowed(mConfig.mBiometricPromptBundle)) { + addCredentialView(true /* animatePanel */, false /* animateContents */); + } else { + throw new IllegalStateException("Unknown configuration: " + + Utils.getAuthenticators(mConfig.mBiometricPromptBundle)); + } + if (mConfig.mSkipIntro) { mContainerState = STATE_SHOWING; } else { @@ -265,7 +366,7 @@ public class AuthContainerView extends LinearLayout // The background panel and content are different views since we need to be able to // animate them separately in other places. mPanelView.setY(mTranslationY); - mScrollView.setY(mTranslationY); + mBiometricScrollView.setY(mTranslationY); setAlpha(0f); postOnAnimation(() -> { @@ -276,12 +377,21 @@ public class AuthContainerView extends LinearLayout .withLayer() .withEndAction(this::onDialogAnimatedIn) .start(); - mScrollView.animate() + mBiometricScrollView.animate() .translationY(0) .setDuration(ANIMATION_DURATION_SHOW_MS) .setInterpolator(mLinearOutSlowIn) .withLayer() .start(); + if (mCredentialView != null && mCredentialView.isAttachedToWindow()) { + mCredentialView.setY(mTranslationY); + mCredentialView.animate() + .translationY(0) + .setDuration(ANIMATION_DURATION_SHOW_MS) + .setInterpolator(mLinearOutSlowIn) + .withLayer() + .start(); + } animate() .alpha(1f) .setDuration(ANIMATION_DURATION_SHOW_MS) @@ -305,7 +415,9 @@ public class AuthContainerView extends LinearLayout @Override public void show(WindowManager wm, @Nullable Bundle savedState) { - mBiometricView.restoreState(savedState); + if (mBiometricView != null) { + mBiometricView.restoreState(savedState); + } wm.addView(this, getLayoutParams(mWindowToken)); } @@ -346,7 +458,15 @@ public class AuthContainerView extends LinearLayout @Override public void onSaveState(@NonNull Bundle outState) { outState.putInt(AuthDialog.KEY_CONTAINER_STATE, mContainerState); - mBiometricView.onSaveState(outState); + // In the case where biometric and credential are both allowed, we can assume that + // biometric isn't showing if credential is showing since biometric is shown first. + outState.putBoolean(AuthDialog.KEY_BIOMETRIC_SHOWING, + mBiometricView != null && mCredentialView == null); + outState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, mCredentialView != null); + + if (mBiometricView != null) { + mBiometricView.onSaveState(outState); + } } @Override @@ -354,6 +474,11 @@ public class AuthContainerView extends LinearLayout return mConfig.mOpPackageName; } + @Override + public void animateToCredentialUI() { + mBiometricView.startTransitionToCredentialUI(); + } + @VisibleForTesting void animateAway(int reason) { animateAway(true /* sendReason */, reason); @@ -391,12 +516,20 @@ public class AuthContainerView extends LinearLayout .withLayer() .withEndAction(endActionRunnable) .start(); - mScrollView.animate() + mBiometricScrollView.animate() .translationY(mTranslationY) .setDuration(ANIMATION_DURATION_AWAY_MS) .setInterpolator(mLinearOutSlowIn) .withLayer() .start(); + if (mCredentialView != null && mCredentialView.isAttachedToWindow()) { + mCredentialView.animate() + .translationY(mTranslationY) + .setDuration(ANIMATION_DURATION_AWAY_MS) + .setInterpolator(mLinearOutSlowIn) + .withLayer() + .start(); + } animate() .alpha(0f) .setDuration(ANIMATION_DURATION_AWAY_MS) @@ -431,7 +564,9 @@ public class AuthContainerView extends LinearLayout return; } mContainerState = STATE_SHOWING; - mBiometricView.onDialogAnimatedIn(); + if (mBiometricView != null) { + mBiometricView.onDialogAnimatedIn(); + } } /** @@ -445,7 +580,7 @@ public class AuthContainerView extends LinearLayout WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("BiometricPrompt"); lp.token = windowToken; return lp; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index d10a3fede412..b75873100025 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -16,6 +16,9 @@ package com.android.systemui.biometrics; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; @@ -23,8 +26,12 @@ import android.app.TaskStackListener; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.hardware.biometrics.Authenticator; +import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -32,6 +39,7 @@ import android.os.RemoteException; import android.util.Log; import android.view.WindowManager; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.systemui.SystemUI; @@ -105,6 +113,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } @Override + public void onDeviceCredentialPressed() { + try { + mReceiver.onDeviceCredentialPressed(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException when handling credential button", e); + } + } + + @Override public void onDismissed(@DismissedReason int reason) { switch (reason) { case AuthDialogCallback.DISMISSED_USER_CANCELED: @@ -116,11 +133,12 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, break; case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE: - sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRMED); + sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED); break; - case AuthDialogCallback.DISMISSED_AUTHENTICATED: - sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED); + case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED: + sendResultAndCleanUp( + BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED); break; case AuthDialogCallback.DISMISSED_ERROR: @@ -131,6 +149,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED); break; + case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED: + sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED); + break; + default: Log.e(TAG, "Unhandled reason: " + reason); break; @@ -156,12 +178,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } } - public AuthController() { - this(new Injector()); + public AuthController(Context context) { + this(context, new Injector()); } @VisibleForTesting - AuthController(Injector injector) { + AuthController(Context context, Injector injector) { + super(context); mInjector = injector; } @@ -185,16 +208,19 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } @Override - public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, - int type, boolean requireConfirmation, int userId, String opPackageName) { + public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, + int biometricModality, boolean requireConfirmation, int userId, String opPackageName) { + final int authenticators = Utils.getAuthenticators(bundle); + if (DEBUG) { - Log.d(TAG, "showBiometricDialog, type: " + type + Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators + + ", biometricModality: " + biometricModality + ", requireConfirmation: " + requireConfirmation); } SomeArgs args = SomeArgs.obtain(); args.arg1 = bundle; args.arg2 = receiver; - args.argi1 = type; + args.argi1 = biometricModality; args.arg3 = requireConfirmation; args.argi2 = userId; args.arg4 = opPackageName; @@ -204,19 +230,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, Log.w(TAG, "mCurrentDialog: " + mCurrentDialog); skipAnimation = true; } + showDialog(args, skipAnimation, null /* savedState */); } @Override - public void onBiometricAuthenticated(boolean authenticated, String failureReason) { - if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated - + " reason: " + failureReason); - - if (authenticated) { - mCurrentDialog.onAuthenticationSucceeded(); - } else { - mCurrentDialog.onAuthenticationFailed(failureReason); - } + public void onBiometricAuthenticated() { + mCurrentDialog.onAuthenticationSucceeded(); } @Override @@ -226,15 +246,51 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mCurrentDialog.onHelp(message); } + private String getErrorString(int modality, int error, int vendorCode) { + switch (modality) { + case TYPE_FACE: + return FaceManager.getErrorString(mContext, error, vendorCode); + + case TYPE_FINGERPRINT: + return FingerprintManager.getErrorString(mContext, error, vendorCode); + + default: + return ""; + } + } + @Override - public void onBiometricError(String error) { - if (DEBUG) Log.d(TAG, "onBiometricError: " + error); - mCurrentDialog.onError(error); + public void onBiometricError(int modality, int error, int vendorCode) { + if (DEBUG) { + Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode)); + } + + final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT) + || (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT); + + // TODO(b/141025588): Create separate methods for handling hard and soft errors. + final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED + || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT); + + if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) { + if (DEBUG) Log.d(TAG, "onBiometricError, lockout"); + mCurrentDialog.animateToCredentialUI(); + } else if (isSoftError) { + final String errorMessage = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED) + ? mContext.getString(R.string.biometric_not_recognized) + : getErrorString(modality, error, vendorCode); + if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage); + mCurrentDialog.onAuthenticationFailed(errorMessage); + } else { + final String errorMessage = getErrorString(modality, error, vendorCode); + if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage); + mCurrentDialog.onError(errorMessage); + } } @Override - public void hideBiometricDialog() { - if (DEBUG) Log.d(TAG, "hideBiometricDialog"); + public void hideAuthenticationDialog() { + if (DEBUG) Log.d(TAG, "hideAuthenticationDialog"); mCurrentDialog.dismissFromSystemServer(); } @@ -262,7 +318,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } if (DEBUG) { - Log.d(TAG, "showDialog, " + Log.d(TAG, "showDialog: " + args + " savedState: " + savedState + " mCurrentDialog: " + mCurrentDialog + " newDialog: " + newDialog @@ -306,6 +362,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, // to send its pending callback immediately. if (savedState.getInt(AuthDialog.KEY_CONTAINER_STATE) != AuthContainerView.STATE_ANIMATING_OUT) { + final boolean credentialShowing = + savedState.getBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING); + if (credentialShowing) { + // TODO: Clean this up + Bundle bundle = (Bundle) mCurrentDialogArgs.arg1; + bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, + Authenticator.TYPE_CREDENTIAL); + } + showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java new file mode 100644 index 000000000000..bebaa4b688db --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2019 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.biometrics; + +import android.content.Context; +import android.text.InputType; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.TextView; + +import com.android.internal.widget.LockPatternChecker; +import com.android.internal.widget.LockscreenCredential; +import com.android.systemui.R; + +/** + * Pin and Password UI + */ +public class AuthCredentialPasswordView extends AuthCredentialView + implements TextView.OnEditorActionListener { + + private static final String TAG = "BiometricPrompt/AuthCredentialPasswordView"; + + private final InputMethodManager mImm; + private EditText mPasswordField; + + public AuthCredentialPasswordView(Context context, + AttributeSet attrs) { + super(context, attrs); + mImm = mContext.getSystemService(InputMethodManager.class); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mPasswordField = findViewById(R.id.lockPassword); + mPasswordField.setOnEditorActionListener(this); + mPasswordField.setOnKeyListener((v, keyCode, event) -> { + if (keyCode != KeyEvent.KEYCODE_BACK) { + return false; + } + if (event.getAction() == KeyEvent.ACTION_UP) { + mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); + } + return true; + }); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mCredentialType == Utils.CREDENTIAL_PIN) { + mPasswordField.setInputType( + InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD); + } + + // Wait a bit to focus the field so the focusable flag on the window is already set then. + post(() -> { + mPasswordField.requestFocus(); + mImm.showSoftInput(mPasswordField, InputMethodManager.SHOW_IMPLICIT); + }); + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + // Check if this was the result of hitting the enter key + final boolean isSoftImeEvent = event == null + && (actionId == EditorInfo.IME_NULL + || actionId == EditorInfo.IME_ACTION_DONE + || actionId == EditorInfo.IME_ACTION_NEXT); + final boolean isKeyboardEnterKey = event != null + && KeyEvent.isConfirmKey(event.getKeyCode()) + && event.getAction() == KeyEvent.ACTION_DOWN; + if (isSoftImeEvent || isKeyboardEnterKey) { + checkPasswordAndUnlock(); + return true; + } + return false; + } + + private void checkPasswordAndUnlock() { + try (LockscreenCredential password = mCredentialType == Utils.CREDENTIAL_PIN + ? LockscreenCredential.createPinOrNone(mPasswordField.getText()) + : LockscreenCredential.createPasswordOrNone(mPasswordField.getText())) { + if (password.isNone()) { + return; + } + + mPendingLockCheck = LockPatternChecker.checkCredential(mLockPatternUtils, + password, mUserId, this::onCredentialChecked); + } + } + + @Override + protected void onCredentialChecked(boolean matched, int timeoutMs) { + super.onCredentialChecked(matched, timeoutMs); + + if (matched) { + mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */); + } else { + mPasswordField.setText(""); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java new file mode 100644 index 000000000000..14414a4225de --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 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.biometrics; + +import android.content.Context; +import android.util.AttributeSet; + +import com.android.internal.widget.LockPatternChecker; +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockscreenCredential; +import com.android.systemui.R; + +import java.util.List; + +/** + * Pattern UI + */ +public class AuthCredentialPatternView extends AuthCredentialView { + + private LockPatternView mLockPatternView; + + private class UnlockPatternListener implements LockPatternView.OnPatternListener { + + @Override + public void onPatternStart() { + + } + + @Override + public void onPatternCleared() { + + } + + @Override + public void onPatternCellAdded(List<LockPatternView.Cell> pattern) { + + } + + @Override + public void onPatternDetected(List<LockPatternView.Cell> pattern) { + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + } + + mLockPatternView.setEnabled(false); + + if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { + // Pattern size is less than the minimum, do not count it as a failed attempt. + onPatternChecked(false /* matched */, 0 /* timeoutMs */); + return; + } + + try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) { + mPendingLockCheck = LockPatternChecker.checkCredential( + mLockPatternUtils, + credential, + mUserId, + this::onPatternChecked); + } + } + + private void onPatternChecked(boolean matched, int timeoutMs) { + AuthCredentialPatternView.this.onCredentialChecked(matched, timeoutMs); + if (timeoutMs > 0) { + mLockPatternView.setEnabled(false); + } else { + mLockPatternView.setEnabled(true); + } + } + } + + @Override + protected void onErrorTimeoutFinish() { + super.onErrorTimeoutFinish(); + mLockPatternView.setEnabled(true); + } + + public AuthCredentialPatternView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mLockPatternView = findViewById(R.id.lockPattern); + mLockPatternView.setOnPatternListener(new UnlockPatternListener()); + mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(mUserId)); + mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled()); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java new file mode 100644 index 000000000000..8c8611e49dfb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2019 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.biometrics; + +import android.content.Context; +import android.hardware.biometrics.BiometricPrompt; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.CountDownTimer; +import android.os.Handler; +import android.os.Looper; +import android.os.SystemClock; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.accessibility.AccessibilityManager; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.Interpolators; +import com.android.systemui.R; + +/** + * Abstract base class for Pin, Pattern, or Password authentication, for + * {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)} + */ +public abstract class AuthCredentialView extends LinearLayout { + + private static final String TAG = "BiometricPrompt/AuthCredentialView"; + private static final int ERROR_DURATION_MS = 3000; + + private final AccessibilityManager mAccessibilityManager; + + protected final Handler mHandler; + + private Bundle mBiometricPromptBundle; + private AuthPanelController mPanelController; + private boolean mShouldAnimatePanel; + private boolean mShouldAnimateContents; + + private TextView mTitleView; + private TextView mSubtitleView; + private TextView mDescriptionView; + protected TextView mErrorView; + + protected @Utils.CredentialType int mCredentialType; + protected final LockPatternUtils mLockPatternUtils; + protected AuthContainerView mContainerView; + protected Callback mCallback; + protected AsyncTask<?, ?, ?> mPendingLockCheck; + protected int mUserId; + protected ErrorTimer mErrorTimer; + + interface Callback { + void onCredentialMatched(); + } + + protected static class ErrorTimer extends CountDownTimer { + private final TextView mErrorView; + private final Context mContext; + + /** + * @param millisInFuture The number of millis in the future from the call + * to {@link #start()} until the countdown is done and {@link + * #onFinish()} + * is called. + * @param countDownInterval The interval along the way to receive + * {@link #onTick(long)} callbacks. + */ + public ErrorTimer(Context context, long millisInFuture, long countDownInterval, + TextView errorView) { + super(millisInFuture, countDownInterval); + mErrorView = errorView; + mContext = context; + } + + @Override + public void onTick(long millisUntilFinished) { + final int secondsCountdown = (int) (millisUntilFinished / 1000); + mErrorView.setText(mContext.getString( + R.string.biometric_dialog_credential_too_many_attempts, secondsCountdown)); + } + + @Override + public void onFinish() { + mErrorView.setText(""); + } + } + + protected final Runnable mClearErrorRunnable = new Runnable() { + @Override + public void run() { + mErrorView.setText(""); + } + }; + + public AuthCredentialView(Context context, AttributeSet attrs) { + super(context, attrs); + + mLockPatternUtils = new LockPatternUtils(mContext); + mHandler = new Handler(Looper.getMainLooper()); + mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); + } + + protected void showError(String error) { + mHandler.removeCallbacks(mClearErrorRunnable); + mErrorView.setText(error); + mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS); + } + + private void setTextOrHide(TextView view, String string) { + if (TextUtils.isEmpty(string)) { + view.setVisibility(View.GONE); + } else { + view.setText(string); + } + + Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); + } + + private void setText(TextView view, String string) { + view.setText(string); + } + + void setUser(int user) { + mUserId = user; + } + + void setCallback(Callback callback) { + mCallback = callback; + } + + void setBiometricPromptBundle(Bundle bundle) { + mBiometricPromptBundle = bundle; + } + + void setPanelController(AuthPanelController panelController, boolean animatePanel) { + mPanelController = panelController; + mShouldAnimatePanel = animatePanel; + } + + void setShouldAnimateContents(boolean animateContents) { + mShouldAnimateContents = animateContents; + } + + void setContainerView(AuthContainerView containerView) { + mContainerView = containerView; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + mCredentialType = Utils.getCredentialType(mContext, mUserId); + + setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE)); + setTextOrHide(mSubtitleView, + mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE)); + setTextOrHide(mDescriptionView, + mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION)); + + // Only animate this if we're transitioning from a biometric view. + if (mShouldAnimateContents) { + setTranslationY(getResources() + .getDimension(R.dimen.biometric_dialog_credential_translation_offset)); + setAlpha(0); + + postOnAnimation(() -> { + animate().translationY(0) + .setDuration(AuthDialog.ANIMATE_CREDENTIAL_INITIAL_DURATION_MS) + .alpha(1.f) + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) + .withLayer() + .start(); + }); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mErrorTimer != null) { + mErrorTimer.cancel(); + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mTitleView = findViewById(R.id.title); + mSubtitleView = findViewById(R.id.subtitle); + mDescriptionView = findViewById(R.id.description); + mErrorView = findViewById(R.id.error); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (mShouldAnimatePanel) { + // Credential view is always full screen. + mPanelController.setUseFullScreen(true); + mPanelController.updateForContentDimensions(mPanelController.getContainerWidth(), + mPanelController.getContainerHeight(), 0 /* animateDurationMs */); + mShouldAnimatePanel = false; + } + } + + protected void onErrorTimeoutFinish() {} + + protected void onCredentialChecked(boolean matched, int timeoutMs) { + if (matched) { + mClearErrorRunnable.run(); + mCallback.onCredentialMatched(); + } else { + if (timeoutMs > 0) { + mHandler.removeCallbacks(mClearErrorRunnable); + long deadline = mLockPatternUtils.setLockoutAttemptDeadline(mUserId, timeoutMs); + mErrorTimer = new ErrorTimer(mContext, + deadline - SystemClock.elapsedRealtime(), + LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS, + mErrorView) { + @Override + public void onFinish() { + onErrorTimeoutFinish(); + mClearErrorRunnable.run(); + } + }; + mErrorTimer.start(); + } else { + final int error; + switch (mCredentialType) { + case Utils.CREDENTIAL_PIN: + error = R.string.biometric_dialog_wrong_pin; + break; + case Utils.CREDENTIAL_PATTERN: + error = R.string.biometric_dialog_wrong_pattern; + break; + case Utils.CREDENTIAL_PASSWORD: + error = R.string.biometric_dialog_wrong_password; + break; + default: + error = R.string.biometric_dialog_wrong_password; + break; + } + showError(getResources().getString(error)); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java index edb29538874c..ca95f9d736fc 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java @@ -31,6 +31,8 @@ import java.lang.annotation.RetentionPolicy; public interface AuthDialog { String KEY_CONTAINER_STATE = "container_state"; + String KEY_BIOMETRIC_SHOWING = "biometric_showing"; + String KEY_CREDENTIAL_SHOWING = "credential_showing"; String KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY = "try_agian_visibility"; String KEY_BIOMETRIC_STATE = "state"; @@ -40,17 +42,38 @@ public interface AuthDialog { String KEY_BIOMETRIC_DIALOG_SIZE = "size"; int SIZE_UNKNOWN = 0; + /** + * Minimal UI, showing only biometric icon. + */ int SIZE_SMALL = 1; + /** + * Normal-sized biometric UI, showing title, icon, buttons, etc. + */ int SIZE_MEDIUM = 2; + /** + * Full-screen credential UI. + */ int SIZE_LARGE = 3; @Retention(RetentionPolicy.SOURCE) @IntDef({SIZE_UNKNOWN, SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE}) @interface DialogSize {} /** - * Animation duration, e.g. small to medium dialog, icon translation, etc. + * Animation duration, from small to medium dialog, including back panel, icon translation, etc + */ + int ANIMATE_SMALL_TO_MEDIUM_DURATION_MS = 150; + /** + * Animation duration from medium to large dialog, including biometric fade out, back panel, etc + */ + int ANIMATE_MEDIUM_TO_LARGE_DURATION_MS = 450; + /** + * Delay before notifying {@link AuthCredentialView} to start animating in. + */ + int ANIMATE_CREDENTIAL_START_DELAY_MS = ANIMATE_MEDIUM_TO_LARGE_DURATION_MS * 2 / 3; + /** + * Animation duration when sliding in credential UI */ - int ANIMATE_DURATION_MS = 150; + int ANIMATE_CREDENTIAL_INITIAL_DURATION_MS = 150; /** * Show the dialog. @@ -101,4 +124,14 @@ public interface AuthDialog { * Get the client's package name */ String getOpPackageName(); + + /** + * Animate to credential UI. Typically called after biometric is locked out. + */ + void animateToCredentialUI(); + + /** + * @return true if device credential is allowed. + */ + boolean isAllowDeviceCredentials(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java index 70752f5f860e..12bb1228a53b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java @@ -27,17 +27,18 @@ public interface AuthDialogCallback { int DISMISSED_USER_CANCELED = 1; int DISMISSED_BUTTON_NEGATIVE = 2; int DISMISSED_BUTTON_POSITIVE = 3; - - int DISMISSED_AUTHENTICATED = 4; + int DISMISSED_BIOMETRIC_AUTHENTICATED = 4; int DISMISSED_ERROR = 5; int DISMISSED_BY_SYSTEM_SERVER = 6; + int DISMISSED_CREDENTIAL_AUTHENTICATED = 7; @IntDef({DISMISSED_USER_CANCELED, DISMISSED_BUTTON_NEGATIVE, DISMISSED_BUTTON_POSITIVE, - DISMISSED_AUTHENTICATED, + DISMISSED_BIOMETRIC_AUTHENTICATED, DISMISSED_ERROR, - DISMISSED_BY_SYSTEM_SERVER}) + DISMISSED_BY_SYSTEM_SERVER, + DISMISSED_CREDENTIAL_AUTHENTICATED}) @interface DismissedReason {} /** @@ -50,4 +51,9 @@ public interface AuthDialogCallback { * Invoked when the "try again" button is clicked */ void onTryAgainPressed(); + + /** + * Invoked when the "use password" button is clicked + */ + void onDeviceCredentialPressed(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java index 55ba0491dc1e..2b8b586961ff 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java @@ -16,15 +16,17 @@ package com.android.systemui.biometrics; +import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.content.Context; +import android.graphics.Color; import android.graphics.Outline; import android.util.Log; import android.view.View; import android.view.ViewOutlineProvider; +import android.view.animation.AccelerateDecelerateInterpolator; import com.android.systemui.R; -import com.android.systemui.biometrics.AuthDialog; /** * Controls the back panel and its animations for the BiometricPrompt UI. @@ -36,8 +38,9 @@ public class AuthPanelController extends ViewOutlineProvider { private final Context mContext; private final View mPanelView; - private final float mCornerRadius; - private final int mBiometricMargin; + private final boolean mIsManagedProfile; + + private boolean mUseFullScreen; private int mContainerWidth; private int mContainerHeight; @@ -45,14 +48,23 @@ public class AuthPanelController extends ViewOutlineProvider { private int mContentWidth; private int mContentHeight; + private float mCornerRadius; + private int mMargin; + @Override public void getOutline(View view, Outline outline) { final int left = (mContainerWidth - mContentWidth) / 2; final int right = mContainerWidth - left; + + // If the content fits within the container, shrink the height to wrap the content. + // Otherwise, set the outline to be the display size minus the margin - the content within + // is scrollable. final int top = mContentHeight < mContainerHeight - ? mContainerHeight - mContentHeight - mBiometricMargin - : mBiometricMargin; - final int bottom = mContainerHeight - mBiometricMargin; + ? mContainerHeight - mContentHeight - mMargin + : mMargin; + + // TODO(b/139954942) Likely don't need to "+1" after we resolve the navbar styling. + final int bottom = mContainerHeight - mMargin + 1; outline.setRoundRect(left, top, right, bottom, mCornerRadius); } @@ -64,11 +76,34 @@ public class AuthPanelController extends ViewOutlineProvider { mContainerHeight = containerHeight; } - public void updateForContentDimensions(int contentWidth, int contentHeight, boolean animate) { + public void setUseFullScreen(boolean fullScreen) { + mUseFullScreen = fullScreen; + } + + public ValueAnimator getTranslationAnimator(float relativeTranslationY) { + final ValueAnimator animator = ValueAnimator.ofFloat( + mPanelView.getY(), mPanelView.getY() - relativeTranslationY); + animator.addUpdateListener(animation -> { + final float translation = (float) animation.getAnimatedValue(); + mPanelView.setTranslationY(translation); + }); + return animator; + } + + public ValueAnimator getAlphaAnimator(float alpha) { + final ValueAnimator animator = ValueAnimator.ofFloat(mPanelView.getAlpha(), alpha); + animator.addUpdateListener(animation -> { + mPanelView.setAlpha((float) animation.getAnimatedValue()); + }); + return animator; + } + + public void updateForContentDimensions(int contentWidth, int contentHeight, + int animateDurationMs) { if (DEBUG) { Log.v(TAG, "Content Width: " + contentWidth + " Height: " + contentHeight - + " Animate: " + animate); + + " Animate: " + animateDurationMs); } if (mContainerWidth == 0 || mContainerHeight == 0) { @@ -76,27 +111,86 @@ public class AuthPanelController extends ViewOutlineProvider { return; } - if (animate) { + final int margin = mUseFullScreen ? 0 : (int) mContext.getResources() + .getDimension(R.dimen.biometric_dialog_border_padding); + final float cornerRadius = mUseFullScreen ? 0 : mContext.getResources() + .getDimension(R.dimen.biometric_dialog_corner_size); + + // When going to full-screen for managed profiles, fade away so the managed profile + // background behind this view becomes visible. + final boolean shouldFadeAway = mUseFullScreen && mIsManagedProfile; + final int alpha = shouldFadeAway ? 0 : 255; + final float elevation = shouldFadeAway ? 0 : + mContext.getResources().getDimension(R.dimen.biometric_dialog_elevation); + + if (animateDurationMs > 0) { + // Animate margin + ValueAnimator marginAnimator = ValueAnimator.ofInt(mMargin, margin); + marginAnimator.addUpdateListener((animation) -> { + mMargin = (int) animation.getAnimatedValue(); + }); + + // Animate corners + ValueAnimator cornerAnimator = ValueAnimator.ofFloat(mCornerRadius, cornerRadius); + cornerAnimator.addUpdateListener((animation) -> { + mCornerRadius = (float) animation.getAnimatedValue(); + }); + + // Animate height ValueAnimator heightAnimator = ValueAnimator.ofInt(mContentHeight, contentHeight); - heightAnimator.setDuration(AuthDialog.ANIMATE_DURATION_MS); heightAnimator.addUpdateListener((animation) -> { mContentHeight = (int) animation.getAnimatedValue(); mPanelView.invalidateOutline(); }); heightAnimator.start(); + + // Animate width + ValueAnimator widthAnimator = ValueAnimator.ofInt(mContentWidth, contentWidth); + widthAnimator.addUpdateListener((animation) -> { + mContentWidth = (int) animation.getAnimatedValue(); + }); + + // Animate background + ValueAnimator alphaAnimator = ValueAnimator.ofInt( + mPanelView.getBackground().getAlpha(), alpha); + alphaAnimator.addUpdateListener((animation) -> { + if (shouldFadeAway) { + mPanelView.getBackground().setAlpha((int) animation.getAnimatedValue()); + } + }); + + // Play together + AnimatorSet as = new AnimatorSet(); + as.setDuration(animateDurationMs); + as.setInterpolator(new AccelerateDecelerateInterpolator()); + as.playTogether(cornerAnimator, widthAnimator, marginAnimator, alphaAnimator); + as.start(); + } else { + mMargin = margin; + mCornerRadius = cornerRadius; mContentWidth = contentWidth; mContentHeight = contentHeight; + mPanelView.getBackground().setAlpha(alpha); mPanelView.invalidateOutline(); } } - AuthPanelController(Context context, View panelView) { + int getContainerWidth() { + return mContainerWidth; + } + + int getContainerHeight() { + return mContainerHeight; + } + + AuthPanelController(Context context, View panelView, boolean isManagedProfile) { mContext = context; mPanelView = panelView; + mIsManagedProfile = isManagedProfile; mCornerRadius = context.getResources() .getDimension(R.dimen.biometric_dialog_corner_size); - mBiometricMargin = (int) context.getResources() + mMargin = (int) context.getResources() .getDimension(R.dimen.biometric_dialog_border_padding); mPanelView.setOutlineProvider(this); mPanelView.setClipToOutline(true); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java index e00cf6abafaa..d6f830dd2e7a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java @@ -18,14 +18,36 @@ package com.android.systemui.biometrics; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; +import android.annotation.IntDef; +import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.hardware.biometrics.Authenticator; +import android.hardware.biometrics.BiometricPrompt; +import android.os.Bundle; +import android.os.UserManager; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + public class Utils { + + public static final int CREDENTIAL_PIN = 1; + public static final int CREDENTIAL_PATTERN = 2; + public static final int CREDENTIAL_PASSWORD = 3; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD}) + @interface CredentialType {} + + static float dpToPixels(Context context, float dp) { return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); @@ -46,4 +68,41 @@ public class Utils { view.sendAccessibilityEventUnchecked(event); view.notifySubtreeAccessibilityStateChanged(view, view, CONTENT_CHANGE_TYPE_SUBTREE); } + + static boolean isDeviceCredentialAllowed(Bundle biometricPromptBundle) { + final int authenticators = getAuthenticators(biometricPromptBundle); + return (authenticators & Authenticator.TYPE_CREDENTIAL) != 0; + } + + static boolean isBiometricAllowed(Bundle biometricPromptBundle) { + final int authenticators = getAuthenticators(biometricPromptBundle); + return (authenticators & Authenticator.TYPE_BIOMETRIC) != 0; + } + + static int getAuthenticators(Bundle biometricPromptBundle) { + return biometricPromptBundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); + } + + static @CredentialType int getCredentialType(Context context, int userId) { + final LockPatternUtils lpu = new LockPatternUtils(context); + switch (lpu.getKeyguardStoredPasswordQuality(userId)) { + case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: + return CREDENTIAL_PATTERN; + case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: + return CREDENTIAL_PIN; + case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: + case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: + case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: + return CREDENTIAL_PASSWORD; + default: + return CREDENTIAL_PASSWORD; + } + } + + static boolean isManagedProfile(Context context, int userId) { + final UserManager userManager = context.getSystemService(UserManager.class); + return userManager.isManagedProfile(userId); + } } diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt index f0e8c16e650a..ff4711cb208a 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt @@ -26,13 +26,12 @@ import android.os.UserHandle import android.util.Log import android.util.SparseArray import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.Dependency.BG_LOOPER_NAME -import com.android.systemui.Dependency.MAIN_HANDLER_NAME import com.android.systemui.Dumpable +import com.android.systemui.dagger.qualifiers.BgLooper +import com.android.systemui.dagger.qualifiers.MainHandler import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject -import javax.inject.Named import javax.inject.Singleton data class ReceiverData( @@ -61,8 +60,8 @@ private const val DEBUG = false @Singleton open class BroadcastDispatcher @Inject constructor ( private val context: Context, - @Named(MAIN_HANDLER_NAME) private val mainHandler: Handler, - @Named(BG_LOOPER_NAME) private val bgLooper: Looper + @MainHandler private val mainHandler: Handler, + @BgLooper private val bgLooper: Looper ) : Dumpable { // Only modify in BG thread @@ -75,7 +74,7 @@ open class BroadcastDispatcher @Inject constructor ( * @param filter A filter to determine what broadcasts should be dispatched to this receiver. * It will only take into account actions and categories for filtering. * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the - * main handler. + * main handler. Pass `null` to use the default. * @param user A user handle to determine which broadcast should be dispatched to this receiver. * By default, it is the current user. */ @@ -83,10 +82,12 @@ open class BroadcastDispatcher @Inject constructor ( fun registerReceiver( receiver: BroadcastReceiver, filter: IntentFilter, - handler: Handler = mainHandler, + handler: Handler? = mainHandler, user: UserHandle = context.user ) { - this.handler.obtainMessage(MSG_ADD_RECEIVER, ReceiverData(receiver, filter, handler, user)) + this.handler + .obtainMessage(MSG_ADD_RECEIVER, + ReceiverData(receiver, filter, handler ?: mainHandler, user)) .sendToTarget() } diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt index d44b63e813e6..54f9950239c2 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt @@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean private const val MSG_REGISTER_RECEIVER = 0 private const val MSG_UNREGISTER_RECEIVER = 1 -private const val TAG = "UniversalReceiver" +private const val TAG = "UserBroadcastDispatcher" private const val DEBUG = false /** @@ -97,7 +97,7 @@ class UserBroadcastDispatcher( private val receiverToReceiverData = ArrayMap<BroadcastReceiver, MutableSet<ReceiverData>>() override fun onReceive(context: Context, intent: Intent) { - bgHandler.post(HandleBroadcastRunnable(actionsToReceivers, context, intent)) + bgHandler.post(HandleBroadcastRunnable(actionsToReceivers, context, intent, pendingResult)) } /** @@ -160,7 +160,8 @@ class UserBroadcastDispatcher( private class HandleBroadcastRunnable( val actionsToReceivers: Map<String, Set<ReceiverData>>, val context: Context, - val intent: Intent + val intent: Intent, + val pendingResult: PendingResult ) : Runnable { override fun run() { if (DEBUG) Log.w(TAG, "Dispatching $intent") @@ -171,6 +172,7 @@ class UserBroadcastDispatcher( ?.forEach { it.handler.post { if (DEBUG) Log.w(TAG, "Dispatching to ${it.receiver}") + it.receiver.pendingResult = pendingResult it.receiver.onReceive(context, intent) } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index c3cee35d37fb..7600b2f3ed7a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -83,23 +83,23 @@ class Bubble { private boolean mSuppressFlyout; public static String groupId(NotificationEntry entry) { - UserHandle user = entry.notification.getUser(); - return user.getIdentifier() + "|" + entry.notification.getPackageName(); + UserHandle user = entry.getSbn().getUser(); + return user.getIdentifier() + "|" + entry.getSbn().getPackageName(); } /** Used in tests when no UI is required. */ @VisibleForTesting(visibility = PRIVATE) Bubble(Context context, NotificationEntry e) { mEntry = e; - mKey = e.key; - mLastUpdated = e.notification.getPostTime(); + mKey = e.getKey(); + mLastUpdated = e.getSbn().getPostTime(); mGroupId = groupId(e); PackageManager pm = context.getPackageManager(); ApplicationInfo info; try { info = pm.getApplicationInfo( - mEntry.notification.getPackageName(), + mEntry.getSbn().getPackageName(), PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_UNAWARE @@ -107,10 +107,10 @@ class Bubble { if (info != null) { mAppName = String.valueOf(pm.getApplicationLabel(info)); } - Drawable appIcon = pm.getApplicationIcon(mEntry.notification.getPackageName()); - mUserBadgedAppIcon = pm.getUserBadgedIcon(appIcon, mEntry.notification.getUser()); + Drawable appIcon = pm.getApplicationIcon(mEntry.getSbn().getPackageName()); + mUserBadgedAppIcon = pm.getUserBadgedIcon(appIcon, mEntry.getSbn().getUser()); } catch (PackageManager.NameNotFoundException unused) { - mAppName = mEntry.notification.getPackageName(); + mAppName = mEntry.getSbn().getPackageName(); } } @@ -127,7 +127,7 @@ class Bubble { } public String getPackageName() { - return mEntry.notification.getPackageName(); + return mEntry.getSbn().getPackageName(); } public String getAppName() { @@ -190,7 +190,7 @@ class Bubble { void updateEntry(NotificationEntry entry) { mEntry = entry; - mLastUpdated = entry.notification.getPostTime(); + mLastUpdated = entry.getSbn().getPostTime(); if (mInflated) { mIconView.update(this); mExpandedView.update(this); @@ -287,7 +287,7 @@ class Bubble { * is an ongoing bubble. */ boolean isOngoing() { - int flags = mEntry.notification.getNotification().flags; + int flags = mEntry.getSbn().getNotification().flags; return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; } @@ -296,8 +296,8 @@ class Bubble { boolean useRes = data.getDesiredHeightResId() != 0; if (useRes) { return getDimenForPackageUser(context, data.getDesiredHeightResId(), - mEntry.notification.getPackageName(), - mEntry.notification.getUser().getIdentifier()); + mEntry.getSbn().getPackageName(), + mEntry.getSbn().getUser().getIdentifier()); } else { return data.getDesiredHeight() * context.getResources().getDisplayMetrics().density; @@ -316,7 +316,7 @@ class Bubble { @Nullable PendingIntent getBubbleIntent(Context context) { - Notification notif = mEntry.notification.getNotification(); + Notification notif = mEntry.getSbn().getNotification(); Notification.BubbleMetadata data = notif.getBubbleMetadata(); if (BubbleController.canLaunchInActivityView(context, mEntry) && data != null) { return data.getIntent(); @@ -327,7 +327,7 @@ class Bubble { Intent getSettingsIntent() { final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); - intent.putExtra(Settings.EXTRA_APP_UID, mEntry.notification.getUid()); + intent.putExtra(Settings.EXTRA_APP_UID, mEntry.getSbn().getUid()); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); @@ -339,7 +339,7 @@ class Bubble { * notification, based on its type. Returns null if there should not be an update message. */ CharSequence getUpdateMessage(Context context) { - final Notification underlyingNotif = mEntry.notification.getNotification(); + final Notification underlyingNotif = mEntry.getSbn().getNotification(); final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle(); try { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 72ada6e90cd0..0231b562ad04 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -18,7 +18,6 @@ package com.android.systemui.bubbles; import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY; import static android.app.Notification.FLAG_BUBBLE; -import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL; import static android.service.notification.NotificationListenerService.REASON_CANCEL; @@ -126,8 +125,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi static final int DISMISS_GROUP_CANCELLED = 9; static final int DISMISS_INVALID_INTENT = 10; - public static final int MAX_BUBBLES = 5; // TODO: actually enforce this - private final Context mContext; private final NotificationEntryManager mNotificationEntryManager; private final BubbleTaskStackListener mTaskStackListener; @@ -265,7 +262,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // More notifications could be added causing summary to no longer // be suppressed -- in this case need to remove the key. final String groupKey = group.summary != null - ? group.summary.notification.getGroupKey() + ? group.summary.getSbn().getGroupKey() : null; if (!suppressed && groupKey != null && mBubbleData.isSummarySuppressed(groupKey)) { @@ -349,7 +346,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi return; } for (NotificationEntry e : notificationData.getNotificationsForCurrentUser()) { - if (savedBubbleKeys.contains(e.key) + if (savedBubbleKeys.contains(e.getKey()) && mNotificationInterruptionStateProvider.shouldBubbleUp(e) && canLaunchInActivityView(mContext, e)) { updateBubble(e, /* suppressFlyout= */ true); @@ -448,7 +445,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key) && !mBubbleData.getBubbleWithKey(key).showInShadeWhenBubble(); NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key); - String groupKey = entry != null ? entry.notification.getGroupKey() : null; + String groupKey = entry != null ? entry.getSbn().getGroupKey() : null; boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey); boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey)); return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed; @@ -531,14 +528,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Override public boolean onNotificationRemoveRequested(String key, int reason) { NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key); - String groupKey = entry != null ? entry.notification.getGroupKey() : null; + String groupKey = entry != null ? entry.getSbn().getGroupKey() : null; ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey); boolean inBubbleData = mBubbleData.hasBubbleWithKey(key); boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey) && mBubbleData.getSummaryKey(groupKey).equals(key)); boolean isSummary = entry != null - && entry.notification.getNotification().isGroupSummary(); + && entry.getSbn().getNotification().isGroupSummary(); boolean isSummaryOfBubbles = (isSuppressedSummary || isSummary) && bubbleChildren != null && !bubbleChildren.isEmpty(); @@ -569,9 +566,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi bubble.setShowInShadeWhenBubble(false); bubble.setShowBubbleDot(false); if (mStackView != null) { - mStackView.updateDotVisibility(entry.key); + mStackView.updateDotVisibility(entry.getKey()); } - mNotificationEntryManager.updateNotifications(); + mNotificationEntryManager.updateNotifications( + "BubbleController.onNotificationRemoveRequested"); return true; } else if (!userRemovedNotif && entry != null) { // This wasn't a user removal so we should remove the bubble as well @@ -584,7 +582,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private boolean handleSummaryRemovalInterception(NotificationEntry summary, boolean userRemovedNotif) { - String groupKey = summary.notification.getGroupKey(); + String groupKey = summary.getSbn().getGroupKey(); ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey); if (userRemovedNotif) { @@ -605,13 +603,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // If the summary was auto-generated we don't need to keep that notification around // because apps can't cancel it; so we only intercept & suppress real summaries. - boolean isAutogroupSummary = (summary.notification.getNotification().flags + boolean isAutogroupSummary = (summary.getSbn().getNotification().flags & FLAG_AUTOGROUP_SUMMARY) != 0; if (!isAutogroupSummary) { - mBubbleData.addSummaryToSuppress(summary.notification.getGroupKey(), - summary.key); + mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(), + summary.getKey()); // Tell shade to update for the suppression - mNotificationEntryManager.updateNotifications(); + mNotificationEntryManager.updateNotifications( + "BubbleController.handleSummaryRemovalInterception"); } return !isAutogroupSummary; } else { @@ -642,11 +641,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi public void onPreEntryUpdated(NotificationEntry entry) { boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry) && canLaunchInActivityView(mContext, entry); - if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.key)) { + if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it - removeBubble(entry.key, DISMISS_NO_LONGER_BUBBLE); + removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE); } else if (shouldBubble) { - Bubble b = mBubbleData.getBubbleWithKey(entry.key); + Bubble b = mBubbleData.getBubbleWithKey(entry.getKey()); updateBubble(entry); } } @@ -696,10 +695,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi && !bubble.showInShadeWhenBubble()) { // The bubble is gone & the notification is gone, time to actually remove it mNotificationEntryManager.performRemoveNotification( - bubble.getEntry().notification, UNDEFINED_DISMISS_REASON); + bubble.getEntry().getSbn(), UNDEFINED_DISMISS_REASON); } else { // Update the flag for SysUI - bubble.getEntry().notification.getNotification().flags &= ~FLAG_BUBBLE; + bubble.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE; // Make sure NoMan knows it's not a bubble anymore so anyone querying it // will get right result back @@ -713,7 +712,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // Check if removed bubble has an associated suppressed group summary that needs // to be removed now. - final String groupKey = bubble.getEntry().notification.getGroupKey(); + final String groupKey = bubble.getEntry().getSbn().getGroupKey(); if (mBubbleData.isSummarySuppressed(groupKey) && mBubbleData.getBubblesInGroup(groupKey).isEmpty()) { // Time to actually remove the summary. @@ -722,20 +721,21 @@ public class BubbleController implements ConfigurationController.ConfigurationLi NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(notifKey); mNotificationEntryManager.performRemoveNotification( - entry.notification, UNDEFINED_DISMISS_REASON); + entry.getSbn(), UNDEFINED_DISMISS_REASON); } // Check if summary should be removed from NoManGroup NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary( - bubble.getEntry().notification); + bubble.getEntry().getSbn()); if (summary != null) { ArrayList<NotificationEntry> summaryChildren = - mNotificationGroupManager.getLogicalChildren(summary.notification); - boolean isSummaryThisNotif = summary.key.equals(bubble.getEntry().key); + mNotificationGroupManager.getLogicalChildren(summary.getSbn()); + boolean isSummaryThisNotif = summary.getKey().equals( + bubble.getEntry().getKey()); if (!isSummaryThisNotif && (summaryChildren == null || summaryChildren.isEmpty())) { mNotificationEntryManager.performRemoveNotification( - summary.notification, UNDEFINED_DISMISS_REASON); + summary.getSbn(), UNDEFINED_DISMISS_REASON); } } } @@ -762,7 +762,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mStackView.setExpanded(true); } - mNotificationEntryManager.updateNotifications(); + mNotificationEntryManager.updateNotifications( + "BubbleData.Listener.applyUpdate"); updateStack(); if (DEBUG_BUBBLE_CONTROLLER) { @@ -964,16 +965,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi + intent); return false; } - if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) { - Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always " - + "for intent: " + intent); - return false; - } - if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) { - Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: " - + intent); - return false; - } return true; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index d43e030ed9eb..4e229c00b891 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -183,8 +183,8 @@ public class BubbleData { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "notificationEntryUpdated: " + entry); } - Bubble bubble = getBubbleWithKey(entry.key); - suppressFlyout = !entry.isVisuallyInterruptive || suppressFlyout; + Bubble bubble = getBubbleWithKey(entry.getKey()); + suppressFlyout = !entry.getRanking().visuallyInterruptive() || suppressFlyout; if (bubble == null) { // Create a new bubble @@ -217,7 +217,7 @@ public class BubbleData { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason); } - doRemove(entry.key, reason); + doRemove(entry.getKey(), reason); dispatchPendingChanges(); } @@ -290,7 +290,7 @@ public class BubbleData { return bubbleChildren; } for (Bubble b : mBubbles) { - if (groupKey.equals(b.getEntry().notification.getGroupKey())) { + if (groupKey.equals(b.getEntry().getSbn().getGroupKey())) { bubbleChildren.add(b); } } @@ -633,7 +633,8 @@ public class BubbleData { try { deleteIntent.send(); } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "Failed to send delete intent for bubble with key: " + entry.key); + Log.w(TAG, "Failed to send delete intent for bubble with key: " + + entry.getKey()); } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 521ebde7d2f0..1d9f6b27fcdd 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -16,6 +16,8 @@ package com.android.systemui.bubbles; +import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.view.Display.INVALID_DISPLAY; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW; @@ -128,8 +130,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList Log.d(TAG, "onActivityViewReady: calling startActivity, " + "bubble=" + getBubbleKey()); } + Intent fillInIntent = new Intent(); + // Apply flags to make behaviour match documentLaunchMode=always. + fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT); + fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); try { - mActivityView.startActivity(mBubbleIntent, options); + mActivityView.startActivity(mBubbleIntent, fillInIntent, options); } catch (RuntimeException e) { // If there's a runtime exception here then there's something // wrong with the intent, we can't really recover / try to populate @@ -495,7 +501,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList if (id == R.id.settings_button) { Intent intent = mBubble.getSettingsIntent(); mStackView.collapseStack(() -> { - mContext.startActivityAsUser(intent, mBubble.getEntry().notification.getUser()); + mContext.startActivityAsUser(intent, mBubble.getEntry().getSbn().getUser()); logBubbleClickEvent(mBubble, StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS); }); @@ -603,7 +609,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList * @param action the user interaction enum. */ private void logBubbleClickEvent(Bubble bubble, int action) { - StatusBarNotification notification = bubble.getEntry().notification; + StatusBarNotification notification = bubble.getEntry().getSbn(); StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, notification.getPackageName(), notification.getNotification().getChannelId(), diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 31cf853dce04..e5af3897dff2 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -181,7 +181,9 @@ public class BubbleStackView extends FrameLayout { */ private float mVerticalPosPercentBeforeRotation = -1; + private int mMaxBubbles; private int mBubbleSize; + private int mBubbleElevation; private int mBubblePaddingTop; private int mBubbleTouchPadding; private int mExpandedViewPadding; @@ -326,7 +328,9 @@ public class BubbleStackView extends FrameLayout { mInflater = LayoutInflater.from(context); Resources res = getResources(); + mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); + mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding); mExpandedAnimateXDistance = @@ -625,7 +629,7 @@ public class BubbleStackView extends FrameLayout { } Bubble topBubble = mBubbleData.getBubbles().get(0); String appName = topBubble.getAppName(); - Notification notification = topBubble.getEntry().notification.getNotification(); + Notification notification = topBubble.getEntry().getSbn().getNotification(); CharSequence titleCharSeq = notification.extras.getCharSequence(Notification.EXTRA_TITLE); String titleStr = getResources().getString(R.string.stream_notification); if (titleCharSeq != null) { @@ -1597,8 +1601,7 @@ public class BubbleStackView extends FrameLayout { for (int i = 0; i < bubbleCount; i++) { BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i); bv.updateDotVisibility(true /* animate */); - bv.setZ((BubbleController.MAX_BUBBLES - * getResources().getDimensionPixelSize(R.dimen.bubble_elevation)) - i); + bv.setZ((mMaxBubbles * mBubbleElevation) - i); // If the dot is on the left, and so is the stack, we need to change the dot position. if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) { bv.setDotPosition(!mStackOnLeftOrWillBe, animate); @@ -1678,7 +1681,7 @@ public class BubbleStackView extends FrameLayout { */ private void logBubbleEvent(@Nullable Bubble bubble, int action) { if (bubble == null || bubble.getEntry() == null - || bubble.getEntry().notification == null) { + || bubble.getEntry().getSbn() == null) { StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, null /* package name */, null /* notification channel */, @@ -1692,7 +1695,7 @@ public class BubbleStackView extends FrameLayout { false /* on-going bubble */, false /* isAppForeground (unused) */); } else { - StatusBarNotification notification = bubble.getEntry().notification; + StatusBarNotification notification = bubble.getEntry().getSbn(); StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, notification.getPackageName(), notification.getNotification().getChannelId(), diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java index 4512aa822e3b..fe4fa90a272d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java @@ -39,7 +39,6 @@ import com.android.launcher3.icons.ShadowGenerator; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; /** * A floating object on the screen that can post message updates. @@ -141,15 +140,6 @@ public class BubbleView extends FrameLayout { mUserBadgedAppIcon = appIcon; } - /** - * @return the {@link ExpandableNotificationRow} view to display notification content when the - * bubble is expanded. - */ - @Nullable - public ExpandableNotificationRow getRowView() { - return (mBubble != null) ? mBubble.getEntry().getRow() : null; - } - /** Changes the dot's visibility to match the bubble view's state. */ void updateDotVisibility(boolean animate) { updateDotVisibility(animate, null /* after */); @@ -230,7 +220,7 @@ public class BubbleView extends FrameLayout { } // Update icon. Notification.BubbleMetadata metadata = mBubble.getEntry().getBubbleMetadata(); - Notification n = mBubble.getEntry().notification.getNotification(); + Notification n = mBubble.getEntry().getSbn().getNotification(); Icon ic = metadata.getIcon(); boolean needsTint = ic.getType() != Icon.TYPE_ADAPTIVE_BITMAP; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java index cb7c998fad8e..8105c6a6e940 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java @@ -37,7 +37,7 @@ import java.util.Locale; * adb shell setprop debug.falsing_log true * * The log gets dumped as part of the SystemUI services. To dump on demand: - * adb shell dumpsys activity service com.android.systemui SystemBars | grep -A 999 FALSING | less + * adb shell dumpsys activity service com.android.systemui StatusBar | grep -A 999 FALSING | less * * To dump into logcat: * adb shell setprop debug.falsing_logcat true diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 914258f48b46..db85fa0a3203 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -17,7 +17,6 @@ package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import android.content.Context; import android.hardware.SensorManager; @@ -31,6 +30,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.classifier.brightline.FalsingDataProvider; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingPlugin; import com.android.systemui.plugins.PluginListener; @@ -41,7 +41,6 @@ import com.android.systemui.util.sensors.ProximitySensor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -62,7 +61,7 @@ public class FalsingManagerProxy implements FalsingManager { @Inject FalsingManagerProxy(Context context, PluginManager pluginManager, - @Named(MAIN_HANDLER_NAME) Handler handler, + @MainHandler Handler handler, ProximitySensor proximitySensor, DeviceConfigProxy deviceConfig) { mProximitySensor = proximitySensor; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java index 85a4d234b5df..b726c3e2783f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java @@ -16,6 +16,9 @@ package com.android.systemui.classifier.brightline; +import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN; +import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; + import android.view.MotionEvent; import java.util.Locale; @@ -29,6 +32,7 @@ import java.util.Locale; class PointerCountClassifier extends FalsingClassifier { private static final int MAX_ALLOWED_POINTERS = 1; + private static final int MAX_ALLOWED_POINTERS_SWIPE_DOWN = 2; private int mMaxPointerCount; PointerCountClassifier(FalsingDataProvider dataProvider) { @@ -50,6 +54,10 @@ class PointerCountClassifier extends FalsingClassifier { @Override public boolean isFalseTouch() { + int interactionType = getInteractionType(); + if (interactionType == QUICK_SETTINGS || interactionType == NOTIFICATION_DRAG_DOWN) { + return mMaxPointerCount > MAX_ALLOWED_POINTERS_SWIPE_DOWN; + } return mMaxPointerCount > MAX_ALLOWED_POINTERS; } diff --git a/packages/SystemUI/src/com/android/systemui/ActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java index 2c8a67270d94..4be610fcd9ee 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.app.Activity; +import com.android.systemui.ForegroundServicesDialog; import com.android.systemui.tuner.TunerActivity; import dagger.Binds; diff --git a/packages/SystemUI/src/com/android/systemui/ComponentBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java index 3b35c61e8eb2..4e4c06e9d447 100644 --- a/packages/SystemUI/src/com/android/systemui/ComponentBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import dagger.Binds; import dagger.Module; diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java index 2cf0f8dafcad..d6d1e418240a 100644 --- a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java @@ -14,11 +14,13 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.app.Activity; import android.app.Service; +import com.android.systemui.SystemUI; + /** * Interface necessary to make Dagger happy. See {@link ContextComponentResolver}. */ diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java index 995263240e2d..d7822c9fc6da 100644 --- a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java @@ -14,11 +14,13 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.app.Activity; import android.app.Service; +import com.android.systemui.SystemUI; + import java.util.Map; import javax.inject.Inject; diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java index 4df7f0d440df..9032c6fc8639 100644 --- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. @@ -14,11 +14,13 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; +import com.android.systemui.ActivityStarterDelegate; import com.android.systemui.appops.AppOpsController; import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.classifier.FalsingManagerProxy; +import com.android.systemui.doze.DozeHost; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.FalsingManager; @@ -30,7 +32,9 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; +import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -48,8 +52,8 @@ import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.FlashlightControllerImpl; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.HotspotControllerImpl; -import com.android.systemui.statusbar.policy.KeyguardMonitor; -import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.LocationControllerImpl; import com.android.systemui.statusbar.policy.NetworkController; @@ -146,7 +150,8 @@ public abstract class DependencyBinder { /** */ @Binds - public abstract KeyguardMonitor provideKeyguardMonitor(KeyguardMonitorImpl controllerImpl); + public abstract KeyguardStateController provideKeyguardMonitor( + KeyguardStateControllerImpl controllerImpl); /** */ @@ -198,6 +203,12 @@ public abstract class DependencyBinder { /** */ @Binds + public abstract SysuiStatusBarStateController providesSysuiStatusBarStateController( + StatusBarStateControllerImpl statusBarStateControllerImpl); + + /** + */ + @Binds public abstract StatusBarIconController provideStatusBarIconController( StatusBarIconControllerImpl controllerImpl); @@ -232,5 +243,10 @@ public abstract class DependencyBinder { /** */ @Binds - public abstract FalsingManager provideFalsingmanager(FalsingManagerProxy falsingManagerImpl); + public abstract FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl); + + /** + */ + @Binds + public abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost); } diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 239cbfe38975..87434f344729 100644 --- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. @@ -14,40 +14,37 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; -import static com.android.systemui.Dependency.BG_HANDLER_NAME; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; -import static com.android.systemui.Dependency.MAIN_LOOPER_NAME; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; -import android.annotation.Nullable; -import android.app.AlarmManager; import android.app.INotificationManager; import android.content.Context; -import android.hardware.SensorPrivacyManager; +import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.NightDisplayListener; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Process; import android.os.ServiceManager; -import android.os.UserHandle; import android.util.DisplayMetrics; import android.view.IWindowManager; -import android.view.WindowManagerGlobal; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; -import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.MainLooper; +import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.plugins.PluginInitializerImpl; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.plugins.PluginManagerImpl; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.DevicePolicyManagerWrapper; -import com.android.systemui.shared.system.PackageManagerWrapper; import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -65,6 +62,10 @@ import dagger.Provides; /** * Provides dependencies for the root component of sysui injection. + * + * Only SystemUI owned classes and instances should go in here. Other, framework-owned classes + * should go in {@link SystemServicesModule}. + * * See SystemUI/docs/dagger.md */ @Module @@ -73,7 +74,7 @@ public class DependencyProvider { @Singleton @Provides @Named(TIME_TICK_HANDLER_NAME) - public Handler provideHandler() { + public Handler provideTimeTickHandler() { HandlerThread thread = new HandlerThread("TimeTick"); thread.start(); return new Handler(thread.getLooper()); @@ -81,7 +82,7 @@ public class DependencyProvider { @Singleton @Provides - @Named(BG_LOOPER_NAME) + @BgLooper public Looper provideBgLooper() { HandlerThread thread = new HandlerThread("SysUiBg", Process.THREAD_PRIORITY_BACKGROUND); @@ -90,52 +91,50 @@ public class DependencyProvider { } /** Main Looper */ - @Singleton @Provides - @Named(MAIN_LOOPER_NAME) + @MainLooper public Looper provideMainLooper() { return Looper.getMainLooper(); } @Singleton @Provides - @Named(BG_HANDLER_NAME) - public Handler provideBgHandler(@Named(BG_LOOPER_NAME) Looper bgLooper) { + @BgHandler + public Handler provideBgHandler(@BgLooper Looper bgLooper) { return new Handler(bgLooper); } @Singleton @Provides - @Named(MAIN_HANDLER_NAME) - public Handler provideMainHandler(@Named(MAIN_LOOPER_NAME) Looper mainLooper) { + @MainHandler + public Handler provideMainHandler(@MainLooper Looper mainLooper) { return new Handler(mainLooper); } - @Singleton + /** */ @Provides - public DataSaverController provideDataSaverController(NetworkController networkController) { - return networkController.getDataSaverController(); + public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) { + return new AmbientDisplayConfiguration(context); } - @Singleton + /** */ @Provides - @Nullable - public LocalBluetoothManager provideLocalBluetoothController(Context context, - @Named(BG_HANDLER_NAME) Handler bgHandler) { - return LocalBluetoothManager.create(context, bgHandler, - UserHandle.ALL); + public Handler provideHandler() { + return new Handler(); } @Singleton @Provides - public MetricsLogger provideMetricsLogger() { - return new MetricsLogger(); + public DataSaverController provideDataSaverController(NetworkController networkController) { + return networkController.getDataSaverController(); } @Singleton @Provides - public IWindowManager provideIWindowManager() { - return WindowManagerGlobal.getWindowManagerService(); + // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used + // anywhere it is needed. + public DisplayMetrics provideDisplayMetrics() { + return new DisplayMetrics(); } @Singleton @@ -155,29 +154,21 @@ public class DependencyProvider { @Singleton @Provides - // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used - // anywhere it is needed. - public DisplayMetrics provideDisplayMetrics() { - return new DisplayMetrics(); - } + public LeakDetector provideLeakDetector() { + return LeakDetector.create(); - @Singleton - @Provides - public SensorPrivacyManager provideSensorPrivacyManager(Context context) { - return context.getSystemService(SensorPrivacyManager.class); } @Singleton @Provides - public LeakDetector provideLeakDetector() { - return LeakDetector.create(); - + public MetricsLogger provideMetricsLogger() { + return new MetricsLogger(); } @Singleton @Provides public NightDisplayListener provideNightDisplayListener(Context context, - @Named(BG_HANDLER_NAME) Handler bgHandler) { + @BgHandler Handler bgHandler) { return new NightDisplayListener(context, bgHandler); } @@ -190,7 +181,7 @@ public class DependencyProvider { @Singleton @Provides public NavigationBarController provideNavigationBarController(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { + @MainHandler Handler mainHandler) { return new NavigationBarController(context, mainHandler); } @@ -203,8 +194,11 @@ public class DependencyProvider { @Singleton @Provides public AutoHideController provideAutoHideController(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { - return new AutoHideController(context, mainHandler); + @MainHandler Handler mainHandler, + NotificationRemoteInputManager notificationRemoteInputManager, + IWindowManager iWindowManager) { + return new AutoHideController(context, mainHandler, notificationRemoteInputManager, + iWindowManager); } @Singleton @@ -221,21 +215,20 @@ public class DependencyProvider { @Singleton @Provides - public PackageManagerWrapper providePackageManagerWrapper() { - return PackageManagerWrapper.getInstance(); + public DeviceProvisionedController provideDeviceProvisionedController(Context context, + @MainHandler Handler mainHandler) { + return new DeviceProvisionedControllerImpl(context, mainHandler); } - @Singleton + /** */ @Provides - public DeviceProvisionedController provideDeviceProvisionedController(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { - return new DeviceProvisionedControllerImpl(context, mainHandler); + public LockPatternUtils provideLockPatternUtils(Context context) { + return new LockPatternUtils(context); } /** */ - @Singleton @Provides - public AlarmManager provideAlarmManager(Context context) { - return context.getSystemService(AlarmManager.class); + public AlwaysOnDisplayPolicy provideAlwaysOnDisplayPolicy(Context context) { + return new AlwaysOnDisplayPolicy(context); } } diff --git a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java index e761a2be0b0f..1f2c0a18f928 100644 --- a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java @@ -14,11 +14,13 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.app.Service; +import com.android.systemui.ImageWallpaper; import com.android.systemui.doze.DozeService; +import com.android.systemui.keyguard.KeyguardService; import dagger.Binds; import dagger.Module; @@ -30,8 +32,21 @@ import dagger.multibindings.IntoMap; */ @Module public abstract class ServiceBinder { + /** */ @Binds @IntoMap @ClassKey(DozeService.class) public abstract Service bindDozeService(DozeService service); + + /** */ + @Binds + @IntoMap + @ClassKey(ImageWallpaper.class) + public abstract Service bindImageWallpaper(ImageWallpaper service); + + /** */ + @Binds + @IntoMap + @ClassKey(KeyguardService.class) + public abstract Service bindKeyguardService(KeyguardService service); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java new file mode 100644 index 000000000000..fffba8cc1639 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 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.dagger; + +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.app.IActivityManager; +import android.app.IWallpaperManager; +import android.app.WallpaperManager; +import android.content.Context; +import android.content.res.Resources; +import android.hardware.SensorPrivacyManager; +import android.os.Handler; +import android.os.PowerManager; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.view.IWindowManager; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import com.android.internal.util.LatencyTracker; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.shared.system.PackageManagerWrapper; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +/** + * Provides Non-SystemUI, Framework-Owned instances to the dependency graph. + */ +@Module +public class SystemServicesModule { + + @Singleton + @Provides + static AlarmManager provideAlarmManager(Context context) { + return context.getSystemService(AlarmManager.class); + } + + @Singleton + @Provides + static IActivityManager provideIActivityManager() { + return ActivityManager.getService(); + } + + @Provides + @Nullable + static IWallpaperManager provideIWallPaperManager() { + return IWallpaperManager.Stub.asInterface( + ServiceManager.getService(Context.WALLPAPER_SERVICE)); + } + + @Singleton + @Provides + static IWindowManager provideIWindowManager() { + return WindowManagerGlobal.getWindowManagerService(); + } + + @Singleton + @Provides + static LatencyTracker provideLatencyTracker(Context context) { + return LatencyTracker.getInstance(context); + } + + @SuppressLint("MissingPermission") + @Singleton + @Provides + @Nullable + static LocalBluetoothManager provideLocalBluetoothController(Context context, + @BgHandler Handler bgHandler) { + return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL); + } + + @Singleton + @Provides + static PackageManagerWrapper providePackageManagerWrapper() { + return PackageManagerWrapper.getInstance(); + } + + /** */ + @Singleton + @Provides + static PowerManager providePowerManager(Context context) { + return context.getSystemService(PowerManager.class); + } + + @Provides + @MainResources + static Resources provideResources(Context context) { + return context.getResources(); + } + + @Singleton + @Provides + static SensorPrivacyManager provideSensorPrivacyManager(Context context) { + return context.getSystemService(SensorPrivacyManager.class); + } + + @Provides + static WallpaperManager provideWallpaperManager(Context context) { + return (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); + } + + @Singleton + @Provides + static WindowManager provideWindowManager(Context context) { + return context.getSystemService(WindowManager.class); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java new file mode 100644 index 000000000000..71c2e389f059 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 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.dagger; + +import com.android.systemui.LatencyTester; +import com.android.systemui.SystemUI; +import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.pip.PipUI; +import com.android.systemui.power.PowerUI; +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.RecentsModule; +import com.android.systemui.util.leak.GarbageMonitor; +import com.android.systemui.volume.VolumeUI; + +import dagger.Binds; +import dagger.Module; +import dagger.multibindings.ClassKey; +import dagger.multibindings.IntoMap; + +/** + * SystemUI objects that are injectable should go here. + */ +@Module(includes = {RecentsModule.class}) +public abstract class SystemUIBinder { + + /** Inject into GarbageMonitor.Service. */ + @Binds + @IntoMap + @ClassKey(GarbageMonitor.Service.class) + public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service service); + + /** Inject into KeyguardViewMediator. */ + @Binds + @IntoMap + @ClassKey(KeyguardViewMediator.class) + public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui); + + /** Inject into LatencyTests. */ + @Binds + @IntoMap + @ClassKey(LatencyTester.class) + public abstract SystemUI bindLatencyTester(LatencyTester sysui); + + /** Inject into PipUI. */ + @Binds + @IntoMap + @ClassKey(PipUI.class) + public abstract SystemUI bindPipUI(PipUI sysui); + + /** Inject into PowerUI. */ + @Binds + @IntoMap + @ClassKey(PowerUI.class) + public abstract SystemUI bindPowerUI(PowerUI sysui); + + /** Inject into Recents. */ + @Binds + @IntoMap + @ClassKey(Recents.class) + public abstract SystemUI bindRecents(Recents sysui); + + + /** Inject into VolumeUI. */ + @Binds + @IntoMap + @ClassKey(VolumeUI.class) + public abstract SystemUI bindVolumeUI(VolumeUI sysui); + +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 262b5ec50d83..c95b50b195b3 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -14,15 +14,14 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; -import android.content.Context; - import androidx.annotation.Nullable; +import com.android.systemui.SystemUI; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.power.EnhancedEstimates; @@ -40,6 +39,8 @@ import javax.inject.Singleton; import dagger.Binds; import dagger.Module; import dagger.Provides; +import dagger.multibindings.ClassKey; +import dagger.multibindings.IntoMap; /** * A dagger module for injecting default implementations of components of System UI that may be @@ -70,11 +71,13 @@ abstract class SystemUIDefaultModule { abstract NotificationData.KeyguardEnvironment bindKeyguardEnvironment( KeyguardEnvironmentImpl keyguardEnvironment); - @Singleton - @Provides - static ShadeController provideShadeController(Context context) { - return SysUiServiceProvider.getComponent(context, StatusBar.class); - } + @Binds + abstract ShadeController provideShadeController(StatusBar statusBar); + + @Binds + @IntoMap + @ClassKey(StatusBar.class) + public abstract SystemUI providesStatusBar(StatusBar statusBar); @Singleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index b0316e22de06..4e60f194e552 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -14,15 +14,17 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.assist.AssistModule; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.people.PeopleHubModule; import com.android.systemui.statusbar.phone.KeyguardLiftController; import com.android.systemui.util.sensors.AsyncSensorManager; @@ -35,7 +37,9 @@ import dagger.Provides; * A dagger module for injecting components of System UI that are not overridden by the System UI * implementation. */ -@Module(includes = {AssistModule.class, ComponentBinder.class}) +@Module(includes = {AssistModule.class, + ComponentBinder.class, + PeopleHubModule.class}) public abstract class SystemUIModule { @Singleton @@ -43,11 +47,13 @@ public abstract class SystemUIModule { @Nullable static KeyguardLiftController provideKeyguardLiftController(Context context, StatusBarStateController statusBarStateController, - AsyncSensorManager asyncSensorManager) { + AsyncSensorManager asyncSensorManager, + KeyguardUpdateMonitor keyguardUpdateMonitor) { if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) { return null; } - return new KeyguardLiftController(statusBarStateController, asyncSensorManager); + return new KeyguardLiftController(statusBarStateController, asyncSensorManager, + keyguardUpdateMonitor); } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java index c70b2fc3292a..113c9c845d95 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java @@ -14,16 +14,18 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import android.content.ContentProvider; +import com.android.systemui.Dependency; +import com.android.systemui.SystemUIAppComponentFactory; +import com.android.systemui.SystemUIFactory; import com.android.systemui.fragments.FragmentService; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.InjectionInflationController; -import com.android.systemui.util.leak.GarbageMonitor; import javax.inject.Named; import javax.inject.Singleton; @@ -37,13 +39,14 @@ import dagger.Component; @Component(modules = { DependencyProvider.class, DependencyBinder.class, + SystemServicesModule.class, SystemUIFactory.ContextHolder.class, SystemUIModule.class, SystemUIDefaultModule.class}) public interface SystemUIRootComponent { /** - * Creates a GarbageMonitor. + * Creates a ContextComponentHelper. */ @Singleton ContextComponentHelper getContextComponentHelper(); @@ -72,12 +75,6 @@ public interface SystemUIRootComponent { InjectionInflationController.ViewCreator createViewCreator(); /** - * Creates a GarbageMonitor. - */ - @Singleton - GarbageMonitor createGarbageMonitor(); - - /** * Whether notification long press is allowed. */ @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java index 4531c892a022..bc6b83ba1def 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java @@ -14,23 +14,17 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger.qualifiers; -import com.android.systemui.keyguard.KeyguardViewMediator; +import static java.lang.annotation.RetentionPolicy.RUNTIME; -import dagger.Binds; -import dagger.Module; -import dagger.multibindings.ClassKey; -import dagger.multibindings.IntoMap; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; -/** - * SystemUI objects that are injectable should go here. - */ -@Module -public abstract class SystemUIBinder { - /** Inject into KeyguardViewMediator. */ - @Binds - @IntoMap - @ClassKey(KeyguardViewMediator.class) - public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui); +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface BgHandler { } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java new file mode 100644 index 000000000000..2aadda1215d5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 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.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface BgLooper { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java new file mode 100644 index 000000000000..79661fa4a738 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 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.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface MainHandler { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java new file mode 100644 index 000000000000..750d7d72035c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 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.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface MainLooper { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java new file mode 100644 index 000000000000..3daeda550b4c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 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.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface MainResources { + // TODO: use attribute to get other, non-main resources? +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java index 1d7e9eacbd2e..419fd622d1df 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java @@ -82,7 +82,7 @@ public class DozeDockHandler implements DozeMachine.Part { private void requestPulse(State dozeState) { if (!mDozeHost.isPulsingBlocked() && dozeState.canPulse()) { - mMachine.requestPulse(DozeLog.PULSE_REASON_DOCKING); + mMachine.requestPulse(DozeEvent.PULSE_REASON_DOCKING); } mPulsePending = false; } @@ -91,7 +91,7 @@ public class DozeDockHandler implements DozeMachine.Part { if (dozeState == State.DOZE_REQUEST_PULSE || dozeState == State.DOZE_PULSING || dozeState == State.DOZE_PULSING_BRIGHT) { final int pulseReason = mMachine.getPulseReason(); - if (pulseReason == DozeLog.PULSE_REASON_DOCKING) { + if (pulseReason == DozeEvent.PULSE_REASON_DOCKING) { mDozeHost.stopPulsing(); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java b/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java new file mode 100644 index 000000000000..ea1def07a309 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2019 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.doze; + +import android.annotation.IntDef; + +import com.android.systemui.log.RichEvent; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * An event related to dozing. {@link DozeLog} stores and prints these events for debugging + * and triaging purposes. + */ +public class DozeEvent extends RichEvent { + public static final int TOTAL_EVENT_TYPES = 19; + + public DozeEvent(int logLevel, int type, String reason) { + super(logLevel, type, reason); + } + + /** + * Event labels for each doze event + * Index corresponds to the integer associated with each {@link EventType} + */ + @Override + public String[] getEventLabels() { + final String[] events = new String[]{ + "PickupWakeup", + "PulseStart", + "PulseFinish", + "NotificationPulse", + "Dozing", + "Fling", + "EmergencyCall", + "KeyguardBouncerChanged", + "ScreenOn", + "ScreenOff", + "MissedTick", + "TimeTickScheduled", + "KeyguardVisibilityChanged", + "DozeStateChanged", + "WakeDisplay", + "ProximityResult", + "PulseDropped", + "PulseDisabledByProx", + "SensorTriggered" + }; + + if (events.length != TOTAL_EVENT_TYPES) { + throw new IllegalStateException("DozeEvent events.length should match TOTAL_EVENT_TYPES" + + " events.length=" + events.length + + " TOTAL_EVENT_LENGTH=" + TOTAL_EVENT_TYPES); + } + return events; + } + + /** + * Converts the reason (integer) to a user-readable string + */ + public static String reasonToString(@Reason int pulseReason) { + switch (pulseReason) { + case PULSE_REASON_INTENT: return "intent"; + case PULSE_REASON_NOTIFICATION: return "notification"; + case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion"; + case REASON_SENSOR_PICKUP: return "pickup"; + case REASON_SENSOR_DOUBLE_TAP: return "doubletap"; + case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress"; + case PULSE_REASON_DOCKING: return "docking"; + case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen"; + case REASON_SENSOR_WAKE_UP: return "wakeup"; + case REASON_SENSOR_TAP: return "tap"; + default: throw new IllegalArgumentException("invalid reason: " + pulseReason); + } + } + + /** + * Builds a DozeEvent. + */ + public static class DozeEventBuilder extends RichEvent.Builder<DozeEventBuilder> { + @Override + public DozeEventBuilder getBuilder() { + return this; + } + + @Override + public RichEvent build() { + return new DozeEvent(mLogLevel, mType, mReason); + } + } + + @IntDef({PICKUP_WAKEUP, PULSE_START, PULSE_FINISH, NOTIFICATION_PULSE, DOZING, FLING, + EMERGENCY_CALL, KEYGUARD_BOUNCER_CHANGED, SCREEN_ON, SCREEN_OFF, MISSED_TICK, + TIME_TICK_SCHEDULED, KEYGUARD_VISIBILITY_CHANGE, DOZE_STATE_CHANGED, WAKE_DISPLAY, + PROXIMITY_RESULT, PULSE_DROPPED, PULSE_DISABLED_BY_PROX, SENSOR_TRIGGERED}) + /** + * Types of DozeEvents + */ + @Retention(RetentionPolicy.SOURCE) + public @interface EventType {} + public static final int PICKUP_WAKEUP = 0; + public static final int PULSE_START = 1; + public static final int PULSE_FINISH = 2; + public static final int NOTIFICATION_PULSE = 3; + public static final int DOZING = 4; + public static final int FLING = 5; + public static final int EMERGENCY_CALL = 6; + public static final int KEYGUARD_BOUNCER_CHANGED = 7; + public static final int SCREEN_ON = 8; + public static final int SCREEN_OFF = 9; + public static final int MISSED_TICK = 10; + public static final int TIME_TICK_SCHEDULED = 11; + public static final int KEYGUARD_VISIBILITY_CHANGE = 12; + public static final int DOZE_STATE_CHANGED = 13; + public static final int WAKE_DISPLAY = 14; + public static final int PROXIMITY_RESULT = 15; + public static final int PULSE_DROPPED = 16; + public static final int PULSE_DISABLED_BY_PROX = 17; + public static final int SENSOR_TRIGGERED = 18; + + public static final int TOTAL_REASONS = 10; + @IntDef({PULSE_REASON_NONE, PULSE_REASON_INTENT, PULSE_REASON_NOTIFICATION, + PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP, + PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP, + PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP}) + @Retention(RetentionPolicy.SOURCE) + public @interface Reason {} + public static final int PULSE_REASON_NONE = -1; + public static final int PULSE_REASON_INTENT = 0; + public static final int PULSE_REASON_NOTIFICATION = 1; + public static final int PULSE_REASON_SENSOR_SIGMOTION = 2; + public static final int REASON_SENSOR_PICKUP = 3; + public static final int REASON_SENSOR_DOUBLE_TAP = 4; + public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5; + public static final int PULSE_REASON_DOCKING = 6; + public static final int REASON_SENSOR_WAKE_UP = 7; + public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8; + public static final int REASON_SENSOR_TAP = 9; +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index bb8c7f1dabf8..33f68cfc1492 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -16,8 +16,10 @@ package com.android.systemui.doze; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.Application; +import android.app.IWallpaperManager; import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorManager; @@ -25,7 +27,6 @@ import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUIApplication; import com.android.systemui.dock.DockManager; @@ -33,49 +34,84 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.sensors.AsyncSensorManager; +import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.util.wakelock.WakeLock; +import javax.inject.Inject; + public class DozeFactory { - public DozeFactory() { + private final FalsingManager mFalsingManager; + private final DozeLog mDozeLog; + private final DozeParameters mDozeParameters; + private final BatteryController mBatteryController; + private final AsyncSensorManager mAsyncSensorManager; + private final AlarmManager mAlarmManager; + private final WakefulnessLifecycle mWakefulnessLifecycle; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final DockManager mDockManager; + private final IWallpaperManager mWallpaperManager; + private final ProximitySensor mProximitySensor; + private final DelayedWakeLock.Builder mDelayedWakeLockBuilder; + private final Handler mHandler; + private final BiometricUnlockController mBiometricUnlockController; + + @Inject + public DozeFactory(FalsingManager falsingManager, DozeLog dozeLog, + DozeParameters dozeParameters, BatteryController batteryController, + AsyncSensorManager asyncSensorManager, AlarmManager alarmManager, + WakefulnessLifecycle wakefulnessLifecycle, KeyguardUpdateMonitor keyguardUpdateMonitor, + DockManager dockManager, @Nullable IWallpaperManager wallpaperManager, + ProximitySensor proximitySensor, + DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, + BiometricUnlockController biometricUnlockController) { + mFalsingManager = falsingManager; + mDozeLog = dozeLog; + mDozeParameters = dozeParameters; + mBatteryController = batteryController; + mAsyncSensorManager = asyncSensorManager; + mAlarmManager = alarmManager; + mWakefulnessLifecycle = wakefulnessLifecycle; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mDockManager = dockManager; + mWallpaperManager = wallpaperManager; + mProximitySensor = proximitySensor; + mDelayedWakeLockBuilder = delayedWakeLockBuilder; + mHandler = handler; + mBiometricUnlockController = biometricUnlockController; } /** Creates a DozeMachine with its parts for {@code dozeService}. */ - public DozeMachine assembleMachine(DozeService dozeService, FalsingManager falsingManager) { - Context context = dozeService; - AsyncSensorManager sensorManager = Dependency.get(AsyncSensorManager.class); - AlarmManager alarmManager = context.getSystemService(AlarmManager.class); - DockManager dockManager = Dependency.get(DockManager.class); - WakefulnessLifecycle wakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); - + DozeMachine assembleMachine(DozeService dozeService) { DozeHost host = getHost(dozeService); - AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context); - DozeParameters params = DozeParameters.getInstance(context); - Handler handler = new Handler(); - WakeLock wakeLock = new DelayedWakeLock(handler, - WakeLock.createPartial(context, "Doze")); + AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(dozeService); + WakeLock wakeLock = mDelayedWakeLockBuilder.setHandler(mHandler).setTag("Doze").build(); DozeMachine.Service wrappedService = dozeService; wrappedService = new DozeBrightnessHostForwarder(wrappedService, host); - wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(wrappedService, params); - wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(wrappedService, - params); + wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded( + wrappedService, mDozeParameters); + wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded( + wrappedService, mDozeParameters); DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock, - wakefulnessLifecycle); + mWakefulnessLifecycle, mBatteryController, mDozeLog); machine.setParts(new DozeMachine.Part[]{ - new DozePauser(handler, machine, alarmManager, params.getPolicy()), - new DozeFalsingManagerAdapter(falsingManager), - createDozeTriggers(context, sensorManager, host, alarmManager, config, params, - handler, wakeLock, machine, dockManager), - createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params), - new DozeScreenState(wrappedService, handler, params, wakeLock), - createDozeScreenBrightness(context, wrappedService, sensorManager, host, params, - handler), - new DozeWallpaperState(context, getBiometricUnlockController(dozeService)), - new DozeDockHandler(context, machine, host, config, handler, dockManager), + new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()), + new DozeFalsingManagerAdapter(mFalsingManager), + createDozeTriggers(dozeService, mAsyncSensorManager, host, mAlarmManager, config, + mDozeParameters, mHandler, wakeLock, machine, mDockManager, mDozeLog), + createDozeUi(dozeService, host, wakeLock, machine, mHandler, mAlarmManager, + mDozeParameters, mDozeLog), + new DozeScreenState(wrappedService, mHandler, host, mDozeParameters, wakeLock), + createDozeScreenBrightness(dozeService, wrappedService, mAsyncSensorManager, host, + mDozeParameters, mHandler), + new DozeWallpaperState( + mWallpaperManager, mBiometricUnlockController, mDozeParameters), + new DozeDockHandler(dozeService, machine, host, config, mHandler, mDockManager), new DozeAuthRemover(dozeService) }); @@ -94,17 +130,19 @@ public class DozeFactory { private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager, DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config, DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine, - DockManager dockManager) { + DockManager dockManager, DozeLog dozeLog) { boolean allowPulseTriggers = true; return new DozeTriggers(context, machine, host, alarmManager, config, params, - sensorManager, handler, wakeLock, allowPulseTriggers, dockManager); + sensorManager, handler, wakeLock, allowPulseTriggers, dockManager, + mProximitySensor, dozeLog); + } private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock, DozeMachine machine, Handler handler, AlarmManager alarmManager, - DozeParameters params) { + DozeParameters params, DozeLog dozeLog) { return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params, - Dependency.get(KeyguardUpdateMonitor.class)); + mKeyguardUpdateMonitor, dozeLog); } public static DozeHost getHost(DozeService service) { @@ -112,10 +150,4 @@ public class DozeFactory { final SystemUIApplication app = (SystemUIApplication) appCandidate; return app.getComponent(DozeHost.class); } - - public static BiometricUnlockController getBiometricUnlockController(DozeService service) { - Application appCandidate = service.getApplication(); - final SystemUIApplication app = (SystemUIApplication) appCandidate; - return app.getComponent(BiometricUnlockController.class); - } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index 07dd2cd77043..d1047e216ec4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -63,9 +63,16 @@ public interface DozeHost { void setDozeScreenBrightness(int value); /** - * Makes scrims black and changes animation durations. + * Fade out screen before switching off the display power mode. + * @param onDisplayOffCallback Executed when the display is black. */ - default void prepareForGentleWakeUp() {} + void prepareForGentleSleep(Runnable onDisplayOffCallback); + + /** + * Cancel pending {@code onDisplayOffCallback} callback. + * @see #prepareForGentleSleep(Runnable) + */ + void cancelGentleSleep(); void onIgnoreTouchWhilePulsing(boolean ignore); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index 8fe9f929b880..2e4466d47927 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -16,282 +16,284 @@ package com.android.systemui.doze; -import android.content.Context; -import android.os.Build; -import android.util.Log; import android.util.TimeUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.Dependency; +import com.android.systemui.DumpController; +import com.android.systemui.log.SysuiLog; import java.io.PrintWriter; -import java.text.SimpleDateFormat; import java.util.Date; -public class DozeLog { +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Logs doze events for debugging and triaging purposes. Logs are dumped in bugreports or on demand: + * adb shell dumpsys activity service com.android.systemui/.SystemUIService \ + * dependency DumpController DozeLog + */ +@Singleton +public class DozeLog extends SysuiLog { private static final String TAG = "DozeLog"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final boolean ENABLED = true; - private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50; - static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); - - public static final int REASONS = 10; - - public static final int PULSE_REASON_NONE = -1; - public static final int PULSE_REASON_INTENT = 0; - public static final int PULSE_REASON_NOTIFICATION = 1; - public static final int PULSE_REASON_SENSOR_SIGMOTION = 2; - public static final int REASON_SENSOR_PICKUP = 3; - public static final int REASON_SENSOR_DOUBLE_TAP = 4; - public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5; - public static final int PULSE_REASON_DOCKING = 6; - public static final int REASON_SENSOR_WAKE_UP = 7; - public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8; - public static final int REASON_SENSOR_TAP = 9; - - private static boolean sRegisterKeyguardCallback = true; - - private static long[] sTimes; - private static String[] sMessages; - private static int sPosition; - private static int sCount; - private static boolean sPulsing; - - private static long sSince; - private static SummaryStats sPickupPulseNearVibrationStats; - private static SummaryStats sPickupPulseNotNearVibrationStats; - private static SummaryStats sNotificationPulseStats; - private static SummaryStats sScreenOnPulsingStats; - private static SummaryStats sScreenOnNotPulsingStats; - private static SummaryStats sEmergencyCallStats; - private static SummaryStats[][] sProxStats; // [reason][near/far] - - public static void tracePickupWakeUp(Context context, boolean withinVibrationThreshold) { - if (!ENABLED) return; - init(context); - log("pickupWakeUp withinVibrationThreshold=" + withinVibrationThreshold); - (withinVibrationThreshold ? sPickupPulseNearVibrationStats - : sPickupPulseNotNearVibrationStats).append(); + + private boolean mPulsing; + private long mSince; + private SummaryStats mPickupPulseNearVibrationStats; + private SummaryStats mPickupPulseNotNearVibrationStats; + private SummaryStats mNotificationPulseStats; + private SummaryStats mScreenOnPulsingStats; + private SummaryStats mScreenOnNotPulsingStats; + private SummaryStats mEmergencyCallStats; + private SummaryStats[][] mProxStats; // [reason][near/far] + + @Inject + public DozeLog(KeyguardUpdateMonitor keyguardUpdateMonitor, DumpController dumpController) { + super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS); + mSince = System.currentTimeMillis(); + mPickupPulseNearVibrationStats = new SummaryStats(); + mPickupPulseNotNearVibrationStats = new SummaryStats(); + mNotificationPulseStats = new SummaryStats(); + mScreenOnPulsingStats = new SummaryStats(); + mScreenOnNotPulsingStats = new SummaryStats(); + mEmergencyCallStats = new SummaryStats(); + mProxStats = new SummaryStats[DozeEvent.TOTAL_REASONS][2]; + for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) { + mProxStats[i][0] = new SummaryStats(); + mProxStats[i][1] = new SummaryStats(); + } + + if (keyguardUpdateMonitor != null) { + keyguardUpdateMonitor.registerCallback(mKeyguardCallback); + } } - public static void tracePulseStart(int reason) { - if (!ENABLED) return; - sPulsing = true; - log("pulseStart reason=" + reasonToString(reason)); + /** + * Appends pickup wakeup event to the logs + */ + public void tracePickupWakeUp(boolean withinVibrationThreshold) { + if (log(DozeEvent.PICKUP_WAKEUP, + "withinVibrationThreshold=" + withinVibrationThreshold)) { + (withinVibrationThreshold ? mPickupPulseNearVibrationStats + : mPickupPulseNotNearVibrationStats).append(); + } } - public static void tracePulseFinish() { - if (!ENABLED) return; - sPulsing = false; - log("pulseFinish"); + /** + * Appends pulse started event to the logs. + * @param reason why the pulse started + */ + public void tracePulseStart(@DozeEvent.Reason int reason) { + if (log(DozeEvent.PULSE_START, DozeEvent.reasonToString(reason))) { + mPulsing = true; + } } - public static void traceNotificationPulse(Context context) { - if (!ENABLED) return; - init(context); - log("notificationPulse"); - sNotificationPulseStats.append(); + /** + * Appends pulse finished event to the logs + */ + public void tracePulseFinish() { + if (log(DozeEvent.PULSE_FINISH)) { + mPulsing = false; + } } - private static void init(Context context) { - synchronized (DozeLog.class) { - if (sMessages == null) { - sTimes = new long[SIZE]; - sMessages = new String[SIZE]; - sSince = System.currentTimeMillis(); - sPickupPulseNearVibrationStats = new SummaryStats(); - sPickupPulseNotNearVibrationStats = new SummaryStats(); - sNotificationPulseStats = new SummaryStats(); - sScreenOnPulsingStats = new SummaryStats(); - sScreenOnNotPulsingStats = new SummaryStats(); - sEmergencyCallStats = new SummaryStats(); - sProxStats = new SummaryStats[REASONS][2]; - for (int i = 0; i < REASONS; i++) { - sProxStats[i][0] = new SummaryStats(); - sProxStats[i][1] = new SummaryStats(); - } - log("init"); - if (sRegisterKeyguardCallback) { - Dependency.get(KeyguardUpdateMonitor.class).registerCallback(sKeyguardCallback); - } - } + /** + * Appends pulse event to the logs + */ + public void traceNotificationPulse() { + if (log(DozeEvent.NOTIFICATION_PULSE)) { + mNotificationPulseStats.append(); } } - public static void traceDozing(Context context, boolean dozing) { - if (!ENABLED) return; - sPulsing = false; - init(context); - log("dozing " + dozing); + /** + * Appends dozing event to the logs + * @param dozing true if dozing, else false + */ + public void traceDozing(boolean dozing) { + if (log(DozeEvent.DOZING, "dozing=" + dozing)) { + mPulsing = false; + } } - public static void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded, + /** + * Appends fling event to the logs + */ + public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded, boolean screenOnFromTouch) { - if (!ENABLED) return; - log("fling expand=" + expand + " aboveThreshold=" + aboveThreshold + " thresholdNeeded=" - + thresholdNeeded + " screenOnFromTouch=" + screenOnFromTouch); + log(DozeEvent.FLING, "expand=" + expand + + " aboveThreshold=" + aboveThreshold + + " thresholdNeeded=" + thresholdNeeded + + " screenOnFromTouch=" + screenOnFromTouch); } - public static void traceEmergencyCall() { - if (!ENABLED) return; - log("emergencyCall"); - sEmergencyCallStats.append(); + /** + * Appends emergency call event to the logs + */ + public void traceEmergencyCall() { + if (log(DozeEvent.EMERGENCY_CALL)) { + mEmergencyCallStats.append(); + } } - public static void traceKeyguardBouncerChanged(boolean showing) { - if (!ENABLED) return; - log("bouncer " + showing); + /** + * Appends keyguard bouncer changed event to the logs + * @param showing true if the keyguard bouncer is showing, else false + */ + public void traceKeyguardBouncerChanged(boolean showing) { + log(DozeEvent.KEYGUARD_BOUNCER_CHANGED, "showing=" + showing); } - public static void traceScreenOn() { - if (!ENABLED) return; - log("screenOn pulsing=" + sPulsing); - (sPulsing ? sScreenOnPulsingStats : sScreenOnNotPulsingStats).append(); - sPulsing = false; + /** + * Appends screen-on event to the logs + */ + public void traceScreenOn() { + if (log(DozeEvent.SCREEN_ON, "pulsing=" + mPulsing)) { + (mPulsing ? mScreenOnPulsingStats : mScreenOnNotPulsingStats).append(); + mPulsing = false; + } } - public static void traceScreenOff(int why) { - if (!ENABLED) return; - log("screenOff why=" + why); + /** + * Appends screen-off event to the logs + * @param why reason the screen is off + */ + public void traceScreenOff(int why) { + log(DozeEvent.SCREEN_OFF, "why=" + why); } - public static void traceMissedTick(String delay) { - if (!ENABLED) return; - log("missedTick by=" + delay); + /** + * Appends missed tick event to the logs + * @param delay of the missed tick + */ + public void traceMissedTick(String delay) { + log(DozeEvent.MISSED_TICK, "delay=" + delay); } - public static void traceTimeTickScheduled(long when, long triggerAt) { - if (!ENABLED) return; - log("timeTickScheduled at=" + FORMAT.format(new Date(when)) + " triggerAt=" - + FORMAT.format(new Date(triggerAt))); + /** + * Appends time tick scheduled event to the logs + * @param when time tick scheduled at + * @param triggerAt time tick trigger at + */ + public void traceTimeTickScheduled(long when, long triggerAt) { + log(DozeEvent.TIME_TICK_SCHEDULED, + "scheduledAt=" + DATE_FORMAT.format(new Date(when)) + + " triggerAt=" + DATE_FORMAT.format(new Date(triggerAt))); } - public static void traceKeyguard(boolean showing) { - if (!ENABLED) return; - log("keyguard " + showing); - if (!showing) { - sPulsing = false; + /** + * Appends keyguard visibility change event to the logs + * @param showing whether the keyguard is now showing + */ + public void traceKeyguard(boolean showing) { + if (log(DozeEvent.KEYGUARD_VISIBILITY_CHANGE, "showing=" + showing) + && !showing) { + mPulsing = false; } } - public static void traceState(DozeMachine.State state) { - if (!ENABLED) return; - log("state " + state); + /** + * Appends doze state changed event to the logs + * @param state new DozeMachine state + */ + public void traceState(DozeMachine.State state) { + log(DozeEvent.DOZE_STATE_CHANGED, state.name()); } /** * Appends wake-display event to the logs. * @param wake if we're waking up or sleeping. */ - public static void traceWakeDisplay(boolean wake) { - if (!ENABLED) return; - log("wakeDisplay " + wake); - } - - public static void traceProximityResult(Context context, boolean near, long millis, - int reason) { - if (!ENABLED) return; - init(context); - log("proximityResult reason=" + reasonToString(reason) + " near=" + near - + " millis=" + millis); - sProxStats[reason][near ? 0 : 1].append(); + public void traceWakeDisplay(boolean wake) { + log(DozeEvent.WAKE_DISPLAY, "wake=" + wake); } - public static String reasonToString(int pulseReason) { - switch (pulseReason) { - case PULSE_REASON_INTENT: return "intent"; - case PULSE_REASON_NOTIFICATION: return "notification"; - case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion"; - case REASON_SENSOR_PICKUP: return "pickup"; - case REASON_SENSOR_DOUBLE_TAP: return "doubletap"; - case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress"; - case PULSE_REASON_DOCKING: return "docking"; - case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen"; - case REASON_SENSOR_WAKE_UP: return "wakeup"; - case REASON_SENSOR_TAP: return "tap"; - default: throw new IllegalArgumentException("bad reason: " + pulseReason); + /** + * Appends proximity result event to the logs + * @param near true if near, else false + * @param millis + * @param reason why proximity result was triggered + */ + public void traceProximityResult(boolean near, long millis, @DozeEvent.Reason int reason) { + if (log(DozeEvent.PROXIMITY_RESULT, + " reason=" + DozeEvent.reasonToString(reason) + + " near=" + near + + " millis=" + millis)) { + mProxStats[reason][near ? 0 : 1].append(); } } - public static void dump(PrintWriter pw) { + /** + * Prints doze log timeline and consolidated stats + * @param pw + */ + public void dump(PrintWriter pw) { synchronized (DozeLog.class) { - if (sMessages == null) return; - pw.println(" Doze log:"); - final int start = (sPosition - sCount + SIZE) % SIZE; - for (int i = 0; i < sCount; i++) { - final int j = (start + i) % SIZE; - pw.print(" "); - pw.print(FORMAT.format(new Date(sTimes[j]))); - pw.print(' '); - pw.println(sMessages[j]); - } + super.dump(null, pw, null); // prints timeline + pw.print(" Doze summary stats (for "); - TimeUtils.formatDuration(System.currentTimeMillis() - sSince, pw); + TimeUtils.formatDuration(System.currentTimeMillis() - mSince, pw); pw.println("):"); - sPickupPulseNearVibrationStats.dump(pw, "Pickup pulse (near vibration)"); - sPickupPulseNotNearVibrationStats.dump(pw, "Pickup pulse (not near vibration)"); - sNotificationPulseStats.dump(pw, "Notification pulse"); - sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)"); - sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)"); - sEmergencyCallStats.dump(pw, "Emergency call"); - for (int i = 0; i < REASONS; i++) { - final String reason = reasonToString(i); - sProxStats[i][0].dump(pw, "Proximity near (" + reason + ")"); - sProxStats[i][1].dump(pw, "Proximity far (" + reason + ")"); + mPickupPulseNearVibrationStats.dump(pw, "Pickup pulse (near vibration)"); + mPickupPulseNotNearVibrationStats.dump(pw, "Pickup pulse (not near vibration)"); + mNotificationPulseStats.dump(pw, "Notification pulse"); + mScreenOnPulsingStats.dump(pw, "Screen on (pulsing)"); + mScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)"); + mEmergencyCallStats.dump(pw, "Emergency call"); + for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) { + final String reason = DozeEvent.reasonToString(i); + mProxStats[i][0].dump(pw, "Proximity near (" + reason + ")"); + mProxStats[i][1].dump(pw, "Proximity far (" + reason + ")"); } } } - private static void log(String msg) { - synchronized (DozeLog.class) { - if (sMessages == null) return; - sTimes[sPosition] = System.currentTimeMillis(); - sMessages[sPosition] = msg; - sPosition = (sPosition + 1) % SIZE; - sCount = Math.min(sCount + 1, SIZE); - } - if (DEBUG) Log.d(TAG, msg); + private boolean log(@DozeEvent.EventType int eventType) { + return log(eventType, ""); } - public static void tracePulseDropped(Context context, boolean pulsePending, - DozeMachine.State state, boolean blocked) { - if (!ENABLED) return; - init(context); - log("pulseDropped pulsePending=" + pulsePending + " state=" - + state + " blocked=" + blocked); + private boolean log(@DozeEvent.EventType int eventType, String msg) { + return super.log(new DozeEvent.DozeEventBuilder() + .setType(eventType) + .setReason(msg) + .build()); } - public static void tracePulseDropped(Context context, String why) { - if (!ENABLED) return; - init(context); - log("pulseDropped why=" + why); + /** + * Appends pulse dropped event to logs + */ + public void tracePulseDropped(boolean pulsePending, DozeMachine.State state, boolean blocked) { + log(DozeEvent.PULSE_DROPPED, "pulsePending=" + pulsePending + " state=" + + state.name() + " blocked=" + blocked); } - public static void tracePulseTouchDisabledByProx(Context context, boolean disabled) { - if (!ENABLED) return; - init(context); - log("pulseTouchDisabledByProx " + disabled); + /** + * Appends pulse dropped event to logs + * @param reason why the pulse was dropped + */ + public void tracePulseDropped(String reason) { + log(DozeEvent.PULSE_DROPPED, "why=" + reason); } - public static void setRegisterKeyguardCallback(boolean registerKeyguardCallback) { - if (!ENABLED) return; - synchronized (DozeLog.class) { - if (sRegisterKeyguardCallback != registerKeyguardCallback && sMessages != null) { - throw new IllegalStateException("Cannot change setRegisterKeyguardCallback " - + "after init()"); - } - sRegisterKeyguardCallback = registerKeyguardCallback; - } + /** + * Appends pulse touch displayed by prox sensor event to logs + * @param disabled + */ + public void tracePulseTouchDisabledByProx(boolean disabled) { + log(DozeEvent.PULSE_DISABLED_BY_PROX, "disabled=" + disabled); } - public static void traceSensor(Context context, int reason) { - if (!ENABLED) return; - init(context); - log("sensor type=" + reasonToString(reason)); + /** + * Appends sensor triggered event to logs + * @param reason why the sensor was triggered + */ + public void traceSensor(@DozeEvent.Reason int reason) { + log(DozeEvent.SENSOR_TRIGGERED, "type=" + DozeEvent.reasonToString(reason)); } - private static class SummaryStats { + private class SummaryStats { private int mCount; public void append() { @@ -305,7 +307,7 @@ public class DozeLog { pw.print(": n="); pw.print(mCount); pw.print(" ("); - final double perHr = (double) mCount / (System.currentTimeMillis() - sSince) + final double perHr = (double) mCount / (System.currentTimeMillis() - mSince) * 1000 * 60 * 60; pw.print(perHr); pw.print("/hr)"); @@ -313,7 +315,7 @@ public class DozeLog { } } - private static final KeyguardUpdateMonitorCallback sKeyguardCallback = + private final KeyguardUpdateMonitorCallback mKeyguardCallback = new KeyguardUpdateMonitorCallback() { @Override public void onEmergencyCallAction() { @@ -340,4 +342,7 @@ public class DozeLog { traceKeyguard(showing); } }; + + private static final int MAX_DOZE_DEBUG_LOGS = 400; + private static final int MAX_DOZE_LOGS = 50; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index 93a51cc20db2..75b1d6c87800 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -27,6 +27,7 @@ import com.android.internal.util.Preconditions; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.Assert; import com.android.systemui.util.wakelock.WakeLock; @@ -45,6 +46,7 @@ public class DozeMachine { static final String TAG = "DozeMachine"; static final boolean DEBUG = DozeService.DEBUG; + private final DozeLog mDozeLog; private static final String REASON_CHANGE_STATE = "DozeMachine#requestState"; private static final String REASON_HELD_FOR_STATE = "DozeMachine#heldForState"; @@ -121,6 +123,7 @@ public class DozeMachine { private final WakeLock mWakeLock; private final AmbientDisplayConfiguration mConfig; private final WakefulnessLifecycle mWakefulnessLifecycle; + private final BatteryController mBatteryController; private Part[] mParts; private final ArrayList<State> mQueuedRequests = new ArrayList<>(); @@ -129,11 +132,14 @@ public class DozeMachine { private boolean mWakeLockHeldForCurrentState = false; public DozeMachine(Service service, AmbientDisplayConfiguration config, - WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle) { + WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle, + BatteryController batteryController, DozeLog dozeLog) { mDozeService = service; mConfig = config; mWakefulnessLifecycle = wakefulnessLifecycle; mWakeLock = wakeLock; + mBatteryController = batteryController; + mDozeLog = dozeLog; } /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */ @@ -155,7 +161,7 @@ public class DozeMachine { @MainThread public void requestState(State requestedState) { Preconditions.checkArgument(requestedState != State.DOZE_REQUEST_PULSE); - requestState(requestedState, DozeLog.PULSE_REASON_NONE); + requestState(requestedState, DozeEvent.PULSE_REASON_NONE); } @MainThread @@ -243,7 +249,7 @@ public class DozeMachine { State oldState = mState; mState = newState; - DozeLog.traceState(newState); + mDozeLog.traceState(newState); Trace.traceCounter(Trace.TRACE_TAG_APP, "doze_machine_state", newState.ordinal()); updatePulseReason(newState, oldState, pulseReason); @@ -257,7 +263,7 @@ public class DozeMachine { if (newState == State.DOZE_REQUEST_PULSE) { mPulseReason = pulseReason; } else if (oldState == State.DOZE_PULSE_DONE) { - mPulseReason = DozeLog.PULSE_REASON_NONE; + mPulseReason = DozeEvent.PULSE_REASON_NONE; } } @@ -316,6 +322,9 @@ public class DozeMachine { Log.i(TAG, "Dropping pulse done because current state is already done: " + mState); return mState; } + if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) { + return State.DOZE; + } if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) { Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState); return mState; @@ -349,7 +358,7 @@ public class DozeMachine { nextState = State.DOZE; } - transitionTo(nextState, DozeLog.PULSE_REASON_NONE); + transitionTo(nextState, DozeEvent.PULSE_REASON_NONE); break; default: break; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index 38ee2fed136d..e1b4f3122861 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -16,6 +16,12 @@ package com.android.systemui.doze; +import static com.android.systemui.doze.DozeMachine.State.DOZE; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING; +import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; + import android.os.Handler; import android.util.Log; import android.view.Display; @@ -48,21 +54,24 @@ public class DozeScreenState implements DozeMachine.Part { private final Handler mHandler; private final Runnable mApplyPendingScreenState = this::applyPendingScreenState; private final DozeParameters mParameters; + private final DozeHost mDozeHost; private int mPendingScreenState = Display.STATE_UNKNOWN; private SettableWakeLock mWakeLock; - public DozeScreenState(DozeMachine.Service service, Handler handler, + public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host, DozeParameters parameters, WakeLock wakeLock) { mDozeService = service; mHandler = handler; mParameters = parameters; + mDozeHost = host; mWakeLock = new SettableWakeLock(wakeLock, TAG); } @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { int screenState = newState.screenState(mParameters); + mDozeHost.cancelGentleSleep(); if (newState == DozeMachine.State.FINISH) { // Make sure not to apply the screen state after DozeService was destroyed. @@ -79,12 +88,13 @@ public class DozeScreenState implements DozeMachine.Part { return; } - boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState); - boolean pulseEnding = oldState == DozeMachine.State.DOZE_PULSE_DONE - && newState == DozeMachine.State.DOZE_AOD; - boolean turningOn = (oldState == DozeMachine.State.DOZE_AOD_PAUSED - || oldState == DozeMachine.State.DOZE) && newState == DozeMachine.State.DOZE_AOD; - boolean justInitialized = oldState == DozeMachine.State.INITIALIZED; + final boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState); + final boolean pulseEnding = oldState == DOZE_PULSE_DONE && newState == DOZE_AOD; + final boolean turningOn = (oldState == DOZE_AOD_PAUSED + || oldState == DOZE) && newState == DOZE_AOD; + final boolean turningOff = (oldState == DOZE_AOD && newState == DOZE) + || (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED); + final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED; if (messagePending || justInitialized || pulseEnding || turningOn) { // During initialization, we hide the navigation bar. That is however only applied after // a traversal; setting the screen state here is immediate however, so it can happen @@ -93,7 +103,7 @@ public class DozeScreenState implements DozeMachine.Part { mPendingScreenState = screenState; // Delay screen state transitions even longer while animations are running. - boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD + boolean shouldDelayTransition = newState == DOZE_AOD && mParameters.shouldControlScreenOff() && !turningOn; if (shouldDelayTransition) { @@ -114,6 +124,8 @@ public class DozeScreenState implements DozeMachine.Part { } else if (DEBUG) { Log.d(TAG, "Pending display state change to " + screenState); } + } else if (turningOff) { + mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState)); } else { applyScreenState(screenState); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 67eefc5588e3..05a234f1e431 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -42,9 +42,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.systemui.Dependency; import com.android.systemui.plugins.SensorManagerPlugin; -import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.ProximitySensor; @@ -82,7 +80,8 @@ public class DozeSensors { public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager, DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, - Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) { + Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy, + DozeLog dozeLog) { mContext = context; mAlarmManager = alarmManager; mSensorManager = sensorManager; @@ -99,59 +98,69 @@ public class DozeSensors { mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION), null /* setting */, dozeParameters.getPulseOnSigMotion(), - DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */, - false /* touchscreen */), + DozeEvent.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */, + false /* touchscreen */, dozeLog), mPickupSensor = new TriggerSensor( mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE), Settings.Secure.DOZE_PICK_UP_GESTURE, true /* settingDef */, config.dozePickupSensorAvailable(), - DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */, + DozeEvent.REASON_SENSOR_PICKUP, false /* touchCoords */, false /* touchscreen */, - false /* ignoresSetting */), + false /* ignoresSetting */, + dozeLog), new TriggerSensor( findSensorWithType(config.doubleTapSensorType()), Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, true /* configured */, - DozeLog.REASON_SENSOR_DOUBLE_TAP, + DozeEvent.REASON_SENSOR_DOUBLE_TAP, dozeParameters.doubleTapReportsTouchCoordinates(), - true /* touchscreen */), + true /* touchscreen */, + dozeLog), new TriggerSensor( findSensorWithType(config.tapSensorType()), Settings.Secure.DOZE_TAP_SCREEN_GESTURE, true /* configured */, - DozeLog.REASON_SENSOR_TAP, + DozeEvent.REASON_SENSOR_TAP, false /* reports touch coordinates */, - true /* touchscreen */), + true /* touchscreen */, + dozeLog), new TriggerSensor( findSensorWithType(config.longPressSensorType()), Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, false /* settingDef */, true /* configured */, - DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, + DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS, true /* reports touch coordinates */, - true /* touchscreen */), + true /* touchscreen */, + dozeLog), new PluginSensor( new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY), Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, mConfig.wakeScreenGestureAvailable() && alwaysOn, - DozeLog.REASON_SENSOR_WAKE_UP, + DozeEvent.REASON_SENSOR_WAKE_UP, false /* reports touch coordinates */, - false /* touchscreen */), + false /* touchscreen */, + dozeLog), new PluginSensor( new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN), Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, mConfig.wakeScreenGestureAvailable(), - DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, + DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, false /* reports touch coordinates */, - false /* touchscreen */, mConfig.getWakeLockScreenDebounce()), + false /* touchscreen */, + mConfig.getWakeLockScreenDebounce(), + dozeLog), }; - mProximitySensor = new ProximitySensor( - context, sensorManager, Dependency.get(PluginManager.class)); + mProximitySensor = new ProximitySensor(context.getResources(), sensorManager); mProximitySensor.register( - proximityEvent -> mProxCallback.accept(!proximityEvent.getNear())); + proximityEvent -> { + if (proximityEvent != null) { + mProxCallback.accept(!proximityEvent.getNear()); + } + }); } /** @@ -305,23 +314,24 @@ public class DozeSensors { protected boolean mRegistered; protected boolean mDisabled; protected boolean mIgnoresSetting; + protected final DozeLog mDozeLog; public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason, - boolean reportsTouchCoordinates, boolean requiresTouchscreen) { + boolean reportsTouchCoordinates, boolean requiresTouchscreen, DozeLog dozeLog) { this(sensor, setting, true /* settingDef */, configured, pulseReason, - reportsTouchCoordinates, requiresTouchscreen); + reportsTouchCoordinates, requiresTouchscreen, dozeLog); } public TriggerSensor(Sensor sensor, String setting, boolean settingDef, boolean configured, int pulseReason, boolean reportsTouchCoordinates, - boolean requiresTouchscreen) { + boolean requiresTouchscreen, DozeLog dozeLog) { this(sensor, setting, settingDef, configured, pulseReason, reportsTouchCoordinates, - requiresTouchscreen, false /* ignoresSetting */); + requiresTouchscreen, false /* ignoresSetting */, dozeLog); } private TriggerSensor(Sensor sensor, String setting, boolean settingDef, boolean configured, int pulseReason, boolean reportsTouchCoordinates, - boolean requiresTouchscreen, boolean ignoresSetting) { + boolean requiresTouchscreen, boolean ignoresSetting, DozeLog dozeLog) { mSensor = sensor; mSetting = setting; mSettingDefault = settingDef; @@ -330,6 +340,7 @@ public class DozeSensors { mReportsTouchCoordinates = reportsTouchCoordinates; mRequiresTouchscreen = requiresTouchscreen; mIgnoresSetting = ignoresSetting; + mDozeLog = dozeLog; } public void setListening(boolean listen) { @@ -386,7 +397,7 @@ public class DozeSensors { @Override @AnyThread public void onTrigger(TriggerEvent event) { - DozeLog.traceSensor(mContext, mPulseReason); + mDozeLog.traceSensor(mPulseReason); mHandler.post(mWakeLock.wrap(() -> { if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event)); if (mSensor != null && mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) { @@ -442,16 +453,17 @@ public class DozeSensors { private long mDebounce; PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, - int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) { + int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, + DozeLog dozeLog) { this(sensor, setting, configured, pulseReason, reportsTouchCoordinates, - requiresTouchscreen, 0L /* debounce */); + requiresTouchscreen, 0L /* debounce */, dozeLog); } PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, - long debounce) { + long debounce, DozeLog dozeLog) { super(null, setting, configured, pulseReason, reportsTouchCoordinates, - requiresTouchscreen); + requiresTouchscreen, dozeLog); mPluginSensor = sensor; mDebounce = debounce; } @@ -497,7 +509,7 @@ public class DozeSensors { @Override public void onSensorChanged(SensorManagerPlugin.SensorEvent event) { - DozeLog.traceSensor(mContext, mPulseReason); + mDozeLog.traceSensor(mPulseReason); mHandler.post(mWakeLock.wrap(() -> { final long now = SystemClock.uptimeMillis(); if (now < mDebounceFrom + mDebounce) { @@ -514,7 +526,7 @@ public class DozeSensors { /** * Called when a sensor requests a pulse - * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP} + * @param pulseReason Requesting sensor, e.g. {@link DozeEvent#REASON_SENSOR_PICKUP} * @param screenX the location on the screen where the sensor fired or -1 * if the sensor doesn't support reporting screen locations. * @param screenY the location on the screen where the sensor fired or -1 diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index e92acfc7f219..08734d27ba55 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -22,10 +22,8 @@ import android.os.SystemClock; import android.service.dreams.DreamService; import android.util.Log; -import com.android.systemui.Dependency; import com.android.systemui.plugins.DozeServicePlugin; import com.android.systemui.plugins.DozeServicePlugin.RequestDoze; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; @@ -38,16 +36,17 @@ public class DozeService extends DreamService implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> { private static final String TAG = "DozeService"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final FalsingManager mFalsingManager; + private final DozeFactory mDozeFactory; private DozeMachine mDozeMachine; private DozeServicePlugin mDozePlugin; private PluginManager mPluginManager; @Inject - public DozeService(FalsingManager falsingManager) { + public DozeService(DozeFactory dozeFactory, PluginManager pluginManager) { setDebug(DEBUG); - mFalsingManager = falsingManager; + mDozeFactory = dozeFactory; + mPluginManager = pluginManager; } @Override @@ -60,9 +59,8 @@ public class DozeService extends DreamService finish(); return; } - mPluginManager = Dependency.get(PluginManager.class); mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */); - mDozeMachine = new DozeFactory().assembleMachine(this, mFalsingManager); + mDozeMachine = mDozeFactory.assembleMachine(this); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 80d4b631314a..b212884ebb98 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -67,12 +67,12 @@ public class DozeTriggers implements DozeMachine.Part { private final Context mContext; private final DozeMachine mMachine; + private final DozeLog mDozeLog; private final DozeSensors mDozeSensors; private final DozeHost mDozeHost; private final AmbientDisplayConfiguration mConfig; private final DozeParameters mDozeParameters; private final AsyncSensorManager mSensorManager; - private final Handler mHandler; private final WakeLock mWakeLock; private final boolean mAllowPulseTriggers; private final UiModeManager mUiModeManager; @@ -89,24 +89,24 @@ public class DozeTriggers implements DozeMachine.Part { public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost, AlarmManager alarmManager, AmbientDisplayConfiguration config, DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler, - WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager) { + WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager, + ProximitySensor proximitySensor, + DozeLog dozeLog) { mContext = context; mMachine = machine; mDozeHost = dozeHost; mConfig = config; mDozeParameters = dozeParameters; mSensorManager = sensorManager; - mHandler = handler; mWakeLock = wakeLock; mAllowPulseTriggers = allowPulseTriggers; mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters, config, wakeLock, this::onSensor, this::onProximityFar, - dozeParameters.getPolicy()); + dozeParameters.getPolicy(), dozeLog); mUiModeManager = mContext.getSystemService(UiModeManager.class); mDockManager = dockManager; - mProxCheck = new ProximitySensor.ProximityCheck( - new ProximitySensor(mContext, mSensorManager, null), - mHandler); + mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler); + mDozeLog = dozeLog; } private void onNotification(Runnable onPulseSuppressedListener) { @@ -116,18 +116,18 @@ public class DozeTriggers implements DozeMachine.Part { if (!sWakeDisplaySensorState) { Log.d(TAG, "Wake display false. Pulse denied."); runIfNotNull(onPulseSuppressedListener); - DozeLog.tracePulseDropped(mContext, "wakeDisplaySensor"); + mDozeLog.tracePulseDropped("wakeDisplaySensor"); return; } mNotificationPulseTime = SystemClock.elapsedRealtime(); if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) { runIfNotNull(onPulseSuppressedListener); - DozeLog.tracePulseDropped(mContext, "pulseOnNotificationsDisabled"); + mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled"); return; } - requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */, + requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */, onPulseSuppressedListener); - DozeLog.traceNotificationPulse(mContext); + mDozeLog.traceNotificationPulse(); } private static void runIfNotNull(Runnable runnable) { @@ -148,8 +148,7 @@ public class DozeTriggers implements DozeMachine.Part { final long start = SystemClock.uptimeMillis(); mProxCheck.check(PROXIMITY_TIMEOUT_DELAY_MS, near -> { final long end = SystemClock.uptimeMillis(); - DozeLog.traceProximityResult( - mContext, + mDozeLog.traceProximityResult( near == null ? false : near, end - start, reason); @@ -162,12 +161,12 @@ public class DozeTriggers implements DozeMachine.Part { @VisibleForTesting void onSensor(int pulseReason, float screenX, float screenY, float[] rawValues) { - boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP; - boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP; - boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP; - boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS; - boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP; - boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN; + boolean isDoubleTap = pulseReason == DozeEvent.REASON_SENSOR_DOUBLE_TAP; + boolean isTap = pulseReason == DozeEvent.REASON_SENSOR_TAP; + boolean isPickup = pulseReason == DozeEvent.REASON_SENSOR_PICKUP; + boolean isLongPress = pulseReason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS; + boolean isWakeDisplay = pulseReason == DozeEvent.REASON_SENSOR_WAKE_UP; + boolean isWakeLockScreen = pulseReason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN; boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0; if (isWakeDisplay) { @@ -182,7 +181,7 @@ public class DozeTriggers implements DozeMachine.Part { } } else { proximityCheckThenCall((result) -> { - if (result) { + if (result != null && result) { // In pocket, drop event. return; } @@ -204,7 +203,7 @@ public class DozeTriggers implements DozeMachine.Part { SystemClock.elapsedRealtime() - mNotificationPulseTime; final boolean withinVibrationThreshold = timeSinceNotification < mDozeParameters.getPickupVibrationThreshold(); - DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold); + mDozeLog.tracePickupWakeUp(withinVibrationThreshold); } } @@ -266,12 +265,12 @@ public class DozeTriggers implements DozeMachine.Part { * transitions. */ private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state) { - DozeLog.traceWakeDisplay(wake); + mDozeLog.traceWakeDisplay(wake); sWakeDisplaySensorState = wake; if (wake) { proximityCheckThenCall((result) -> { - if (result) { + if (result != null && result) { // In pocket, drop event. return; } @@ -280,9 +279,9 @@ public class DozeTriggers implements DozeMachine.Part { // Logs AOD open due to sensor wake up. mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING) .setType(MetricsEvent.TYPE_OPEN) - .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP)); + .setSubtype(DozeEvent.REASON_SENSOR_WAKE_UP)); } - }, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP); + }, true /* alreadyPerformedProxCheck */, DozeEvent.REASON_SENSOR_WAKE_UP); } else { boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED); boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING); @@ -291,7 +290,7 @@ public class DozeTriggers implements DozeMachine.Part { // Logs AOD close due to sensor wake up. mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING) .setType(MetricsEvent.TYPE_CLOSE) - .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP)); + .setSubtype(DozeEvent.REASON_SENSOR_WAKE_UP)); } } } @@ -349,7 +348,6 @@ public class DozeTriggers implements DozeMachine.Part { private void checkTriggersAtInit() { if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR - || mDozeHost.isPowerSaveActive() || mDozeHost.isBlockingDoze() || !mDozeHost.isProvisioned()) { mMachine.requestState(DozeMachine.State.FINISH); @@ -364,14 +362,14 @@ public class DozeTriggers implements DozeMachine.Part { // When already pulsing we're allowed to show the wallpaper directly without // requesting a new pulse. if (mMachine.getState() == DozeMachine.State.DOZE_PULSING - && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { + && reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { mMachine.requestState(DozeMachine.State.DOZE_PULSING_BRIGHT); return; } if (mPulsePending || !mAllowPulseTriggers || !canPulse()) { if (mAllowPulseTriggers) { - DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(), + mDozeLog.tracePulseDropped(mPulsePending, mMachine.getState(), mDozeHost.isPulsingBlocked()); } runIfNotNull(onPulseSuppressedListener); @@ -380,9 +378,9 @@ public class DozeTriggers implements DozeMachine.Part { mPulsePending = true; proximityCheckThenCall((result) -> { - if (result) { + if (result != null && result) { // in pocket, abort pulse - DozeLog.tracePulseDropped(mContext, "inPocket"); + mDozeLog.tracePulseDropped("inPocket"); mPulsePending = false; runIfNotNull(onPulseSuppressedListener); } else { @@ -404,7 +402,7 @@ public class DozeTriggers implements DozeMachine.Part { private void continuePulseRequest(int reason) { mPulsePending = false; if (mDozeHost.isPulsingBlocked() || !canPulse()) { - DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(), + mDozeLog.tracePulseDropped(mPulsePending, mMachine.getState(), mDozeHost.isPulsingBlocked()); return; } @@ -428,7 +426,7 @@ public class DozeTriggers implements DozeMachine.Part { public void onReceive(Context context, Intent intent) { if (PULSE_ACTION.equals(intent.getAction())) { if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent"); - requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */ + requestPulse(DozeEvent.PULSE_REASON_INTENT, false, /* performedProxCheck */ null /* onPulseSupressedListener */); } if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) { @@ -485,8 +483,8 @@ public class DozeTriggers implements DozeMachine.Part { @Override public void onPowerSaveChanged(boolean active) { - if (active) { - mMachine.requestState(DozeMachine.State.FINISH); + if (mDozeHost.isPowerSaveActive()) { + mMachine.requestState(DozeMachine.State.DOZE); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 1f33af8c3f55..f1557838fd73 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -49,6 +49,7 @@ public class DozeUi implements DozeMachine.Part { private final AlarmTimeout mTimeTicker; private final boolean mCanAnimateTransition; private final DozeParameters mDozeParameters; + private final DozeLog mDozeLog; private boolean mKeyguardShowing; private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback = @@ -65,7 +66,8 @@ public class DozeUi implements DozeMachine.Part { public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine, WakeLock wakeLock, DozeHost host, Handler handler, - DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor) { + DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor, + DozeLog dozeLog) { mContext = context; mMachine = machine; mWakeLock = wakeLock; @@ -75,6 +77,7 @@ public class DozeUi implements DozeMachine.Part { mDozeParameters = params; mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler); keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback); + mDozeLog = dozeLog; } /** @@ -83,7 +86,8 @@ public class DozeUi implements DozeMachine.Part { */ private void updateAnimateScreenOff() { if (mCanAnimateTransition) { - final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing; + final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing + && !mHost.isPowerSaveActive(); mDozeParameters.setControlScreenOffAnimation(controlScreenOff); mHost.setAnimateScreenOff(controlScreenOff); } @@ -96,7 +100,7 @@ public class DozeUi implements DozeMachine.Part { public void onPulseStarted() { try { mMachine.requestState( - reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN + reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN ? DozeMachine.State.DOZE_PULSING_BRIGHT : DozeMachine.State.DOZE_PULSING); } catch (IllegalStateException e) { @@ -131,7 +135,6 @@ public class DozeUi implements DozeMachine.Part { break; case DOZE: case DOZE_AOD_PAUSED: - mHost.prepareForGentleWakeUp(); unscheduleTimeTick(); break; case DOZE_REQUEST_PULSE: @@ -175,7 +178,7 @@ public class DozeUi implements DozeMachine.Part { long delta = roundToNextMinute(time) - System.currentTimeMillis(); boolean scheduled = mTimeTicker.schedule(delta, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); if (scheduled) { - DozeLog.traceTimeTickScheduled(time, time + delta); + mDozeLog.traceTimeTickScheduled(time, time + delta); } mLastTimeTickElapsed = SystemClock.elapsedRealtime(); } @@ -192,7 +195,7 @@ public class DozeUi implements DozeMachine.Part { long millisSinceLastTick = SystemClock.elapsedRealtime() - mLastTimeTickElapsed; if (millisSinceLastTick > TIME_TICK_DEADLINE_MILLIS) { String delay = Formatter.formatShortElapsedTime(mContext, millisSinceLastTick); - DozeLog.traceMissedTick(delay); + mDozeLog.traceMissedTick(delay); Log.e(DozeMachine.TAG, "Missed AOD time tick by " + delay); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java index 35c8b741381c..9457dc9e580b 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java @@ -17,12 +17,9 @@ package com.android.systemui.doze; import android.app.IWallpaperManager; -import android.content.Context; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; @@ -42,17 +39,10 @@ public class DozeWallpaperState implements DozeMachine.Part { private final BiometricUnlockController mBiometricUnlockController; private boolean mIsAmbientMode; - public DozeWallpaperState(Context context, - BiometricUnlockController biometricUnlockController) { - this(IWallpaperManager.Stub.asInterface( - ServiceManager.getService(Context.WALLPAPER_SERVICE)), - biometricUnlockController, - DozeParameters.getInstance(context)); - } - - @VisibleForTesting - DozeWallpaperState(IWallpaperManager wallpaperManagerService, - BiometricUnlockController biometricUnlockController, DozeParameters parameters) { + public DozeWallpaperState( + IWallpaperManager wallpaperManagerService, + BiometricUnlockController biometricUnlockController, + DozeParameters parameters) { mWallpaperManagerService = wallpaperManagerService; mBiometricUnlockController = biometricUnlockController; mDozeParameters = parameters; diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java index b4cc571be061..1b4857ebb209 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java @@ -22,7 +22,7 @@ import android.view.View; import com.android.systemui.ConfigurationChangedReceiver; import com.android.systemui.Dumpable; -import com.android.systemui.SystemUIRootComponent; +import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.qs.QSFragment; import com.android.systemui.statusbar.phone.NavigationBarFragment; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java index e8ef454bd466..c11127d1dc0b 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java @@ -35,6 +35,10 @@ public class GlobalActionsComponent extends SystemUI implements Callbacks, Globa private Extension<GlobalActions> mExtension; private IStatusBarService mBarService; + public GlobalActionsComponent(Context context) { + super(context); + } + @Override public void start() { mBarService = IStatusBarService.Stub.asInterface( diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index c9c6a0c6868b..22846bc02a38 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -90,14 +90,15 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.MultiListLayout; import com.android.systemui.MultiListLayout.MultiListAdapter; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarWindowController; -import com.android.systemui.statusbar.phone.UnlockMethodCache; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.EmergencyDialerConstants; import com.android.systemui.util.leak.RotationUtils; import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator; @@ -187,7 +188,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); - context.registerReceiver(mBroadcastReceiver, filter); + Dependency.get(BroadcastDispatcher.class).registerReceiver(mBroadcastReceiver, filter); ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); @@ -213,14 +214,17 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, Dependency.get(ConfigurationController.class).addCallback(this); mActivityStarter = Dependency.get(ActivityStarter.class); - UnlockMethodCache unlockMethodCache = UnlockMethodCache.getInstance(context); - unlockMethodCache.addListener( - () -> { - if (mDialog != null && mDialog.mPanelController != null) { - boolean locked = !unlockMethodCache.canSkipBouncer(); - mDialog.mPanelController.onDeviceLockStateChanged(locked); - } - }); + KeyguardStateController keyguardStateController = + Dependency.get(KeyguardStateController.class); + keyguardStateController.addCallback(new KeyguardStateController.Callback() { + @Override + public void onUnlockedChanged() { + if (mDialog != null && mDialog.mPanelController != null) { + boolean locked = !keyguardStateController.canDismissLockScreen(); + mDialog.mPanelController.onDeviceLockStateChanged(locked); + } + } + }); } /** @@ -665,8 +669,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, // Take an "interactive" bugreport. MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE); - ActivityManager.getService().requestBugReport( - ActivityManager.BUGREPORT_OPTION_INTERACTIVE); + ActivityManager.getService().requestInteractiveBugReport(); } catch (RemoteException e) { } } @@ -683,8 +686,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, try { // Take a "full" bugreport. MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL); - ActivityManager.getService().requestBugReport( - ActivityManager.BUGREPORT_OPTION_FULL); + ActivityManager.getService().requestFullBugReport(); } catch (RemoteException e) { } return false; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 6f8665a87dee..b9fe827bb1c9 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -40,14 +40,14 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks { private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f; private final Context mContext; - private final KeyguardMonitor mKeyguardMonitor; + private final KeyguardStateController mKeyguardStateController; private final DeviceProvisionedController mDeviceProvisionedController; private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension; private GlobalActionsDialog mGlobalActions; @@ -55,7 +55,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks public GlobalActionsImpl(Context context) { mContext = context; - mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + mKeyguardStateController = Dependency.get(KeyguardStateController.class); mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallback(this); mPanelExtension = Dependency.get(ExtensionController.class) @@ -79,7 +79,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks if (mGlobalActions == null) { mGlobalActions = new GlobalActionsDialog(mContext, manager); } - mGlobalActions.showDialog(mKeyguardMonitor.isShowing(), + mGlobalActions.showDialog(mKeyguardStateController.isShowing(), mDeviceProvisionedController.isDeviceProvisioned(), mPanelExtension.get()); Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth(); diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java index aac721e3cb56..c6812a75b3bf 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java @@ -22,6 +22,7 @@ import static android.opengl.EGL14.EGL_CONFIG_CAVEAT; import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION; import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY; import static android.opengl.EGL14.EGL_DEPTH_SIZE; +import static android.opengl.EGL14.EGL_EXTENSIONS; import static android.opengl.EGL14.EGL_GREEN_SIZE; import static android.opengl.EGL14.EGL_NONE; import static android.opengl.EGL14.EGL_NO_CONTEXT; @@ -41,6 +42,7 @@ import static android.opengl.EGL14.eglGetDisplay; import static android.opengl.EGL14.eglGetError; import static android.opengl.EGL14.eglInitialize; import static android.opengl.EGL14.eglMakeCurrent; +import static android.opengl.EGL14.eglQueryString; import static android.opengl.EGL14.eglSwapBuffers; import static android.opengl.EGL14.eglTerminate; @@ -63,6 +65,8 @@ public class EglHelper { // Below two constants make drawing at low priority, so other things can preempt our drawing. private static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100; private static final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103; + private static final boolean DEBUG = true; + private static final String EGL_IMG_CONTEXT_PRIORITY = "EGL_IMG_context_priority"; private EGLDisplay mEglDisplay; private EGLConfig mEglConfig; @@ -70,6 +74,7 @@ public class EglHelper { private EGLSurface mEglSurface; private final int[] mEglVersion = new int[2]; private boolean mEglReady; + private boolean mContextPrioritySupported; /** * Initialize EGL and prepare EglSurface. @@ -105,10 +110,22 @@ public class EglHelper { return false; } + mContextPrioritySupported = isContextPrioritySuppported(); + mEglReady = true; return true; } + private boolean isContextPrioritySuppported() { + String[] extensions = eglQueryString(mEglDisplay, EGL_EXTENSIONS).split(" "); + for (String extension : extensions) { + if (extension.equals(EGL_IMG_CONTEXT_PRIORITY)) { + return true; + } + } + return false; + } + private EGLConfig chooseEglConfig() { int[] configsCount = new int[1]; EGLConfig[] configs = new EGLConfig[1]; @@ -146,6 +163,10 @@ public class EglHelper { * @return true if EglSurface is ready. */ public boolean createEglSurface(SurfaceHolder surfaceHolder) { + if (DEBUG) { + Log.d(TAG, "createEglSurface start"); + } + if (hasEglDisplay()) { mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0); } else { @@ -163,6 +184,9 @@ public class EglHelper { return false; } + if (DEBUG) { + Log.d(TAG, "createEglSurface done"); + } return true; } @@ -190,8 +214,19 @@ public class EglHelper { * @return true if EglContext is ready. */ public boolean createEglContext() { - int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_NONE}; + if (DEBUG) { + Log.d(TAG, "createEglContext start"); + } + + int[] attrib_list = new int[5]; + int idx = 0; + attrib_list[idx++] = EGL_CONTEXT_CLIENT_VERSION; + attrib_list[idx++] = 2; + if (mContextPrioritySupported) { + attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; + attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LOW_IMG; + } + attrib_list[idx++] = EGL_NONE; if (hasEglDisplay()) { mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0); } else { @@ -203,6 +238,10 @@ public class EglHelper { Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError())); return false; } + + if (DEBUG) { + Log.d(TAG, "createEglContext done"); + } return true; } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java index b154e66a846e..99c55f13a8a3 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java @@ -19,6 +19,7 @@ package com.android.systemui.glwallpaper; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.util.Log; import com.android.systemui.Interpolators; @@ -30,6 +31,7 @@ class ImageRevealHelper { private static final String TAG = ImageRevealHelper.class.getSimpleName(); private static final float MAX_REVEAL = 0f; private static final float MIN_REVEAL = 1f; + private static final boolean DEBUG = true; private final ValueAnimator mAnimator; private final RevealStateListener mRevealListener; @@ -57,6 +59,9 @@ class ImageRevealHelper { @Override public void onAnimationEnd(Animator animation) { if (!mIsCanceled && mRevealListener != null) { + if (DEBUG) { + Log.d(TAG, "transition end"); + } mRevealListener.onRevealEnd(); } mIsCanceled = false; @@ -65,6 +70,9 @@ class ImageRevealHelper { @Override public void onAnimationStart(Animator animation) { if (mRevealListener != null) { + if (DEBUG) { + Log.d(TAG, "transition start"); + } mRevealListener.onRevealStart(true /* animate */); } } @@ -82,6 +90,9 @@ class ImageRevealHelper { } void updateAwake(boolean awake, long duration) { + if (DEBUG) { + Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration); + } mAwake = awake; mAnimator.setDuration(duration); if (duration == 0) { diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 29606347f009..a8371e31d71c 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -46,6 +46,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, private static final String TAG = ImageWallpaperRenderer.class.getSimpleName(); private static final float SCALE_VIEWPORT_MIN = 1f; private static final float SCALE_VIEWPORT_MAX = 1.1f; + private static final boolean DEBUG = true; private final WallpaperManager mWallpaperManager; private final ImageGLProgram mProgram; @@ -107,6 +108,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, } private boolean loadBitmap() { + if (DEBUG) { + Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap); + } if (mWallpaperManager != null && mBitmap == null) { mBitmap = mWallpaperManager.getBitmap(); mWallpaperManager.forgetLoadedWallpaper(); @@ -119,6 +123,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight); } } + if (DEBUG) { + Log.d(TAG, "loadBitmap done"); + } return mBitmap != null; } @@ -223,6 +230,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, out.print(prefix); out.print("mXOffset="); out.print(mXOffset); out.print(prefix); out.print("mYOffset="); out.print(mYOffset); out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold()); + out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal()); mWallpaper.dump(prefix, fd, out, args); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java index 7d52a9a08c2a..42f455af7df4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java +++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java @@ -117,6 +117,10 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha private int mState; + public KeyboardUI(Context context) { + super(context); + } + @Override public void start() { mContext = super.mContext; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java index b3481c52d7f2..4a33590f64d2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java @@ -19,9 +19,13 @@ package com.android.systemui.keyguard; import android.os.Handler; import android.os.Message; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Dispatches the lifecycles keyguard gets from WindowManager on the main thread. */ +@Singleton public class KeyguardLifecyclesDispatcher { static final int SCREEN_TURNING_ON = 0; @@ -37,6 +41,7 @@ public class KeyguardLifecyclesDispatcher { private final ScreenLifecycle mScreenLifecycle; private final WakefulnessLifecycle mWakefulnessLifecycle; + @Inject public KeyguardLifecyclesDispatcher(ScreenLifecycle screenLifecycle, WakefulnessLifecycle wakefulnessLifecycle) { mScreenLifecycle = screenLifecycle; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 81247cd2f727..9f4056fdf65b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -33,25 +33,30 @@ import com.android.internal.policy.IKeyguardDrawnCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardService; import com.android.internal.policy.IKeyguardStateCallback; -import com.android.systemui.Dependency; import com.android.systemui.SystemUIApplication; +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton public class KeyguardService extends Service { static final String TAG = "KeyguardService"; static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD; - private KeyguardViewMediator mKeyguardViewMediator; - private KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher; + private final KeyguardViewMediator mKeyguardViewMediator; + private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher; + + @Inject + public KeyguardService(KeyguardViewMediator keyguardViewMediator, + KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher) { + super(); + mKeyguardViewMediator = keyguardViewMediator; + mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; + } @Override public void onCreate() { ((SystemUIApplication) getApplication()).startServicesIfNeeded(); - mKeyguardViewMediator = - ((SystemUIApplication) getApplication()).getComponent(KeyguardViewMediator.class); - mKeyguardLifecyclesDispatcher = new KeyguardLifecyclesDispatcher( - Dependency.get(ScreenLifecycle.class), - Dependency.get(WakefulnessLifecycle.class)); - } @Override diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 1d2de6027d76..e66a9fadb937 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -60,7 +60,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -107,8 +106,8 @@ public class KeyguardSliceProvider extends SliceProvider implements protected final Uri mMediaUri; private final Date mCurrentTime = new Date(); private final Handler mHandler; + private final Handler mMediaHandler; private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm; - private final Object mMediaToken = new Object(); private DozeParameters mDozeParameters; @VisibleForTesting protected SettableWakeLock mMediaWakeLock; @@ -174,17 +173,13 @@ public class KeyguardSliceProvider extends SliceProvider implements } }; - public KeyguardSliceProvider() { - this(new Handler()); - } - public static KeyguardSliceProvider getAttachedInstance() { return KeyguardSliceProvider.sInstance; } - @VisibleForTesting - KeyguardSliceProvider(Handler handler) { - mHandler = handler; + public KeyguardSliceProvider() { + mHandler = new Handler(); + mMediaHandler = new Handler(); mSliceUri = Uri.parse(KEYGUARD_SLICE_URI); mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI); mDateUri = Uri.parse(KEYGUARD_DATE_URI); @@ -328,7 +323,7 @@ public class KeyguardSliceProvider extends SliceProvider implements mContentResolver = getContext().getContentResolver(); mNextAlarmController = new NextAlarmControllerImpl(getContext()); mNextAlarmController.addCallback(this); - mZenModeController = new ZenModeControllerImpl(getContext(), mHandler); + mZenModeController = Dependency.get(ZenModeController.class); mZenModeController.addCallback(this); mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern); mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0); @@ -470,16 +465,18 @@ public class KeyguardSliceProvider extends SliceProvider implements public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) { synchronized (this) { boolean nextVisible = NotificationMediaManager.isPlayingState(state); - mHandler.removeCallbacksAndMessages(mMediaToken); + mMediaHandler.removeCallbacksAndMessages(null); if (mMediaIsVisible && !nextVisible && mStatusBarState != StatusBarState.SHADE) { // We need to delay this event for a few millis when stopping to avoid jank in the // animation. The media app might not send its update when buffering, and the slice // would end up without a header for 0.5 second. mMediaWakeLock.setAcquired(true); - mHandler.postDelayed(() -> { - updateMediaStateLocked(metadata, state); - mMediaWakeLock.setAcquired(false); - }, mMediaToken, 2000); + mMediaHandler.postDelayed(() -> { + synchronized (this) { + updateMediaStateLocked(metadata, state); + mMediaWakeLock.setAcquired(false); + } + }, 2000); } else { mMediaWakeLock.setAcquired(false); updateMediaStateLocked(metadata, state); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 7fb6909be724..800d021c4ce7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -68,6 +68,7 @@ import android.view.WindowManagerPolicyConstants; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IKeyguardDrawnCallback; import com.android.internal.policy.IKeyguardExitCallback; @@ -100,6 +101,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; +import javax.inject.Singleton; /** * Mediates requests related to the keyguard. This includes queries about the @@ -142,6 +144,7 @@ import javax.inject.Inject; * directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI * thread of the keyguard. */ +@Singleton public class KeyguardViewMediator extends SystemUI { private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000; private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000; @@ -232,7 +235,7 @@ public class KeyguardViewMediator extends SystemUI { */ private PowerManager.WakeLock mShowKeyguardWakeLock; - private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; // these are protected by synchronized (this) @@ -315,7 +318,7 @@ public class KeyguardViewMediator extends SystemUI { * the keyguard. */ private boolean mWaitingUntilKeyguardVisible = false; - private LockPatternUtils mLockPatternUtils; + private final LockPatternUtils mLockPatternUtils; private boolean mKeyguardDonePending = false; private boolean mHideAnimationRun = false; private boolean mHideAnimationRunning = false; @@ -648,29 +651,26 @@ public class KeyguardViewMediator extends SystemUI { @Override public int getBouncerPromptReason() { - // TODO(b/140053364) - return whitelistIpcs(() -> { - int currentUser = ActivityManager.getCurrentUser(); - boolean trust = mTrustManager.isTrustUsuallyManaged(currentUser); - boolean biometrics = mUpdateMonitor.isUnlockingWithBiometricsPossible(currentUser); - boolean any = trust || biometrics; - KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker = - mUpdateMonitor.getStrongAuthTracker(); - int strongAuth = strongAuthTracker.getStrongAuthForUser(currentUser); - - if (any && !strongAuthTracker.hasUserAuthenticatedSinceBoot()) { - return KeyguardSecurityView.PROMPT_REASON_RESTART; - } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) != 0) { - return KeyguardSecurityView.PROMPT_REASON_TIMEOUT; - } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != 0) { - return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; - } else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) { - return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; - } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) { - return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT; - } - return KeyguardSecurityView.PROMPT_REASON_NONE; - }); + int currentUser = KeyguardUpdateMonitor.getCurrentUser(); + boolean trust = mUpdateMonitor.isTrustUsuallyManaged(currentUser); + boolean biometrics = mUpdateMonitor.isUnlockingWithBiometricsPossible(currentUser); + boolean any = trust || biometrics; + KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker = + mUpdateMonitor.getStrongAuthTracker(); + int strongAuth = strongAuthTracker.getStrongAuthForUser(currentUser); + + if (any && !strongAuthTracker.hasUserAuthenticatedSinceBoot()) { + return KeyguardSecurityView.PROMPT_REASON_RESTART; + } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) != 0) { + return KeyguardSecurityView.PROMPT_REASON_TIMEOUT; + } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != 0) { + return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; + } else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) { + return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; + } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) { + return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT; + } + return KeyguardSecurityView.PROMPT_REASON_NONE; } @Override @@ -682,10 +682,26 @@ public class KeyguardViewMediator extends SystemUI { }; @Inject - public KeyguardViewMediator(FalsingManager falsingManager) { - super(); - + public KeyguardViewMediator( + Context context, + FalsingManager falsingManager, + LockPatternUtils lockPatternUtils) { + this(context, falsingManager, lockPatternUtils, SystemUIFactory.getInstance()); + } + + @VisibleForTesting + KeyguardViewMediator( + Context context, + FalsingManager falsingManager, + LockPatternUtils lockPatternUtils, + SystemUIFactory systemUIFactory) { + super(context); mFalsingManager = falsingManager; + mLockPatternUtils = lockPatternUtils; + mStatusBarKeyguardViewManager = systemUIFactory.createStatusBarKeyguardViewManager( + mContext, + mViewMediatorCallback, + mLockPatternUtils); } public void userActivity() { @@ -699,7 +715,7 @@ public class KeyguardViewMediator extends SystemUI { private void setupLocked() { mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); + mTrustManager = mContext.getSystemService(TrustManager.class); mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard"); mShowKeyguardWakeLock.setReferenceCounted(false); @@ -723,7 +739,6 @@ public class KeyguardViewMediator extends SystemUI { mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - mLockPatternUtils = new LockPatternUtils(mContext); KeyguardUpdateMonitor.setCurrentUser(ActivityManager.getCurrentUser()); // Assume keyguard is showing (unless it's disabled) until we know for sure, unless Keyguard @@ -737,9 +752,6 @@ public class KeyguardViewMediator extends SystemUI { setShowingLocked(false /* showing */, true /* forceCallbacks */); } - mStatusBarKeyguardViewManager = - SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext, - mViewMediatorCallback, mLockPatternUtils); final ContentResolver cr = mContext.getContentResolver(); mDeviceInteractive = mPM.isInteractive(); @@ -789,7 +801,6 @@ public class KeyguardViewMediator extends SystemUI { synchronized (this) { setupLocked(); } - putComponent(KeyguardViewMediator.class, this); } /** @@ -822,6 +833,11 @@ public class KeyguardViewMediator extends SystemUI { mDeviceInteractive = false; mGoingToSleep = true; + // Reset keyguard going away state so we can start listening for fingerprint. We + // explicitly DO NOT want to call mStatusBarWindowController.setKeyguardGoingAway(false) + // here, since that will mess with the device lock state. + mUpdateMonitor.setKeyguardGoingAway(false); + // Lock immediately based on setting if secure (user has a pin/pattern/password). // This also "locks" the device when not secure to provide easy access to the // camera while preventing unwanted input. @@ -1569,12 +1585,14 @@ public class KeyguardViewMediator extends SystemUI { handleNotifyFinishedGoingToSleep(); break; case NOTIFY_SCREEN_TURNING_ON: - Trace.beginSection("KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNING_ON"); + Trace.beginSection( + "KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNING_ON"); handleNotifyScreenTurningOn((IKeyguardDrawnCallback) msg.obj); Trace.endSection(); break; case NOTIFY_SCREEN_TURNED_ON: - Trace.beginSection("KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNED_ON"); + Trace.beginSection( + "KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNED_ON"); handleNotifyScreenTurnedOn(); Trace.endSection(); break; @@ -1582,7 +1600,8 @@ public class KeyguardViewMediator extends SystemUI { handleNotifyScreenTurnedOff(); break; case NOTIFY_STARTED_WAKING_UP: - Trace.beginSection("KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP"); + Trace.beginSection( + "KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP"); handleNotifyStartedWakingUp(); Trace.endSection(); break; @@ -1611,14 +1630,16 @@ public class KeyguardViewMediator extends SystemUI { handleDismiss(message.getCallback(), message.getMessage()); break; case START_KEYGUARD_EXIT_ANIM: - Trace.beginSection("KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM"); + Trace.beginSection( + "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM"); StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj; handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration); mFalsingManager.onSucccessfulUnlock(); Trace.endSection(); break; case KEYGUARD_DONE_PENDING_TIMEOUT: - Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE_PENDING_TIMEOUT"); + Trace.beginSection("KeyguardViewMediator#handleMessage" + + " KEYGUARD_DONE_PENDING_TIMEOUT"); Log.w(TAG, "Timeout while waiting for activity drawn!"); Trace.endSection(); break; @@ -1796,8 +1817,8 @@ public class KeyguardViewMediator extends SystemUI { mHideAnimationRun = false; adjustStatusBarLocked(); userActivity(); - mUpdateMonitor.setKeyguardGoingAway(false /* away */); - mStatusBarWindowController.setKeyguardGoingAway(false /* goingAway */); + mUpdateMonitor.setKeyguardGoingAway(false); + mStatusBarWindowController.setKeyguardGoingAway(false); mShowKeyguardWakeLock.release(); } mKeyguardDisplayManager.show(); @@ -1829,8 +1850,8 @@ public class KeyguardViewMediator extends SystemUI { .KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS; } - mUpdateMonitor.setKeyguardGoingAway(true /* goingAway */); - mStatusBarWindowController.setKeyguardGoingAway(true /* goingAway */); + mUpdateMonitor.setKeyguardGoingAway(true); + mStatusBarWindowController.setKeyguardGoingAway(true); // Don't actually hide the Keyguard at the moment, wait for window // manager until it tells us it's safe to do so with diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java index a9fe54bae19d..4d061e18ebba 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java @@ -49,6 +49,12 @@ public class WorkLockActivity extends Activity { private static final String TAG = "WorkLockActivity"; /** + * Add additional extra {@link com.android.settings.password.ConfirmDeviceCredentialActivity} to + * enable device policy management enforcement from systemui. + */ + public static final String EXTRA_FROM_WORK_LOCK_ACTIVITY = "from_work_lock_activity"; + + /** * Contains a {@link TaskDescription} for the activity being covered. */ static final String EXTRA_TASK_DESCRIPTION = @@ -151,6 +157,7 @@ public class WorkLockActivity extends Activity { if (target != null) { credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); + credential.putExtra(EXTRA_FROM_WORK_LOCK_ACTIVITY, true); } startActivityForResult(credential, REQUEST_CODE_CONFIRM_CREDENTIALS); diff --git a/packages/SystemUI/src/com/android/systemui/log/Event.java b/packages/SystemUI/src/com/android/systemui/log/Event.java new file mode 100644 index 000000000000..92862a2bc74c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/Event.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 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.log; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Stores information about an event that occurred in SystemUI to be used for debugging and triage. + * Every event has a time stamp, log level and message. + * Events are stored in {@link SysuiLog} and can be printed in a dumpsys. + */ +public class Event { + public static final int UNINITIALIZED = -1; + + @IntDef({ERROR, WARN, INFO, DEBUG, VERBOSE}) + @Retention(RetentionPolicy.SOURCE) + public @interface Level {} + public static final int VERBOSE = 2; + public static final int DEBUG = 3; + public static final int INFO = 4; + public static final int WARN = 5; + public static final int ERROR = 6; + + private long mTimestamp; + private @Level int mLogLevel = DEBUG; + protected String mMessage; + + public Event(String message) { + mTimestamp = System.currentTimeMillis(); + mMessage = message; + } + + public Event(@Level int logLevel, String message) { + mTimestamp = System.currentTimeMillis(); + mLogLevel = logLevel; + mMessage = message; + } + + public String getMessage() { + return mMessage; + } + + public long getTimestamp() { + return mTimestamp; + } + + public @Level int getLogLevel() { + return mLogLevel; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java new file mode 100644 index 000000000000..89b7a8181c44 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2019 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.log; + +/** + * Stores information about an event that occurred in SystemUI to be used for debugging and triage. + * Every rich event has a time stamp, event type, and log level, with the option to provide the + * reason this event was triggered. + * Events are stored in {@link SysuiLog} and can be printed in a dumpsys. + */ +public abstract class RichEvent extends Event { + private final int mType; + private final String mReason; + + /** + * Create a rich event that includes an event type that matches with an index in the array + * getEventLabels(). + */ + public RichEvent(@Event.Level int logLevel, int type, String reason) { + super(logLevel, null); + final int numEvents = getEventLabels().length; + if (type < 0 || type >= numEvents) { + throw new IllegalArgumentException("Unsupported event type. Events only supported" + + " from 0 to " + (numEvents - 1) + ", but given type=" + type); + } + mType = type; + mReason = reason; + mMessage = getEventLabels()[mType] + " " + mReason; + } + + /** + * Returns an array of the event labels. The index represents the event type and the + * corresponding String stored at that index is the user-readable representation of that event. + * @return array of user readable events, where the index represents its event type constant + */ + public abstract String[] getEventLabels(); + + public int getType() { + return mType; + } + + public String getReason() { + return mReason; + } + + /** + * Builder to build a RichEvent. + * @param <B> Log specific builder that is extending this builder + */ + public abstract static class Builder<B extends Builder<B>> { + public static final int UNINITIALIZED = -1; + + private B mBuilder = getBuilder(); + protected int mType = UNINITIALIZED; + protected String mReason; + protected @Level int mLogLevel; + + /** + * Get the log-specific builder. + */ + public abstract B getBuilder(); + + /** + * Build the log-specific event. + */ + public abstract RichEvent build(); + + /** + * Optional - set the log level. Defaults to DEBUG. + */ + public B setLogLevel(@Level int logLevel) { + mLogLevel = logLevel; + return mBuilder; + } + + /** + * Required - set the event type. These events must correspond with the events from + * getEventLabels(). + */ + public B setType(int type) { + mType = type; + return mBuilder; + } + + /** + * Optional - set the reason why this event was triggered. + */ + public B setReason(String reason) { + mReason = reason; + return mBuilder; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java new file mode 100644 index 000000000000..a6e10e6b345b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2019 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.log; + +import android.os.Build; +import android.os.SystemProperties; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.DumpController; +import com.android.systemui.Dumpable; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayDeque; +import java.util.Locale; + +/** + * Thread-safe logger in SystemUI which prints logs to logcat and stores logs to be + * printed by the DumpController. This is an alternative to printing directly + * to avoid logs being deleted by chatty. The number of logs retained is varied based on + * whether the build is {@link Build.IS_DEBUGGABLE}. + * + * To manually view the logs via adb: + * adb shell dumpsys activity service com.android.systemui/.SystemUIService \ + * dependency DumpController <SysuiLogId> + */ +public class SysuiLog implements Dumpable { + public static final SimpleDateFormat DATE_FORMAT = + new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US); + + private final Object mDataLock = new Object(); + private final String mId; + private final int mMaxLogs; + private boolean mEnabled; + + @VisibleForTesting protected ArrayDeque<Event> mTimeline; + + /** + * Creates a SysuiLog + * To enable or disable logs, set the system property and then restart the device: + * adb shell setprop sysui.log.enabled.<id> true/false && adb reboot + * @param dumpController where to register this logger's dumpsys + * @param id user-readable tag for this logger + * @param maxDebugLogs maximum number of logs to retain when {@link sDebuggable} is true + * @param maxLogs maximum number of logs to retain when {@link sDebuggable} is false + */ + public SysuiLog(DumpController dumpController, String id, int maxDebugLogs, int maxLogs) { + this(dumpController, id, sDebuggable ? maxDebugLogs : maxLogs, + SystemProperties.getBoolean(SYSPROP_ENABLED_PREFIX + id, DEFAULT_ENABLED)); + } + + @VisibleForTesting + protected SysuiLog(DumpController dumpController, String id, int maxLogs, boolean enabled) { + mId = id; + mMaxLogs = maxLogs; + mEnabled = enabled; + mTimeline = mEnabled ? new ArrayDeque<>(mMaxLogs) : null; + dumpController.registerDumpable(mId, this); + } + + public SysuiLog(DumpController dumpController, String id) { + this(dumpController, id, DEFAULT_MAX_DEBUG_LOGS, DEFAULT_MAX_LOGS); + } + + /** + * Logs an event to the timeline which can be printed by the dumpsys. + * May also log to logcat if enabled. + * @return true if event was logged, else false + */ + public boolean log(Event event) { + if (!mEnabled) { + return false; + } + + synchronized (mDataLock) { + if (mTimeline.size() >= mMaxLogs) { + mTimeline.removeFirst(); + } + + mTimeline.add(event); + } + + if (LOG_TO_LOGCAT_ENABLED) { + final String strEvent = eventToString(event); + switch (event.getLogLevel()) { + case Event.VERBOSE: + Log.v(mId, strEvent); + break; + case Event.DEBUG: + Log.d(mId, strEvent); + break; + case Event.ERROR: + Log.e(mId, strEvent); + break; + case Event.INFO: + Log.i(mId, strEvent); + break; + case Event.WARN: + Log.w(mId, strEvent); + break; + } + } + return true; + } + + /** + * @return user-readable string of the given event + */ + public String eventToString(Event event) { + StringBuilder sb = new StringBuilder(); + sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp())); + sb.append(" "); + sb.append(event.getMessage()); + return sb.toString(); + } + + /** + * only call on this method if you have the mDataLock + */ + private void dumpTimelineLocked(PrintWriter pw) { + pw.println("\tTimeline:"); + + for (Event event : mTimeline) { + pw.println("\t" + eventToString(event)); + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(mId + ":"); + + if (mEnabled) { + synchronized (mDataLock) { + dumpTimelineLocked(pw); + } + } else { + pw.print(" - Logging disabled."); + } + } + + private static boolean sDebuggable = Build.IS_DEBUGGABLE; + private static final String SYSPROP_ENABLED_PREFIX = "sysui.log.enabled."; + private static final boolean LOG_TO_LOGCAT_ENABLED = sDebuggable; + private static final boolean DEFAULT_ENABLED = sDebuggable; + private static final int DEFAULT_MAX_DEBUG_LOGS = 100; + private static final int DEFAULT_MAX_LOGS = 50; +} diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java index aebadf936e0c..4c96de232810 100644 --- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java @@ -59,6 +59,10 @@ public class RingtonePlayer extends SystemUI { private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG); private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>(); + public RingtonePlayer(Context context) { + super(context); + } + @Override public void start() { mAsyncPlayer.setUsesWakeLock(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java index e0fc31bc70b8..05be4259dd3b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java @@ -27,5 +27,6 @@ public interface BasePipManager { default void expandPip() {} default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {} void onConfigurationChanged(Configuration newConfig); + default void setShelfHeight(boolean visible, int height) {} default void dump(PrintWriter pw) {} } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index 6795bff6409a..686e7db86c3b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -55,6 +55,12 @@ public class PipBoundsHandler { private final Rect mTmpInsets = new Rect(); private final Point mTmpDisplaySize = new Point(); + /** + * Tracks the destination bounds, used for any following + * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} calculations. + */ + private final Rect mLastDestinationBounds = new Rect(); + private IPinnedStackController mPinnedStackController; private ComponentName mLastPipComponentName; private float mReentrySnapFraction = INVALID_SNAP_FRACTION; @@ -120,19 +126,26 @@ public class PipBoundsHandler { } /** - * Responds to IPinnedStackListener on IME visibility change. + * Sets both shelf visibility and its height if applicable. + * @return {@code true} if the internal shelf state is changed, {@code false} otherwise. */ - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mIsImeShowing = imeVisible; - mImeHeight = imeHeight; + public boolean setShelfHeight(boolean shelfVisible, int shelfHeight) { + final boolean shelfShowing = shelfVisible && shelfHeight > 0; + if (shelfShowing == mIsShelfShowing && shelfHeight == mShelfHeight) { + return false; + } + + mIsShelfShowing = shelfVisible; + mShelfHeight = shelfHeight; + return true; } /** - * Responds to IPinnedStackListener on shelf visibility change. + * Responds to IPinnedStackListener on IME visibility change. */ - public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) { - mIsShelfShowing = shelfVisible; - mShelfHeight = shelfHeight; + public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { + mIsImeShowing = imeVisible; + mImeHeight = imeHeight; } /** @@ -185,6 +198,10 @@ public class PipBoundsHandler { mLastPipComponentName = null; } + public Rect getLastDestinationBounds() { + return mLastDestinationBounds; + } + /** * Responds to IPinnedStackListener on {@link DisplayInfo} change. * It will normally follow up with a @@ -232,6 +249,7 @@ public class PipBoundsHandler { try { mPinnedStackController.startAnimation(destinationBounds, sourceRectHint, -1 /* animationDuration */); + mLastDestinationBounds.set(destinationBounds); } catch (RemoteException e) { Log.e(TAG, "Failed to start PiP animation from SysUI", e); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java index 37c8163702cf..f1e801b3a474 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java @@ -19,6 +19,7 @@ package com.android.systemui.pip; import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; +import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.UserHandle; @@ -30,15 +31,24 @@ import com.android.systemui.statusbar.CommandQueue; import java.io.FileDescriptor; import java.io.PrintWriter; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Controls the picture-in-picture window. */ +@Singleton public class PipUI extends SystemUI implements CommandQueue.Callbacks { private BasePipManager mPipManager; private boolean mSupportsPip; + @Inject + public PipUI(Context context) { + super(context); + } + @Override public void start() { PackageManager pm = mContext.getPackageManager(); @@ -59,7 +69,6 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks { mPipManager.initialize(mContext); getComponent(CommandQueue.class).addCallback(this); - putComponent(PipUI.class, this); } @Override @@ -85,6 +94,14 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks { mPipManager.onConfigurationChanged(newConfig); } + public void setShelfHeight(boolean visible, int height) { + if (mPipManager == null) { + return; + } + + mPipManager.setShelfHeight(visible, height); + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mPipManager == null) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java index 8224365e7b96..3f15966c7fbb 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java @@ -107,7 +107,7 @@ public class PipDismissViewController { | LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); lp.setTitle("pip-dismiss-overlay"); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; mWindowManager.addView(mDismissView, lp); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 8dfae32a1939..369073c6564d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -143,14 +143,6 @@ public class PipManager implements BasePipManager { } @Override - public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) { - mHandler.post(() -> { - mPipBoundsHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight); - mTouchHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight); - }); - } - - @Override public void onMinimizedStateChanged(boolean isMinimized) { mHandler.post(() -> { mPipBoundsHandler.onMinimizedStateChanged(isMinimized); @@ -161,14 +153,8 @@ public class PipManager implements BasePipManager { @Override public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment) { - mHandler.post(() -> { - // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first. - mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, - animatingBounds, mTmpDisplayInfo); - mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, - animatingBounds, fromImeAdjustment, fromShelfAdjustment, - mTmpDisplayInfo.rotation); - }); + mHandler.post(() -> updateMovementBounds(animatingBounds, fromImeAdjustment, + fromShelfAdjustment)); } @Override @@ -280,6 +266,31 @@ public class PipManager implements BasePipManager { } /** + * Sets both shelf visibility and its height. + */ + @Override + public void setShelfHeight(boolean visible, int height) { + mHandler.post(() -> { + final boolean changed = mPipBoundsHandler.setShelfHeight(visible, height); + if (changed) { + mTouchHandler.onShelfVisibilityChanged(visible, height); + updateMovementBounds(mPipBoundsHandler.getLastDestinationBounds(), + false /* fromImeAdjustment */, true /* fromShelfAdjustment */); + } + }); + } + + private void updateMovementBounds(Rect animatingBounds, boolean fromImeAdjustment, + boolean fromShelfAdjustment) { + // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler first. + mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, + animatingBounds, mTmpDisplayInfo); + mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, + animatingBounds, fromImeAdjustment, fromShelfAdjustment, + mTmpDisplayInfo.rotation); + } + + /** * Gets an instance of {@link PipManager}. */ public static PipManager getInstance() { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 4f2a6d82a08e..c452f64ff6f0 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -90,7 +90,7 @@ public class PipMenuActivity extends Activity { public static final int MESSAGE_UPDATE_ACTIONS = 4; public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5; public static final int MESSAGE_ANIMATION_ENDED = 6; - public static final int MESSAGE_TOUCH_EVENT = 7; + public static final int MESSAGE_POINTER_EVENT = 7; private static final int INITIAL_DISMISS_DELAY = 3500; private static final int POST_INTERACTION_DISMISS_DELAY = 2000; @@ -165,9 +165,9 @@ public class PipMenuActivity extends Activity { break; } - case MESSAGE_TOUCH_EVENT: { + case MESSAGE_POINTER_EVENT: { final MotionEvent ev = (MotionEvent) msg.obj; - dispatchTouchEvent(ev); + dispatchPointerEvent(ev); break; } } @@ -219,6 +219,9 @@ public class PipMenuActivity extends Activity { updateFromIntent(getIntent()); setTitle(R.string.pip_menu_title); setDisablePreviewScreenshots(true); + + // Hide without an animation. + getWindow().setExitTransition(null); } @Override @@ -247,6 +250,9 @@ public class PipMenuActivity extends Activity { protected void onStop() { super.onStop(); + // In cases such as device lock, hide and finish it so that it can be recreated on the top + // next time it starts, see also {@link #onUserLeaveHint} + hideMenu(); cancelDelayedFinish(); } @@ -266,6 +272,17 @@ public class PipMenuActivity extends Activity { } } + /** + * Dispatch a pointer event from {@link PipTouchHandler}. + */ + private void dispatchPointerEvent(MotionEvent event) { + if (event.isTouchEvent()) { + dispatchTouchEvent(event); + } else { + dispatchGenericMotionEvent(event); + } + } + @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!mAllowTouches) { @@ -285,8 +302,6 @@ public class PipMenuActivity extends Activity { public void finish() { notifyActivityCallback(null); super.finish(); - // Hide without an animation (the menu should already be invisible at this point) - overridePendingTransition(0, 0); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 62c59e5842ff..b8e0b81e5e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -508,12 +508,12 @@ public class PipMenuActivityController { } /** - * Handles touch event sent from pip input consumer. + * Handles a pointer event sent from pip input consumer. */ - void handleTouchEvent(MotionEvent ev) { + void handlePointerEvent(MotionEvent ev) { if (mToActivityMessenger != null) { Message m = Message.obtain(); - m.what = PipMenuActivity.MESSAGE_TOUCH_EVENT; + m.what = PipMenuActivity.MESSAGE_POINTER_EVENT; m.obj = ev; try { mToActivityMessenger.send(m); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 1f36d97ce308..f59b372762bd 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -132,6 +132,7 @@ public class PipTouchHandler { private boolean mSendingHoverAccessibilityEvents; private boolean mMovementWithinMinimize; private boolean mMovementWithinDismiss; + private PipAccessibilityInteractionConnection mConnection; // Touch state private final PipTouchState mTouchState; @@ -213,9 +214,10 @@ public class PipTouchHandler { // Register the listener for input consumer touch events inputConsumerController.setInputListener(this::handleTouchEvent); inputConsumerController.setRegistrationListener(this::onRegistrationChanged); - onRegistrationChanged(inputConsumerController.isRegistered()); mPipBoundsHandler = pipBoundsHandler; + mConnection = new PipAccessibilityInteractionConnection(mMotionHelper, + this::onAccessibilityShowMenu, mHandler); } public void setTouchEnabled(boolean enabled) { @@ -339,9 +341,7 @@ public class PipTouchHandler { private void onRegistrationChanged(boolean isRegistered) { mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered - ? new PipAccessibilityInteractionConnection(mMotionHelper, - this::onAccessibilityShowMenu, mHandler) : null); - + ? mConnection : null); if (!isRegistered && mTouchState.isUserInteracting()) { // If the input consumer is unregistered while the user is interacting, then we may not // get the final TOUCH_UP event, so clean up the dismiss target as well @@ -409,27 +409,15 @@ public class PipTouchHandler { } case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_MOVE: { - if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) { - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); - event.setImportantForAccessibility(true); - event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID); - event.setWindowId( - AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID); - mAccessibilityManager.sendAccessibilityEvent(event); + if (!shouldDeliverToMenu && !mSendingHoverAccessibilityEvents) { + sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); mSendingHoverAccessibilityEvents = true; } break; } case MotionEvent.ACTION_HOVER_EXIT: { - if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) { - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); - event.setImportantForAccessibility(true); - event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID); - event.setWindowId( - AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID); - mAccessibilityManager.sendAccessibilityEvent(event); + if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) { + sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); mSendingHoverAccessibilityEvents = false; } break; @@ -445,12 +433,25 @@ public class PipTouchHandler { mMenuController.pokeMenu(); } - mMenuController.handleTouchEvent(cloneEvent); + mMenuController.handlePointerEvent(cloneEvent); } return true; } + private void sendAccessibilityHoverEvent(int type) { + if (!mAccessibilityManager.isEnabled()) { + return; + } + + AccessibilityEvent event = AccessibilityEvent.obtain(type); + event.setImportantForAccessibility(true); + event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID); + event.setWindowId( + AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID); + mAccessibilityManager.sendAccessibilityEvent(event); + } + /** * Updates the appearance of the menu and scrim on top of the PiP while dismissing. */ @@ -523,6 +524,10 @@ public class PipTouchHandler { * Sets the menu visibility. */ private void setMenuState(int menuState, boolean resize) { + if (mMenuState == menuState && !resize) { + return; + } + if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) { // Save the current snap fraction and if we do not drag or move the PiP, then // we store back to this snap fraction. Otherwise, we'll reset the snap @@ -571,6 +576,9 @@ public class PipTouchHandler { } mMenuState = menuState; updateMovementBounds(menuState); + // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip + // as well, or it can't handle a11y focus and pip menu can't perform any action. + onRegistrationChanged(menuState == MENU_STATE_NONE); if (menuState != MENU_STATE_CLOSE) { MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL); } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 75dc39722bcf..f60d9db7ac9c 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -45,6 +45,7 @@ import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.statusbar.phone.StatusBar; import java.io.FileDescriptor; @@ -53,6 +54,12 @@ import java.time.Duration; import java.util.Arrays; import java.util.concurrent.Future; +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Lazy; + +@Singleton public class PowerUI extends SystemUI { static final String TAG = "PowerUI"; @@ -97,6 +104,16 @@ public class PowerUI extends SystemUI { private IThermalEventListener mSkinThermalEventListener; private IThermalEventListener mUsbThermalEventListener; + private final BroadcastDispatcher mBroadcastDispatcher; + private final Lazy<StatusBar> mStatusBarLazy; + + @Inject + public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher, + Lazy<StatusBar> statusBarLazy) { + super(context); + mBroadcastDispatcher = broadcastDispatcher; + mStatusBarLazy = statusBarLazy; + } public void start() { mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -211,7 +228,7 @@ public class PowerUI extends SystemUI { filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_USER_SWITCHED); - mContext.registerReceiver(this, filter, null, mHandler); + mBroadcastDispatcher.registerReceiver(this, filter, mHandler); } @Override @@ -653,8 +670,7 @@ public class PowerUI extends SystemUI { int status = temp.getStatus(); if (status >= Temperature.THROTTLING_EMERGENCY) { - StatusBar statusBar = getComponent(StatusBar.class); - if (statusBar != null && !statusBar.isDeviceInVrMode()) { + if (!mStatusBarLazy.get().isDeviceInVrMode()) { mWarnings.showHighTemperatureWarning(); Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called " + ", current skin status = " + status diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index 631b8b7a14a0..22fb4c0dbdb5 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -22,25 +22,20 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.os.Handler -import android.os.Looper -import android.os.Message -import android.os.UserHandle -import android.os.UserManager +import android.os.* import android.provider.DeviceConfig import com.android.internal.annotations.VisibleForTesting import com.android.internal.config.sysui.SystemUiDeviceConfigFlags -import com.android.systemui.Dependency.BG_HANDLER_NAME -import com.android.systemui.Dependency.MAIN_HANDLER_NAME +import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController -import com.android.systemui.Dumpable +import com.android.systemui.dagger.qualifiers.BgHandler +import com.android.systemui.dagger.qualifiers.MainHandler import java.io.FileDescriptor import java.io.PrintWriter import java.lang.ref.WeakReference import javax.inject.Inject -import javax.inject.Named import javax.inject.Singleton fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, @@ -50,8 +45,8 @@ fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_P class PrivacyItemController @Inject constructor( val context: Context, private val appOpsController: AppOpsController, - @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler, - @Named(BG_HANDLER_NAME) private val bgHandler: Handler + @MainHandler private val uiHandler: Handler, + @BgHandler private val bgHandler: Handler ) : Dumpable { @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt new file mode 100644 index 000000000000..f710f7fc47e2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2019 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 android.content.Context +import android.content.res.Configuration +import android.view.View +import android.view.ViewGroup +import com.android.systemui.R +import com.android.systemui.qs.TileLayout.exactly + +class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTileLayout { + + protected val mRecords = ArrayList<QSPanel.TileRecord>() + private var _listening = false + private var smallTileSize = 0 + private val twoLineHeight + get() = smallTileSize * 2 + cellMarginVertical + private var cellMarginHorizontal = 0 + private var cellMarginVertical = 0 + + init { + isFocusableInTouchMode = true + clipChildren = false + clipToPadding = false + + updateResources() + } + + override fun addTile(tile: QSPanel.TileRecord) { + mRecords.add(tile) + tile.tile.setListening(this, _listening) + addTileView(tile) + } + + protected fun addTileView(tile: QSPanel.TileRecord) { + addView(tile.tileView) + } + + override fun removeTile(tile: QSPanel.TileRecord) { + mRecords.remove(tile) + tile.tile.setListening(this, false) + removeView(tile.tileView) + } + + override fun removeAllViews() { + mRecords.forEach { it.tile.setListening(this, false) } + mRecords.clear() + super.removeAllViews() + } + + override fun getOffsetTop(tile: QSPanel.TileRecord?) = top + + override fun updateResources(): Boolean { + with(mContext.resources) { + smallTileSize = getDimensionPixelSize(R.dimen.qs_quick_tile_size) + cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) + cellMarginVertical = getDimensionPixelSize(R.dimen.new_qs_vertical_margin) + } + requestLayout() + return false + } + + override fun setListening(listening: Boolean) { + if (_listening == listening) return + _listening = listening + for (record in mRecords) { + record.tile.setListening(this, listening) + } + } + + override fun getNumVisibleTiles() = mRecords.size + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + updateResources() + } + + override fun onFinishInflate() { + updateResources() + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + var previousView: View = this + var tiles = 0 + + mRecords.forEach { + val tileView = it.tileView + if (tileView.visibility != View.GONE) { + tileView.updateAccessibilityOrder(previousView) + previousView = tileView + tiles++ + tileView.measure(exactly(smallTileSize), exactly(smallTileSize)) + } + } + + val height = twoLineHeight + val columns = tiles / 2 + val width = paddingStart + paddingEnd + + columns * smallTileSize + + (columns - 1) * cellMarginHorizontal + setMeasuredDimension(width, height) + } + + override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + val tiles = mRecords.filter { it.tileView.visibility != View.GONE } + tiles.forEachIndexed { + index, tile -> + val column = index % (tiles.size / 2) + val left = getLeftForColumn(column) + val top = if (index < tiles.size / 2) 0 else getTopBottomRow() + tile.tileView.layout(left, top, left + smallTileSize, top + smallTileSize) + } + } + + private fun getLeftForColumn(column: Int) = column * (smallTileSize + cellMarginHorizontal) + + private fun getTopBottomRow() = smallTileSize + cellMarginVertical +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 9221b6852112..a267bbb92ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -14,6 +14,7 @@ package com.android.systemui.qs; +import android.provider.Settings; import android.util.Log; import android.view.View; import android.view.View.OnAttachStateChangeListener; @@ -267,6 +268,17 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mAllViews.add(tileView); count++; } + + + int flag = Settings.System.getInt(mQsPanel.getContext().getContentResolver(), + "qs_media_player", 0); + if (flag == 1) { + View qsMediaView = mQsPanel.getMediaPanel(); + View qqsMediaView = mQuickQsPanel.getMediaPlayer().getView(); + translationXBuilder.addFloat(qsMediaView, "alpha", 0, 1); + translationXBuilder.addFloat(qqsMediaView, "alpha", 1, 0); + } + if (mAllowFancy) { // Make brightness appear static position and alpha in through second half. View brightness = mQsPanel.getBrightnessView(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java index 27a517c52458..8fe687ba297c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java @@ -17,12 +17,15 @@ package com.android.systemui.qs; import static com.android.systemui.Dependency.BG_HANDLER; -import static com.android.systemui.Dependency.BG_HANDLER_NAME; +import static com.android.systemui.Dependency.MAIN_LOOPER; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; +import android.annotation.MainThread; import android.content.Context; import android.content.Intent; import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.provider.Settings; import android.telephony.SubscriptionManager; import android.util.AttributeSet; @@ -36,9 +39,13 @@ import androidx.annotation.VisibleForTesting; import com.android.keyguard.CarrierTextController; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.MainLooper; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.policy.NetworkController; +import java.util.function.Consumer; + import javax.inject.Inject; import javax.inject.Named; @@ -46,7 +53,6 @@ import javax.inject.Named; * Displays Carrier name and network status in QS */ public class QSCarrierGroup extends LinearLayout implements - CarrierTextController.CarrierTextCallback, NetworkController.SignalCallback, View.OnClickListener { private static final String TAG = "QSCarrierGroup"; @@ -56,12 +62,14 @@ public class QSCarrierGroup extends LinearLayout implements private static final int SIM_SLOTS = 3; private final NetworkController mNetworkController; private final Handler mBgHandler; + private final H mMainHandler; private View[] mCarrierDividers = new View[SIM_SLOTS - 1]; private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS]; private TextView mNoSimTextView; private final CellSignalState[] mInfos = new CellSignalState[SIM_SLOTS]; private CarrierTextController mCarrierTextController; + private CarrierTextController.CarrierTextCallback mCallback; private ActivityStarter mActivityStarter; private boolean mListening; @@ -69,11 +77,19 @@ public class QSCarrierGroup extends LinearLayout implements @Inject public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, NetworkController networkController, ActivityStarter activityStarter, - @Named(BG_HANDLER_NAME) Handler handler) { + @BgHandler Handler handler, + @MainLooper Looper looper) { super(context, attrs); mNetworkController = networkController; mActivityStarter = activityStarter; mBgHandler = handler; + mMainHandler = new H(looper, this::handleUpdateCarrierInfo, this::handleUpdateState); + mCallback = new Callback(mMainHandler); + } + + @VisibleForTesting + protected CarrierTextController.CarrierTextCallback getCallback() { + return mCallback; } @VisibleForTesting @@ -81,7 +97,8 @@ public class QSCarrierGroup extends LinearLayout implements this(context, attrs, Dependency.get(NetworkController.class), Dependency.get(ActivityStarter.class), - Dependency.get(BG_HANDLER)); + Dependency.get(BG_HANDLER), + Dependency.get(MAIN_LOOPER)); } @Override @@ -136,14 +153,20 @@ public class QSCarrierGroup extends LinearLayout implements if (mNetworkController.hasVoiceCallingFeature()) { mNetworkController.addCallback(this); } - mCarrierTextController.setListening(this); + mCarrierTextController.setListening(mCallback); } else { mNetworkController.removeCallback(this); mCarrierTextController.setListening(null); } } + @MainThread private void handleUpdateState() { + if (!mMainHandler.getLooper().isCurrentThread()) { + mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); + return; + } + for (int i = 0; i < SIM_SLOTS; i++) { mCarrierGroups[i].updateState(mInfos[i]); } @@ -163,8 +186,13 @@ public class QSCarrierGroup extends LinearLayout implements return SubscriptionManager.getSlotIndex(subscriptionId); } - @Override - public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { + @MainThread + private void handleUpdateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { + if (!mMainHandler.getLooper().isCurrentThread()) { + mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget(); + return; + } + mNoSimTextView.setVisibility(View.GONE); if (!info.airplaneMode && info.anySimReady) { boolean[] slotSeen = new boolean[SIM_SLOTS]; @@ -207,7 +235,7 @@ public class QSCarrierGroup extends LinearLayout implements mNoSimTextView.setText(info.carrierText); mNoSimTextView.setVisibility(View.VISIBLE); } - handleUpdateState(); + handleUpdateState(); // handleUpdateCarrierInfo is always called from main thread. } @Override @@ -231,7 +259,7 @@ public class QSCarrierGroup extends LinearLayout implements mInfos[slotIndex].contentDescription = statusIcon.contentDescription; mInfos[slotIndex].typeContentDescription = typeContentDescription; mInfos[slotIndex].roaming = roaming; - handleUpdateState(); + mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); } @Override @@ -241,7 +269,7 @@ public class QSCarrierGroup extends LinearLayout implements mInfos[i].visible = false; } } - handleUpdateState(); + mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); } static final class CellSignalState { @@ -251,4 +279,47 @@ public class QSCarrierGroup extends LinearLayout implements String typeContentDescription; boolean roaming; } + + private static class H extends Handler { + private Consumer<CarrierTextController.CarrierTextCallbackInfo> mUpdateCarrierInfo; + private Runnable mUpdateState; + static final int MSG_UPDATE_CARRIER_INFO = 0; + static final int MSG_UPDATE_STATE = 1; + + H(Looper looper, + Consumer<CarrierTextController.CarrierTextCallbackInfo> updateCarrierInfo, + Runnable updateState) { + super(looper); + mUpdateCarrierInfo = updateCarrierInfo; + mUpdateState = updateState; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_CARRIER_INFO: + mUpdateCarrierInfo.accept( + (CarrierTextController.CarrierTextCallbackInfo) msg.obj); + break; + case MSG_UPDATE_STATE: + mUpdateState.run(); + break; + default: + super.handleMessage(msg); + } + } + } + + private static class Callback implements CarrierTextController.CarrierTextCallback { + private H mMainHandler; + + Callback(H handler) { + mMainHandler = handler; + } + + @Override + public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { + mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java new file mode 100644 index 000000000000..af418f6308a0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2019 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 android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.Icon; +import android.graphics.drawable.RippleDrawable; +import android.media.MediaMetadata; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.media.session.PlaybackState; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RemoteViews; +import android.widget.TextView; + +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + +import com.android.settingslib.media.MediaOutputSliceConstants; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.MediaTransferManager; + +/** + * Single media player for carousel in QSPanel + */ +public class QSMediaPlayer { + + private static final String TAG = "QSMediaPlayer"; + + private Context mContext; + private LinearLayout mMediaNotifView; + private MediaSession.Token mToken; + private MediaController mController; + private int mWidth; + private int mHeight; + + /** + * + * @param context + * @param parent + * @param width + * @param height + */ + public QSMediaPlayer(Context context, ViewGroup parent, int width, int height) { + mContext = context; + LayoutInflater inflater = LayoutInflater.from(mContext); + mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qs_media_panel, parent, false); + + mWidth = width; + mHeight = height; + } + + public View getView() { + return mMediaNotifView; + } + + /** + * + * @param token token for this media session + * @param icon app notification icon + * @param iconColor foreground color (for text, icons) + * @param bgColor background color + * @param actionsContainer a LinearLayout containing the media action buttons + * @param notif + */ + public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, + View actionsContainer, Notification notif) { + Log.d(TAG, "got media session: " + token); + mToken = token; + mController = new MediaController(mContext, token); + MediaMetadata mMediaMetadata = mController.getMetadata(); + Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif); + + // Album art + addAlbumArtBackground(mMediaMetadata, bgColor, mWidth, mHeight); + + // Reuse notification header instead of reimplementing everything + RemoteViews headerRemoteView = builder.makeNotificationHeader(); + LinearLayout headerView = mMediaNotifView.findViewById(R.id.header); + View result = headerRemoteView.apply(mContext, headerView); + result.setPadding(0, 0, 0, 0); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, 75); + result.setLayoutParams(lp); + headerView.removeAllViews(); + headerView.addView(result); + + View seamless = headerView.findViewById(com.android.internal.R.id.media_seamless); + seamless.setVisibility(View.VISIBLE); + + // App icon + ImageView appIcon = headerView.findViewById(com.android.internal.R.id.icon); + Drawable iconDrawable = icon.loadDrawable(mContext); + iconDrawable.setTint(iconColor); + appIcon.setImageDrawable(iconDrawable); + + // App title + TextView appName = headerView.findViewById(com.android.internal.R.id.app_name_text); + String appNameString = builder.loadHeaderAppName(); + appName.setText(appNameString); + appName.setTextColor(iconColor); + + // Action + mMediaNotifView.setOnClickListener(v -> { + try { + notif.contentIntent.send(); + // Also close shade + mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Pending intent was canceled"); + e.printStackTrace(); + } + }); + + // Separator + TextView separator = headerView.findViewById(com.android.internal.R.id.header_text_divider); + separator.setTextColor(iconColor); + + // Album name + TextView albumName = headerView.findViewById(com.android.internal.R.id.header_text); + String albumString = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM); + if (!albumString.isEmpty()) { + albumName.setText(albumString); + albumName.setTextColor(iconColor); + albumName.setVisibility(View.VISIBLE); + separator.setVisibility(View.VISIBLE); + } else { + albumName.setVisibility(View.GONE); + separator.setVisibility(View.GONE); + } + + // Transfer chip + MediaTransferManager mediaTransferManager = new MediaTransferManager(mContext); + View transferBackgroundView = headerView.findViewById( + com.android.internal.R.id.media_seamless); + LinearLayout viewLayout = (LinearLayout) transferBackgroundView; + RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground(); + GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); + rect.setStroke(2, iconColor); + rect.setColor(bgColor); + ImageView transferIcon = headerView.findViewById( + com.android.internal.R.id.media_seamless_image); + transferIcon.setBackgroundColor(bgColor); + transferIcon.setImageTintList(ColorStateList.valueOf(iconColor)); + TextView transferText = headerView.findViewById( + com.android.internal.R.id.media_seamless_text); + transferText.setTextColor(iconColor); + + ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); + transferBackgroundView.setOnClickListener(v -> { + final Intent intent = new Intent() + .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT); + mActivityStarter.startActivity(intent, false, true /* dismissShade */, + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + }); + + // Artist name + TextView artistText = mMediaNotifView.findViewById(R.id.header_title); + String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST); + artistText.setText(artistName); + artistText.setTextColor(iconColor); + + // Song name + TextView titleText = mMediaNotifView.findViewById(R.id.header_text); + String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE); + titleText.setText(songName); + titleText.setTextColor(iconColor); + + // Media controls + LinearLayout parentActionsLayout = (LinearLayout) actionsContainer; + final int[] actionIds = { + R.id.action0, + R.id.action1, + R.id.action2, + R.id.action3, + R.id.action4 + }; + final int[] notifActionIds = { + com.android.internal.R.id.action0, + com.android.internal.R.id.action1, + com.android.internal.R.id.action2, + com.android.internal.R.id.action3, + com.android.internal.R.id.action4 + }; + for (int i = 0; i < parentActionsLayout.getChildCount() && i < actionIds.length; i++) { + ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]); + ImageButton thatBtn = parentActionsLayout.findViewById(notifActionIds[i]); + if (thatBtn == null || thatBtn.getDrawable() == null) { + thisBtn.setVisibility(View.GONE); + continue; + } + + Drawable thatIcon = thatBtn.getDrawable(); + thisBtn.setImageDrawable(thatIcon.mutate()); + thisBtn.setVisibility(View.VISIBLE); + thisBtn.setOnClickListener(v -> { + Log.d(TAG, "clicking on other button"); + thatBtn.performClick(); + }); + } + } + + public MediaSession.Token getMediaSessionToken() { + return mToken; + } + + public String getMediaPlayerPackage() { + return mController.getPackageName(); + } + + /** + * Check whether the media controlled by this player is currently playing + * @return whether it is playing, or false if no controller information + */ + public boolean isPlaying() { + if (mController == null) { + return false; + } + + PlaybackState state = mController.getPlaybackState(); + if (state == null) { + return false; + } + + return (state.getState() == PlaybackState.STATE_PLAYING); + } + + private void addAlbumArtBackground(MediaMetadata metadata, int bgColor, int width, int height) { + Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); + if (albumArt != null) { + + Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true); + Bitmap scaled = scaleBitmap(original, width, height); + Canvas canvas = new Canvas(scaled); + + // Add translucent layer over album art to improve contrast + Paint p = new Paint(); + p.setStyle(Paint.Style.FILL); + p.setColor(bgColor); + p.setAlpha(200); + canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p); + + RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create( + mContext.getResources(), scaled); + roundedDrawable.setCornerRadius(20); + + mMediaNotifView.setBackground(roundedDrawable); + } else { + Log.e(TAG, "No album art available"); + } + } + + private Bitmap scaleBitmap(Bitmap original, int width, int height) { + Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(cropped); + + float scale = (float) cropped.getWidth() / (float) original.getWidth(); + float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f; + Matrix transformation = new Matrix(); + transformation.postTranslate(0, dy); + transformation.preScale(scale, scale); + + Paint paint = new Paint(); + paint.setFilterBitmap(true); + canvas.drawBitmap(original, transformation, paint); + + return cropped; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 00b57da846fe..4c813482f58e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -24,14 +24,22 @@ import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.drawable.Icon; +import android.media.session.MediaSession; import android.metrics.LogMaker; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.provider.Settings; +import android.service.notification.StatusBarNotification; import android.service.quicksettings.Tile; import android.util.AttributeSet; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import com.android.internal.logging.MetricsLogger; @@ -49,6 +57,8 @@ import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.ToggleSliderView; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.phone.NPVPluginManager; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener; import com.android.systemui.tuner.TunerService; @@ -78,6 +88,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private final QSTileRevealController mQsTileRevealController; + private final LinearLayout mMediaCarousel; + private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>(); + protected boolean mExpanded; protected boolean mListening; @@ -98,6 +111,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private BrightnessMirrorController mBrightnessMirrorController; private View mDivider; + private FrameLayout mPluginFrame; + private final PluginManager mPluginManager; + private NPVPluginManager mNPVPluginManager; + public QSPanel(Context context) { this(context, null); } @@ -106,9 +123,13 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne this(context, attrs, null); } + public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) { + this(context, attrs, dumpController, null); + } + @Inject public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - DumpController dumpController) { + DumpController dumpController, PluginManager pluginManager) { super(context, attrs); mContext = context; @@ -128,6 +149,27 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne addDivider(); + // Add media carousel + int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0); + if (flag == 1) { + HorizontalScrollView mediaScrollView = new HorizontalScrollView(mContext); + mediaScrollView.setHorizontalScrollBarEnabled(false); + int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height); + int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); + LayoutParams lpView = new LayoutParams(LayoutParams.MATCH_PARENT, playerHeight); + lpView.setMarginStart(padding); + lpView.setMarginEnd(padding); + addView(mediaScrollView, lpView); + + LayoutParams lpCarousel = new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT); + mMediaCarousel = new LinearLayout(mContext); + mMediaCarousel.setOrientation(LinearLayout.HORIZONTAL); + mediaScrollView.addView(mMediaCarousel, lpCarousel); + } else { + mMediaCarousel = null; + } + mFooter = new QSSecurityFooter(this, context); addView(mFooter.getView()); @@ -136,6 +178,82 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mDumpController = dumpController; updateResources(); + + mPluginManager = pluginManager; + if (mPluginManager != null && Settings.System.getInt( + mContext.getContentResolver(), "npv_plugin_flag", 0) == 2) { + mPluginFrame = (FrameLayout) LayoutInflater.from(mContext).inflate( + R.layout.status_bar_expanded_plugin_frame, this, false); + addView(mPluginFrame); + mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager); + } + + } + + /** + * Add or update a player for the associated media session + * @param token + * @param icon + * @param iconColor + * @param bgColor + * @param actionsContainer + * @param notif + */ + public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, + View actionsContainer, StatusBarNotification notif) { + int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); + if (flag != 1) { + // Shouldn't happen, but just in case + Log.e(TAG, "Tried to add media session without player!"); + return; + } + QSMediaPlayer player = null; + String packageName = notif.getPackageName(); + for (QSMediaPlayer p : mMediaPlayers) { + if (p.getMediaSessionToken().equals(token)) { + Log.d(TAG, "a player for this session already exists"); + player = p; + break; + } + + if (packageName.equals(p.getMediaPlayerPackage())) { + Log.d(TAG, "found an old session for this app"); + player = p; + break; + } + } + + int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height); + int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width); + int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); + LayoutParams lp = new LayoutParams(playerWidth, ViewGroup.LayoutParams.MATCH_PARENT); + lp.setMarginStart(padding); + lp.setMarginEnd(padding); + + if (player == null) { + Log.d(TAG, "creating new player"); + + player = new QSMediaPlayer(mContext, this, playerWidth, playerHeight); + + if (player.isPlaying()) { + mMediaCarousel.addView(player.getView(), 0, lp); // add in front + } else { + mMediaCarousel.addView(player.getView(), lp); // add at end + } + } else if (player.isPlaying()) { + // move it to the front + mMediaCarousel.removeView(player.getView()); + mMediaCarousel.addView(player.getView(), 0, lp); + } + + Log.d(TAG, "setting player session"); + player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer, + notif.getNotification()); + mMediaPlayers.add(player); + } + + protected View getMediaPanel() { + return mMediaCarousel; } protected void addDivider() { @@ -380,6 +498,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mListening) { refreshAllTiles(); } + if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening); } public void setListening(boolean listening, boolean expanded) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 1e763cf79240..b395c3c336d3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -30,11 +30,12 @@ import android.service.quicksettings.Tile; import android.text.TextUtils; import android.util.Log; -import com.android.systemui.Dependency; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; @@ -61,7 +62,6 @@ import java.util.List; import java.util.function.Predicate; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; @@ -94,8 +94,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D public QSTileHost(Context context, StatusBarIconController iconController, QSFactoryImpl defaultFactory, - @Named(Dependency.MAIN_HANDLER_NAME) Handler mainHandler, - @Named(Dependency.BG_LOOPER_NAME) Looper bgLooper, + @MainHandler Handler mainHandler, + @BgLooper Looper bgLooper, PluginManager pluginManager, TunerService tunerService, Provider<AutoTileManager> autoTiles, diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java new file mode 100644 index 000000000000..ae66cd576765 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2019 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 android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.media.MediaMetadata; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.media.session.PlaybackState; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + +import com.android.systemui.R; + +/** + * QQS mini media player + */ +public class QuickQSMediaPlayer { + + private static final String TAG = "QQSMediaPlayer"; + + private Context mContext; + private LinearLayout mMediaNotifView; + private MediaSession.Token mToken; + private MediaController mController; + + /** + * + * @param context + * @param parent + */ + public QuickQSMediaPlayer(Context context, ViewGroup parent) { + mContext = context; + LayoutInflater inflater = LayoutInflater.from(mContext); + mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qqs_media_panel, parent, false); + } + + public View getView() { + return mMediaNotifView; + } + + /** + * + * @param token token for this media session + * @param icon app notification icon + * @param iconColor foreground color (for text, icons) + * @param bgColor background color + * @param actionsContainer a LinearLayout containing the media action buttons + */ + public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, + View actionsContainer) { + Log.d(TAG, "Setting media session: " + token); + mToken = token; + mController = new MediaController(mContext, token); + MediaMetadata mMediaMetadata = mController.getMetadata(); + + // Album art + addAlbumArtBackground(mMediaMetadata, bgColor); + + // App icon + ImageView appIcon = mMediaNotifView.findViewById(R.id.icon); + Drawable iconDrawable = icon.loadDrawable(mContext); + iconDrawable.setTint(iconColor); + appIcon.setImageDrawable(iconDrawable); + + // Artist name + TextView appText = mMediaNotifView.findViewById(R.id.header_title); + String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST); + appText.setText(artistName); + appText.setTextColor(iconColor); + + // Song name + TextView titleText = mMediaNotifView.findViewById(R.id.header_text); + String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE); + titleText.setText(songName); + titleText.setTextColor(iconColor); + + // Action buttons + LinearLayout parentActionsLayout = (LinearLayout) actionsContainer; + final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2}; + + // TODO some apps choose different buttons to show in compact mode + final int[] notifActionIds = { + com.android.internal.R.id.action1, + com.android.internal.R.id.action2, + com.android.internal.R.id.action3 + }; + for (int i = 0; i < parentActionsLayout.getChildCount() && i < actionIds.length; i++) { + ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]); + ImageButton thatBtn = parentActionsLayout.findViewById(notifActionIds[i]); + if (thatBtn == null || thatBtn.getDrawable() == null) { + thisBtn.setVisibility(View.GONE); + continue; + } + + Drawable thatIcon = thatBtn.getDrawable(); + thisBtn.setImageDrawable(thatIcon.mutate()); + thisBtn.setVisibility(View.VISIBLE); + + thisBtn.setOnClickListener(v -> { + Log.d(TAG, "clicking on other button"); + thatBtn.performClick(); + }); + } + } + + public MediaSession.Token getMediaSessionToken() { + return mToken; + } + + /** + * Check whether the media controlled by this player is currently playing + * @return whether it is playing, or false if no controller information + */ + public boolean isPlaying() { + if (mController == null) { + return false; + } + + PlaybackState state = mController.getPlaybackState(); + if (state == null) { + return false; + } + + return (state.getState() == PlaybackState.STATE_PLAYING); + } + + private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) { + Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); + if (albumArt != null) { + Rect bounds = new Rect(); + mMediaNotifView.getBoundsOnScreen(bounds); + int width = bounds.width(); + int height = bounds.height(); + + Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true); + Bitmap scaled = scaleBitmap(original, width, height); + Canvas canvas = new Canvas(scaled); + + // Add translucent layer over album art to improve contrast + Paint p = new Paint(); + p.setStyle(Paint.Style.FILL); + p.setColor(bgColor); + p.setAlpha(200); + canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p); + + RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create( + mContext.getResources(), scaled); + roundedDrawable.setCornerRadius(20); + + mMediaNotifView.setBackground(roundedDrawable); + } else { + Log.e(TAG, "No album art available"); + } + } + + private Bitmap scaleBitmap(Bitmap original, int width, int height) { + Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(cropped); + + float scale = (float) cropped.getWidth() / (float) original.getWidth(); + float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f; + Matrix transformation = new Matrix(); + transformation.postTranslate(0, dy); + transformation.preScale(scale, scale); + + Paint paint = new Paint(); + paint.setFilterBitmap(true); + canvas.drawBitmap(original, transformation, paint); + + return cropped; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 85aafa06961a..dcd4633a79d2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -21,6 +21,7 @@ import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEX import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; +import android.provider.Settings; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; @@ -55,6 +56,7 @@ public class QuickQSPanel extends QSPanel { private boolean mDisabledByPolicy; private int mMaxTiles; protected QSPanel mFullPanel; + private QuickQSMediaPlayer mMediaPlayer; @Inject public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, @@ -69,11 +71,43 @@ public class QuickQSPanel extends QSPanel { } removeView((View) mTileLayout); } - sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); - mTileLayout = new HeaderTileLayout(context); - mTileLayout.setListening(mListening); - addView((View) mTileLayout, 0 /* Between brightness and footer */); - super.setPadding(0, 0, 0, 0); + + int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0); + if (flag == 1) { + LinearLayout mHorizontalLinearLayout = new LinearLayout(mContext); + mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL); + mHorizontalLinearLayout.setClipChildren(false); + mHorizontalLinearLayout.setClipToPadding(false); + + LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1); + + mTileLayout = new DoubleLineTileLayout(context); + lp.setMarginEnd(10); + lp.setMarginStart(0); + mHorizontalLinearLayout.addView((View) mTileLayout, lp); + + mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout); + + lp.setMarginEnd(0); + lp.setMarginStart(10); + mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp); + + sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); + + mTileLayout.setListening(mListening); + addView(mHorizontalLinearLayout, 0 /* Between brightness and footer */); + super.setPadding(0, 0, 0, 0); + } else { + sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); + mTileLayout = new HeaderTileLayout(context); + mTileLayout.setListening(mListening); + addView((View) mTileLayout, 0 /* Between brightness and footer */); + super.setPadding(0, 0, 0, 0); + } + } + + public QuickQSMediaPlayer getMediaPlayer() { + return mMediaPlayer; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index d20b22815805..592e3881ea97 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -257,10 +257,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mNextAlarmTextView.setSelected(true); mPermissionsHubEnabled = PrivacyItemControllerKt.isPermissionsHubEnabled(); - // Change the ignored slots when DeviceConfig flag changes - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY, - mContext.getMainExecutor(), mPropertiesListener); - } private List<String> getIgnoredIconSlots() { @@ -396,9 +392,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams()); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); + + int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); if (mQsDisabled) { lp.height = resources.getDimensionPixelSize( com.android.internal.R.dimen.quick_qs_offset_height); + } else if (flag == 1) { + lp.height = Math.max(getMinimumHeight(), + resources.getDimensionPixelSize( + com.android.internal.R.dimen.quick_qs_total_height_with_media)); } else { lp.height = Math.max(getMinimumHeight(), resources.getDimensionPixelSize( @@ -489,6 +491,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements super.onAttachedToWindow(); mStatusBarIconController.addIconGroup(mIconManager); requestApplyInsets(); + // Change the ignored slots when DeviceConfig flag changes + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY, + mContext.getMainExecutor(), mPropertiesListener); } @Override @@ -527,6 +532,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements public void onDetachedFromWindow() { setListening(false); mStatusBarIconController.removeIconGroup(mIconManager); + DeviceConfig.removeOnPropertiesChangedListener(mPropertiesListener); super.onDetachedFromWindow(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 14addb99c0c5..37743ec55517 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -2,6 +2,7 @@ package com.android.systemui.qs; import android.content.Context; import android.content.res.Resources; +import android.provider.Settings; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -31,6 +32,9 @@ public class TileLayout extends ViewGroup implements QSTileLayout { private boolean mListening; protected int mMaxAllowedRows = 3; + // Prototyping with less rows + private final boolean mLessRows; + public TileLayout(Context context) { this(context, null); } @@ -38,7 +42,9 @@ public class TileLayout extends ViewGroup implements QSTileLayout { public TileLayout(Context context, AttributeSet attrs) { super(context, attrs); setFocusableInTouchMode(true); + mLessRows = Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0; updateResources(); + } @Override @@ -89,6 +95,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top); mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side); mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows)); + if (mLessRows) mMaxAllowedRows = Math.max(1, mMaxAllowedRows - 1); if (mColumns != columns) { mColumns = columns; requestLayout(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 38153ecd59c9..7fb520766977 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -46,8 +46,8 @@ import com.android.systemui.qs.QSDetailClipper; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; -import com.android.systemui.statusbar.policy.KeyguardMonitor; -import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.KeyguardStateController.Callback; import java.util.ArrayList; import java.util.List; @@ -68,7 +68,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private final QSDetailClipper mClipper; private final LightBarController mLightBarController; - private KeyguardMonitor mKeyguardMonitor; + private KeyguardStateController mKeyguardStateController; private final ScreenLifecycle mScreenLifecycle; private final TileQueryHelper mTileQueryHelper; private final View mTransparentView; @@ -89,7 +89,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene @Inject public QSCustomizer(Context context, AttributeSet attrs, LightBarController lightBarController, - KeyguardMonitor keyguardMonitor, + KeyguardStateController keyguardStateController, ScreenLifecycle screenLifecycle) { super(new ContextThemeWrapper(context, R.style.edit_theme), attrs); @@ -124,7 +124,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene animator.setMoveDuration(TileAdapter.MOVE_DURATION); mRecyclerView.setItemAnimator(animator); mLightBarController = lightBarController; - mKeyguardMonitor = keyguardMonitor; + mKeyguardStateController = keyguardStateController; mScreenLifecycle = screenLifecycle; updateNavBackDrop(getResources().getConfiguration()); } @@ -187,7 +187,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene queryTiles(); mNotifQsContainer.setCustomizerAnimating(true); mNotifQsContainer.setCustomizerShowing(true); - mKeyguardMonitor.addCallback(mKeyguardCallback); + mKeyguardStateController.addCallback(mKeyguardCallback); updateNavColors(); } } @@ -203,7 +203,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene queryTiles(); mNotifQsContainer.setCustomizerAnimating(false); mNotifQsContainer.setCustomizerShowing(true); - mKeyguardMonitor.addCallback(mKeyguardCallback); + mKeyguardStateController.addCallback(mKeyguardCallback); updateNavColors(); } } @@ -227,7 +227,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene } mNotifQsContainer.setCustomizerAnimating(animate); mNotifQsContainer.setCustomizerShowing(false); - mKeyguardMonitor.removeCallback(mKeyguardCallback); + mKeyguardStateController.removeCallback(mKeyguardCallback); updateNavColors(); } } @@ -283,7 +283,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene public void saveInstanceState(Bundle outState) { if (isShown) { - mKeyguardMonitor.removeCallback(mKeyguardCallback); + mKeyguardStateController.removeCallback(mKeyguardCallback); } outState.putBoolean(EXTRA_QS_CUSTOMIZING, mCustomizing); } @@ -315,7 +315,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene @Override public void onKeyguardShowingChanged() { if (!isAttachedToWindow()) return; - if (mKeyguardMonitor.isShowing() && !mOpening) { + if (mKeyguardStateController.isShowing() && !mOpening) { hide(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 2542abdbef72..bd3297b3d39c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -277,20 +277,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta selectPosition(holder.getAdapterPosition(), v); } }); - if (mNeedsFocus) { - // Wait for this to get laid out then set its focus. - // Ensure that tile gets laid out so we get the callback. - holder.mTileView.requestLayout(); - holder.mTileView.addOnLayoutChangeListener(new OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - holder.mTileView.removeOnLayoutChangeListener(this); - holder.mTileView.requestFocus(); - } - }); - mNeedsFocus = false; - } + focusOnHolder(holder); return; } @@ -330,16 +317,38 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta } else { if (position < mEditIndex && canRemoveTiles()) { showAccessibilityDialog(position, v); + } else if (position < mEditIndex && !canRemoveTiles()) { + startAccessibleMove(position); } else { startAccessibleAdd(position); } } } }); + if (position == mAccessibilityFromIndex) { + focusOnHolder(holder); + } } } } + private void focusOnHolder(Holder holder) { + if (mNeedsFocus) { + // Wait for this to get laid out then set its focus. + // Ensure that tile gets laid out so we get the callback. + holder.mTileView.requestLayout(); + holder.mTileView.addOnLayoutChangeListener(new OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + holder.mTileView.removeOnLayoutChangeListener(this); + holder.mTileView.requestFocus(); + } + }); + mNeedsFocus = false; + } + } + private boolean canRemoveTiles() { return mCurrentSpecs.size() > mMinNumTiles; } @@ -396,6 +405,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mAccessibilityFromIndex = position; mAccessibilityFromLabel = mTiles.get(position).state.label; mAccessibilityAction = ACTION_MOVE; + mNeedsFocus = true; notifyDataSetChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 466c8082f0b9..411980b399bd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -39,6 +39,7 @@ import android.text.format.DateUtils; import android.util.Log; import android.view.IWindowManager; import android.view.WindowManagerGlobal; +import android.widget.Switch; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; @@ -82,6 +83,11 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener mTile = new Tile(); updateDefaultTileAndIcon(); mServiceManager = host.getTileServices().getTileWrapper(this); + if (mServiceManager.isBooleanTile()) { + // Replace states with BooleanState + resetStates(); + } + mService = mServiceManager.getTileService(); mServiceManager.setTileChangeListener(this); mUser = ActivityManager.getCurrentUser(); @@ -246,8 +252,10 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener @Override public State newTileState() { - State state = new State(); - return state; + if (mServiceManager != null && mServiceManager.isBooleanTile()) { + return new BooleanState(); + } + return new State(); } @Override @@ -336,6 +344,12 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } else { state.contentDescription = state.label; } + + if (state instanceof BooleanState) { + state.expandedAccessibilityClassName = Switch.class.getName(); + ((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE); + } + } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java index effea6a877b8..f59e0c2d9bc2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java @@ -131,6 +131,24 @@ public class TileLifecycleManager extends BroadcastReceiver implements } /** + * Determines whether the associated TileService is a Boolean Tile. + * + * @return true if {@link TileService#META_DATA_BOOLEAN_TILE} is set to {@code true} for this + * tile + * @see TileService#META_DATA_BOOLEAN_TILE + */ + public boolean isBooleanTile() { + try { + ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(), + PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA); + return info.metaData != null + && info.metaData.getBoolean(TileService.META_DATA_BOOLEAN_TILE, false); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + /** * Binds just long enough to send any queued messages, then unbinds. */ public void flushMessagesAndUnbind() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java index 2a7e55fe6f8f..0b4e6485551c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java @@ -123,6 +123,10 @@ public class TileServiceManager { return mStateManager.isActiveTile(); } + public boolean isBooleanTile() { + return mStateManager.isBooleanTile(); + } + public void setShowingDialog(boolean dialog) { mShowingDialog = dialog; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java index 23f36e94abae..13cfa78b7c1e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -39,7 +39,7 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Dependency; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.phone.StatusBarIconController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.ArrayList; import java.util.Collections; @@ -296,14 +296,16 @@ public class TileServices extends IQSService.Stub { @Override public boolean isLocked() { - KeyguardMonitor keyguardMonitor = Dependency.get(KeyguardMonitor.class); - return keyguardMonitor.isShowing(); + KeyguardStateController keyguardStateController = + Dependency.get(KeyguardStateController.class); + return keyguardStateController.isShowing(); } @Override public boolean isSecure() { - KeyguardMonitor keyguardMonitor = Dependency.get(KeyguardMonitor.class); - return keyguardMonitor.isSecure() && keyguardMonitor.isShowing(); + KeyguardStateController keyguardStateController = + Dependency.get(KeyguardStateController.class); + return keyguardStateController.isMethodSecure() && keyguardStateController.isShowing(); } private CustomTile getTileForToken(IBinder token) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index daaee4cd5336..1c8e451e52f7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tileimpl; import android.content.Context; import android.os.Build; +import android.provider.Settings; import android.util.Log; import android.view.ContextThemeWrapper; @@ -32,6 +33,7 @@ import com.android.systemui.qs.tiles.BluetoothTile; import com.android.systemui.qs.tiles.CastTile; import com.android.systemui.qs.tiles.CellularTile; import com.android.systemui.qs.tiles.ColorInversionTile; +import com.android.systemui.qs.tiles.ControlsTile; import com.android.systemui.qs.tiles.DataSaverTile; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.FlashlightTile; @@ -58,6 +60,7 @@ public class QSFactoryImpl implements QSFactory { private final Provider<WifiTile> mWifiTileProvider; private final Provider<BluetoothTile> mBluetoothTileProvider; + private final Provider<ControlsTile> mControlsTileProvider; private final Provider<CellularTile> mCellularTileProvider; private final Provider<DndTile> mDndTileProvider; private final Provider<ColorInversionTile> mColorInversionTileProvider; @@ -81,6 +84,7 @@ public class QSFactoryImpl implements QSFactory { @Inject public QSFactoryImpl(Provider<WifiTile> wifiTileProvider, Provider<BluetoothTile> bluetoothTileProvider, + Provider<ControlsTile> controlsTileProvider, Provider<CellularTile> cellularTileProvider, Provider<DndTile> dndTileProvider, Provider<ColorInversionTile> colorInversionTileProvider, @@ -100,6 +104,7 @@ public class QSFactoryImpl implements QSFactory { Provider<UiModeNightTile> uiModeNightTileProvider) { mWifiTileProvider = wifiTileProvider; mBluetoothTileProvider = bluetoothTileProvider; + mControlsTileProvider = controlsTileProvider; mCellularTileProvider = cellularTileProvider; mDndTileProvider = dndTileProvider; mColorInversionTileProvider = colorInversionTileProvider; @@ -138,6 +143,11 @@ public class QSFactoryImpl implements QSFactory { return mWifiTileProvider.get(); case "bt": return mBluetoothTileProvider.get(); + case "controls": + if (Settings.System.getInt(mHost.getContext().getContentResolver(), + "qs_controls_tile_enabled", 0) == 1) { + return mControlsTileProvider.get(); + } else return null; case "cell": return mCellularTileProvider.get(); case "dnd": diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 681de378ff57..e0f26cd1a267 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -139,6 +139,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy mQSSettingsPanelOption = QSSettingsControllerKt.getQSSettingsPanelOption(); } + protected final void resetStates() { + mState = newTileState(); + mTmpState = newTileState(); + } + @NonNull @Override public Lifecycle getLifecycle() { @@ -629,6 +634,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy } } + /** + * Dumps the state of this tile along with its name. + * + * This may be used for CTS testing of tiles. + */ @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(this.getClass().getSimpleName() + ":"); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 2f1e01421ea5..d4e9fdff1b24 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -46,7 +46,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.NetworkController; import java.util.ArrayList; @@ -62,7 +62,7 @@ public class CastTile extends QSTileImpl<BooleanState> { private final CastController mController; private final CastDetailAdapter mDetailAdapter; - private final KeyguardMonitor mKeyguard; + private final KeyguardStateController mKeyguard; private final NetworkController mNetworkController; private final Callback mCallback = new Callback(); private final ActivityStarter mActivityStarter; @@ -71,12 +71,13 @@ public class CastTile extends QSTileImpl<BooleanState> { private static final String WFD_ENABLE = "persist.debug.wfd.enable"; @Inject - public CastTile(QSHost host, CastController castController, KeyguardMonitor keyguardMonitor, - NetworkController networkController, ActivityStarter activityStarter) { + public CastTile(QSHost host, CastController castController, + KeyguardStateController keyguardStateController, NetworkController networkController, + ActivityStarter activityStarter) { super(host); mController = castController; mDetailAdapter = new CastDetailAdapter(); - mKeyguard = keyguardMonitor; + mKeyguard = keyguardStateController; mNetworkController = networkController; mActivityStarter = activityStarter; mController.observe(this, mCallback); @@ -266,7 +267,8 @@ public class CastTile extends QSTileImpl<BooleanState> { } }; - private final class Callback implements CastController.Callback, KeyguardMonitor.Callback { + private final class Callback implements CastController.Callback, + KeyguardStateController.Callback { @Override public void onCastDevicesChanged() { refreshState(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 2967cf5ea427..e32fc32d4aa0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -98,6 +98,9 @@ public class CellularTile extends QSTileImpl<SignalState> { @Override public Intent getLongClickIntent() { + if (getState().state == Tile.STATE_UNAVAILABLE) { + return new Intent(Settings.ACTION_WIRELESS_SETTINGS); + } return getCellularSettingIntent(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java new file mode 100644 index 000000000000..0a59618b59a1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import com.android.systemui.R; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.HomeControlsPlugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.qs.DetailAdapter; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.shared.plugins.PluginManager; + +import javax.inject.Inject; + + +/** + * Temporary control test for prototyping + */ +public class ControlsTile extends QSTileImpl<BooleanState> { + private ControlsDetailAdapter mDetailAdapter; + private final ActivityStarter mActivityStarter; + private PluginManager mPluginManager; + private HomeControlsPlugin mPlugin; + private Intent mHomeAppIntent; + + @Inject + public ControlsTile(QSHost host, + ActivityStarter activityStarter, + PluginManager pluginManager) { + super(host); + mActivityStarter = activityStarter; + mPluginManager = pluginManager; + mDetailAdapter = (ControlsDetailAdapter) createDetailAdapter(); + + mHomeAppIntent = new Intent(Intent.ACTION_VIEW); + mHomeAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mHomeAppIntent.setComponent(new ComponentName("com.google.android.apps.chromecast.app", + "com.google.android.apps.chromecast.app.DiscoveryActivity")); + } + + @Override + public DetailAdapter getDetailAdapter() { + return mDetailAdapter; + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void handleSetListening(boolean listening) { + + } + + @Override + public void setDetailListening(boolean listening) { + if (mPlugin == null) return; + + mPlugin.setVisible(listening); + } + + @Override + protected void handleClick() { + showDetail(true); + } + + @Override + public Intent getLongClickIntent() { + return mHomeAppIntent; + } + + @Override + protected void handleSecondaryClick() { + showDetail(true); + } + + @Override + public CharSequence getTileLabel() { + return "Controls"; + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + state.icon = ResourceIcon.get(R.drawable.ic_lightbulb_outline_gm2_24px); + state.label = "Controls"; + } + + @Override + public boolean supportsDetailView() { + return getDetailAdapter() != null && mQSSettingsPanelOption == QSSettingsPanel.OPEN_CLICK; + } + + @Override + public int getMetricsCategory() { + return -1; + } + + @Override + protected String composeChangeAnnouncement() { + if (mState.value) { + return "On"; + } else { + return "Off"; + } + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + protected DetailAdapter createDetailAdapter() { + mDetailAdapter = new ControlsDetailAdapter(); + return mDetailAdapter; + } + + private class ControlsDetailAdapter implements DetailAdapter { + private View mDetailView; + protected LinearLayout mHomeControlsLayout; + + public CharSequence getTitle() { + return "Controls"; + } + + public Boolean getToggleState() { + return null; + } + + public boolean getToggleEnabled() { + return false; + } + + public View createDetailView(Context context, View convertView, final ViewGroup parent) { + mHomeControlsLayout = (LinearLayout) LayoutInflater.from(context).inflate( + R.layout.home_controls, parent, false); + mHomeControlsLayout.setVisibility(View.VISIBLE); + mPluginManager.addPluginListener( + new PluginListener<HomeControlsPlugin>() { + @Override + public void onPluginConnected(HomeControlsPlugin plugin, + Context pluginContext) { + mPlugin = plugin; + mPlugin.sendParentGroup(mHomeControlsLayout); + mPlugin.setVisible(true); + } + + @Override + public void onPluginDisconnected(HomeControlsPlugin plugin) { + + } + }, HomeControlsPlugin.class, false); + return mHomeControlsLayout; + } + + public Intent getSettingsIntent() { + return mHomeAppIntent; + } + + public void setToggleState(boolean state) { + + } + + public int getMetricsCategory() { + return -1; + } + + public boolean hasHeader() { + return false; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index 2755e9880b58..dafdd89ee62c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -101,11 +101,15 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements state.slash = new SlashState(); } state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label); + state.secondaryLabel = ""; if (!mFlashlightController.isAvailable()) { state.icon = mIcon; state.slash.isSlashed = true; + state.secondaryLabel = mContext.getString( + R.string.quick_settings_flashlight_camera_in_use); state.contentDescription = mContext.getString( - R.string.accessibility_quick_settings_flashlight_unavailable); + R.string.accessibility_quick_settings_flashlight_unavailable) + + ", " + state.secondaryLabel; state.state = Tile.STATE_UNAVAILABLE; return; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index 837ea9fc5f4e..fbdca3ba1c7b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -28,7 +28,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.tileimpl.QSTileImpl; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback; @@ -40,16 +40,16 @@ public class LocationTile extends QSTileImpl<BooleanState> { private final Icon mIcon = ResourceIcon.get(R.drawable.ic_location); private final LocationController mController; - private final KeyguardMonitor mKeyguard; + private final KeyguardStateController mKeyguard; private final ActivityStarter mActivityStarter; private final Callback mCallback = new Callback(); @Inject public LocationTile(QSHost host, LocationController locationController, - KeyguardMonitor keyguardMonitor, ActivityStarter activityStarter) { + KeyguardStateController keyguardStateController, ActivityStarter activityStarter) { super(host); mController = locationController; - mKeyguard = keyguardMonitor; + mKeyguard = keyguardStateController; mActivityStarter = activityStarter; mController.observe(this, mCallback); mKeyguard.observe(this, mCallback); @@ -71,7 +71,7 @@ public class LocationTile extends QSTileImpl<BooleanState> { @Override protected void handleClick() { - if (mKeyguard.isSecure() && mKeyguard.isShowing()) { + if (mKeyguard.isMethodSecure() && mKeyguard.isShowing()) { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { final boolean wasEnabled = mState.value; mHost.openPanels(); @@ -97,7 +97,7 @@ public class LocationTile extends QSTileImpl<BooleanState> { // Work around for bug 15916487: don't show location tile on top of lock screen. After the // bug is fixed, this should be reverted to only hiding it on secure lock screens: - // state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing()); + // state.visible = !(mKeyguard.isMethodSecure() && mKeyguard.isShowing()); state.value = locationEnabled; checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_SHARE_LOCATION); if (state.disabledByPolicy == false) { @@ -133,7 +133,7 @@ public class LocationTile extends QSTileImpl<BooleanState> { } private final class Callback implements LocationChangeCallback, - KeyguardMonitor.Callback { + KeyguardStateController.Callback { @Override public void onLocationSettingsChanged(boolean enabled) { refreshState(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 9268ee0705a2..3fc139882693 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -58,6 +58,7 @@ import com.android.internal.policy.ScreenDecorationsUtils; import com.android.systemui.Dumpable; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.model.SysUiState; +import com.android.systemui.pip.PipUI; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -100,6 +101,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000; private final Context mContext; + private final PipUI mPipUI; private SysUiState mSysUiState; private final Handler mHandler; private final NavigationBarController mNavBarController; @@ -353,6 +355,19 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + @Override + public void setShelfHeight(boolean visible, int shelfHeight) { + if (!verifyCaller("setShelfHeight")) { + return; + } + long token = Binder.clearCallingIdentity(); + try { + mPipUI.setShelfHeight(visible, shelfHeight); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { @@ -464,8 +479,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis public OverviewProxyService(Context context, DeviceProvisionedController provisionController, NavigationBarController navBarController, NavigationModeController navModeController, StatusBarWindowController statusBarWinController, - SysUiState sysUiState) { + SysUiState sysUiState, PipUI pipUI) { mContext = context; + mPipUI = pipUI; mHandler = new Handler(); mNavBarController = navBarController; mStatusBarWinController = statusBarWinController; diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 0fc4fe72bd54..0a8264bcef87 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -17,29 +17,36 @@ package com.android.systemui.recents; import android.content.ContentResolver; +import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; import android.provider.Settings; -import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.CommandQueue; import java.io.FileDescriptor; import java.io.PrintWriter; +import javax.inject.Inject; + /** * A proxy to a Recents implementation. */ public class Recents extends SystemUI implements CommandQueue.Callbacks { - private RecentsImplementation mImpl; + private final RecentsImplementation mImpl; + + @Inject + public Recents(Context context, RecentsImplementation impl) { + super(context); + mImpl = impl; + } @Override public void start() { getComponent(CommandQueue.class).addCallback(this); putComponent(Recents.class, this); - mImpl = createRecentsImplementationFromConfig(); mImpl.onStart(mContext, this); } @@ -139,28 +146,6 @@ public class Recents extends SystemUI implements CommandQueue.Callbacks { (Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0); } - /** - * @return The recents implementation from the config. - */ - private RecentsImplementation createRecentsImplementationFromConfig() { - final String clsName = mContext.getString(R.string.config_recentsComponent); - if (clsName == null || clsName.length() == 0) { - throw new RuntimeException("No recents component configured", null); - } - Class<?> cls = null; - try { - cls = mContext.getClassLoader().loadClass(clsName); - } catch (Throwable t) { - throw new RuntimeException("Error loading recents component: " + clsName, t); - } - try { - RecentsImplementation impl = (RecentsImplementation) cls.newInstance(); - return impl; - } catch (Throwable t) { - throw new RuntimeException("Error creating recents component: " + clsName, t); - } - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mImpl.dump(pw); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java new file mode 100644 index 000000000000..55552850890f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 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.recents; + +import android.content.Context; + +import com.android.systemui.R; + +import dagger.Module; +import dagger.Provides; + +/** + * Dagger injection module for {@link RecentsImplementation} + */ +@Module +public class RecentsModule { + /** + * @return The {@link RecentsImplementation} from the config. + */ + @Provides + public RecentsImplementation provideRecentsImpl(Context context) { + final String clsName = context.getString(R.string.config_recentsComponent); + if (clsName == null || clsName.length() == 0) { + throw new RuntimeException("No recents component configured", null); + } + Class<?> cls = null; + try { + cls = context.getClassLoader().loadClass(clsName); + } catch (Throwable t) { + throw new RuntimeException("Error loading recents component: " + clsName, t); + } + try { + RecentsImplementation impl = (RecentsImplementation) cls.newInstance(); + return impl; + } catch (Throwable t) { + throw new RuntimeException("Error creating recents component: " + clsName, t); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java index d0c47345a83a..aa6444973a6f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java @@ -61,8 +61,10 @@ import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; import android.widget.TextView; +import com.android.systemui.Dependency; import com.android.systemui.Prefs; import com.android.systemui.R; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; @@ -245,10 +247,15 @@ public class RecentsOnboarding { private final View.OnAttachStateChangeListener mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { + + private final BroadcastDispatcher mBroadcastDispatcher = Dependency.get( + BroadcastDispatcher.class); + @Override public void onViewAttachedToWindow(View view) { if (view == mLayout) { - mContext.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); + mBroadcastDispatcher.registerReceiver(mReceiver, + new IntentFilter(Intent.ACTION_SCREEN_OFF)); mLayoutAttachedToWindow = true; if (view.getTag().equals(R.string.recents_swipe_up_onboarding)) { mHasDismissedSwipeUpTip = false; @@ -273,7 +280,7 @@ public class RecentsOnboarding { } mOverviewOpenedCountSinceQuickScrubTipDismiss = 0; } - mContext.unregisterReceiver(mReceiver); + mBroadcastDispatcher.unregisterReceiver(mReceiver); } } }; @@ -335,10 +342,11 @@ public class RecentsOnboarding { private void notifyOnTip(int action, int target) { try { IOverviewProxy overviewProxy = mOverviewProxyService.getProxy(); - if(overviewProxy != null) { + if (overviewProxy != null) { overviewProxy.onTip(action, target); } - } catch (RemoteException e) {} + } catch (RemoteException e) { + } } public void onNavigationModeChanged(int mode) { @@ -489,7 +497,7 @@ public class RecentsOnboarding { WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, flags, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("RecentsOnboarding"); lp.gravity = gravity; return lp; diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index c3c0d63f66c4..2d1c08719119 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -47,6 +47,7 @@ import android.widget.TextView; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.phone.NavigationBarView; @@ -127,7 +128,7 @@ public class ScreenPinningRequest implements View.OnClickListener, | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); lp.token = new Binder(); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("ScreenPinningConfirmation"); lp.gravity = Gravity.FILL; return lp; @@ -159,6 +160,8 @@ public class ScreenPinningRequest implements View.OnClickListener, private ValueAnimator mColorAnim; private ViewGroup mLayout; private boolean mShowCancel; + private final BroadcastDispatcher mBroadcastDispatcher = + Dependency.get(BroadcastDispatcher.class); public RequestWindowView(Context context, boolean showCancel) { super(context); @@ -212,7 +215,7 @@ public class ScreenPinningRequest implements View.OnClickListener, IntentFilter filter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_SCREEN_OFF); - mContext.registerReceiver(mReceiver, filter); + mBroadcastDispatcher.registerReceiver(mReceiver, filter); } private void inflateView(int rotation) { @@ -313,7 +316,7 @@ public class ScreenPinningRequest implements View.OnClickListener, @Override public void onDetachedFromWindow() { - mContext.unregisterReceiver(mReceiver); + mBroadcastDispatcher.unregisterReceiver(mReceiver); } protected void onConfigurationChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index 1e0a9f157ff5..176676fcb9b3 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -152,6 +152,20 @@ public class BrightnessController implements ToggleSlider.Listener { private final Runnable mStartListeningRunnable = new Runnable() { @Override public void run() { + if (mListening) { + return; + } + mListening = true; + + if (mVrManager != null) { + try { + mVrManager.registerListener(mVrStateCallbacks); + mIsVrModeEnabled = mVrManager.getVrModeState(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register VR mode state listener: ", e); + } + } + mBrightnessObserver.startObserving(); mUserTracker.startTracking(); @@ -167,6 +181,19 @@ public class BrightnessController implements ToggleSlider.Listener { private final Runnable mStopListeningRunnable = new Runnable() { @Override public void run() { + if (!mListening) { + return; + } + mListening = false; + + if (mVrManager != null) { + try { + mVrManager.unregisterListener(mVrStateCallbacks); + } catch (RemoteException e) { + Log.e(TAG, "Failed to unregister VR mode state listener: ", e); + } + } + mBrightnessObserver.stopObserving(); mUserTracker.stopTracking(); @@ -297,39 +324,12 @@ public class BrightnessController implements ToggleSlider.Listener { } public void registerCallbacks() { - if (mListening) { - return; - } - - if (mVrManager != null) { - try { - mVrManager.registerListener(mVrStateCallbacks); - mIsVrModeEnabled = mVrManager.getVrModeState(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to register VR mode state listener: ", e); - } - } - mBackgroundHandler.post(mStartListeningRunnable); - mListening = true; } /** Unregister all call backs, both to and from the controller */ public void unregisterCallbacks() { - if (!mListening) { - return; - } - - if (mVrManager != null) { - try { - mVrManager.unregisterListener(mVrStateCallbacks); - } catch (RemoteException e) { - Log.e(TAG, "Failed to unregister VR mode state listener: ", e); - } - } - mBackgroundHandler.post(mStopListeningRunnable); - mListening = false; mControlValueInitialized = false; } diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java index 07675e248906..df9791d1bd37 100644 --- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java @@ -19,6 +19,7 @@ package com.android.systemui.shortcut; import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; +import android.content.Context; import android.content.res.Configuration; import android.os.RemoteException; import android.util.Log; @@ -52,6 +53,10 @@ public class ShortcutKeyDispatcher extends SystemUI protected final long SC_DOCK_LEFT = META_MASK | KeyEvent.KEYCODE_LEFT_BRACKET; protected final long SC_DOCK_RIGHT = META_MASK | KeyEvent.KEYCODE_RIGHT_BRACKET; + public ShortcutKeyDispatcher(Context context) { + super(context); + } + /** * Registers a shortcut key to window manager. * @param shortcutCode packed representation of shortcut key code and meta information diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index cd2074fd64b8..c8b2b6aee0b3 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -19,6 +19,7 @@ package com.android.systemui.stackdivider; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import android.content.Context; import android.content.res.Configuration; import android.os.RemoteException; import android.util.Log; @@ -50,6 +51,10 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks { private boolean mHomeStackResizable = false; private ForcedResizableInfoActivityController mForcedResizableController; + public Divider(Context context) { + super(context); + } + @Override public void start() { mWindowManager = new DividerWindowManager(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java index 33bcefb323f8..e24a3625769c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java @@ -65,7 +65,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim Log.v(TAG, "showNotification"); } addAlertEntry(entry); - updateNotification(entry.key, true /* alert */); + updateNotification(entry.getKey(), true /* alert */); entry.setInterruption(); } @@ -182,7 +182,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim protected final void addAlertEntry(@NonNull NotificationEntry entry) { AlertEntry alertEntry = createAlertEntry(); alertEntry.setEntry(entry); - mAlertEntries.put(entry.key, alertEntry); + mAlertEntries.put(entry.getKey(), alertEntry); onAlertEntryAdded(alertEntry); entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); } @@ -251,7 +251,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim @Override public boolean shouldExtendLifetime(NotificationEntry entry) { - return !canRemoveImmediately(entry.key); + return !canRemoveImmediately(entry.getKey()); } @Override @@ -260,7 +260,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim mExtendedLifetimeAlertEntries.add(entry); // We need to make sure that entries are stopping to alert eventually, let's remove // this as soon as possible. - AlertEntry alertEntry = mAlertEntries.get(entry.key); + AlertEntry alertEntry = mAlertEntries.get(entry.getKey()); alertEntry.removeAsSoonAsPossible(); } else { mExtendedLifetimeAlertEntries.remove(entry); @@ -276,7 +276,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim @Nullable protected Runnable mRemoveAlertRunnable; public void setEntry(@NonNull final NotificationEntry entry) { - setEntry(entry, () -> removeAlertEntry(entry.key)); + setEntry(entry, () -> removeAlertEntry(entry.getKey())); } public void setEntry(@NonNull final NotificationEntry entry, @@ -332,7 +332,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim public int compareTo(@NonNull AlertEntry alertEntry) { return (mPostTime < alertEntry.mPostTime) ? 1 : ((mPostTime == alertEntry.mPostTime) - ? mEntry.key.compareTo(alertEntry.mEntry.key) : -1); + ? mEntry.getKey().compareTo(alertEntry.mEntry.getKey()) : -1); } public void reset() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 134d4b87a159..34f543702d96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -22,6 +22,8 @@ import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEF import static android.inputmethodservice.InputMethodService.IME_INVISIBLE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.TYPE_TOP_BAR; import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS; @@ -43,12 +45,17 @@ import android.os.Looper; import android.os.Message; import android.util.Pair; import android.util.SparseArray; +import android.view.InsetsFlags; +import android.view.InsetsState.InternalInsetType; +import android.view.View; +import android.view.WindowInsetsController.Appearance; import androidx.annotation.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.view.AppearanceRegion; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.policy.CallbackController; @@ -76,7 +83,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_EXPAND_NOTIFICATIONS = 3 << MSG_SHIFT; private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT; private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT; - private static final int MSG_SET_SYSTEMUI_VISIBILITY = 6 << MSG_SHIFT; + private static final int MSG_SYSTEM_BAR_APPEARANCE_CHANGED = 6 << MSG_SHIFT; private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT; private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT; private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT; @@ -115,6 +122,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT; private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT; private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT; + private static final int MSG_SHOW_TRANSIENT = 48 << MSG_SHIFT; + private static final int MSG_ABORT_TRANSIENT = 49 << MSG_SHIFT; + private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -160,28 +170,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void animateExpandSettingsPanel(String obj) { } /** - * Called to notify visibility flag changes. - * @see IStatusBar#setSystemUiVisibility(int, int, int, int, int, Rect, Rect). - * - * @param displayId The id of the display to notify. - * @param vis The visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will - * be reported separately in fullscreenStackVis and dockedStackVis. - * @param fullscreenStackVis The flags which only apply in the region of the fullscreen - * stack, which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR. - * @param dockedStackVis The flags that only apply in the region of the docked stack, which - * is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR. - * @param mask Which flags to change. - * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen - * coordinates. - * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates. - * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME. - */ - default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, - boolean navbarColorManagedByIme) { - } - - /** * Called to notify IME window status changes. * * @param displayId The id of the display to notify. @@ -270,12 +258,13 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void onRotationProposal(int rotation, boolean isValid) { } - default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, - int type, boolean requireConfirmation, int userId, String opPackageName) { } - default void onBiometricAuthenticated(boolean authenticated, String failureReason) { } + default void showAuthenticationDialog(Bundle bundle, + IBiometricServiceReceiverInternal receiver, int biometricModality, + boolean requireConfirmation, int userId, String opPackageName) { } + default void onBiometricAuthenticated() { } default void onBiometricHelp(String message) { } - default void onBiometricError(String error) { } - default void hideBiometricDialog() { } + default void onBiometricError(int modality, int error, int vendorCode) { } + default void hideAuthenticationDialog() { } /** * @see IStatusBar#onDisplayReady(int) @@ -291,6 +280,28 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< * @see IStatusBar#onRecentsAnimationStateChanged(boolean) */ default void onRecentsAnimationStateChanged(boolean running) { } + + /** + * @see IStatusBar#onSystemBarAppearanceChanged(int, int, AppearanceRegion[], boolean). + */ + default void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { } + + /** + * @see IStatusBar#showTransient(int, int[]). + */ + default void showTransient(int displayId, @InternalInsetType int[] types) { } + + /** + * @see IStatusBar#abortTransient(int, int[]). + */ + default void abortTransient(int displayId, @InternalInsetType int[] types) { } + + /** + * @see IStatusBar#topAppWindowChanged(int, boolean, boolean). + */ + default void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { + } } @VisibleForTesting @@ -455,28 +466,53 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } } + // TODO(b/118118435): Remove this function after migration @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean navbarColorManagedByIme) { synchronized (mLock) { - // Don't coalesce these, since it might have one time flags set such as - // STATUS_BAR_UNHIDE which might get lost. + final boolean hasDockedStack = !dockedStackBounds.isEmpty(); + final boolean transientStatus = (vis & View.STATUS_BAR_TRANSIENT) != 0; + final boolean transientNavigation = (vis & View.NAVIGATION_BAR_TRANSIENT) != 0; + if (transientStatus && transientNavigation) { + showTransient(displayId, new int[]{TYPE_TOP_BAR, TYPE_NAVIGATION_BAR}); + } else if (transientStatus) { + showTransient(displayId, new int[]{TYPE_TOP_BAR}); + abortTransient(displayId, new int[]{TYPE_NAVIGATION_BAR}); + } else if (transientNavigation) { + showTransient(displayId, new int[]{TYPE_NAVIGATION_BAR}); + abortTransient(displayId, new int[]{TYPE_TOP_BAR}); + } else { + abortTransient(displayId, new int[]{TYPE_TOP_BAR, TYPE_NAVIGATION_BAR}); + } SomeArgs args = SomeArgs.obtain(); args.argi1 = displayId; - args.argi2 = vis; - args.argi3 = fullscreenStackVis; - args.argi4 = dockedStackVis; - args.argi5 = mask; - args.argi6 = navbarColorManagedByIme ? 1 : 0; - args.arg1 = fullscreenStackBounds; - args.arg2 = dockedStackBounds; - mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget(); + args.argi2 = InsetsFlags.getAppearance(vis); + args.argi3 = navbarColorManagedByIme ? 1 : 0; + final int fullscreenAppearance = InsetsFlags.getAppearance(fullscreenStackVis); + final int dockedAppearance = InsetsFlags.getAppearance(dockedStackVis); + args.arg1 = hasDockedStack + ? new AppearanceRegion[]{ + new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds), + new AppearanceRegion(dockedAppearance, dockedStackBounds)} + : new AppearanceRegion[]{ + new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds)}; + mHandler.obtainMessage(MSG_SYSTEM_BAR_APPEARANCE_CHANGED, args).sendToTarget(); } } @Override - public void topAppWindowChanged(int displayId, boolean menuVisible) { } + public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { + synchronized (mLock) { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = displayId; + args.argi2 = isFullscreen ? 1 : 0; + args.argi3 = isImmersive ? 1 : 0; + mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, args).sendToTarget(); + } + + } @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, @@ -740,13 +776,13 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, - int type, boolean requireConfirmation, int userId, String opPackageName) { + public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, + int biometricModality, boolean requireConfirmation, int userId, String opPackageName) { synchronized (mLock) { SomeArgs args = SomeArgs.obtain(); args.arg1 = bundle; args.arg2 = receiver; - args.argi1 = type; + args.argi1 = biometricModality; args.arg3 = requireConfirmation; args.argi2 = userId; args.arg4 = opPackageName; @@ -756,12 +792,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void onBiometricAuthenticated(boolean authenticated, String failureReason) { + public void onBiometricAuthenticated() { synchronized (mLock) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = authenticated; - args.arg2 = failureReason; - mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget(); + mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget(); } } @@ -773,14 +806,18 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void onBiometricError(String error) { + public void onBiometricError(int modality, int error, int vendorCode) { synchronized (mLock) { - mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget(); + SomeArgs args = SomeArgs.obtain(); + args.argi1 = modality; + args.argi2 = error; + args.argi3 = vendorCode; + mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, args).sendToTarget(); } } @Override - public void hideBiometricDialog() { + public void hideAuthenticationDialog() { synchronized (mLock) { mHandler.obtainMessage(MSG_BIOMETRIC_HIDE).sendToTarget(); } @@ -826,6 +863,33 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } } + @Override + public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { + synchronized (mLock) { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = displayId; + args.argi2 = appearance; + args.argi3 = navbarColorManagedByIme ? 1 : 0; + args.arg1 = appearanceRegions; + mHandler.obtainMessage(MSG_SYSTEM_BAR_APPEARANCE_CHANGED, args).sendToTarget(); + } + } + + @Override + public void showTransient(int displayId, int[] types) { + synchronized (mLock) { + mHandler.obtainMessage(MSG_SHOW_TRANSIENT, displayId, 0, types).sendToTarget(); + } + } + + @Override + public void abortTransient(int displayId, int[] types) { + synchronized (mLock) { + mHandler.obtainMessage(MSG_ABORT_TRANSIENT, displayId, 0, types).sendToTarget(); + } + } + private final class H extends Handler { private H(Looper l) { super(l); @@ -878,15 +942,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< mCallbacks.get(i).animateExpandSettingsPanel((String) msg.obj); } break; - case MSG_SET_SYSTEMUI_VISIBILITY: - args = (SomeArgs) msg.obj; - for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3, - args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2, - args.argi6 == 1); - } - args.recycle(); - break; case MSG_SHOW_IME_BUTTON: args = (SomeArgs) msg.obj; handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */, @@ -1032,10 +1087,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED); SomeArgs someArgs = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).showBiometricDialog( + mCallbacks.get(i).showAuthenticationDialog( (Bundle) someArgs.arg1, (IBiometricServiceReceiverInternal) someArgs.arg2, - someArgs.argi1 /* type */, + someArgs.argi1 /* biometricModality */, (boolean) someArgs.arg3 /* requireConfirmation */, someArgs.argi2 /* userId */, (String) someArgs.arg4 /* opPackageName */); @@ -1044,13 +1099,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< break; } case MSG_BIOMETRIC_AUTHENTICATED: { - SomeArgs someArgs = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onBiometricAuthenticated( - (boolean) someArgs.arg1 /* authenticated */, - (String) someArgs.arg2 /* failureReason */); + mCallbacks.get(i).onBiometricAuthenticated(); } - someArgs.recycle(); break; } case MSG_BIOMETRIC_HELP: @@ -1059,13 +1110,19 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } break; case MSG_BIOMETRIC_ERROR: + SomeArgs someArgs = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onBiometricError((String) msg.obj); + mCallbacks.get(i).onBiometricError( + someArgs.argi1 /* modality */, + someArgs.argi2 /* error */, + someArgs.argi3 /* vendorCode */ + ); } + someArgs.recycle(); break; case MSG_BIOMETRIC_HIDE: for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).hideBiometricDialog(); + mCallbacks.get(i).hideAuthenticationDialog(); } break; case MSG_SHOW_CHARGING_ANIMATION: @@ -1093,6 +1150,39 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< mCallbacks.get(i).onRecentsAnimationStateChanged(msg.arg1 > 0); } break; + case MSG_SYSTEM_BAR_APPEARANCE_CHANGED: + args = (SomeArgs) msg.obj; + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).onSystemBarAppearanceChanged(args.argi1, args.argi2, + (AppearanceRegion[]) args.arg1, args.argi3 == 1); + } + args.recycle(); + break; + case MSG_SHOW_TRANSIENT: { + final int displayId = msg.arg1; + final int[] types = (int[]) msg.obj; + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).showTransient(displayId, types); + } + break; + } + case MSG_ABORT_TRANSIENT: { + final int displayId = msg.arg1; + final int[] types = (int[]) msg.obj; + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).abortTransient(displayId, types); + } + break; + } + case MSG_TOP_APP_WINDOW_CHANGED: { + args = (SomeArgs) msg.obj; + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).topAppWindowChanged( + args.argi1, args.argi2 != 0, args.argi3 != 0); + } + args.recycle(); + break; + } } } } @@ -1100,6 +1190,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< // Need this class since CommandQueue already extends IStatusBar.Stub, so CommandQueueStart // is needed so it can extend SystemUI. public static class CommandQueueStart extends SystemUI { + public CommandQueueStart(Context context) { + super(context); + } + @Override public void start() { putComponent(CommandQueue.class, new CommandQueue(mContext)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java new file mode 100644 index 000000000000..341c49a87156 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import android.annotation.NonNull; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.provider.DeviceConfig; +import android.util.ArrayMap; + +import com.android.systemui.dagger.qualifiers.BgHandler; + +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Class to manage simple DeviceConfig-based feature flags. + * + * To enable or disable a flag, run: + * + * {@code + * $ adb shell device_config put systemui <key> <true|false> +* } + * + * You will probably need to restart systemui for the changes to be picked up: + * + * {@code + * $ adb shell am crash com.android.systemui + * } + */ +@Singleton +public class FeatureFlags { + private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>(); + + @Inject + public FeatureFlags(@BgHandler Handler bgHandler) { + DeviceConfig.addOnPropertiesChangedListener( + "systemui", + new HandlerExecutor(bgHandler), + this::onPropertiesChanged); + } + + public boolean isNewNotifPipelineEnabled() { + return getDeviceConfigFlag("notification.newpipeline.enabled", false); + } + + private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { + synchronized (mCachedDeviceConfigFlags) { + for (String key : properties.getKeyset()) { + mCachedDeviceConfigFlags.remove(key); + } + } + } + + private boolean getDeviceConfigFlag(String key, boolean defaultValue) { + synchronized (mCachedDeviceConfigFlags) { + Boolean flag = mCachedDeviceConfigFlags.get(key); + if (flag == null) { + flag = DeviceConfig.getBoolean("systemui", key, defaultValue); + mCachedDeviceConfigFlags.put(key, flag); + } + return flag; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 156e3c072dbe..681f3abaea91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -57,8 +57,8 @@ import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.UnlockMethodCache; import com.android.systemui.statusbar.policy.AccessibilityController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -72,7 +72,7 @@ import java.util.IllegalFormatConversionException; * Controls the indications and error messages shown on the Keyguard */ public class KeyguardIndicationController implements StateListener, - UnlockMethodCache.OnUnlockMethodChangedListener { + KeyguardStateController.Callback { private static final String TAG = "KeyguardIndication"; private static final boolean DEBUG_CHARGING_SPEED = false; @@ -86,7 +86,7 @@ public class KeyguardIndicationController implements StateListener, private final Context mContext; private final ShadeController mShadeController; private final AccessibilityController mAccessibilityController; - private final UnlockMethodCache mUnlockMethodCache; + private final KeyguardStateController mKeyguardStateController; private final StatusBarStateController mStatusBarStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private ViewGroup mIndicationArea; @@ -137,7 +137,7 @@ public class KeyguardIndicationController implements StateListener, WakeLock.createPartial(context, "Doze:KeyguardIndication"), Dependency.get(ShadeController.class), Dependency.get(AccessibilityController.class), - UnlockMethodCache.getInstance(context), + Dependency.get(KeyguardStateController.class), Dependency.get(StatusBarStateController.class), Dependency.get(KeyguardUpdateMonitor.class)); } @@ -148,14 +148,15 @@ public class KeyguardIndicationController implements StateListener, @VisibleForTesting KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon, LockPatternUtils lockPatternUtils, WakeLock wakeLock, ShadeController shadeController, - AccessibilityController accessibilityController, UnlockMethodCache unlockMethodCache, + AccessibilityController accessibilityController, + KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, KeyguardUpdateMonitor keyguardUpdateMonitor) { mContext = context; mLockIcon = lockIcon; mShadeController = shadeController; mAccessibilityController = accessibilityController; - mUnlockMethodCache = unlockMethodCache; + mKeyguardStateController = keyguardStateController; mStatusBarStateController = statusBarStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; // lock icon is not used on all form factors. @@ -181,7 +182,7 @@ public class KeyguardIndicationController implements StateListener, mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); mKeyguardUpdateMonitor.registerCallback(mTickReceiver); mStatusBarStateController.addCallback(this); - mUnlockMethodCache.addListener(this); + mKeyguardStateController.addCallback(this); } public void setIndicationArea(ViewGroup indicationArea) { @@ -583,7 +584,7 @@ public class KeyguardIndicationController implements StateListener, } @Override - public void onUnlockMethodStateChanged() { + public void onUnlockedChanged() { updateIndication(!mDozing); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index 7bcbd3683130..1f389049f423 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import static com.android.systemui.SysUiServiceProvider.getComponent; import android.content.Context; @@ -37,6 +36,8 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.systemui.Dependency; +import com.android.systemui.assist.AssistHandleViewController; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.phone.AutoHideController; @@ -47,7 +48,6 @@ import com.android.systemui.statusbar.phone.NavigationBarView; import com.android.systemui.statusbar.policy.BatteryController; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; @@ -66,7 +66,7 @@ public class NavigationBarController implements Callbacks { SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>(); @Inject - public NavigationBarController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) { + public NavigationBarController(Context context, @MainHandler Handler handler) { mContext = context; mHandler = handler; mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); @@ -151,9 +151,11 @@ public class NavigationBarController implements Callbacks { // Dependency problem. AutoHideController autoHideController = isOnDefaultDisplay ? Dependency.get(AutoHideController.class) - : new AutoHideController(context, mHandler); + : new AutoHideController(context, mHandler, + Dependency.get(NotificationRemoteInputManager.class), + Dependency.get(IWindowManager.class)); navBar.setAutoHideController(autoHideController); - navBar.restoreSystemUiVisibilityState(); + navBar.restoreAppearanceAndTransientState(); mNavigationBars.append(displayId, navBar); if (result != null) { @@ -230,7 +232,15 @@ public class NavigationBarController implements Callbacks { } /** @return {@link NavigationBarFragment} on the default display. */ + @Nullable public NavigationBarFragment getDefaultNavigationBarFragment() { return mNavigationBars.get(DEFAULT_DISPLAY); } + + /** @return {@link AssistHandleViewController} (only on the default display). */ + @Nullable + public AssistHandleViewController getAssistHandlerViewController() { + NavigationBarFragment navBar = getDefaultNavigationBarFragment(); + return navBar == null ? null : navBar.getAssistHandlerViewController(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index 14009214bdc5..c4de2d3572bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.notification.NotificationEntryManag import static com.android.systemui.statusbar.phone.StatusBar.DEBUG; import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_CHILD_NOTIFICATIONS; +import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.NotificationManager; import android.content.ComponentName; @@ -57,8 +58,9 @@ public class NotificationListener extends NotificationListenerWithPlugins { private final NotificationGroupManager mGroupManager = Dependency.get(NotificationGroupManager.class); - private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>(); private final Context mContext; + private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>(); + @Nullable private NotifServiceListener mDownstreamListener; @Inject public NotificationListener(Context context) { @@ -69,6 +71,10 @@ public class NotificationListener extends NotificationListenerWithPlugins { mSettingsListeners.add(listener); } + public void setDownstreamListener(NotifServiceListener downstreamListener) { + mDownstreamListener = downstreamListener; + } + @Override public void onListenerConnected() { if (DEBUG) Log.d(TAG, "onListenerConnected"); @@ -81,6 +87,9 @@ public class NotificationListener extends NotificationListenerWithPlugins { final RankingMap currentRanking = getCurrentRanking(); Dependency.get(Dependency.MAIN_HANDLER).post(() -> { for (StatusBarNotification sbn : notifications) { + if (mDownstreamListener != null) { + mDownstreamListener.onNotificationPosted(sbn, currentRanking); + } mEntryManager.addNotification(sbn, currentRanking); } }); @@ -95,6 +104,11 @@ public class NotificationListener extends NotificationListenerWithPlugins { if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) { Dependency.get(Dependency.MAIN_HANDLER).post(() -> { processForRemoteInput(sbn.getNotification(), mContext); + + if (mDownstreamListener != null) { + mDownstreamListener.onNotificationPosted(sbn, rankingMap); + } + String key = sbn.getKey(); boolean isUpdate = mEntryManager.getNotificationData().get(key) != null; @@ -113,7 +127,7 @@ public class NotificationListener extends NotificationListenerWithPlugins { mEntryManager.removeNotification(key, rankingMap, UNDEFINED_DISMISS_REASON); } else { mEntryManager.getNotificationData() - .updateRanking(rankingMap); + .updateRanking(rankingMap, "onNotificationPosted"); } return; } @@ -133,6 +147,9 @@ public class NotificationListener extends NotificationListenerWithPlugins { if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) { final String key = sbn.getKey(); Dependency.get(Dependency.MAIN_HANDLER).post(() -> { + if (mDownstreamListener != null) { + mDownstreamListener.onNotificationRemoved(sbn, rankingMap, reason); + } mEntryManager.removeNotification(key, rankingMap, reason); }); } @@ -149,6 +166,9 @@ public class NotificationListener extends NotificationListenerWithPlugins { if (rankingMap != null) { RankingMap r = onPluginRankingUpdate(rankingMap); Dependency.get(Dependency.MAIN_HANDLER).post(() -> { + if (mDownstreamListener != null) { + mDownstreamListener.onNotificationRankingUpdate(rankingMap); + } mEntryManager.updateNotificationRanking(r); }); } @@ -175,4 +195,12 @@ public class NotificationListener extends NotificationListenerWithPlugins { default void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) { } } + + /** Interface for listening to add/remove events that we receive from NotificationManager. */ + public interface NotifServiceListener { + void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap); + void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap); + void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason); + void onNotificationRankingUpdate(RankingMap rankingMap); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index f782fab7e49f..7adf7af89f0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -54,7 +54,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -76,7 +76,8 @@ public class NotificationLockscreenUserManagerImpl implements private final DeviceProvisionedController mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); - private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + private final KeyguardStateController mKeyguardStateController = Dependency.get( + KeyguardStateController.class); // Lazy private NotificationEntryManager mEntryManager; @@ -105,7 +106,7 @@ public class NotificationLockscreenUserManagerImpl implements isCurrentProfile(getSendingUserId())) { mUsersAllowingPrivateNotifications.clear(); updateLockscreenNotificationSetting(); - getEntryManager().updateNotifications(); + getEntryManager().updateNotifications("ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED"); } } }; @@ -123,7 +124,7 @@ public class NotificationLockscreenUserManagerImpl implements updatePublicMode(); // The filtering needs to happen before the update call below in order to make sure // the presenter has the updated notifications from the new user - getEntryManager().getNotificationData().filterAndSort(); + getEntryManager().getNotificationData().filterAndSort("user switched"); mPresenter.onUserSwitched(mCurrentUserId); for (UserChangedListener listener : mListeners) { @@ -204,7 +205,8 @@ public class NotificationLockscreenUserManagerImpl implements mUsersAllowingNotifications.clear(); // ... and refresh all the notifications updateLockscreenNotificationSetting(); - getEntryManager().updateNotifications(); + getEntryManager().updateNotifications("LOCK_SCREEN_SHOW_NOTIFICATIONS," + + " or LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS change"); } }; @@ -213,7 +215,8 @@ public class NotificationLockscreenUserManagerImpl implements public void onChange(boolean selfChange) { updateLockscreenNotificationSetting(); if (mDeviceProvisionedController.isDeviceProvisioned()) { - getEntryManager().updateNotifications(); + getEntryManager().updateNotifications("LOCK_SCREEN_ALLOW_REMOTE_INPUT" + + " or ZEN_MODE change"); } } }; @@ -320,7 +323,7 @@ public class NotificationLockscreenUserManagerImpl implements exceedsPriorityThreshold = entry.getBucket() != BUCKET_SILENT; } else { exceedsPriorityThreshold = - !getEntryManager().getNotificationData().isAmbient(entry.key); + !getEntryManager().getNotificationData().isAmbient(entry.getKey()); } return mShowLockscreenNotifications && exceedsPriorityThreshold; } @@ -442,15 +445,15 @@ public class NotificationLockscreenUserManagerImpl implements /** @return true if the entry needs redaction when on the lockscreen. */ public boolean needsRedaction(NotificationEntry ent) { - int userId = ent.notification.getUserId(); + int userId = ent.getSbn().getUserId(); boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId); boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId); boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction; boolean notificationRequestsRedaction = - ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE; - boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey()); + ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE; + boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey()); return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen; } @@ -507,8 +510,8 @@ public class NotificationLockscreenUserManagerImpl implements // asking if the keyguard is showing. We still need to check it though because showing the // camera on the keyguard has a state of SHADE but the keyguard is still showing. final boolean showingKeyguard = mState != StatusBarState.SHADE - || mKeyguardMonitor.isShowing(); - final boolean devicePublic = showingKeyguard && isSecure(getCurrentUserId()); + || mKeyguardStateController.isShowing(); + final boolean devicePublic = showingKeyguard && mKeyguardStateController.isMethodSecure(); // Look for public mode users. Users are considered public in either case of: @@ -523,7 +526,7 @@ public class NotificationLockscreenUserManagerImpl implements boolean needsSeparateChallenge = whitelistIpcs(() -> mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)); if (!devicePublic && userId != getCurrentUserId() - && needsSeparateChallenge && isSecure(userId)) { + && needsSeparateChallenge && mLockPatternUtils.isSecure(userId)) { // Keyguard.isDeviceLocked is updated asynchronously, assume that all profiles // with separate challenge are locked when keyguard is visible to avoid race. isProfilePublic = showingKeyguard || mKeyguardManager.isDeviceLocked(userId); @@ -531,7 +534,7 @@ public class NotificationLockscreenUserManagerImpl implements setLockscreenPublicMode(isProfilePublic, userId); mUsersWithSeperateWorkChallenge.put(userId, needsSeparateChallenge); } - getEntryManager().updateNotifications(); + getEntryManager().updateNotifications("NotificationLockscreenUserManager.updatePublicMode"); } @Override @@ -545,7 +548,7 @@ public class NotificationLockscreenUserManagerImpl implements // // asking if the keyguard is showing. We still need to check it though because showing the // // camera on the keyguard has a state of SHADE but the keyguard is still showing. // final boolean showingKeyguard = mState != StatusBarState.SHADE -// || mKeyguardMonitor.isShowing(); +// || mKeyguardStateController.isShowing(); // final boolean devicePublic = showingKeyguard && isSecure(getCurrentUserId()); // // @@ -570,10 +573,6 @@ public class NotificationLockscreenUserManagerImpl implements // } // } - private boolean isSecure(int userId) { - return mKeyguardMonitor.isSecure() || mLockPatternUtils.isSecure(userId); - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NotificationLockscreenUserManager state:"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 00a12a9e4409..0988e347945c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -17,10 +17,6 @@ package com.android.systemui.statusbar; import static com.android.systemui.Dependency.MAIN_HANDLER; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; -import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_FADING; -import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; -import static com.android.systemui.statusbar.phone.BiometricUnlockController - .MODE_WAKE_AND_UNLOCK_PULSING; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK; import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER; import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; @@ -67,7 +63,7 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ScrimState; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBarWindowController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -94,7 +90,8 @@ public class NotificationMediaManager implements Dumpable { private final StatusBarStateController mStatusBarStateController = Dependency.get(StatusBarStateController.class); private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class); - private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + private final KeyguardStateController mKeyguardStateController = Dependency.get( + KeyguardStateController.class); private final KeyguardBypassController mKeyguardBypassController; private static final HashSet<Integer> PAUSED_MEDIA_STATES = new HashSet<>(); static { @@ -207,7 +204,7 @@ public class NotificationMediaManager implements Dumpable { NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { - onNotificationRemoved(entry.key); + onNotificationRemoved(entry.getKey()); } }); @@ -287,7 +284,7 @@ public class NotificationMediaManager implements Dumpable { if (entry.isMediaNotification()) { final MediaSession.Token token = - entry.notification.getNotification().extras.getParcelable( + entry.getSbn().getNotification().extras.getParcelable( Notification.EXTRA_MEDIA_SESSION); if (token != null) { MediaController aController = new MediaController(mContext, token); @@ -295,7 +292,7 @@ public class NotificationMediaManager implements Dumpable { getMediaControllerPlaybackState(aController)) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching " - + entry.notification.getKey()); + + entry.getSbn().getKey()); } mediaNotification = entry; controller = aController; @@ -324,10 +321,10 @@ public class NotificationMediaManager implements Dumpable { for (int i = 0; i < N; i++) { final NotificationEntry entry = activeNotifications.get(i); - if (entry.notification.getPackageName().equals(pkg)) { + if (entry.getSbn().getPackageName().equals(pkg)) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: found controller matching " - + entry.notification.getKey()); + + entry.getSbn().getKey()); } controller = aController; mediaNotification = entry; @@ -354,8 +351,8 @@ public class NotificationMediaManager implements Dumpable { } if (mediaNotification != null - && !mediaNotification.notification.getKey().equals(mMediaNotificationKey)) { - mMediaNotificationKey = mediaNotification.notification.getKey(); + && !mediaNotification.getSbn().getKey().equals(mMediaNotificationKey)) { + mMediaNotificationKey = mediaNotification.getSbn().getKey(); if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" + mMediaNotificationKey); @@ -364,7 +361,7 @@ public class NotificationMediaManager implements Dumpable { } if (metaDataChanged) { - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("NotificationMediaManager - metaDataChanged"); } dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */); @@ -461,7 +458,7 @@ public class NotificationMediaManager implements Dumpable { boolean wakeAndUnlock = mBiometricUnlockController != null && mBiometricUnlockController.isWakeAndUnlock(); - if (mKeyguardMonitor.isLaunchTransitionFadingAway() || wakeAndUnlock) { + if (mKeyguardStateController.isLaunchTransitionFadingAway() || wakeAndUnlock) { mBackdrop.setVisibility(View.INVISIBLE); Trace.endSection(); return; @@ -599,7 +596,7 @@ public class NotificationMediaManager implements Dumpable { boolean cannotAnimateDoze = shadeController != null && shadeController.isDozing() && !ScrimState.AOD.getAnimateChange(); - boolean needsBypassFading = mKeyguardMonitor.isBypassFadingAnimation(); + boolean needsBypassFading = mKeyguardStateController.isBypassFadingAnimation(); if (((mBiometricUnlockController != null && mBiometricUnlockController.getMode() == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING || cannotAnimateDoze) && !needsBypassFading) @@ -626,10 +623,12 @@ public class NotificationMediaManager implements Dumpable { mBackdropBack.setImageDrawable(null); mHandler.post(mHideBackdropFront); }); - if (mKeyguardMonitor.isKeyguardFadingAway()) { + if (mKeyguardStateController.isKeyguardFadingAway()) { mBackdrop.animate() - .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration()) - .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) + .setDuration( + mKeyguardStateController.getShortenedFadingAwayDuration()) + .setStartDelay( + mKeyguardStateController.getKeyguardFadingAwayDelay()) .setInterpolator(Interpolators.LINEAR) .start(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index c9050d492191..c838ac5315a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -17,8 +17,6 @@ package com.android.systemui.statusbar; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -52,6 +50,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -69,7 +68,6 @@ import java.util.Objects; import java.util.Set; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; import dagger.Lazy; @@ -262,7 +260,7 @@ public class NotificationRemoteInputManager implements Dumpable { NotificationEntryManager notificationEntryManager, Lazy<ShadeController> shadeController, StatusBarStateController statusBarStateController, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { + @MainHandler Handler mainHandler) { mContext = context; mLockscreenUserManager = lockscreenUserManager; mSmartReplyController = smartReplyController; @@ -293,7 +291,7 @@ public class NotificationRemoteInputManager implements Dumpable { mSmartReplyController.stopSending(entry); if (removedByUser && entry != null) { - onPerformRemoveNotification(entry, entry.key); + onPerformRemoveNotification(entry, entry.getKey()); } } }); @@ -307,8 +305,8 @@ public class NotificationRemoteInputManager implements Dumpable { @Override public void onRemoteInputSent(NotificationEntry entry) { if (FORCE_REMOTE_INPUT_HISTORY - && isNotificationKeptForRemoteInputHistory(entry.key)) { - mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key); + && isNotificationKeptForRemoteInputHistory(entry.getKey())) { + mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey()); } else if (mEntriesKeptForRemoteInputActive.contains(entry)) { // We're currently holding onto this notification, but from the apps point of // view it is already canceled, so we'll need to cancel it on the apps behalf @@ -316,18 +314,18 @@ public class NotificationRemoteInputManager implements Dumpable { // bit. mMainHandler.postDelayed(() -> { if (mEntriesKeptForRemoteInputActive.remove(entry)) { - mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key); + mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey()); } }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY); } try { - mBarService.onNotificationDirectReplied(entry.notification.getKey()); + mBarService.onNotificationDirectReplied(entry.getSbn().getKey()); if (entry.editedSuggestionInfo != null) { boolean modifiedBeforeSending = !TextUtils.equals(entry.remoteInputText, entry.editedSuggestionInfo.originalText); mBarService.onNotificationSmartReplySent( - entry.notification.getKey(), + entry.getSbn().getKey(), entry.editedSuggestionInfo.index, entry.editedSuggestionInfo.originalText, NotificationLogger @@ -487,7 +485,7 @@ public class NotificationRemoteInputManager implements Dumpable { NotificationEntry entry = mEntriesKeptForRemoteInputActive.valueAt(i); mRemoteInputController.removeRemoteInput(entry, null); if (mNotificationLifetimeFinishedCallback != null) { - mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key); + mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey()); } } mEntriesKeptForRemoteInputActive.clear(); @@ -501,14 +499,15 @@ public class NotificationRemoteInputManager implements Dumpable { if (!FORCE_REMOTE_INPUT_HISTORY) { return false; } - return (mRemoteInputController.isSpinning(entry.key) || entry.hasJustSentRemoteInput()); + return (mRemoteInputController.isSpinning(entry.getKey()) + || entry.hasJustSentRemoteInput()); } public boolean shouldKeepForSmartReplyHistory(NotificationEntry entry) { if (!FORCE_REMOTE_INPUT_HISTORY) { return false; } - return mSmartReplyController.isSendingSmartReply(entry.key); + return mSmartReplyController.isSendingSmartReply(entry.getKey()); } public void checkRemoteInputOutside(MotionEvent event) { @@ -529,7 +528,7 @@ public class NotificationRemoteInputManager implements Dumpable { @VisibleForTesting StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry, CharSequence remoteInputText, boolean showSpinner) { - StatusBarNotification sbn = entry.notification; + StatusBarNotification sbn = entry.getSbn(); Notification.Builder b = Notification.Builder .recoverBuilder(mContext, sbn.getNotification().clone()); @@ -637,12 +636,12 @@ public class NotificationRemoteInputManager implements Dumpable { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Keeping notification around after sending remote input " - + entry.key); + + entry.getKey()); } - mKeysKeptForRemoteInputHistory.add(entry.key); + mKeysKeptForRemoteInputHistory.add(entry.getKey()); } else { - mKeysKeptForRemoteInputHistory.remove(entry.key); + mKeysKeptForRemoteInputHistory.remove(entry.getKey()); } } } @@ -675,12 +674,12 @@ public class NotificationRemoteInputManager implements Dumpable { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Keeping notification around after sending smart reply " - + entry.key); + + entry.getKey()); } - mKeysKeptForRemoteInputHistory.add(entry.key); + mKeysKeptForRemoteInputHistory.add(entry.getKey()); } else { - mKeysKeptForRemoteInputHistory.remove(entry.key); + mKeysKeptForRemoteInputHistory.remove(entry.getKey()); mSmartReplyController.stopSending(entry); } } @@ -701,7 +700,7 @@ public class NotificationRemoteInputManager implements Dumpable { if (shouldExtend) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Keeping notification around while remote input active " - + entry.key); + + entry.getKey()); } mEntriesKeptForRemoteInputActive.add(entry); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java index 266fe8dc2708..564d8bc14c8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java @@ -54,7 +54,7 @@ public class NotificationUiAdjustment { public static NotificationUiAdjustment extractFromNotificationEntry( NotificationEntry entry) { return new NotificationUiAdjustment( - entry.key, entry.getSmartActions(), entry.getSmartReplies()); + entry.getKey(), entry.getSmartActions(), entry.getSmartReplies()); } public static boolean needReinflate( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 50d9bae77bea..d6b87afc53b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -16,19 +16,19 @@ package com.android.systemui.statusbar; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.content.Context; import android.content.res.Resources; import android.os.Handler; import android.os.Trace; import android.os.UserHandle; +import android.provider.Settings; import android.util.Log; import android.view.View; import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -47,7 +47,6 @@ import java.util.List; import java.util.Stack; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; import dagger.Lazy; @@ -88,6 +87,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle private final BubbleController mBubbleController; private final DynamicPrivacyController mDynamicPrivacyController; private final KeyguardBypassController mBypassController; + private final Context mContext; private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; @@ -99,8 +99,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle private boolean mIsHandleDynamicPrivacyChangeScheduled; @Inject - public NotificationViewHierarchyManager(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler, + public NotificationViewHierarchyManager(Context context, @MainHandler Handler mainHandler, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, @@ -110,6 +109,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle KeyguardBypassController bypassController, BubbleController bubbleController, DynamicPrivacyController privacyController) { + mContext = context; mHandler = mainHandler; mLockscreenUserManager = notificationLockscreenUserManager; mBypassController = bypassController; @@ -146,14 +146,18 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle final int N = activeNotifications.size(); for (int i = 0; i < N; i++) { NotificationEntry ent = activeNotifications.get(i); + int flag = Settings.System.getInt(mContext.getContentResolver(), + "qs_media_player", 0); + boolean hideMedia = (flag == 1); if (ent.isRowDismissed() || ent.isRowRemoved() - || mBubbleController.isBubbleNotificationSuppressedFromShade(ent.key)) { + || (ent.isMediaNotification() && hideMedia) + || mBubbleController.isBubbleNotificationSuppressedFromShade(ent.getKey())) { // we don't want to update removed notifications because they could // temporarily become children if they were isolated before. continue; } - int userId = ent.notification.getUserId(); + int userId = ent.getSbn().getUserId(); // Display public version of the notification if we need to redact. // TODO: This area uses a lot of calls into NotificationLockscreenUserManager. @@ -174,8 +178,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle currentUserId); ent.setSensitive(sensitive, deviceSensitive); ent.getRow().setNeedsRedaction(needsRedaction); - if (mGroupManager.isChildInGroupWithSummary(ent.notification)) { - NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification); + if (mGroupManager.isChildInGroupWithSummary(ent.getSbn())) { + NotificationEntry summary = mGroupManager.getGroupSummary(ent.getSbn()); List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(summary.getRow()); if (orderedChildren == null) { @@ -382,7 +386,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle ExpandableNotificationRow row = stack.pop(); NotificationEntry entry = row.getEntry(); boolean isChildNotification = - mGroupManager.isChildInGroupWithSummary(entry.notification); + mGroupManager.isChildInGroupWithSummary(entry.getSbn()); row.setOnKeyguard(onKeyguard); @@ -394,15 +398,15 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle && !row.isLowPriority())); } - int userId = entry.notification.getUserId(); + int userId = entry.getSbn().getUserId(); boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup( - entry.notification) && !entry.isRowRemoved(); + entry.getSbn()) && !entry.isRowRemoved(); boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry); if (!showOnKeyguard) { // min priority notifications should show if their summary is showing - if (mGroupManager.isChildInGroupWithSummary(entry.notification)) { + if (mGroupManager.isChildInGroupWithSummary(entry.getSbn())) { NotificationEntry summary = mGroupManager.getLogicalGroupSummary( - entry.notification); + entry.getSbn()); if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(summary)) { showOnKeyguard = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index a70dc7c0ec5d..e516af590e34 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -27,7 +27,6 @@ import android.os.SystemClock import android.view.MotionEvent import android.view.VelocityTracker import android.view.ViewConfiguration -import com.android.systemui.Dependency import com.android.systemui.Gefingerpoken import com.android.systemui.Interpolators @@ -58,7 +57,8 @@ constructor( private val bypassController: KeyguardBypassController, private val headsUpManager: HeadsUpManagerPhone, private val roundnessManager: NotificationRoundnessManager, - private val statusBarStateController: StatusBarStateController + private val statusBarStateController: StatusBarStateController, + private val falsingManager: FalsingManager ) : Gefingerpoken { companion object { private val RUBBERBAND_FACTOR_STATIC = 0.25f @@ -99,7 +99,6 @@ constructor( private val mTemp2 = IntArray(2) private var mDraggedFarEnough: Boolean = false private var mStartingChild: ExpandableView? = null - private val mFalsingManager: FalsingManager private var mPulsing: Boolean = false var isWakingToShadeLocked: Boolean = false private set @@ -109,7 +108,7 @@ constructor( private var velocityTracker: VelocityTracker? = null private val isFalseTouch: Boolean - get() = mFalsingManager.isFalseTouch + get() = falsingManager.isFalseTouch var qsExpanded: Boolean = false var pulseExpandAbortListener: Runnable? = null var bouncerShowing: Boolean = false @@ -118,7 +117,6 @@ constructor( mMinDragDistance = context.resources.getDimensionPixelSize( R.dimen.keyguard_drag_down_min_distance) mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop.toFloat() - mFalsingManager = Dependency.get(FalsingManager::class.java) mPowerManager = context.getSystemService(PowerManager::class.java) } @@ -151,7 +149,7 @@ constructor( MotionEvent.ACTION_MOVE -> { val h = y - mInitialTouchY if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) { - mFalsingManager.onStartExpandingFromPulse() + falsingManager.onStartExpandingFromPulse() isExpanding = true captureStartingChild(mInitialTouchX, mInitialTouchY) mInitialTouchY = y @@ -192,7 +190,7 @@ constructor( velocityTracker!!.computeCurrentVelocity(1000 /* units */) val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 && statusBarStateController.state != StatusBarState.SHADE - if (!mFalsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) { + if (!falsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) { finishExpansion() } else { cancelExpansion() @@ -297,7 +295,7 @@ constructor( private fun cancelExpansion() { isExpanding = false - mFalsingManager.onExpansionFromPulseStopped() + falsingManager.onExpansionFromPulseStopped() if (mStartingChild != null) { reset(mStartingChild!!) mStartingChild = null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java index 736b9ebea5c3..7bdb21d0eac5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java @@ -58,9 +58,9 @@ public class SmartReplyController { public void smartReplySent(NotificationEntry entry, int replyIndex, CharSequence reply, int notificationLocation, boolean modifiedBeforeSending) { mCallback.onSmartReplySent(entry, reply); - mSendingKeys.add(entry.key); + mSendingKeys.add(entry.getKey()); try { - mBarService.onNotificationSmartReplySent(entry.notification.getKey(), replyIndex, reply, + mBarService.onNotificationSmartReplySent(entry.getSbn().getKey(), replyIndex, reply, notificationLocation, modifiedBeforeSending); } catch (RemoteException e) { // Nothing to do, system going down @@ -74,14 +74,14 @@ public class SmartReplyController { NotificationEntry entry, int actionIndex, Notification.Action action, boolean generatedByAssistant) { final int count = mEntryManager.getNotificationData().getActiveNotifications().size(); - final int rank = mEntryManager.getNotificationData().getRank(entry.key); + final int rank = mEntryManager.getNotificationData().getRank(entry.getKey()); NotificationVisibility.NotificationLocation location = NotificationLogger.getNotificationLocation(entry); final NotificationVisibility nv = NotificationVisibility.obtain( - entry.key, rank, count, true, location); + entry.getKey(), rank, count, true, location); try { mBarService.onNotificationActionClick( - entry.key, actionIndex, action, nv, generatedByAssistant); + entry.getKey(), actionIndex, action, nv, generatedByAssistant); } catch (RemoteException e) { // Nothing to do, system going down } @@ -101,7 +101,7 @@ public class SmartReplyController { public void smartSuggestionsAdded(final NotificationEntry entry, int replyCount, int actionCount, boolean generatedByAssistant, boolean editBeforeSending) { try { - mBarService.onNotificationSmartSuggestionsAdded(entry.notification.getKey(), replyCount, + mBarService.onNotificationSmartSuggestionsAdded(entry.getSbn().getKey(), replyCount, actionCount, generatedByAssistant, editBeforeSending); } catch (RemoteException e) { // Nothing to do, system going down @@ -110,7 +110,7 @@ public class SmartReplyController { public void stopSending(final NotificationEntry entry) { if (entry != null) { - mSendingKeys.remove(entry.notification.getKey()); + mSendingKeys.remove(entry.getSbn().getKey()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 8b9268e1888a..e81e5cae5bfc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -21,7 +21,6 @@ import android.animation.ValueAnimator; import android.text.format.DateFormat; import android.util.FloatProperty; import android.util.Log; -import android.view.View; import android.view.animation.Interpolator; import com.android.internal.annotations.GuardedBy; @@ -80,9 +79,14 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE]; /** - * Current SystemUiVisibility + * If any of the system bars is hidden. */ - private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; + private boolean mIsFullscreen = false; + + /** + * If the navigation bar can stay hidden when the display gets tapped. + */ + private boolean mIsImmersive = false; /** * If the device is currently pulsing (AOD2). @@ -320,12 +324,13 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll } @Override - public void setSystemUiVisibility(int visibility) { - if (mSystemUiVisibility != visibility) { - mSystemUiVisibility = visibility; + public void setFullscreenState(boolean isFullscreen, boolean isImmersive) { + if (mIsFullscreen != isFullscreen || mIsImmersive != isImmersive) { + mIsFullscreen = isFullscreen; + mIsImmersive = isImmersive; synchronized (mListeners) { for (RankedListener rl : new ArrayList<>(mListeners)) { - rl.mListener.onSystemUiVisibilityChanged(mSystemUiVisibility); + rl.mListener.onFullscreenStateChanged(isFullscreen, isImmersive); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java index 2ad979ab64e3..07b35502478f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -111,9 +111,9 @@ public interface SysuiStatusBarStateController extends StatusBarStateController boolean isKeyguardRequested(); /** - * Set systemui visibility + * Set the fullscreen state */ - void setSystemUiVisibility(int visibility); + void setFullscreenState(boolean isFullscreen, boolean isImmersive); /** * Set pulsing diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index 13c6f2730d08..9b312341c583 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -38,7 +38,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.NotificationPanelView; -import com.android.systemui.statusbar.phone.StatusBarWindowView; +import com.android.systemui.statusbar.phone.StatusBarWindowViewController; /** * A class that allows activities to be launched in a seamless way where the notification @@ -55,8 +55,8 @@ public class ActivityLaunchAnimator { private static final long LAUNCH_TIMEOUT = 500; private final NotificationPanelView mNotificationPanel; private final NotificationListContainer mNotificationContainer; - private final StatusBarWindowView mStatusBarWindow; private final float mWindowCornerRadius; + private final StatusBarWindowViewController mStatusBarWindowViewController; private Callback mCallback; private final Runnable mTimeoutRunnable = () -> { setAnimationPending(false); @@ -66,16 +66,17 @@ public class ActivityLaunchAnimator { private boolean mAnimationRunning; private boolean mIsLaunchForActivity; - public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow, + public ActivityLaunchAnimator( + StatusBarWindowViewController statusBarWindowViewController, Callback callback, NotificationPanelView notificationPanel, NotificationListContainer container) { mNotificationPanel = notificationPanel; mNotificationContainer = container; - mStatusBarWindow = statusBarWindow; + mStatusBarWindowViewController = statusBarWindowViewController; mCallback = callback; mWindowCornerRadius = ScreenDecorationsUtils - .getWindowCornerRadius(statusBarWindow.getResources()); + .getWindowCornerRadius(mStatusBarWindowViewController.getView().getResources()); } public RemoteAnimationAdapter getLaunchAnimation( @@ -112,11 +113,11 @@ public class ActivityLaunchAnimator { private void setAnimationPending(boolean pending) { mAnimationPending = pending; - mStatusBarWindow.setExpandAnimationPending(pending); + mStatusBarWindowViewController.setExpandAnimationPending(pending); if (pending) { - mStatusBarWindow.postDelayed(mTimeoutRunnable, LAUNCH_TIMEOUT); + mStatusBarWindowViewController.getView().postDelayed(mTimeoutRunnable, LAUNCH_TIMEOUT); } else { - mStatusBarWindow.removeCallbacks(mTimeoutRunnable); + mStatusBarWindowViewController.getView().removeCallbacks(mTimeoutRunnable); } } @@ -246,7 +247,7 @@ public class ActivityLaunchAnimator { private void setExpandAnimationRunning(boolean running) { mNotificationPanel.setLaunchingNotification(running); mSourceNotification.setExpandAnimationRunning(running); - mStatusBarWindow.setExpandAnimationRunning(running); + mStatusBarWindowViewController.setExpandAnimationRunning(running); mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null); mAnimationRunning = running; if (!running) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java index e4bd4fa1ae75..a0b144b4497c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java @@ -16,16 +16,13 @@ package com.android.systemui.statusbar.notification; -import android.content.Context; import android.util.ArraySet; -import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.UnlockMethodCache; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; import javax.inject.Singleton; @@ -34,12 +31,11 @@ import javax.inject.Singleton; * A controller which dynamically controls the visibility of Notification content */ @Singleton -public class DynamicPrivacyController implements UnlockMethodCache.OnUnlockMethodChangedListener { +public class DynamicPrivacyController implements KeyguardStateController.Callback { - private final UnlockMethodCache mUnlockMethodCache; + private final KeyguardStateController mKeyguardStateController; private final NotificationLockscreenUserManager mLockscreenUserManager; private final StatusBarStateController mStateController; - private final KeyguardMonitor mKeyguardMonitor; private ArraySet<Listener> mListeners = new ArraySet<>(); private boolean mLastDynamicUnlocked; @@ -47,30 +43,18 @@ public class DynamicPrivacyController implements UnlockMethodCache.OnUnlockMetho private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Inject - DynamicPrivacyController(Context context, - KeyguardMonitor keyguardMonitor, - NotificationLockscreenUserManager notificationLockscreenUserManager, - StatusBarStateController stateController) { - this(notificationLockscreenUserManager, keyguardMonitor, - UnlockMethodCache.getInstance(context), - stateController); - } - - @VisibleForTesting DynamicPrivacyController(NotificationLockscreenUserManager notificationLockscreenUserManager, - KeyguardMonitor keyguardMonitor, - UnlockMethodCache unlockMethodCache, + KeyguardStateController keyguardStateController, StatusBarStateController stateController) { mLockscreenUserManager = notificationLockscreenUserManager; mStateController = stateController; - mUnlockMethodCache = unlockMethodCache; - mKeyguardMonitor = keyguardMonitor; - mUnlockMethodCache.addListener(this); + mKeyguardStateController = keyguardStateController; + mKeyguardStateController.addCallback(this); mLastDynamicUnlocked = isDynamicallyUnlocked(); } @Override - public void onUnlockMethodStateChanged() { + public void onUnlockedChanged() { if (isDynamicPrivacyEnabled()) { // We only want to notify our listeners if dynamic privacy is actually active boolean dynamicallyUnlocked = isDynamicallyUnlocked(); @@ -92,8 +76,9 @@ public class DynamicPrivacyController implements UnlockMethodCache.OnUnlockMetho } public boolean isDynamicallyUnlocked() { - return (mUnlockMethodCache.canSkipBouncer() || mKeyguardMonitor.isKeyguardGoingAway() - || mKeyguardMonitor.isKeyguardFadingAway()) + return (mKeyguardStateController.canDismissLockScreen() + || mKeyguardStateController.isKeyguardGoingAway() + || mKeyguardStateController.isKeyguardFadingAway()) && isDynamicPrivacyEnabled(); } @@ -107,7 +92,7 @@ public class DynamicPrivacyController implements UnlockMethodCache.OnUnlockMetho */ public boolean isInLockedDownShade() { if (!mStatusBarKeyguardViewManager.isShowing() - || !mUnlockMethodCache.isMethodSecure()) { + || !mKeyguardStateController.isMethodSecure()) { return false; } int state = mStateController.getState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index f3201ec73d63..f284f737b26d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -31,6 +31,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.SynchronousUserSwitchObserver; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -55,16 +56,16 @@ import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUI; import com.android.systemui.UiOffloadThread; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.NotificationChannels; import java.util.List; -/** The clsss to show notification(s) of instant apps. This may show multiple notifications on +/** The class to show notification(s) of instant apps. This may show multiple notifications on * splitted screen. */ public class InstantAppNotifier extends SystemUI - implements CommandQueue.Callbacks, KeyguardMonitor.Callback { + implements CommandQueue.Callbacks, KeyguardStateController.Callback { private static final String TAG = "InstantAppNotifier"; public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5; @@ -72,13 +73,15 @@ public class InstantAppNotifier extends SystemUI private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>(); private boolean mDockedStackExists; - private KeyguardMonitor mKeyguardMonitor; + private KeyguardStateController mKeyguardStateController; - public InstantAppNotifier() {} + public InstantAppNotifier(Context context) { + super(context); + } @Override public void start() { - mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + mKeyguardStateController = Dependency.get(KeyguardStateController.class); // listen for user / profile change. try { @@ -88,7 +91,7 @@ public class InstantAppNotifier extends SystemUI } SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this); - mKeyguardMonitor.addCallback(this); + mKeyguardStateController.addCallback(this); DockedStackExistsListener.register( exists -> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java new file mode 100644 index 000000000000..31921a436747 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification; + +import android.util.Log; + +import com.android.systemui.statusbar.NotificationListener; +import com.android.systemui.statusbar.notification.collection.NotifCollection; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Initialization code for the new notification pipeline. + */ +@Singleton +public class NewNotifPipeline { + private final NotifCollection mNotifCollection; + + @Inject + public NewNotifPipeline( + NotifCollection notifCollection) { + mNotifCollection = notifCollection; + } + + /** Hooks the new pipeline up to NotificationManager */ + public void initialize( + NotificationListener notificationService) { + mNotifCollection.attach(notificationService); + + Log.d(TAG, "Notif pipeline initialized"); + } + + private static final String TAG = "NewNotifPipeline"; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java index d71d40781f2e..005f01dc8df0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java @@ -80,7 +80,7 @@ public class NotificationAlertingManager { NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { - stopAlerting(entry.key); + stopAlerting(entry.getKey()); } }); } @@ -104,7 +104,7 @@ public class NotificationAlertingManager { mHeadsUpManager.showNotification(entry); if (!mShadeController.get().isDozing()) { // Mark as seen immediately - setNotificationShown(entry.notification); + setNotificationShown(entry.getSbn()); } } else { entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP); @@ -113,16 +113,16 @@ public class NotificationAlertingManager { } private void updateAlertState(NotificationEntry entry) { - boolean alertAgain = alertAgain(entry, entry.notification.getNotification()); + boolean alertAgain = alertAgain(entry, entry.getSbn().getNotification()); boolean shouldAlert; shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry); - final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.key); + final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.getKey()); if (wasAlerting) { if (shouldAlert) { - mHeadsUpManager.updateNotification(entry.key, alertAgain); - } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.key)) { + mHeadsUpManager.updateNotification(entry.getKey(), alertAgain); + } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.getKey())) { // We don't want this to be interrupting anymore, let's remove it - mHeadsUpManager.removeNotification(entry.key, false /* removeImmediately */); + mHeadsUpManager.removeNotification(entry.getKey(), false /* removeImmediately */); } } else if (shouldAlert && alertAgain) { // This notification was updated to be alerting, show it! diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index b6b149dd049a..b4dc538f8e0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -20,8 +20,8 @@ import static android.service.notification.NotificationListenerService.REASON_ER import android.annotation.Nullable; import android.app.Notification; -import android.content.Context; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.Log; @@ -40,6 +40,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; +import com.android.systemui.statusbar.notification.logging.NotifEvent; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; @@ -92,6 +94,7 @@ public class NotificationEntryManager implements private NotificationListenerService.RankingMap mLatestRankingMap; @VisibleForTesting protected NotificationData mNotificationData; + private NotifLog mNotifLog; @VisibleForTesting final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders @@ -107,7 +110,7 @@ public class NotificationEntryManager implements pw.println("null"); } else { for (NotificationEntry entry : mPendingNotifications.values()) { - pw.println(entry.notification); + pw.println(entry.getSbn()); } } pw.println(" Lifetime-extended notifications:"); @@ -116,15 +119,18 @@ public class NotificationEntryManager implements } else { for (Map.Entry<NotificationEntry, NotificationLifetimeExtender> entry : mRetainedNotifications.entrySet()) { - pw.println(" " + entry.getKey().notification + " retained by " + pw.println(" " + entry.getKey().getSbn() + " retained by " + entry.getValue().getClass().getName()); } } } @Inject - public NotificationEntryManager(Context context) { - mNotificationData = new NotificationData(context); + public NotificationEntryManager( + NotificationData notificationData, + NotifLog notifLog) { + mNotificationData = notificationData; + mNotifLog = notifLog; } /** Adds a {@link NotificationEntryListener}. */ @@ -132,6 +138,14 @@ public class NotificationEntryManager implements mNotificationEntryListeners.add(listener); } + /** + * Removes a {@link NotificationEntryListener} previously registered via + * {@link #addNotificationEntryListener(NotificationEntryListener)}. + */ + public void removeNotificationEntryListener(NotificationEntryListener listener) { + mNotificationEntryListeners.remove(listener); + } + /** Sets the {@link NotificationRemoveInterceptor}. */ public void setNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) { mRemoveInterceptor = interceptor; @@ -178,7 +192,7 @@ public class NotificationEntryManager implements @Override public void onReorderingAllowed() { - updateNotifications(); + updateNotifications("reordering is now allowed"); } /** @@ -203,15 +217,19 @@ public class NotificationEntryManager implements return NotificationVisibility.obtain(key, rank, count, true, location); } - private void abortExistingInflation(String key) { + private void abortExistingInflation(String key, String reason) { if (mPendingNotifications.containsKey(key)) { NotificationEntry entry = mPendingNotifications.get(key); entry.abortTask(); mPendingNotifications.remove(key); + mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry.getSbn(), null, + "PendingNotification aborted. " + reason); } NotificationEntry addedEntry = mNotificationData.get(key); if (addedEntry != null) { addedEntry.abortTask(); + mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.getSbn(), + null, reason); } } @@ -234,11 +252,11 @@ public class NotificationEntryManager implements @Override public void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags) { - mPendingNotifications.remove(entry.key); + mPendingNotifications.remove(entry.getKey()); // If there was an async task started after the removal, we don't want to add it back to // the list, otherwise we might get leaks. if (!entry.isRowRemoved()) { - boolean isNew = mNotificationData.get(entry.key) == null; + boolean isNew = mNotificationData.get(entry.getKey()) == null; if (isNew) { for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onEntryInflated(entry, inflatedFlags); @@ -247,7 +265,7 @@ public class NotificationEntryManager implements for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onBeforeNotificationAdded(entry); } - updateNotifications(); + updateNotifications("onAsyncInflationFinished"); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onNotificationAdded(entry); } @@ -276,7 +294,8 @@ public class NotificationEntryManager implements if (mRemoveInterceptor != null && mRemoveInterceptor.onNotificationRemoveRequested(key, reason)) { - // Remove intercepted; skip + // Remove intercepted; log and skip + mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED); return; } @@ -291,13 +310,17 @@ public class NotificationEntryManager implements if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) { extendLifetime(pendingEntry, extender); lifetimeExtended = true; + mNotifLog.log( + NotifEvent.LIFETIME_EXTENDED, + pendingEntry.getSbn(), + "pendingEntry extendedBy=" + extender.toString()); } } } } if (!lifetimeExtended) { - abortExistingInflation(key); + abortExistingInflation(key, "removeNotification"); } if (entry != null) { @@ -310,6 +333,10 @@ public class NotificationEntryManager implements mLatestRankingMap = ranking; extendLifetime(entry, extender); lifetimeExtended = true; + mNotifLog.log( + NotifEvent.LIFETIME_EXTENDED, + entry.getSbn(), + "entry extendedBy=" + extender.toString()); break; } } @@ -329,10 +356,12 @@ public class NotificationEntryManager implements handleGroupSummaryRemoved(key); mNotificationData.remove(key, ranking); - updateNotifications(); + updateNotifications("removeNotificationInternal"); Dependency.get(LeakDetector.class).trackGarbage(entry); removedByUser |= entryDismissed; + mNotifLog.log(NotifEvent.NOTIF_REMOVED, entry.getSbn(), + "removedByUser=" + removedByUser); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onEntryRemoved(entry, visibility, removedByUser); } @@ -353,7 +382,7 @@ public class NotificationEntryManager implements private void handleGroupSummaryRemoved(String key) { NotificationEntry entry = mNotificationData.get(key); if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) { - if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) { + if (entry.getSbn().getOverrideGroupKey() != null && !entry.isRowDismissed()) { // We don't want to remove children for autobundled notifications as they are not // always cancelled. We only remove them if they were dismissed by the user. return; @@ -364,7 +393,7 @@ public class NotificationEntryManager implements } for (int i = 0; i < childEntries.size(); i++) { NotificationEntry childEntry = childEntries.get(i); - boolean isForeground = (entry.notification.getNotification().flags + boolean isForeground = (entry.getSbn().getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; boolean keepForReply = getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry) @@ -389,8 +418,8 @@ public class NotificationEntryManager implements Log.d(TAG, "addNotification key=" + key); } - mNotificationData.updateRanking(rankingMap); - NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); + mNotificationData.updateRanking(rankingMap, "addNotificationInternal"); + Ranking ranking = new Ranking(); rankingMap.getRanking(key, ranking); NotificationEntry entry = new NotificationEntry(notification, ranking); @@ -400,9 +429,9 @@ public class NotificationEntryManager implements requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, REASON_CANCEL)); - abortExistingInflation(key); - + abortExistingInflation(key, "addNotification"); mPendingNotifications.put(key, entry); + mNotifLog.log(NotifEvent.NOTIF_ADDED, entry.getSbn()); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onPendingEntryAdded(entry); } @@ -423,7 +452,7 @@ public class NotificationEntryManager implements if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); final String key = notification.getKey(); - abortExistingInflation(key); + abortExistingInflation(key, "updateNotification"); NotificationEntry entry = mNotificationData.get(key); if (entry == null) { return; @@ -433,15 +462,15 @@ public class NotificationEntryManager implements // to keep its lifetime extended. cancelLifetimeExtension(entry); - mNotificationData.update(entry, ranking, notification); - + mNotificationData.update(entry, ranking, notification, "updateNotificationInternal"); + mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry.getSbn(), entry.getRanking()); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onPreEntryUpdated(entry); } requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, REASON_CANCEL)); - updateNotifications(); + updateNotifications("updateNotificationInternal"); if (DEBUG) { // Is this for you? @@ -465,8 +494,12 @@ public class NotificationEntryManager implements } } - public void updateNotifications() { - mNotificationData.filterAndSort(); + /** + * Update the notifications + * @param reason why the notifications are updating + */ + public void updateNotifications(String reason) { + mNotificationData.filterAndSort(reason); if (mPresenter != null) { mPresenter.updateNotificationViews(); } @@ -484,24 +517,24 @@ public class NotificationEntryManager implements for (NotificationEntry entry : entries) { NotificationUiAdjustment adjustment = NotificationUiAdjustment.extractFromNotificationEntry(entry); - oldAdjustments.put(entry.key, adjustment); - oldImportances.put(entry.key, entry.getImportance()); + oldAdjustments.put(entry.getKey(), adjustment); + oldImportances.put(entry.getKey(), entry.getImportance()); } // Populate notification entries from the new rankings. - mNotificationData.updateRanking(rankingMap); + mNotificationData.updateRanking(rankingMap, "updateNotificationRanking"); updateRankingOfPendingNotifications(rankingMap); // By comparing the old and new UI adjustments, reinflate the view accordingly. for (NotificationEntry entry : entries) { requireBinder().onNotificationRankingUpdated( entry, - oldImportances.get(entry.key), - oldAdjustments.get(entry.key), + oldImportances.get(entry.getKey()), + oldAdjustments.get(entry.getKey()), NotificationUiAdjustment.extractFromNotificationEntry(entry)); } - updateNotifications(); + updateNotifications("updateNotificationRanking"); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onNotificationRankingUpdated(rankingMap); @@ -513,10 +546,11 @@ public class NotificationEntryManager implements if (rankingMap == null) { return; } - NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); for (NotificationEntry pendingNotification : mPendingNotifications.values()) { - rankingMap.getRanking(pendingNotification.key, ranking); - pendingNotification.setRanking(ranking); + Ranking ranking = new Ranking(); + if (rankingMap.getRanking(pendingNotification.getKey(), ranking)) { + pendingNotification.setRanking(ranking); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java index 5a0b88cbdc28..b1164093acdd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java @@ -85,7 +85,7 @@ public class NotificationFilter { * @return true if the provided notification should NOT be shown right now. */ public boolean shouldFilterOut(NotificationEntry entry) { - final StatusBarNotification sbn = entry.notification; + final StatusBarNotification sbn = entry.getSbn(); if (!(getEnvironment().isDeviceProvisioned() || showNotificationEvenIfUnprovisioned(sbn))) { return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java index 9362d2d5e2c9..7d09932dcad0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java @@ -39,6 +39,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.HeadsUpManager; import javax.inject.Inject; @@ -63,6 +64,7 @@ public class NotificationInterruptionStateProvider { private final Context mContext; private final PowerManager mPowerManager; private final IDreamManager mDreamManager; + private final BatteryController mBatteryController; private NotificationPresenter mPresenter; private HeadsUpManager mHeadsUpManager; @@ -75,13 +77,14 @@ public class NotificationInterruptionStateProvider { @Inject public NotificationInterruptionStateProvider(Context context, NotificationFilter filter, - StatusBarStateController stateController) { + StatusBarStateController stateController, BatteryController batteryController) { this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), IDreamManager.Stub.asInterface( ServiceManager.checkService(DreamService.DREAM_SERVICE)), new AmbientDisplayConfiguration(context), filter, + batteryController, stateController); } @@ -92,10 +95,12 @@ public class NotificationInterruptionStateProvider { IDreamManager dreamManager, AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter notificationFilter, + BatteryController batteryController, StatusBarStateController statusBarStateController) { mContext = context; mPowerManager = powerManager; mDreamManager = dreamManager; + mBatteryController = batteryController; mAmbientDisplayConfiguration = ambientDisplayConfiguration; mNotificationFilter = notificationFilter; mStatusBarStateController = statusBarStateController; @@ -159,7 +164,7 @@ public class NotificationInterruptionStateProvider { * @return true if the entry should bubble up, false otherwise */ public boolean shouldBubbleUp(NotificationEntry entry) { - final StatusBarNotification sbn = entry.notification; + final StatusBarNotification sbn = entry.getSbn(); if (!canAlertCommon(entry)) { return false; @@ -211,7 +216,7 @@ public class NotificationInterruptionStateProvider { } private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { - StatusBarNotification sbn = entry.notification; + StatusBarNotification sbn = entry.getSbn(); if (!mUseHeadsUp) { if (DEBUG_HEADS_UP) { @@ -284,7 +289,7 @@ public class NotificationInterruptionStateProvider { * @return true if the entry should ambient pulse, false otherwise */ private boolean shouldHeadsUpWhenDozing(NotificationEntry entry) { - StatusBarNotification sbn = entry.notification; + StatusBarNotification sbn = entry.getSbn(); if (!mAmbientDisplayConfiguration.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) { if (DEBUG_HEADS_UP) { @@ -293,6 +298,13 @@ public class NotificationInterruptionStateProvider { return false; } + if (mBatteryController.isAodPowerSave()) { + if (DEBUG_HEADS_UP) { + Log.d(TAG, "No pulsing: disabled by battery saver: " + sbn.getKey()); + } + return false; + } + if (!canAlertCommon(entry)) { if (DEBUG_HEADS_UP) { Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey()); @@ -324,7 +336,7 @@ public class NotificationInterruptionStateProvider { */ @VisibleForTesting public boolean canAlertCommon(NotificationEntry entry) { - StatusBarNotification sbn = entry.notification; + StatusBarNotification sbn = entry.getSbn(); if (mNotificationFilter.shouldFilterOut(entry)) { if (DEBUG || DEBUG_HEADS_UP) { @@ -351,7 +363,7 @@ public class NotificationInterruptionStateProvider { */ @VisibleForTesting public boolean canAlertAwakeCommon(NotificationEntry entry) { - StatusBarNotification sbn = entry.notification; + StatusBarNotification sbn = entry.getSbn(); if (mPresenter.isDeviceInVrMode()) { if (DEBUG_HEADS_UP) { @@ -411,7 +423,7 @@ public class NotificationInterruptionStateProvider { * @return {@code true} if we should launch the full screen intent */ public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { - return entry.notification.getNotification().fullScreenIntent != null + return entry.getSbn().getNotification().fullScreenIntent != null && (!shouldHeadsUp(entry) || mStatusBarStateController.getState() == StatusBarState.KEYGUARD); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java index 769cbb7b984c..533dfb6ee4aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java @@ -73,7 +73,7 @@ public class NotificationListController { @Override public void onBeforeNotificationAdded(NotificationEntry entry) { - tagForeground(entry.notification); + tagForeground(entry.getSbn()); } }; @@ -81,7 +81,7 @@ public class NotificationListController { new DeviceProvisionedListener() { @Override public void onDeviceProvisionedChanged() { - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("device provisioned changed"); } }; @@ -106,7 +106,7 @@ public class NotificationListController { if (foregroundKey != null) { mEntryManager .getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon); - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("app opp changed pkg=" + pkg); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt new file mode 100644 index 000000000000..009551168010 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification + +import android.content.Context +import android.provider.DeviceConfig + +import com.android.internal.annotations.VisibleForTesting +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING +import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING +import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE +import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT +import com.android.systemui.util.DeviceConfigProxy + +import javax.inject.Inject + +private var sUsePeopleFiltering: Boolean? = null + +/** + * Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config. + */ +class NotificationSectionsFeatureManager @Inject constructor( + val proxy: DeviceConfigProxy, + val context: Context +) { + + fun isFilteringEnabled(): Boolean { + return usePeopleFiltering(proxy) + } + + fun getNotificationBuckets(): IntArray { + return when { + isFilteringEnabled() -> + intArrayOf(BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT) + NotificationUtils.useNewInterruptionModel(context) -> + intArrayOf(BUCKET_ALERTING, BUCKET_SILENT) + else -> + intArrayOf(BUCKET_ALERTING) + } + } + + fun getNumberOfBuckets(): Int { + return getNotificationBuckets().size + } + + @VisibleForTesting + fun clearCache() { + sUsePeopleFiltering = null + } +} + +private fun usePeopleFiltering(proxy: DeviceConfigProxy): Boolean { + if (sUsePeopleFiltering == null) { + sUsePeopleFiltering = proxy.getBoolean( + DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, true) + } + + return sUsePeopleFiltering!! +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java index dfbbf987dd96..1af47dd0f4c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java @@ -36,7 +36,6 @@ public class NotificationUtils { private static final int[] sLocationOffset = new int[2]; @Nullable private static Boolean sUseNewInterruptionModel = null; - @Nullable private static Boolean sUsePeopleFiltering = null; public static boolean isGrayscale(ImageView v, ContrastColorUtil colorUtil) { Object isGrayscale = v.getTag(R.id.icon_is_grayscale); @@ -88,17 +87,4 @@ public class NotificationUtils { } return sUseNewInterruptionModel; } - - /** - * Caches and returns the value of the people filtering setting. Cannot change except through - * process restarts. - */ - public static boolean usePeopleFiltering(Context context) { - if (sUsePeopleFiltering == null) { - sUsePeopleFiltering = context.getResources().getBoolean( - R.bool.config_usePeopleFiltering); - } - - return sUsePeopleFiltering; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 2f67f90a115e..8a23e3796e9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -37,10 +37,10 @@ import javax.inject.Singleton @Singleton class NotificationWakeUpCoordinator @Inject constructor( - private val mContext: Context, private val mHeadsUpManagerPhone: HeadsUpManagerPhone, private val statusBarStateController: StatusBarStateController, - private val bypassController: KeyguardBypassController) + private val bypassController: KeyguardBypassController, + private val dozeParameters: DozeParameters) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener { @@ -67,7 +67,6 @@ class NotificationWakeUpCoordinator @Inject constructor( private var mVisibilityAmount = 0.0f private var mLinearVisibilityAmount = 0.0f private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>() - private val mDozeParameters: DozeParameters private var pulseExpanding: Boolean = false private val wakeUpListeners = arrayListOf<WakeUpListener>() private var state: Int = StatusBarState.KEYGUARD @@ -146,7 +145,6 @@ class NotificationWakeUpCoordinator @Inject constructor( init { mHeadsUpManagerPhone.addListener(this) statusBarStateController.addCallback(this) - mDozeParameters = DozeParameters.getInstance(mContext) addListener(object : WakeUpListener { override fun onFullyHiddenChanged(isFullyHidden: Boolean) { if (isFullyHidden && mNotificationsVisibleForExpansion) { @@ -377,7 +375,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } private fun shouldAnimateVisibility() = - mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking() + dozeParameters.getAlwaysOn() && !dozeParameters.getDisplayNeedsBlanking() interface WakeUpListener { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java index 6fe4abee6133..1daf48492ea0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.notification; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.os.Handler; import android.os.SystemClock; import android.view.View; @@ -25,6 +23,7 @@ import android.view.View; import androidx.collection.ArraySet; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -35,7 +34,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -64,8 +62,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl @Inject public VisualStabilityManager( - NotificationEntryManager notificationEntryManager, - @Named(MAIN_HANDLER_NAME) Handler handler) { + NotificationEntryManager notificationEntryManager, @MainHandler Handler handler) { mHandler = handler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java new file mode 100644 index 000000000000..ecce6ea1b211 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection; + +import android.service.notification.NotificationStats.DismissalSentiment; +import android.service.notification.NotificationStats.DismissalSurface; + +import com.android.internal.statusbar.NotificationVisibility; + +/** Information that must be supplied when dismissing a notification on the behalf of the user. */ +public class DismissedByUserStats { + public final @DismissalSurface int dismissalSurface; + public final @DismissalSentiment int dismissalSentiment; + public final NotificationVisibility notificationVisibility; + + public DismissedByUserStats( + @DismissalSurface int dismissalSurface, + @DismissalSentiment int dismissalSentiment, + NotificationVisibility notificationVisibility) { + this.dismissalSurface = dismissalSurface; + this.dismissalSentiment = dismissalSentiment; + this.notificationVisibility = notificationVisibility; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java new file mode 100644 index 000000000000..b5513529d7ba --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection; + +import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; +import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL; +import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; +import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED; +import static android.service.notification.NotificationListenerService.REASON_CLICK; +import static android.service.notification.NotificationListenerService.REASON_ERROR; +import static android.service.notification.NotificationListenerService.REASON_GROUP_OPTIMIZATION; +import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; +import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL; +import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL; +import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED; +import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED; +import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED; +import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF; +import static android.service.notification.NotificationListenerService.REASON_SNOOZED; +import static android.service.notification.NotificationListenerService.REASON_TIMEOUT; +import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED; +import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.IntDef; +import android.annotation.MainThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.RemoteException; +import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; +import android.service.notification.StatusBarNotification; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.statusbar.NotificationListener; +import com.android.systemui.statusbar.NotificationListener.NotifServiceListener; +import com.android.systemui.util.Assert; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Keeps a record of all of the "active" notifications, i.e. the notifications that are currently + * posted to the phone. This collection is unsorted, ungrouped, and unfiltered. Just because a + * notification appears in this collection doesn't mean that it's currently present in the shade + * (notifications can be hidden for a variety of reasons). Code that cares about what notifications + * are *visible* right now should register listeners later in the pipeline. + * + * Each notification is represented by a {@link NotificationEntry}, which is itself made up of two + * parts: a {@link StatusBarNotification} and a {@link Ranking}. When notifications are updated, + * their underlying SBNs and Rankings are swapped out, but the enclosing NotificationEntry (and its + * associated key) remain the same. In general, an SBN can only be updated when the notification is + * reposted by the source app; Rankings are updated much more often, usually every time there is an + * update from any kind from NotificationManager. + * + * In general, this collection closely mirrors the list maintained by NotificationManager, but it + * can occasionally diverge due to lifetime extenders (see + * {@link #addNotificationLifetimeExtender(NotifLifetimeExtender)}). + * + * Interested parties can register listeners + * ({@link #addCollectionListener(NotifCollectionListener)}) to be informed when notifications are + * added, updated, or removed. + */ +@MainThread +@Singleton +public class NotifCollection { + private final IStatusBarService mStatusBarService; + + private final Map<String, NotificationEntry> mNotificationSet = new ArrayMap<>(); + private final Collection<NotificationEntry> mReadOnlyNotificationSet = + Collections.unmodifiableCollection(mNotificationSet.values()); + + @Nullable private NotifListBuilder mListBuilder; + private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>(); + private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>(); + + private boolean mAttached = false; + private boolean mAmDispatchingToOtherCode; + + @Inject + public NotifCollection(IStatusBarService statusBarService) { + Assert.isMainThread(); + mStatusBarService = statusBarService; + } + + /** Initializes the NotifCollection and registers it to receive notification events. */ + public void attach(NotificationListener listenerService) { + Assert.isMainThread(); + if (mAttached) { + throw new RuntimeException("attach() called twice"); + } + mAttached = true; + + listenerService.setDownstreamListener(mNotifServiceListener); + } + + /** + * Sets the class responsible for converting the collection into the list of currently-visible + * notifications. + */ + public void setListBuilder(NotifListBuilder listBuilder) { + Assert.isMainThread(); + mListBuilder = listBuilder; + } + + /** + * Returns the list of "active" notifications, i.e. the notifications that are currently posted + * to the phone. In general, this tracks closely to the list maintained by NotificationManager, + * but it can diverge slightly due to lifetime extenders. + * + * The returned list is read-only, unsorted, unfiltered, and ungrouped. + */ + public Collection<NotificationEntry> getNotifs() { + Assert.isMainThread(); + return mReadOnlyNotificationSet; + } + + /** + * Registers a listener to be informed when notifications are added, removed or updated. + */ + public void addCollectionListener(NotifCollectionListener listener) { + Assert.isMainThread(); + mNotifCollectionListeners.add(listener); + } + + /** + * Registers a lifetime extender. Lifetime extenders can cause notifications that have been + * dismissed or retracted to be temporarily retained in the collection. + */ + public void addNotificationLifetimeExtender(NotifLifetimeExtender extender) { + Assert.isMainThread(); + checkForReentrantCall(); + if (mLifetimeExtenders.contains(extender)) { + throw new IllegalArgumentException("Extender " + extender + " already added."); + } + mLifetimeExtenders.add(extender); + extender.setCallback(this::onEndLifetimeExtension); + } + + /** + * Dismiss a notification on behalf of the user. + */ + public void dismissNotification( + NotificationEntry entry, + @CancellationReason int reason, + @NonNull DismissedByUserStats stats) { + Assert.isMainThread(); + checkNotNull(stats); + checkForReentrantCall(); + + removeNotification(entry.getKey(), null, reason, stats); + } + + private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { + Assert.isMainThread(); + + NotificationEntry entry = mNotificationSet.get(sbn.getKey()); + + if (entry == null) { + // A new notification! + Log.d(TAG, "POSTED " + sbn.getKey()); + + entry = new NotificationEntry(sbn, requireRanking(rankingMap, sbn.getKey())); + mNotificationSet.put(sbn.getKey(), entry); + applyRanking(rankingMap); + + dispatchOnEntryAdded(entry); + + } else { + // Update to an existing entry + Log.d(TAG, "UPDATED " + sbn.getKey()); + + // Notification is updated so it is essentially re-added and thus alive again. Don't + // need to keep its lifetime extended. + cancelLifetimeExtension(entry); + + entry.setSbn(sbn); + applyRanking(rankingMap); + + dispatchOnEntryUpdated(entry); + } + + rebuildList(); + } + + private void onNotificationRemoved( + StatusBarNotification sbn, + @Nullable RankingMap rankingMap, + int reason) { + Assert.isMainThread(); + Log.d(TAG, "REMOVED " + sbn.getKey() + " reason=" + reason); + removeNotification(sbn.getKey(), rankingMap, reason, null); + } + + private void onNotificationRankingUpdate(RankingMap rankingMap) { + Assert.isMainThread(); + applyRanking(rankingMap); + rebuildList(); + } + + private void removeNotification( + String key, + @Nullable RankingMap rankingMap, + @CancellationReason int reason, + DismissedByUserStats dismissedByUserStats) { + + NotificationEntry entry = mNotificationSet.get(key); + if (entry == null) { + throw new IllegalStateException("No notification to remove with key " + key); + } + + entry.mLifetimeExtenders.clear(); + mAmDispatchingToOtherCode = true; + for (NotifLifetimeExtender extender : mLifetimeExtenders) { + if (extender.shouldExtendLifetime(entry, reason)) { + entry.mLifetimeExtenders.add(extender); + } + } + mAmDispatchingToOtherCode = false; + + if (!isLifetimeExtended(entry)) { + mNotificationSet.remove(entry.getKey()); + + if (dismissedByUserStats != null) { + try { + mStatusBarService.onNotificationClear( + entry.getSbn().getPackageName(), + entry.getSbn().getTag(), + entry.getSbn().getId(), + entry.getSbn().getUser().getIdentifier(), + entry.getSbn().getKey(), + dismissedByUserStats.dismissalSurface, + dismissedByUserStats.dismissalSentiment, + dismissedByUserStats.notificationVisibility); + } catch (RemoteException e) { + // system process is dead if we're here. + } + } + + if (rankingMap != null) { + applyRanking(rankingMap); + } + + dispatchOnEntryRemoved(entry, reason, dismissedByUserStats != null /* removedByUser */); + } + + rebuildList(); + } + + private void applyRanking(RankingMap rankingMap) { + for (NotificationEntry entry : mNotificationSet.values()) { + if (!isLifetimeExtended(entry)) { + Ranking ranking = requireRanking(rankingMap, entry.getKey()); + entry.setRanking(ranking); + } + } + } + + private void rebuildList() { + if (mListBuilder != null) { + mListBuilder.onBuildList(mReadOnlyNotificationSet); + } + } + + private void onEndLifetimeExtension(NotifLifetimeExtender extender, NotificationEntry entry) { + Assert.isMainThread(); + if (!mAttached) { + return; + } + checkForReentrantCall(); + + if (!entry.mLifetimeExtenders.remove(extender)) { + throw new IllegalStateException( + String.format( + "Cannot end lifetime extension for extender \"%s\" (%s)", + extender.getName(), + extender)); + } + + if (!isLifetimeExtended(entry)) { + // TODO: This doesn't need to be undefined -- we can set either EXTENDER_EXPIRED or + // save the original reason + removeNotification(entry.getKey(), null, REASON_UNKNOWN, null); + } + } + + private void cancelLifetimeExtension(NotificationEntry entry) { + mAmDispatchingToOtherCode = true; + for (NotifLifetimeExtender extender : entry.mLifetimeExtenders) { + extender.cancelLifetimeExtension(entry); + } + mAmDispatchingToOtherCode = false; + entry.mLifetimeExtenders.clear(); + } + + private boolean isLifetimeExtended(NotificationEntry entry) { + return entry.mLifetimeExtenders.size() > 0; + } + + private void checkForReentrantCall() { + if (mAmDispatchingToOtherCode) { + throw new IllegalStateException("Reentrant call detected"); + } + } + + private static Ranking requireRanking(RankingMap rankingMap, String key) { + // TODO: Modify RankingMap so that we don't have to make a copy here + Ranking ranking = new Ranking(); + if (!rankingMap.getRanking(key, ranking)) { + throw new IllegalArgumentException("Ranking map doesn't contain key: " + key); + } + return ranking; + } + + private void dispatchOnEntryAdded(NotificationEntry entry) { + mAmDispatchingToOtherCode = true; + if (mListBuilder != null) { + mListBuilder.onBeginDispatchToListeners(); + } + for (NotifCollectionListener listener : mNotifCollectionListeners) { + listener.onEntryAdded(entry); + } + mAmDispatchingToOtherCode = false; + } + + private void dispatchOnEntryUpdated(NotificationEntry entry) { + mAmDispatchingToOtherCode = true; + if (mListBuilder != null) { + mListBuilder.onBeginDispatchToListeners(); + } + for (NotifCollectionListener listener : mNotifCollectionListeners) { + listener.onEntryUpdated(entry); + } + mAmDispatchingToOtherCode = false; + } + + private void dispatchOnEntryRemoved( + NotificationEntry entry, + @CancellationReason int reason, + boolean removedByUser) { + mAmDispatchingToOtherCode = true; + if (mListBuilder != null) { + mListBuilder.onBeginDispatchToListeners(); + } + for (NotifCollectionListener listener : mNotifCollectionListeners) { + listener.onEntryRemoved(entry, reason, removedByUser); + } + mAmDispatchingToOtherCode = false; + } + + private final NotifServiceListener mNotifServiceListener = new NotifServiceListener() { + @Override + public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { + NotifCollection.this.onNotificationPosted(sbn, rankingMap); + } + + @Override + public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) { + NotifCollection.this.onNotificationRemoved(sbn, rankingMap, REASON_UNKNOWN); + } + + @Override + public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, + int reason) { + NotifCollection.this.onNotificationRemoved(sbn, rankingMap, reason); + } + + @Override + public void onNotificationRankingUpdate(RankingMap rankingMap) { + NotifCollection.this.onNotificationRankingUpdate(rankingMap); + } + }; + + private static final String TAG = "NotifCollection"; + + @IntDef(prefix = { "REASON_" }, value = { + REASON_UNKNOWN, + REASON_CLICK, + REASON_CANCEL_ALL, + REASON_ERROR, + REASON_PACKAGE_CHANGED, + REASON_USER_STOPPED, + REASON_PACKAGE_BANNED, + REASON_APP_CANCEL, + REASON_APP_CANCEL_ALL, + REASON_LISTENER_CANCEL, + REASON_LISTENER_CANCEL_ALL, + REASON_GROUP_SUMMARY_CANCELED, + REASON_GROUP_OPTIMIZATION, + REASON_PACKAGE_SUSPENDED, + REASON_PROFILE_TURNED_OFF, + REASON_UNAUTOBUNDLED, + REASON_CHANNEL_BANNED, + REASON_SNOOZED, + REASON_TIMEOUT, + }) + @Retention(RetentionPolicy.SOURCE) + @interface CancellationReason {} + + public static final int REASON_UNKNOWN = 0; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java new file mode 100644 index 000000000000..032620e14336 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection; + +import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason; + +/** + * Listener interface for {@link NotifCollection}. + */ +public interface NotifCollectionListener { + /** + * Called whenever a notification with a new key is posted. + */ + default void onEntryAdded(NotificationEntry entry) { + } + + /** + * Called whenever a notification with the same key as an existing notification is posted. By + * the time this listener is called, the entry's SBN and Ranking will already have been updated. + */ + default void onEntryUpdated(NotificationEntry entry) { + } + + /** + * Called immediately after a notification has been removed from the collection. + */ + default void onEntryRemoved( + NotificationEntry entry, + @CancellationReason int reason, + boolean removedByUser) { + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java new file mode 100644 index 000000000000..2c7b13866c10 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection; + +import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason; + +/** + * A way for other code to temporarily extend the lifetime of a notification after it has been + * retracted. See {@link NotifCollection#addNotificationLifetimeExtender(NotifLifetimeExtender)}. + */ +public interface NotifLifetimeExtender { + /** Name to associate with this extender (for the purposes of debugging) */ + String getName(); + + /** + * Called on the extender immediately after it has been registered. The extender should hang on + * to this callback and execute it whenever it no longer needs to extend the lifetime of a + * notification. + */ + void setCallback(OnEndLifetimeExtensionCallback callback); + + /** + * Called by the NotifCollection whenever a notification has been retracted (by the app) or + * dismissed (by the user). If the extender returns true, it is considered to be extending the + * lifetime of that notification. Lifetime-extended notifications are kept around until all + * active extenders expire their extension by calling onEndLifetimeExtension(). This method is + * called on all lifetime extenders even if earlier ones return true (in other words, multiple + * lifetime extenders can be extending a notification at the same time). + */ + boolean shouldExtendLifetime(NotificationEntry entry, @CancellationReason int reason); + + /** + * Called by the NotifCollection to inform a lifetime extender that its extension of a notif + * is no longer valid (usually because the notif has been reposted and so no longer needs + * lifetime extension). The extender should clean up any references it has to the notif in + * question. + */ + void cancelLifetimeExtension(NotificationEntry entry); + + /** Callback for notifying the NotifCollection that a lifetime extension has expired. */ + interface OnEndLifetimeExtensionCallback { + void onEndLifetimeExtension(NotifLifetimeExtender extender, NotificationEntry entry); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java new file mode 100644 index 000000000000..17fef6850f97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection; + +import java.util.Collection; + +/** + * Interface for the class responsible for converting a NotifCollection into the final sorted, + * filtered, and grouped list of currently visible notifications. + */ +public interface NotifListBuilder { + /** + * Called after the NotifCollection has received an update from NotificationManager but before + * it dispatches any change events to its listeners. This is to inform the list builder that + * the first stage of the pipeline has been triggered. After events have been dispatched, + * onBuildList() will be called. + * + * While onBuildList() is always called after this method is called, the converse is not always + * true: sometimes the NotifCollection applies an update that does not need to dispatch events, + * in which case this method will be skipped and onBuildList will be called directly. + */ + void onBeginDispatchToListeners(); + + /** + * Called by the NotifCollection to indicate that something in the collection has changed and + * that the list builder should regenerate the list. + */ + void onBuildList(Collection<NotificationEntry> entries); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java index aacb2dd0682f..7d0ce5c4fa28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -24,7 +24,6 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Person; -import android.content.Context; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.SnoozeCriterion; @@ -35,7 +34,10 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; +import com.android.systemui.statusbar.notification.logging.NotifEvent; +import com.android.systemui.statusbar.notification.logging.NotifLog; +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -46,6 +48,8 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; +import javax.inject.Inject; + /** * The list of currently displaying notifications. */ @@ -72,9 +76,17 @@ public class NotificationData { private RankingMap mRankingMap; private final Ranking mTmpRanking = new Ranking(); private final boolean mUsePeopleFiltering; + private final NotifLog mNotifLog; + private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; - public NotificationData(Context context) { - mUsePeopleFiltering = NotificationUtils.usePeopleFiltering(context); + @Inject + public NotificationData( + NotificationSectionsFeatureManager sectionsFeatureManager, + NotifLog notifLog, + PeopleNotificationIdentifier peopleNotificationIdentifier) { + mUsePeopleFiltering = sectionsFeatureManager.isFilteringEnabled(); + mNotifLog = notifLog; + mPeopleNotificationIdentifier = peopleNotificationIdentifier; } public void setHeadsUpManager(HeadsUpManager headsUpManager) { @@ -86,10 +98,13 @@ public class NotificationData { new Comparator<NotificationEntry>() { @Override public int compare(NotificationEntry a, NotificationEntry b) { - final StatusBarNotification na = a.notification; - final StatusBarNotification nb = b.notification; - int aRank = getRank(a.key); - int bRank = getRank(b.key); + final StatusBarNotification na = a.getSbn(); + final StatusBarNotification nb = b.getSbn(); + int aRank = getRank(a.getKey()); + int bRank = getRank(b.getKey()); + + boolean aPeople = isPeopleNotification(a); + boolean bPeople = isPeopleNotification(b); boolean aMedia = isImportantMedia(a); boolean bMedia = isImportantMedia(b); @@ -100,8 +115,8 @@ public class NotificationData { boolean aHeadsUp = a.isRowHeadsUp(); boolean bHeadsUp = b.isRowHeadsUp(); - if (mUsePeopleFiltering && a.hasAssociatedPeople() != b.hasAssociatedPeople()) { - return a.hasAssociatedPeople() ? -1 : 1; + if (mUsePeopleFiltering && aPeople != bPeople) { + return aPeople ? -1 : 1; } else if (aHeadsUp != bHeadsUp) { return aHeadsUp ? -1 : 1; } else if (aHeadsUp) { @@ -157,7 +172,7 @@ public class NotificationData { final int len = mEntries.size(); for (int i = 0; i < len; i++) { NotificationEntry entry = mEntries.valueAt(i); - final StatusBarNotification sbn = entry.notification; + final StatusBarNotification sbn = entry.getSbn(); if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) { continue; } @@ -173,11 +188,11 @@ public class NotificationData { public void add(NotificationEntry entry) { synchronized (mEntries) { - mEntries.put(entry.notification.getKey(), entry); + mEntries.put(entry.getSbn().getKey(), entry); } mGroupManager.onEntryAdded(entry); - updateRankingAndSort(mRankingMap); + updateRankingAndSort(mRankingMap, "addEntry=" + entry.getSbn()); } public NotificationEntry remove(String key, RankingMap ranking) { @@ -187,7 +202,7 @@ public class NotificationData { } if (removed == null) return null; mGroupManager.onEntryRemoved(removed); - updateRankingAndSort(ranking); + updateRankingAndSort(ranking, "removeEntry=" + removed.getSbn()); return removed; } @@ -195,15 +210,19 @@ public class NotificationData { public void update( NotificationEntry entry, RankingMap ranking, - StatusBarNotification notification) { - updateRanking(ranking); - final StatusBarNotification oldNotification = entry.notification; - entry.setNotification(notification); + StatusBarNotification notification, + String reason) { + updateRanking(ranking, reason); + final StatusBarNotification oldNotification = entry.getSbn(); + entry.setSbn(notification); mGroupManager.onEntryUpdated(entry, oldNotification); } - public void updateRanking(RankingMap ranking) { - updateRankingAndSort(ranking); + /** + * Update ranking and trigger a re-sort + */ + public void updateRanking(RankingMap ranking, String reason) { + updateRankingAndSort(ranking, reason); } public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) { @@ -211,9 +230,9 @@ public class NotificationData { final int len = mEntries.size(); for (int i = 0; i < len; i++) { NotificationEntry entry = mEntries.valueAt(i); - if (uid == entry.notification.getUid() - && pkg.equals(entry.notification.getPackageName()) - && key.equals(entry.key)) { + if (uid == entry.getSbn().getUid() + && pkg.equals(entry.getSbn().getPackageName()) + && key.equals(entry.getKey())) { if (showIcon) { entry.mActiveAppOps.add(appOp); } else { @@ -240,7 +259,7 @@ public class NotificationData { final ArrayList<NotificationEntry> logicalChildren = mGroupManager.getLogicalChildren(statusBarNotification); for (NotificationEntry child : logicalChildren) { - if (isHighPriority(child.notification)) { + if (isHighPriority(child.getSbn())) { return true; } } @@ -327,17 +346,17 @@ public class NotificationData { } private boolean isImportantMedia(NotificationEntry e) { - int importance = e.ranking().getImportance(); - boolean media = e.key.equals(getMediaManager().getMediaNotificationKey()) + int importance = e.getRanking().getImportance(); + boolean media = e.getKey().equals(getMediaManager().getMediaNotificationKey()) && importance > NotificationManager.IMPORTANCE_MIN; return media; } private boolean isSystemMax(NotificationEntry e) { - int importance = e.ranking().getImportance(); + int importance = e.getRanking().getImportance(); boolean sys = importance >= NotificationManager.IMPORTANCE_HIGH - && isSystemNotification(e.notification); + && isSystemNotification(e.getSbn()); return sys; } @@ -350,7 +369,7 @@ public class NotificationData { return false; } - private void updateRankingAndSort(RankingMap rankingMap) { + private void updateRankingAndSort(RankingMap rankingMap, String reason) { if (rankingMap != null) { mRankingMap = rankingMap; synchronized (mEntries) { @@ -358,22 +377,22 @@ public class NotificationData { for (int i = 0; i < len; i++) { NotificationEntry entry = mEntries.valueAt(i); Ranking newRanking = new Ranking(); - if (!getRanking(entry.key, newRanking)) { + if (!getRanking(entry.getKey(), newRanking)) { continue; } entry.setRanking(newRanking); - final StatusBarNotification oldSbn = entry.notification.cloneLight(); + final StatusBarNotification oldSbn = entry.getSbn().cloneLight(); final String overrideGroupKey = newRanking.getOverrideGroupKey(); if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) { - entry.notification.setOverrideGroupKey(overrideGroupKey); + entry.getSbn().setOverrideGroupKey(overrideGroupKey); mGroupManager.onEntryUpdated(entry, oldSbn); } - entry.setIsHighPriority(isHighPriority(entry.notification)); + entry.setIsHighPriority(isHighPriority(entry.getSbn())); } } } - filterAndSort(); + filterAndSort(reason); } /** @@ -391,7 +410,11 @@ public class NotificationData { // TODO: This should not be public. Instead the Environment should notify this class when // anything changed, and this class should call back the UI so it updates itself. - public void filterAndSort() { + /** + * Filters and sorts the list of notification entries + */ + public void filterAndSort(String reason) { + mNotifLog.log(NotifEvent.FILTER_AND_SORT, reason); mSortedAndFiltered.clear(); synchronized (mEntries) { @@ -432,7 +455,7 @@ public class NotificationData { boolean isHeadsUp, boolean isMedia, boolean isSystemMax) { - if (mUsePeopleFiltering && e.hasAssociatedPeople()) { + if (mUsePeopleFiltering && isPeopleNotification(e)) { e.setBucket(BUCKET_PEOPLE); } else if (isHeadsUp || isMedia || isSystemMax || e.isHighPriority()) { e.setBucket(BUCKET_ALERTING); @@ -441,6 +464,10 @@ public class NotificationData { } } + private boolean isPeopleNotification(NotificationEntry e) { + return mPeopleNotificationIdentifier.isPeopleNotification(e.getSbn()); + } + public void dump(PrintWriter pw, String indent) { int filteredLen = mSortedAndFiltered.size(); pw.print(indent); @@ -466,10 +493,10 @@ public class NotificationData { } private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) { - getRanking(e.key, mTmpRanking); + getRanking(e.getKey(), mTmpRanking); pw.print(indent); - pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); - StatusBarNotification n = e.notification; + pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.icon); + StatusBarNotification n = e.getSbn(); pw.print(indent); pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" + mTmpRanking.getImportance()); @@ -483,19 +510,6 @@ public class NotificationData { } /** - * Get the current set of buckets for notification entries, as defined here - */ - public static int[] getNotificationBuckets(Context context) { - if (NotificationUtils.usePeopleFiltering(context)) { - return new int[]{BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT}; - } else if (NotificationUtils.useNewInterruptionModel(context)) { - return new int[]{BUCKET_ALERTING, BUCKET_SILENT}; - } else { - return new int[]{BUCKET_ALERTING}; - } - } - - /** * Provides access to keyguard state and user settings dependent data. */ public interface KeyguardEnvironment { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index c3211e307845..71fc5490d656 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -30,6 +30,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICAT import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; +import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING; import android.annotation.NonNull; @@ -84,23 +85,30 @@ import java.util.Objects; * clean this up in the future. */ public final class NotificationEntry { - private static final long LAUNCH_COOLDOWN = 2000; - private static final long REMOTE_INPUT_COOLDOWN = 500; - private static final long INITIALIZATION_DELAY = 400; - private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN; - private static final int COLOR_INVALID = 1; - public final String key; - public StatusBarNotification notification; + private final String mKey; + private StatusBarNotification mSbn; private Ranking mRanking; - public boolean noisy; + + /* + * Bookkeeping members + */ + + /** List of lifetime extenders that are extending the lifetime of this notification. */ + final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>(); + + + /* + * Old members + * TODO: Remove every member beneath this line if possible + */ + public StatusBarIconView icon; public StatusBarIconView expandedIcon; public StatusBarIconView centeredIcon; public StatusBarIconView aodIcon; private boolean interruption; - public boolean autoRedacted; // whether the redacted notification was generated by us public int targetSdk; private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET; public CharSequence remoteInputText; @@ -141,12 +149,6 @@ public final class NotificationEntry { private boolean hasSentReply; /** - * Whether this notification has changed in visual appearance since the previous post. - * New notifications are interruptive by default. - */ - public boolean isVisuallyInterruptive; - - /** * Whether this notification is shown to the user as a high priority notification: visible on * the lock screen/status bar and in the top section in the shade. */ @@ -161,34 +163,42 @@ public final class NotificationEntry { public NotificationEntry( @NonNull StatusBarNotification sbn, @NonNull Ranking ranking) { - this.key = sbn.getKey(); - setNotification(sbn); + checkNotNull(sbn); + checkNotNull(sbn.getKey()); + checkNotNull(ranking); + + mKey = sbn.getKey(); + setSbn(sbn); setRanking(ranking); } /** The key for this notification. Guaranteed to be immutable and unique */ - public String key() { - return key; + public String getKey() { + return mKey; } /** * The StatusBarNotification that represents one half of a NotificationEntry (the other half * being the Ranking). This object is swapped out whenever a notification is updated. */ - public StatusBarNotification sbn() { - return notification; + public StatusBarNotification getSbn() { + return mSbn; } /** * Should only be called by NotificationEntryManager and friends. * TODO: Make this package-private */ - public void setNotification(StatusBarNotification sbn) { - if (sbn.getKey() != null && key != null && !sbn.getKey().equals(key)) { + public void setSbn(@NonNull StatusBarNotification sbn) { + checkNotNull(sbn); + checkNotNull(sbn.getKey()); + + if (!sbn.getKey().equals(mKey)) { throw new IllegalArgumentException("New key " + sbn.getKey() - + " doesn't match existing key " + key); + + " doesn't match existing key " + mKey); } - notification = sbn; + + mSbn = sbn; updatePeopleList(); } @@ -197,7 +207,7 @@ public final class NotificationEntry { * StatusBarNotification). This object is swapped out whenever a the ranking is updated (which * generally occurs whenever anything changes in the notification list). */ - public Ranking ranking() { + public Ranking getRanking() { return mRanking; } @@ -206,14 +216,22 @@ public final class NotificationEntry { * TODO: Make this package-private */ public void setRanking(@NonNull Ranking ranking) { - if (!ranking.getKey().equals(key)) { + checkNotNull(ranking); + checkNotNull(ranking.getKey()); + + if (!ranking.getKey().equals(mKey)) { throw new IllegalArgumentException("New key " + ranking.getKey() - + " doesn't match existing key " + key); + + " doesn't match existing key " + mKey); } + mRanking = ranking; - isVisuallyInterruptive = ranking.visuallyInterruptive(); } + + /* + * Convenience getters for SBN and Ranking members + */ + public NotificationChannel getChannel() { return mRanking.getChannel(); } @@ -261,6 +279,12 @@ public final class NotificationEntry { } + /* + * Old methods + * + * TODO: Remove as many of these as possible + */ + public void setInterruption() { interruption = true; } @@ -278,13 +302,13 @@ public final class NotificationEntry { } public boolean isBubble() { - return (notification.getNotification().flags & FLAG_BUBBLE) != 0; + return (mSbn.getNotification().flags & FLAG_BUBBLE) != 0; } private void updatePeopleList() { mAssociatedPeople.clear(); - Bundle extras = notification.getNotification().extras; + Bundle extras = mSbn.getNotification().extras; if (extras == null) { return; } @@ -296,7 +320,7 @@ public final class NotificationEntry { } if (Notification.MessagingStyle.class.equals( - notification.getNotification().getNotificationStyle())) { + mSbn.getNotification().getNotificationStyle())) { final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES); if (!ArrayUtils.isEmpty(messages)) { for (Notification.MessagingStyle.Message message : @@ -316,7 +340,7 @@ public final class NotificationEntry { * Returns the data needed for a bubble for this notification, if it exists. */ public Notification.BubbleMetadata getBubbleMetadata() { - return notification.getNotification().getBubbleMetadata(); + return mSbn.getNotification().getBubbleMetadata(); } /** @@ -438,7 +462,7 @@ public final class NotificationEntry { }); // Construct the centered icon - if (notification.getNotification().isMediaNotification()) { + if (mSbn.getNotification().isMediaNotification()) { centeredIcon = new StatusBarIconView(context, sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); centeredIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); @@ -478,8 +502,8 @@ public final class NotificationEntry { // Update the icon Notification n = sbn.getNotification(); final StatusBarIcon ic = new StatusBarIcon( - notification.getUser(), - notification.getPackageName(), + mSbn.getUser(), + mSbn.getPackageName(), n.getSmallIcon(), n.iconLevel, n.number, @@ -503,7 +527,7 @@ public final class NotificationEntry { public int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor) { int rawColor = isLowPriority ? Notification.COLOR_DEFAULT : - notification.getNotification().color; + mSbn.getNotification().color; if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) { return mCachedContrastColor; } @@ -569,7 +593,7 @@ public final class NotificationEntry { if (!hasSentReply) { return false; } - Bundle extras = notification.getNotification().extras; + Bundle extras = mSbn.getNotification().extras; CharSequence[] replyTexts = extras.getCharSequenceArray( Notification.EXTRA_REMOTE_INPUT_HISTORY); if (!ArrayUtils.isEmpty(replyTexts)) { @@ -771,7 +795,7 @@ public final class NotificationEntry { * @see #canViewBeDismissed() */ public boolean isClearable() { - if (notification == null || !notification.isClearable()) { + if (!mSbn.isClearable()) { return false; } @@ -794,15 +818,15 @@ public final class NotificationEntry { @VisibleForTesting boolean isExemptFromDndVisualSuppression() { - if (isNotificationBlockedByPolicy(notification.getNotification())) { + if (isNotificationBlockedByPolicy(mSbn.getNotification())) { return false; } - if ((notification.getNotification().flags + if ((mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { return true; } - if (notification.getNotification().isMediaNotification()) { + if (mSbn.getNotification().isMediaNotification()) { return true; } if (mIsSystemNotification != null && mIsSystemNotification) { @@ -935,4 +959,10 @@ public final class NotificationEntry { this.index = index; } } + + private static final long LAUNCH_COOLDOWN = 2000; + private static final long REMOTE_INPUT_COOLDOWN = 500; + private static final long INITIALIZATION_DELAY = 400; + private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN; + private static final int COLOR_INVALID = 1; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java index 60cf995ad8d1..396c5fefd8b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java @@ -41,6 +41,8 @@ import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.NotificationClicker; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.logging.NotifEvent; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; @@ -72,6 +74,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private final boolean mAllowLongPress; private final KeyguardBypassController mKeyguardBypassController; private final StatusBarStateController mStatusBarStateController; + private final NotifLog mNotifLog; private NotificationRemoteInputManager mRemoteInputManager; private NotificationPresenter mPresenter; @@ -85,12 +88,14 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { public NotificationRowBinderImpl(Context context, boolean allowLongPress, KeyguardBypassController keyguardBypassController, - StatusBarStateController statusBarStateController) { + StatusBarStateController statusBarStateController, + NotifLog notifLog) { mContext = context; mMessagingUtil = new NotificationMessagingUtil(context); mAllowLongPress = allowLongPress; mKeyguardBypassController = keyguardBypassController; mStatusBarStateController = statusBarStateController; + mNotifLog = notifLog; } private NotificationRemoteInputManager getRemoteInputManager() { @@ -130,9 +135,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { throws InflationException { ViewGroup parent = mListContainer.getViewParentForNotification(entry); PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext, - entry.notification.getUser().getIdentifier()); + entry.getSbn().getUser().getIdentifier()); - final StatusBarNotification sbn = entry.notification; + final StatusBarNotification sbn = entry.getSbn(); if (entry.rowExists()) { entry.updateIcons(mContext, sbn); entry.reset(); @@ -143,6 +148,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { row -> { bindRow(entry, pmUser, sbn, row, onDismissRunnable); updateNotification(entry, pmUser, sbn, row); + mNotifLog.log(NotifEvent.INFLATED, sbn); }); } } @@ -150,7 +156,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private void bindRow(NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row, Runnable onDismissRunnable) { - row.setExpansionLogger(mExpansionLogger, entry.notification.getKey()); + row.setExpansionLogger(mExpansionLogger, entry.getSbn().getKey()); row.setBypassController(mKeyguardBypassController); row.setStatusBarStateController(mStatusBarStateController); row.setGroupManager(mGroupManager); @@ -207,8 +213,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { entry.reset(); PackageManager pmUser = StatusBar.getPackageManagerForUser( mContext, - entry.notification.getUser().getIdentifier()); - updateNotification(entry, pmUser, entry.notification, entry.getRow()); + entry.getSbn().getUser().getIdentifier()); + updateNotification(entry, pmUser, entry.getSbn(), entry.getRow()); } else { // Once the RowInflaterTask is done, it will pick up the updated entry, so // no-op here. @@ -242,7 +248,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { // TODO: should updates to the entry be happening somewhere else? entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); - entry.autoRedacted = entry.notification.getNotification().publicVersion == null; entry.setRow(row); row.setOnActivatedListener(mPresenter); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java new file mode 100644 index 000000000000..8ebbca26fa5b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.logging; + +import android.annotation.IntDef; +import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.StatusBarNotification; + +import com.android.systemui.log.RichEvent; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * An event related to notifications. {@link NotifLog} stores and prints these events for debugging + * and triaging purposes. We do not store a copy of the status bar notification nor ranking + * here to mitigate memory usage. + */ +public class NotifEvent extends RichEvent { + public static final int TOTAL_EVENT_TYPES = 11; + + /** + * Creates a NotifEvent with an event type that matches with an index in the array + * getSupportedEvents() and {@link EventType}. + * + * The status bar notification and ranking objects are stored as shallow copies of the current + * state of the event when this event occurred. + */ + public NotifEvent(int logLevel, int type, String reason, StatusBarNotification sbn, + Ranking ranking) { + super(logLevel, type, reason); + mMessage += getExtraInfo(sbn, ranking); + } + + private String getExtraInfo(StatusBarNotification sbn, Ranking ranking) { + StringBuilder extraInfo = new StringBuilder(); + + if (sbn != null) { + extraInfo.append(" Sbn="); + extraInfo.append(sbn); + } + + if (ranking != null) { + extraInfo.append(" Ranking="); + extraInfo.append(ranking); + } + + return extraInfo.toString(); + } + + /** + * Event labels for NotifEvents + * Index corresponds to the {@link EventType} + */ + @Override + public String[] getEventLabels() { + final String[] events = new String[]{ + "NotifAdded", + "NotifRemoved", + "NotifUpdated", + "Filter", + "Sort", + "FilterAndSort", + "NotifVisibilityChanged", + "LifetimeExtended", + "RemoveIntercepted", + "InflationAborted", + "Inflated" + }; + + if (events.length != TOTAL_EVENT_TYPES) { + throw new IllegalStateException("NotifEvents events.length should match " + + TOTAL_EVENT_TYPES + + " events.length=" + events.length + + " TOTAL_EVENT_LENGTH=" + TOTAL_EVENT_TYPES); + } + return events; + } + + /** + * Builds a NotifEvent. + */ + public static class NotifEventBuilder extends RichEvent.Builder<NotifEventBuilder> { + private StatusBarNotification mSbn; + private Ranking mRanking; + + @Override + public NotifEventBuilder getBuilder() { + return this; + } + + /** + * Stores the status bar notification object. A shallow copy is stored in the NotifEvent's + * constructor. + */ + public NotifEventBuilder setSbn(StatusBarNotification sbn) { + mSbn = sbn; + return this; + } + + /** + * Stores the ranking object. A shallow copy is stored in the NotifEvent's + * constructor. + */ + public NotifEventBuilder setRanking(Ranking ranking) { + mRanking = ranking; + return this; + } + + @Override + public RichEvent build() { + return new NotifEvent(mLogLevel, mType, mReason, mSbn, mRanking); + } + } + + @IntDef({NOTIF_ADDED, + NOTIF_REMOVED, + NOTIF_UPDATED, + FILTER, + SORT, + FILTER_AND_SORT, + NOTIF_VISIBILITY_CHANGED, + LIFETIME_EXTENDED, + REMOVE_INTERCEPTED, + INFLATION_ABORTED, + INFLATED + }) + + /** + * Types of NotifEvents + */ + @Retention(RetentionPolicy.SOURCE) + public @interface EventType {} + public static final int NOTIF_ADDED = 0; + public static final int NOTIF_REMOVED = 1; + public static final int NOTIF_UPDATED = 2; + public static final int FILTER = 3; + public static final int SORT = 4; + public static final int FILTER_AND_SORT = 5; + public static final int NOTIF_VISIBILITY_CHANGED = 6; + public static final int LIFETIME_EXTENDED = 7; + // unable to remove notif - removal intercepted by {@link NotificationRemoveInterceptor} + public static final int REMOVE_INTERCEPTED = 8; + public static final int INFLATION_ABORTED = 9; + public static final int INFLATED = 10; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java new file mode 100644 index 000000000000..129283107894 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.logging; + +import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.StatusBarNotification; + +import com.android.systemui.DumpController; +import com.android.systemui.log.SysuiLog; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Logs systemui notification events for debugging and triaging purposes. Logs are dumped in + * bugreports or on demand: + * adb shell dumpsys activity service com.android.systemui/.SystemUIService \ + * dependency DumpController NotifLog + */ +@Singleton +public class NotifLog extends SysuiLog { + private static final String TAG = "NotifLog"; + private static final int MAX_DOZE_DEBUG_LOGS = 400; + private static final int MAX_DOZE_LOGS = 50; + + @Inject + public NotifLog(DumpController dumpController) { + super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS); + } + + /** + * Logs a {@link NotifEvent} with a notification, ranking and message + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, + Ranking ranking, String msg) { + return log(new NotifEvent.NotifEventBuilder() + .setType(eventType) + .setSbn(sbn) + .setRanking(ranking) + .setReason(msg) + .build()); + } + + /** + * Logs a {@link NotifEvent} + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType) { + return log(eventType, null, null, null); + } + + /** + * Logs a {@link NotifEvent} with a message + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType, String msg) { + return log(eventType, null, null, msg); + } + + /** + * Logs a {@link NotifEvent} with a notification + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn) { + return log(eventType, sbn, null, ""); + } + + /** + * Logs a {@link NotifEvent} with a notification + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, String msg) { + return log(eventType, sbn, null, msg); + } + + /** + * Logs a {@link NotifEvent} with a ranking + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType, Ranking ranking) { + return log(eventType, null, ranking, ""); + } + + /** + * Logs a {@link NotifEvent} with a notification and ranking + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, + Ranking ranking) { + return log(eventType, sbn, ranking, ""); + } + + /** + * Logs a {@link NotifEvent} with a notification entry + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType, NotificationEntry entry) { + return log(eventType, entry.getSbn(), entry.getRanking(), ""); + } + + /** + * Logs a {@link NotifEvent} with a notification entry + * @return true if successfully logged, else false + */ + public boolean log(@NotifEvent.EventType int eventType, NotificationEntry entry, + String msg) { + return log(eventType, entry.getSbn(), entry.getRanking(), msg); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java index 81275fda57e5..b7f408ebe557 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java @@ -126,7 +126,7 @@ public class NotificationLogger implements StateListener { int N = activeNotifications.size(); for (int i = 0; i < N; i++) { NotificationEntry entry = activeNotifications.get(i); - String key = entry.notification.getKey(); + String key = entry.getSbn().getKey(); boolean isVisible = mListContainer.isInVisibleLocation(entry); NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible, getNotificationLocation(entry)); @@ -214,14 +214,14 @@ public class NotificationLogger implements StateListener { NotificationVisibility visibility, boolean removedByUser) { if (removedByUser && visibility != null) { - logNotificationClear(entry.key, entry.notification, visibility); + logNotificationClear(entry.getKey(), entry.getSbn(), visibility); } - mExpansionStateLogger.onEntryRemoved(entry.key); + mExpansionStateLogger.onEntryRemoved(entry.getKey()); } @Override public void onEntryReinflated(NotificationEntry entry) { - mExpansionStateLogger.onEntryReinflated(entry.key); + mExpansionStateLogger.onEntryReinflated(entry.getKey()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt new file mode 100644 index 000000000000..2c0c9420a8c4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.people + +import android.app.PendingIntent +import android.graphics.drawable.Drawable + +/** `ViewModel` for PeopleHub view. */ +data class PeopleHubViewModel(val people: Sequence<PersonViewModel>, val isVisible: Boolean) + +/** `ViewModel` for a single "Person' in PeopleHub. */ +data class PersonViewModel( + val name: CharSequence, + val icon: Drawable, + val onClick: () -> Unit +) + +/** `Model` for PeopleHub. */ +data class PeopleHubModel(val people: Collection<PersonModel>) + +/** `Model` for a single "Person" in PeopleHub. */ +data class PersonModel( + val key: PersonKey, + val name: CharSequence, + val avatar: Drawable, + val clickIntent: PendingIntent +) + +/** Unique identifier for a Person in PeopleHub. */ +typealias PersonKey = String
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt new file mode 100644 index 000000000000..45709893a7b5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.people + +import dagger.Binds +import dagger.Module + +@Module +abstract class PeopleHubModule { + + @Binds + abstract fun peopleHubSectionFooterViewController( + impl: PeopleHubSectionFooterViewAdapterImpl + ): PeopleHubSectionFooterViewAdapter + + @Binds + abstract fun peopleHubDataSource(impl: PeopleHubDataSourceImpl): DataSource<PeopleHubModel> + + @Binds + abstract fun peopleHubViewModelFactoryDataSource( + impl: PeopleHubViewModelFactoryDataSourceImpl + ): DataSource<PeopleHubViewModelFactory> + + @Binds + abstract fun peopleNotificationIdentifier( + impl: PeopleNotificationIdentifierImpl + ): PeopleNotificationIdentifier + + @Binds + abstract fun notificationPersonExtractor( + pluginImpl: NotificationPersonExtractorPluginBoundary + ): NotificationPersonExtractor +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt new file mode 100644 index 000000000000..fe257d9fd252 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.people + +import android.app.Notification +import android.content.Context +import android.graphics.Canvas +import android.graphics.ColorFilter +import android.graphics.PixelFormat +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.os.UserHandle +import android.service.notification.StatusBarNotification +import android.util.TypedValue +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import com.android.internal.statusbar.NotificationVisibility +import com.android.internal.widget.MessagingGroup +import com.android.launcher3.icons.BaseIconFactory +import com.android.systemui.R +import com.android.systemui.plugins.NotificationPersonExtractorPlugin +import com.android.systemui.statusbar.notification.NotificationEntryListener +import com.android.systemui.statusbar.notification.NotificationEntryManager +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.policy.ExtensionController +import java.util.ArrayDeque +import javax.inject.Inject +import javax.inject.Singleton + +private const val MAX_STORED_INACTIVE_PEOPLE = 10 + +interface NotificationPersonExtractor { + fun extractPerson(sbn: StatusBarNotification): PersonModel? + fun extractPersonKey(sbn: StatusBarNotification): String? +} + +@Singleton +class NotificationPersonExtractorPluginBoundary @Inject constructor( + extensionController: ExtensionController, + private val context: Context +) : NotificationPersonExtractor { + + private var plugin: NotificationPersonExtractorPlugin? = null + + init { + plugin = extensionController + .newExtension(NotificationPersonExtractorPlugin::class.java) + .withPlugin(NotificationPersonExtractorPlugin::class.java) + .withCallback { extractor -> + plugin = extractor + } + .build() + .get() + } + + override fun extractPerson(sbn: StatusBarNotification) = + plugin?.extractPerson(sbn)?.let { data -> + val badged = addBadgeToDrawable(data.avatar, context, sbn.packageName, sbn.user) + PersonModel(data.key, data.name, badged, data.clickIntent) + } + + override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn) +} + +@Singleton +class PeopleHubDataSourceImpl @Inject constructor( + private val notificationEntryManager: NotificationEntryManager, + private val peopleHubManager: PeopleHubManager, + private val extractor: NotificationPersonExtractor +) : DataSource<PeopleHubModel> { + + private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>() + + private val notificationEntryListener = object : NotificationEntryListener { + override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) = + addVisibleEntry(entry) + + override fun onEntryReinflated(entry: NotificationEntry) = addVisibleEntry(entry) + + override fun onPostEntryUpdated(entry: NotificationEntry) = addVisibleEntry(entry) + + override fun onEntryRemoved( + entry: NotificationEntry, + visibility: NotificationVisibility?, + removedByUser: Boolean + ) = removeVisibleEntry(entry) + } + + private fun removeVisibleEntry(entry: NotificationEntry) { + val key = extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey() + if (key?.let(peopleHubManager::removeActivePerson) == true) { + updateUi() + } + } + + private fun addVisibleEntry(entry: NotificationEntry) { + val personModel = extractor.extractPerson(entry.sbn) ?: entry.extractPerson() + if (personModel?.let(peopleHubManager::addActivePerson) == true) { + updateUi() + } + } + + override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription { + val registerWithNotificationEntryManager = dataListeners.isEmpty() + dataListeners.add(listener) + if (registerWithNotificationEntryManager) { + notificationEntryManager.addNotificationEntryListener(notificationEntryListener) + } else { + listener.onDataChanged(peopleHubManager.getPeopleHubModel()) + } + return object : Subscription { + override fun unsubscribe() { + dataListeners.remove(listener) + if (dataListeners.isEmpty()) { + notificationEntryManager + .removeNotificationEntryListener(notificationEntryListener) + } + } + } + } + + private fun updateUi() { + val model = peopleHubManager.getPeopleHubModel() + for (listener in dataListeners) { + listener.onDataChanged(model) + } + } +} + +@Singleton +class PeopleHubManager @Inject constructor() { + + private val activePeople = mutableMapOf<PersonKey, PersonModel>() + private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE) + + fun removeActivePerson(key: PersonKey): Boolean { + activePeople.remove(key)?.let { data -> + if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) { + inactivePeople.removeLast() + } + inactivePeople.push(data) + return true + } + return false + } + + fun addActivePerson(person: PersonModel): Boolean { + activePeople[person.key] = person + return inactivePeople.removeIf { it.key == person.key } + } + + fun getPeopleHubModel(): PeopleHubModel = PeopleHubModel(inactivePeople) +} + +private val ViewGroup.children + get(): Sequence<View> = sequence { + for (i in 0 until childCount) { + yield(getChildAt(i)) + } + } + +private fun ViewGroup.childrenWithId(id: Int): Sequence<View> = children.filter { it.id == id } + +private fun NotificationEntry.extractPerson(): PersonModel? { + if (!isMessagingNotification()) { + return null + } + val clickIntent = sbn.notification.contentIntent + ?: return null + val extras = sbn.notification.extras + val name = extras.getString(Notification.EXTRA_CONVERSATION_TITLE) + ?: extras.getString(Notification.EXTRA_TITLE) + ?: return null + val drawable = extractAvatarFromRow(this) ?: return null + val badgedAvatar = addBadgeToDrawable(drawable, row.context, sbn.packageName, sbn.user) + return PersonModel(key, name, badgedAvatar, clickIntent) +} + +private fun addBadgeToDrawable( + drawable: Drawable, + context: Context, + packageName: String, + user: UserHandle +): Drawable { + val pm = context.packageManager + val appInfo = pm.getApplicationInfoAsUser(packageName, 0, user) + return object : Drawable() { + override fun draw(canvas: Canvas) { + val iconBounds = getBounds() + val factory = object : BaseIconFactory( + context, + 0 /* unused */, + iconBounds.width(), + true) {} + val badge = factory.createBadgedIconBitmap( + appInfo.loadIcon(pm), + user, + true, + appInfo.isInstantApp, + null) + val badgeDrawable = BitmapDrawable(context.resources, badge.icon) + .apply { + alpha = drawable.alpha + colorFilter = drawable.colorFilter + val badgeWidth = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 15f, + context.resources.displayMetrics + ).toInt() + setBounds( + iconBounds.left + (iconBounds.width() - badgeWidth), + iconBounds.top + (iconBounds.height() - badgeWidth), + iconBounds.right, + iconBounds.bottom) + } + drawable.bounds = iconBounds + drawable.draw(canvas) + badgeDrawable.draw(canvas) + } + + override fun setAlpha(alpha: Int) { + drawable.alpha = alpha + } + + override fun setColorFilter(colorFilter: ColorFilter?) { + drawable.colorFilter = colorFilter + } + + @PixelFormat.Opacity + override fun getOpacity(): Int = PixelFormat.OPAQUE + } +} + +private fun extractAvatarFromRow(entry: NotificationEntry): Drawable? = + entry.row + ?.childrenWithId(R.id.expanded) + ?.mapNotNull { it as? ViewGroup } + ?.flatMap { + it.childrenWithId(com.android.internal.R.id.status_bar_latest_event_content) + } + ?.mapNotNull { + it.findViewById<ViewGroup>(com.android.internal.R.id.notification_messaging) + } + ?.mapNotNull { messagesView -> + messagesView.children + .mapNotNull { it as? MessagingGroup } + .lastOrNull() + ?.findViewById<ImageView>(com.android.internal.R.id.message_icon) + ?.drawable + } + ?.firstOrNull() + +private fun NotificationEntry.extractPersonKey(): PersonKey? = + if (isMessagingNotification()) key else null + +private fun NotificationEntry.isMessagingNotification() = + sbn.notification.notificationStyle == Notification.MessagingStyle::class.java
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt new file mode 100644 index 000000000000..5c354089dfe9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.people + +import android.view.View +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager +import javax.inject.Inject +import javax.inject.Singleton + +/** Boundary between the View and PeopleHub, as seen by the View. */ +interface PeopleHubSectionFooterViewAdapter { + fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) +} + +/** Abstract `View` representation of PeopleHub footer in [NotificationSectionsManager]. */ +interface PeopleHubSectionFooterViewBoundary { + /** View used for animating the activity launch caused by clicking a person in the hub. */ + val associatedViewForClickAnimation: View + + /** [DataListener]s for individual people in the hub. */ + val personViewAdapters: Sequence<DataListener<PersonViewModel?>> + + /** Sets the visibility of the Hub in the notification shade. */ + fun setVisible(isVisible: Boolean) +} + +/** Creates a [PeopleHubViewModel] given some additional information required from the `View`. */ +interface PeopleHubViewModelFactory { + + /** + * Creates a [PeopleHubViewModel] that, when clicked, starts an activity using an animation + * involving the given [view]. + */ + fun createWithAssociatedClickView(view: View): PeopleHubViewModel +} + +/** + * Wraps a [PeopleHubSectionFooterViewBoundary] in a [DataListener], and connects it to the data + * pipeline. + * + * @param dataSource PeopleHub data pipeline. + */ +@Singleton +class PeopleHubSectionFooterViewAdapterImpl @Inject constructor( + private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory> +) : PeopleHubSectionFooterViewAdapter { + + override fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) { + dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary)) + } +} + +private class PeopleHubDataListenerImpl( + private val viewBoundary: PeopleHubSectionFooterViewBoundary +) : DataListener<PeopleHubViewModelFactory> { + + override fun onDataChanged(data: PeopleHubViewModelFactory) { + val viewModel = data.createWithAssociatedClickView( + viewBoundary.associatedViewForClickAnimation + ) + viewBoundary.setVisible(viewModel.isVisible) + val padded = viewModel.people + repeated(null) + for ((personAdapter, personModel) in viewBoundary.personViewAdapters.zip(padded)) { + personAdapter.onDataChanged(personModel) + } + } +} + +/** + * Converts [PeopleHubModel]s into [PeopleHubViewModelFactory]s. + * + * This class serves as the glue between the View layer (which depends on + * [PeopleHubSectionFooterViewBoundary]) and the Data layer (which produces [PeopleHubModel]s). + */ +@Singleton +class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor( + private val activityStarter: ActivityStarter, + private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel> +) : DataSource<PeopleHubViewModelFactory> { + + override fun registerListener(listener: DataListener<PeopleHubViewModelFactory>) = + dataSource.registerListener(PeopleHubModelListenerImpl(activityStarter, listener)) +} + +private class PeopleHubModelListenerImpl( + private val activityStarter: ActivityStarter, + private val dataListener: DataListener<PeopleHubViewModelFactory> +) : DataListener<PeopleHubModel> { + + override fun onDataChanged(data: PeopleHubModel) = + dataListener.onDataChanged(PeopleHubViewModelFactoryImpl(data, activityStarter)) +} + +private class PeopleHubViewModelFactoryImpl( + private val data: PeopleHubModel, + private val activityStarter: ActivityStarter +) : PeopleHubViewModelFactory { + + override fun createWithAssociatedClickView(view: View): PeopleHubViewModel { + val personViewModels = data.people.asSequence().map { personModel -> + val onClick = { + activityStarter.startPendingIntentDismissingKeyguard( + personModel.clickIntent, + null, + view + ) + } + PersonViewModel(personModel.name, personModel.avatar, onClick) + } + return PeopleHubViewModel(personViewModels, data.people.isNotEmpty()) + } +} + +private fun <T> repeated(value: T): Sequence<T> = sequence { + while (true) { + yield(value) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt new file mode 100644 index 000000000000..bfd4070846f3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.people + +import android.app.Notification +import android.service.notification.StatusBarNotification +import javax.inject.Inject +import javax.inject.Singleton + +interface PeopleNotificationIdentifier { + fun isPeopleNotification(sbn: StatusBarNotification): Boolean +} + +@Singleton +class PeopleNotificationIdentifierImpl @Inject constructor( + private val personExtractor: NotificationPersonExtractor +) : PeopleNotificationIdentifier { + + override fun isPeopleNotification(sbn: StatusBarNotification) = + sbn.notification.notificationStyle == Notification.MessagingStyle::class.java || + personExtractor.extractPersonKey(sbn) != null +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt new file mode 100644 index 000000000000..3ca3792a680f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.people + +/** Boundary between a View and data pipeline, as seen by the pipeline. */ +interface DataListener<in T> { + fun onDataChanged(data: T) +} + +/** Convert all data using the given [mapper] before invoking this [DataListener]. */ +fun <S, T> DataListener<T>.contraMap(mapper: (S) -> T): DataListener<S> = object : DataListener<S> { + override fun onDataChanged(data: S) = onDataChanged(mapper(data)) +} + +/** Boundary between a View and data pipeline, as seen by the View. */ +interface DataSource<out T> { + fun registerListener(listener: DataListener<T>): Subscription +} + +/** Represents a registration with a [DataSource]. */ +interface Subscription { + /** Removes the previously registered [DataListener] from the [DataSource] */ + fun unsubscribe() +} + +/** Transform all data coming out of this [DataSource] using the given [mapper]. */ +fun <S, T> DataSource<S>.map(mapper: (S) -> T): DataSource<T> = object : DataSource<T> { + override fun registerListener(listener: DataListener<T>) = + registerListener(listener.contraMap(mapper)) +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 8d7325118139..a817f54e77ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -180,6 +180,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView initDimens(); } + public FalsingManager getFalsingManager() { + return mFalsingManager; + } + private void updateColors() { mNormalColor = mContext.getColor(R.color.notification_material_background_color); mTintedRippleColor = mContext.getColor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 0f6ce2153488..536db67a8795 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -74,7 +74,6 @@ import com.android.internal.widget.CachingIconView; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; @@ -221,7 +220,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private ViewStub mGutsStub; private boolean mIsSystemChildExpanded; private boolean mIsPinned; - private FalsingManager mFalsingManager; private boolean mExpandAnimationRunning; private AboveShelfChangedListener mAboveShelfChangedListener; private HeadsUpManager mHeadsUpManager; @@ -444,7 +442,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView */ public void setEntry(@NonNull NotificationEntry entry) { mEntry = entry; - mStatusBarNotification = entry.notification; + mStatusBarNotification = entry.getSbn(); cacheIsSystemNotification(); } @@ -1205,7 +1203,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView // Let's update our childrencontainer. This is intentionally not guarded with // mIsSummaryWithChildren since we might have had children but not anymore. if (mChildrenContainer != null) { - mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification); + mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.getSbn()); } if (mGuts != null) { NotificationGuts oldGuts = mGuts; @@ -1449,6 +1447,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } setDismissed(fromAccessibility); if (mEntry.isClearable()) { + // TODO: beverlyt, log dismissal // TODO: track dismiss sentiment if (mOnDismissRunnable != null) { mOnDismissRunnable.run(); @@ -1636,7 +1635,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public ExpandableNotificationRow(Context context, AttributeSet attrs) { super(context, attrs); - mFalsingManager = Dependency.get(FalsingManager.class); // TODO: inject into a controller. mNotificationInflater = new NotificationContentInflater(this); mMenuRow = new NotificationMenuRow(mContext); mImageResolver = new NotificationInlineImageResolver(context, @@ -2208,7 +2206,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * @param allowChildExpansion whether a call to this method allows expanding children */ public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) { - mFalsingManager.setNotificationExpanded(); + getFalsingManager().setNotificationExpanded(); if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion && !mChildrenContainer.showingAsLowPriority()) { final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification); @@ -2305,7 +2303,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private void updateRippleAllowed() { boolean allowed = isOnKeyguard() - || mEntry.notification.getNotification().contentIntent == null; + || mEntry.getSbn().getNotification().contentIntent == null; setRippleAllowed(allowed); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java index 73093c6f471f..37f63c9779f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java @@ -139,7 +139,8 @@ public class NotificationBlockingHelperManager { mBlockingHelperRow.setBlockingHelperShowing(false); if (mBlockingHelperRow.isAttachedToWindow()) { - Dependency.get(NotificationEntryManager.class).updateNotifications(); + Dependency.get(NotificationEntryManager.class).updateNotifications( + "dismissCurrentBlockingHelper"); } mBlockingHelperRow = null; return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index a612a1721c41..a91a119d4ea7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -230,7 +230,7 @@ public class NotificationContentInflater { } // Only inflate the ones that are set. reInflateFlags &= mInflationFlags; - StatusBarNotification sbn = mRow.getEntry().notification; + StatusBarNotification sbn = mRow.getEntry().getSbn(); // To check if the notification has inline image and preload inline image if necessary. mRow.getImageResolver().preloadImages(sbn.getNotification()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index f30a8b12ab39..b12c76c750a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -1097,7 +1097,7 @@ public class NotificationContentView extends FrameLayout { } public void onNotificationUpdated(NotificationEntry entry) { - mStatusBarNotification = entry.notification; + mStatusBarNotification = entry.getSbn(); mOnContentViewInactiveListeners.clear(); mBeforeN = entry.targetSdk < Build.VERSION_CODES.N; updateAllSingleLineViews(); @@ -1179,7 +1179,7 @@ public class NotificationContentView extends FrameLayout { : mHeadsUpInflatedSmartReplies.getSmartRepliesAndActions(); if (DEBUG) { Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.", - entry.notification.getKey(), + entry.getSbn().getKey(), mCurrentSmartRepliesAndActions.smartActions == null ? 0 : mCurrentSmartRepliesAndActions.smartActions.actions.size(), mCurrentSmartRepliesAndActions.smartReplies == null ? 0 : @@ -1253,7 +1253,7 @@ public class NotificationContentView extends FrameLayout { } } if (hasRemoteInput) { - int color = entry.notification.getNotification().color; + int color = entry.getSbn().getNotification().color; if (color == Notification.COLOR_DEFAULT) { color = mContext.getColor(R.color.default_remote_input_background); } @@ -1267,7 +1267,7 @@ public class NotificationContentView extends FrameLayout { if (existingPendingIntent != null || existing.isActive()) { // The current action could be gone, or the pending intent no longer valid. // If we find a matching action in the new notification, focus, otherwise close. - Notification.Action[] actions = entry.notification.getNotification().actions; + Notification.Action[] actions = entry.getSbn().getNotification().actions; if (existingPendingIntent != null) { existing.setPendingIntent(existingPendingIntent); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java index 4700baae8fab..18d436ff7659 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java @@ -421,7 +421,7 @@ public class NotificationGuts extends FrameLayout { } /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */ - private static class AnimateCloseListener extends AnimatorListenerAdapter { + private class AnimateCloseListener extends AnimatorListenerAdapter { final View mView; private final GutsContent mGutsContent; @@ -433,8 +433,10 @@ public class NotificationGuts extends FrameLayout { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - mView.setVisibility(View.GONE); - mGutsContent.onFinishedClosing(); + if (!isExposed()) { + mView.setVisibility(View.GONE); + mGutsContent.onFinishedClosing(); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 9d0dd6b683cf..1de2cbb982a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -100,6 +100,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx protected String mKeyToRemoveOnGutsClosed; private StatusBar mStatusBar; + private Runnable mOpenRunnable; @Inject public NotificationGutsManager( @@ -343,6 +344,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls, int x, int y, boolean resetMenu) { if (mNotificationGutsExposed != null) { + mNotificationGutsExposed.removeCallbacks(mOpenRunnable); mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force); } if (resetMenu) { @@ -445,7 +447,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx // ensure that it's laid but not visible until actually laid out guts.setVisibility(View.INVISIBLE); // Post to ensure the the guts are properly laid out. - guts.post(new Runnable() { + mOpenRunnable = new Runnable() { @Override public void run() { if (row.getWindowToken() == null) { @@ -470,7 +472,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mListContainer.onHeightChanged(row, true /* needsAnimation */); mGutsMenuItem = menuItem; } - }); + }; + guts.post(mOpenRunnable); return true; } @@ -491,15 +494,17 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx @Override public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) { if (shouldExtend) { - mKeyToRemoveOnGutsClosed = entry.key; + mKeyToRemoveOnGutsClosed = entry.getKey(); if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Keeping notification because it's showing guts. " + entry.key); + Log.d(TAG, "Keeping notification because it's showing guts. " + entry.getKey()); } } else { - if (mKeyToRemoveOnGutsClosed != null && mKeyToRemoveOnGutsClosed.equals(entry.key)) { + if (mKeyToRemoveOnGutsClosed != null + && mKeyToRemoveOnGutsClosed.equals(entry.getKey())) { mKeyToRemoveOnGutsClosed = null; if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Notification that was kept for guts was updated. " + entry.key); + Log.d(TAG, "Notification that was kept for guts was updated. " + + entry.getKey()); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index 0e0802ef233f..23643133e121 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -28,6 +28,7 @@ import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.metrics.LogMaker; import android.os.Handler; +import android.provider.Settings; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; @@ -43,9 +44,12 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.MediaNotificationView; import com.android.systemui.Dependency; +import com.android.systemui.qs.QSPanel; +import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.phone.StatusBarWindowController; import java.util.Timer; import java.util.TimerTask; @@ -188,9 +192,29 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi mActions = mView.findViewById(com.android.internal.R.id.media_actions); mIsViewVisible = mView.isShown(); - final MediaSession.Token token = mRow.getEntry().notification.getNotification().extras + final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras .getParcelable(Notification.EXTRA_MEDIA_SESSION); + int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); + if (flag == 1) { + StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class); + QuickQSPanel panel = ctrl.getStatusBarView().findViewById( + com.android.systemui.R.id.quick_qs_panel); + panel.getMediaPlayer().setMediaSession(token, + mRow.getStatusBarNotification().getNotification().getSmallIcon(), + getNotificationHeader().getOriginalIconColor(), + mRow.getCurrentBackgroundTint(), + mActions); + QSPanel bigPanel = ctrl.getStatusBarView().findViewById( + com.android.systemui.R.id.quick_settings_panel); + bigPanel.addMediaSession(token, + mRow.getStatusBarNotification().getNotification().getSmallIcon(), + getNotificationHeader().getOriginalIconColor(), + mRow.getCurrentBackgroundTint(), + mActions, + mRow.getStatusBarNotification()); + } + boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar(); if (token == null || (COMPACT_MEDIA_TAG.equals(mView.getTag()) && !showCompactSeekbar)) { if (mSeekBarView != null) { @@ -431,7 +455,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi * @return new LogMaker */ private LogMaker newLog(int event) { - String packageName = mRow.getEntry().notification.getPackageName(); + String packageName = mRow.getEntry().getSbn().getPackageName(); return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR) .setType(event) @@ -443,7 +467,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi * @return new LogMaker */ private LogMaker newLog(int event, int subtype) { - String packageName = mRow.getEntry().notification.getPackageName(); + String packageName = mRow.getEntry().getSbn().getPackageName(); return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR) .setType(event) .setSubtype(subtype) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 3950003e64ce..c2eff8a6a776 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -63,7 +63,7 @@ public abstract class NotificationViewWrapper implements TransformableView { return new NotificationMessagingTemplateViewWrapper(ctx, v, row); } Class<? extends Notification.Style> style = - row.getEntry().notification.getNotification().getNotificationStyle(); + row.getEntry().getSbn().getNotification().getNotificationStyle(); if (Notification.DecoratedCustomViewStyle.class.equals(style)) { return new NotificationDecoratedCustomViewWrapper(ctx, v, row); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index f3d068a9d610..ecab188a7481 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -22,7 +22,6 @@ import android.content.Context; import android.util.MathUtils; import android.view.View; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; @@ -410,7 +409,7 @@ public class AmbientState { if (!mPulsing || mHeadUpManager == null) { return false; } - return mHeadUpManager.isAlerting(entry.key); + return mHeadUpManager.isAlerting(entry.getKey()); } public boolean isPanelTracking() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index ec0c6348fd89..b4f7b59349d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -16,10 +16,9 @@ package com.android.systemui.statusbar.notification.stack; -import android.content.Context; import android.util.MathUtils; -import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -52,8 +51,8 @@ public class NotificationRoundnessManager implements OnHeadsUpChangedListener { @Inject NotificationRoundnessManager( KeyguardBypassController keyguardBypassController, - Context context) { - int numberOfSections = NotificationData.getNotificationBuckets(context).length; + NotificationSectionsFeatureManager sectionsFeatureManager) { + int numberOfSections = sectionsFeatureManager.getNumberOfBuckets(); mFirstInSectionViews = new ActivatableNotificationView[numberOfSections]; mLastInSectionViews = new ActivatableNotificationView[numberOfSections]; mTmpFirstInSectionViews = new ActivatableNotificationView[numberOfSections]; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java index d0444ae81b89..54d406615ae0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java @@ -23,6 +23,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.PendingIntent; import android.content.Intent; import android.provider.Settings; import android.view.LayoutInflater; @@ -33,6 +34,10 @@ import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.people.DataListener; +import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter; +import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewBoundary; +import com.android.systemui.statusbar.notification.people.PersonViewModel; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -42,6 +47,8 @@ import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.List; +import kotlin.sequences.Sequence; + /** * Manages the boundaries of the two notification sections (high priority and low priority). Also * shows/hides the headers for those sections where appropriate. @@ -58,11 +65,39 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section private final StatusBarStateController mStatusBarStateController; private final ConfigurationController mConfigurationController; private final int mNumberOfSections; - private boolean mInitialized = false; + private SectionHeaderView mGentleHeader; private boolean mGentleHeaderVisible = false; + private boolean mPeopleHubVisible = false; + private PeopleHubView mPeopleHubView; + private final PeopleHubSectionFooterViewAdapter mPeopleHubViewAdapter; + private final PeopleHubSectionFooterViewBoundary mPeopleHubViewBoundary = + new PeopleHubSectionFooterViewBoundary() { + @Override + public void setVisible(boolean isVisible) { + if (mPeopleHubVisible != isVisible) { + mPeopleHubVisible = isVisible; + if (mInitialized) { + updateSectionBoundaries(); + } + } + } + + @NonNull + @Override + public View getAssociatedViewForClickAnimation() { + return mPeopleHubView; + } + + @NonNull + @Override + public Sequence<DataListener<PersonViewModel>> getPersonViewAdapters() { + return mPeopleHubView.getPersonViewAdapters(); + } + }; + @Nullable private View.OnClickListener mOnClearGentleNotifsClickListener; NotificationSectionsManager( @@ -70,11 +105,13 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section ActivityStarter activityStarter, StatusBarStateController statusBarStateController, ConfigurationController configurationController, + PeopleHubSectionFooterViewAdapter peopleHubViewAdapter, int numberOfSections) { mParent = parent; mActivityStarter = activityStarter; mStatusBarStateController = statusBarStateController; mConfigurationController = configurationController; + mPeopleHubViewAdapter = peopleHubViewAdapter; mNumberOfSections = numberOfSections; } @@ -94,6 +131,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section } mInitialized = true; reinflateViews(layoutInflater); + mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary); mConfigurationController.addCallback(mConfigurationListener); } @@ -101,23 +139,39 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section * Reinflates the entire notification header, including all decoration views. */ void reinflateViews(LayoutInflater layoutInflater) { - int oldPos = -1; + int oldGentleHeaderPos = -1; + int oldPeopleHubPos = -1; if (mGentleHeader != null) { if (mGentleHeader.getTransientContainer() != null) { mGentleHeader.getTransientContainer().removeView(mGentleHeader); } else if (mGentleHeader.getParent() != null) { - oldPos = mParent.indexOfChild(mGentleHeader); + oldGentleHeaderPos = mParent.indexOfChild(mGentleHeader); mParent.removeView(mGentleHeader); } } + if (mPeopleHubView != null) { + if (mPeopleHubView.getTransientContainer() != null) { + mPeopleHubView.getTransientContainer().removeView(mPeopleHubView); + } else if (mPeopleHubView.getParent() != null) { + oldPeopleHubPos = mParent.indexOfChild(mPeopleHubView); + mParent.removeView(mPeopleHubView); + } + } mGentleHeader = (SectionHeaderView) layoutInflater.inflate( R.layout.status_bar_notification_section_header, mParent, false); mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick); mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick); - if (oldPos != -1) { - mParent.addView(mGentleHeader, oldPos); + if (oldGentleHeaderPos != -1) { + mParent.addView(mGentleHeader, oldGentleHeaderPos); + } + + mPeopleHubView = (PeopleHubView) layoutInflater.inflate( + R.layout.people_strip, mParent, false); + + if (oldPeopleHubPos != -1) { + mParent.addView(mPeopleHubView, oldPeopleHubPos); } } @@ -145,7 +199,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section } if (!begin) { - begin = view == mGentleHeader; + begin = view == mGentleHeader || previous == mPeopleHubView; } return begin; @@ -161,6 +215,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section return ((ExpandableNotificationRow) view).getEntry().getBucket(); } else if (view == mGentleHeader) { return BUCKET_SILENT; + } else if (view == mPeopleHubView) { + return BUCKET_PEOPLE; } throw new IllegalArgumentException("I don't know how to find a bucket for this view :("); @@ -175,6 +231,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section return; } + int lastPersonIndex = -1; int firstGentleNotifIndex = -1; final int n = mParent.getChildCount(); @@ -183,6 +240,9 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (row.getEntry().getBucket() == BUCKET_PEOPLE) { + lastPersonIndex = i; + } if (row.getEntry().getBucket() == BUCKET_SILENT) { firstGentleNotifIndex = i; break; @@ -190,6 +250,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section } } + if (adjustPeopleHubVisibilityAndPosition(lastPersonIndex)) { + // make room for peopleHub + firstGentleNotifIndex++; + } + adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex); mGentleHeader.setAreThereDismissableGentleNotifs( @@ -232,6 +297,36 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section } } + private boolean adjustPeopleHubVisibilityAndPosition(int lastPersonIndex) { + final boolean showPeopleHeader = mPeopleHubVisible + && mNumberOfSections > 2 + && mStatusBarStateController.getState() != StatusBarState.KEYGUARD; + final int currentHubIndex = mParent.indexOfChild(mPeopleHubView); + final boolean currentlyVisible = currentHubIndex >= 0; + int targetIndex = lastPersonIndex + 1; + + if (!showPeopleHeader) { + if (currentlyVisible) { + mParent.removeView(mPeopleHubView); + } + } else { + if (!currentlyVisible) { + if (mPeopleHubView.getTransientContainer() != null) { + mPeopleHubView.getTransientContainer().removeTransientView(mPeopleHubView); + mPeopleHubView.setTransientContainer(null); + } + mParent.addView(mPeopleHubView, targetIndex); + return true; + } else if (currentHubIndex != targetIndex - 1) { + if (currentHubIndex < targetIndex) { + targetIndex--; + } + mParent.changeViewPosition(mPeopleHubView, targetIndex); + } + } + return false; + } + /** * Updates the boundaries (as tracked by their first and last views) of the priority sections. * @@ -284,12 +379,12 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section ActivatableNotificationView first = s.getFirstVisibleChild(); String fs = first == null ? "(null)" : (first instanceof ExpandableNotificationRow) - ? ((ExpandableNotificationRow) first).getEntry().key + ? ((ExpandableNotificationRow) first).getEntry().getKey() : Integer.toHexString(System.identityHashCode(first)); ActivatableNotificationView last = s.getLastVisibleChild(); String ls = last == null ? "(null)" : (last instanceof ExpandableNotificationRow) - ? ((ExpandableNotificationRow) last).getEntry().key + ? ((ExpandableNotificationRow) last).getEntry().getKey() : Integer.toHexString(System.identityHashCode(last)); android.util.Log.d(TAG, "updateSections: f=" + fs + " s=" + i); android.util.Log.d(TAG, "updateSections: l=" + ls + " s=" + i); @@ -324,6 +419,10 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section } } + private void handlePeopleHubClick(PendingIntent pendingIntent) { + mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent, null, mPeopleHubView); + } + /** * For now, declare the available notification buckets (sections) here so that other * presentation code can decide what to do based on an entry's buckets diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index a67018ef9710..6dca7ee9e872 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -109,13 +109,14 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent; import com.android.systemui.statusbar.notification.VisualStabilityManager; -import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -473,8 +474,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mHeadsUpInset; private HeadsUpAppearanceController mHeadsUpAppearanceController; private NotificationIconAreaController mIconAreaController; - private final NotificationLockscreenUserManager mLockscreenUserManager = - Dependency.get(NotificationLockscreenUserManager.class); + private final NotificationLockscreenUserManager mLockscreenUserManager; private final Rect mTmpRect = new Rect(); private final NotificationEntryManager mEntryManager = Dependency.get(NotificationEntryManager.class); @@ -497,8 +497,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private NotificationPanelView mNotificationPanel; private final ShadeController mShadeController = Dependency.get(ShadeController.class); - private final NotificationGutsManager - mNotificationGutsManager = Dependency.get(NotificationGutsManager.class); + private final NotificationGutsManager mNotificationGutsManager; private final NotificationSectionsManager mSectionsManager; private boolean mAnimateBottomOnLayout; private float mLastSentAppear; @@ -517,7 +516,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd StatusBarStateController statusBarStateController, HeadsUpManagerPhone headsUpManager, KeyguardBypassController keyguardBypassController, - FalsingManager falsingManager) { + FalsingManager falsingManager, + NotificationLockscreenUserManager notificationLockscreenUserManager, + NotificationGutsManager notificationGutsManager, + NotificationSectionsFeatureManager sectionsFeatureManager, + PeopleHubSectionFooterViewAdapter peopleHubViewAdapter) { super(context, attrs, 0, 0); Resources res = getResources(); @@ -525,19 +528,22 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mRoundnessManager = notificationRoundnessManager; + mLockscreenUserManager = notificationLockscreenUserManager; + mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; mHeadsUpManager.addListener(mRoundnessManager); mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed); mKeyguardBypassController = keyguardBypassController; mFalsingManager = falsingManager; - int[] buckets = NotificationData.getNotificationBuckets(context); + int[] buckets = sectionsFeatureManager.getNotificationBuckets(); mSectionsManager = new NotificationSectionsManager( this, activityStarter, statusBarStateController, configurationController, + peopleHubViewAdapter, buckets.length); mSectionsManager.initialize(LayoutInflater.from(context)); mSectionsManager.setOnClearGentleNotifsClickListener(v -> { @@ -599,7 +605,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override public void onPostEntryUpdated(NotificationEntry entry) { - if (!entry.notification.isClearable()) { + if (!entry.getSbn().isClearable()) { // The user may have performed a dismiss action on the notification, since it's // not clearable we should snap it back. snapViewIfNeeded(entry); @@ -1624,7 +1630,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd if (!mIsExpanded && row.isHeadsUp() && row.isPinned() && mHeadsUpManager.getTopEntry().getRow() != row && mGroupManager.getGroupSummary( - mHeadsUpManager.getTopEntry().notification) + mHeadsUpManager.getTopEntry().getSbn()) != entry) { continue; } @@ -5332,7 +5338,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd requestChildrenUpdate(); onUpdateRowStates(); - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("StatusBar state changed"); updateVisibility(); } @@ -5522,12 +5528,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd // TODO: This is a listener method; we shouldn't be calling it. Can we just // call performRemoveNotification as below? mEntryManager.removeNotification( - rowToRemove.getEntry().key, + rowToRemove.getEntry().getKey(), null /* ranking */, NotificationListenerService.REASON_CANCEL_ALL); } else { mEntryManager.performRemoveNotification( - rowToRemove.getEntry().notification, + rowToRemove.getEntry().getSbn(), NotificationListenerService.REASON_CANCEL_ALL); } } else { @@ -6491,12 +6497,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) { - mStatusBar.requestNotificationUpdate(); + mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren"); } @Override public void onGroupsChanged() { - mStatusBar.requestNotificationUpdate(); + mStatusBar.requestNotificationUpdate("onGroupsChanged"); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt new file mode 100644 index 000000000000..e31ee024fa36 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.android.systemui.R +import com.android.systemui.statusbar.notification.people.PersonViewModel +import com.android.systemui.statusbar.notification.people.DataListener +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView + +class PeopleHubView(context: Context, attrs: AttributeSet) : + ActivatableNotificationView(context, attrs) { + + private lateinit var contents: ViewGroup + private lateinit var personControllers: List<PersonDataListenerImpl> + val personViewAdapters: Sequence<DataListener<PersonViewModel?>> + get() = personControllers.asSequence() + + override fun onFinishInflate() { + super.onFinishInflate() + contents = requireViewById(R.id.people_list) + personControllers = (0 until contents.childCount) + .asSequence() + .mapNotNull { idx -> + (contents.getChildAt(idx) as? LinearLayout)?.let(::PersonDataListenerImpl) + } + .toList() + } + + override fun getContentView(): View = contents + + private inner class PersonDataListenerImpl(val viewGroup: ViewGroup) : + DataListener<PersonViewModel?> { + + val nameView = viewGroup.requireViewById<TextView>(R.id.person_name) + val avatarView = viewGroup.requireViewById<ImageView>(R.id.person_icon) + + override fun onDataChanged(data: PersonViewModel?) { + viewGroup.visibility = data?.let { View.VISIBLE } ?: View.INVISIBLE + nameView.text = data?.name + avatarView.setImageDrawable(data?.icon) + viewGroup.setOnClickListener { data?.onClick?.invoke() } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java index 5912cd7b6433..f9b936763308 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java @@ -16,75 +16,52 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; -import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; - import android.content.Context; -import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.view.IWindowManager; import android.view.MotionEvent; -import android.view.View; -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; -import com.android.systemui.SysUiServiceProvider; -import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.statusbar.NotificationRemoteInputManager; import javax.inject.Inject; -import javax.inject.Named; /** A controller to control all auto-hide things. */ -public class AutoHideController implements CommandQueue.Callbacks { +public class AutoHideController { private static final String TAG = "AutoHideController"; private final IWindowManager mWindowManagerService; private final Handler mHandler; private final NotificationRemoteInputManager mRemoteInputManager; - private final CommandQueue mCommandQueue; private StatusBar mStatusBar; private NavigationBarFragment mNavigationBar; - @VisibleForTesting - int mDisplayId; - @VisibleForTesting - int mSystemUiVisibility; - // last value sent to window manager - private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE; + private int mDisplayId; private boolean mAutoHideSuspended; - private static final long AUTOHIDE_TIMEOUT_MS = 2250; + private static final long AUTO_HIDE_TIMEOUT_MS = 2250; private final Runnable mAutoHide = () -> { - int requested = mSystemUiVisibility & ~getTransientMask(); - if (mSystemUiVisibility != requested) { - notifySystemUiVisibilityChanged(requested); + if (isAnyTransientBarShown()) { + hideTransientBars(); } }; @Inject - public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) { - mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class); - mCommandQueue.addCallback(this); + public AutoHideController(Context context, @MainHandler Handler handler, + NotificationRemoteInputManager notificationRemoteInputManager, + IWindowManager iWindowManager) { mHandler = handler; - mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); - mWindowManagerService = Dependency.get(IWindowManager.class); + mRemoteInputManager = notificationRemoteInputManager; + mWindowManagerService = iWindowManager; mDisplayId = context.getDisplayId(); } - @Override - public void onDisplayRemoved(int displayId) { - if (displayId == mDisplayId) { - mCommandQueue.removeCallback(this); - } - } - void setStatusBar(StatusBar statusBar) { mStatusBar = statusBar; } @@ -93,50 +70,18 @@ public class AutoHideController implements CommandQueue.Callbacks { mNavigationBar = navigationBar; } - @Override - public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, - boolean navbarColorManagedByIme) { - if (displayId != mDisplayId) { - return; - } - int oldVal = mSystemUiVisibility; - int newVal = (oldVal & ~mask) | (vis & mask); - int diff = newVal ^ oldVal; - - if (diff != 0) { - mSystemUiVisibility = newVal; - - // ready to unhide - if (hasStatusBar() && (vis & View.STATUS_BAR_UNHIDE) != 0) { - mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; - } - - if (hasNavigationBar() && (vis & View.NAVIGATION_BAR_UNHIDE) != 0) { - mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; - } - - // Re-send setSystemUiVisibility to update un-hide status. - if (mSystemUiVisibility != newVal) { - mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility, - fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds, - dockedStackBounds, navbarColorManagedByIme); - } - - notifySystemUiVisibilityChanged(mSystemUiVisibility); - } - } - - @VisibleForTesting - void notifySystemUiVisibilityChanged(int vis) { + private void hideTransientBars() { try { - if (mLastDispatchedSystemUiVisibility != vis) { - mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis); - mLastDispatchedSystemUiVisibility = vis; - } + mWindowManagerService.hideTransientBars(mDisplayId); } catch (RemoteException ex) { Log.w(TAG, "Cannot get WindowManager"); } + if (mStatusBar != null) { + mStatusBar.clearTransient(); + } + if (mNavigationBar != null) { + mNavigationBar.clearTransient(); + } } void resumeSuspendedAutoHide() { @@ -155,13 +100,12 @@ public class AutoHideController implements CommandQueue.Callbacks { if (checkBarModesRunnable != null) { mHandler.removeCallbacks(checkBarModesRunnable); } - mAutoHideSuspended = (mSystemUiVisibility & getTransientMask()) != 0; + mAutoHideSuspended = isAnyTransientBarShown(); } void touchAutoHide() { // update transient bar auto hide - if ((hasStatusBar() && mStatusBar.getStatusBarMode() == MODE_SEMI_TRANSPARENT) - || hasNavigationBar() && mNavigationBar.isSemiTransparent()) { + if (isAnyTransientBarShown()) { scheduleAutoHide(); } else { cancelAutoHide(); @@ -169,9 +113,9 @@ public class AutoHideController implements CommandQueue.Callbacks { } private Runnable getCheckBarModesRunnable() { - if (hasStatusBar()) { + if (mStatusBar != null) { return () -> mStatusBar.checkBarModes(); - } else if (hasNavigationBar()) { + } else if (mNavigationBar != null) { return () -> mNavigationBar.checkNavBarModes(); } else { return null; @@ -185,15 +129,14 @@ public class AutoHideController implements CommandQueue.Callbacks { private void scheduleAutoHide() { cancelAutoHide(); - mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS); + mHandler.postDelayed(mAutoHide, AUTO_HIDE_TIMEOUT_MS); } void checkUserAutoHide(MotionEvent event) { - boolean shouldAutoHide = - (mSystemUiVisibility & getTransientMask()) != 0 // a transient bar is revealed. + boolean shouldAutoHide = isAnyTransientBarShown() && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar. && event.getX() == 0 && event.getY() == 0; - if (hasStatusBar()) { + if (mStatusBar != null) { // a touch outside both bars shouldAutoHide &= !mRemoteInputManager.getController().isRemoteInputActive(); } @@ -207,23 +150,8 @@ public class AutoHideController implements CommandQueue.Callbacks { mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear } - private int getTransientMask() { - int mask = 0; - if (hasStatusBar()) { - mask |= View.STATUS_BAR_TRANSIENT; - } - if (hasNavigationBar()) { - mask |= View.NAVIGATION_BAR_TRANSIENT; - } - return mask; - } - - boolean hasNavigationBar() { - return mNavigationBar != null; - } - - @VisibleForTesting - boolean hasStatusBar() { - return mStatusBar != null; + private boolean isAnyTransientBarShown() { + return (mStatusBar != null && mStatusBar.isTransientShown()) + || mNavigationBar != null && mNavigationBar.isTransientShown(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index 007c50c8765d..837517e2cc9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -21,7 +21,7 @@ import android.os.Handler; import android.provider.Settings.Secure; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; +import com.android.systemui.dagger.qualifiers.BgHandler; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.SecureSetting; @@ -33,7 +33,6 @@ import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.HotspotController.Callback; import javax.inject.Inject; -import javax.inject.Named; /** * Manages which tiles should be automatically added to QS. @@ -58,7 +57,7 @@ public class AutoTileManager { @Inject public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host, - @Named(Dependency.BG_HANDLER_NAME) Handler handler, + @BgHandler Handler handler, HotspotController hotspotController, DataSaverController dataSaverController, ManagedProfileController managedProfileController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index 211a40a91101..e6731e6b8a34 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java @@ -44,11 +44,11 @@ public class BarTransitions { private static final boolean DEBUG = false; private static final boolean DEBUG_COLORS = false; - public static final int MODE_OPAQUE = 0; + public static final int MODE_TRANSPARENT = 0; public static final int MODE_SEMI_TRANSPARENT = 1; public static final int MODE_TRANSLUCENT = 2; public static final int MODE_LIGHTS_OUT = 3; - public static final int MODE_TRANSPARENT = 4; + public static final int MODE_OPAQUE = 4; public static final int MODE_WARNING = 5; public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6; @@ -72,7 +72,7 @@ public class BarTransitions { private final View mView; protected final BarBackgroundDrawable mBarBackground; - private int mMode; + private @TransitionMode int mMode; private boolean mAlwaysOpaque = false; public BarTransitions(View view, int gradientResourceId) { 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 2d012c93f42b..bd3d848eaf7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import android.annotation.IntDef; import android.content.Context; +import android.content.res.Resources; import android.hardware.biometrics.BiometricSourceType; import android.metrics.LogMaker; import android.os.Handler; @@ -34,24 +35,28 @@ import com.android.keyguard.KeyguardConstants; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dependency; +import com.android.systemui.dagger.qualifiers.MainResources; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import javax.inject.Inject; + /** * Controller which coordinates all the biometric unlocking actions with the UI. */ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { - private static final String TAG = "BiometricUnlockController"; + private static final String TAG = "BiometricUnlockCtrl"; private static final boolean DEBUG_BIO_WAKELOCK = KeyguardConstants.DEBUG_BIOMETRIC_WAKELOCK; private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000; - private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock"; + private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock"; @IntDef(prefix = { "MODE_" }, value = { MODE_NONE, @@ -127,7 +132,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { private final KeyguardBypassController mKeyguardBypassController; private PowerManager.WakeLock mWakeLock; private final KeyguardUpdateMonitor mUpdateMonitor; - private final UnlockMethodCache mUnlockMethodCache; + private DozeParameters mDozeParameters; + private final KeyguardStateController mKeyguardStateController; private final StatusBarWindowController mStatusBarWindowController; private final Context mContext; private final int mWakeUpDelay; @@ -143,36 +149,20 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { private boolean mHasScreenTurnedOnSinceAuthenticating; private boolean mFadedAwayAfterWakeAndUnlock; - private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); + private final MetricsLogger mMetricsLogger; - public BiometricUnlockController(Context context, - DozeScrimController dozeScrimController, - KeyguardViewMediator keyguardViewMediator, - ScrimController scrimController, - StatusBar statusBar, - UnlockMethodCache unlockMethodCache, Handler handler, + @Inject + public BiometricUnlockController(Context context, DozeScrimController dozeScrimController, + KeyguardViewMediator keyguardViewMediator, ScrimController scrimController, + StatusBar statusBar, KeyguardStateController keyguardStateController, Handler handler, KeyguardUpdateMonitor keyguardUpdateMonitor, - KeyguardBypassController keyguardBypassController) { - this(context, dozeScrimController, keyguardViewMediator, scrimController, statusBar, - unlockMethodCache, handler, keyguardUpdateMonitor, - context.getResources() - .getInteger(com.android.internal.R.integer.config_wakeUpDelayDoze), - keyguardBypassController); - } - - @VisibleForTesting - protected BiometricUnlockController(Context context, - DozeScrimController dozeScrimController, - KeyguardViewMediator keyguardViewMediator, - ScrimController scrimController, - StatusBar statusBar, - UnlockMethodCache unlockMethodCache, Handler handler, - KeyguardUpdateMonitor keyguardUpdateMonitor, - int wakeUpDelay, - KeyguardBypassController keyguardBypassController) { + @MainResources Resources resources, + KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters, + MetricsLogger metricsLogger) { mContext = context; mPowerManager = context.getSystemService(PowerManager.class); mUpdateMonitor = keyguardUpdateMonitor; + mDozeParameters = dozeParameters; mUpdateMonitor.registerCallback(this); mMediaManager = Dependency.get(NotificationMediaManager.class); Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver); @@ -182,11 +172,12 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { mKeyguardViewMediator = keyguardViewMediator; mScrimController = scrimController; mStatusBar = statusBar; - mUnlockMethodCache = unlockMethodCache; + mKeyguardStateController = keyguardStateController; mHandler = handler; - mWakeUpDelay = wakeUpDelay; + mWakeUpDelay = resources.getInteger(com.android.internal.R.integer.config_wakeUpDelayDoze); mKeyguardBypassController = keyguardBypassController; mKeyguardBypassController.setUnlockController(this); + mMetricsLogger = metricsLogger; } public void setStatusBarKeyguardViewManager( @@ -286,7 +277,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { } // During wake and unlock, we need to draw black before waking up to avoid abrupt // brightness changes due to display state transitions. - boolean alwaysOnEnabled = DozeParameters.getInstance(mContext).getAlwaysOn(); + boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn(); boolean delayWakeUp = mode == MODE_WAKE_AND_UNLOCK && alwaysOnEnabled && mWakeUpDelay > 0; Runnable wakeUp = ()-> { if (!wasDeviceInteractive) { @@ -416,7 +407,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { return MODE_ONLY_WAKE; } else if (mDozeScrimController.isPulsing() && unlockingAllowed) { return MODE_WAKE_AND_UNLOCK_PULSING; - } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) { + } else if (unlockingAllowed || !mKeyguardStateController.isMethodSecure()) { return MODE_WAKE_AND_UNLOCK; } else { return MODE_SHOW_BOUNCER; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index d655b2fef411..e78b85e5fd57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -38,7 +38,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.policy.EncryptionHelper; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; @@ -57,7 +57,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public static final int FADE_IN_DELAY = 50; private PhoneStatusBarView mStatusBar; private StatusBarStateController mStatusBarStateController; - private KeyguardMonitor mKeyguardMonitor; + private KeyguardStateController mKeyguardStateController; private NetworkController mNetworkController; private LinearLayout mSystemIconArea; private View mClockView; @@ -79,7 +79,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + mKeyguardStateController = Dependency.get(KeyguardStateController.class); mNetworkController = Dependency.get(NetworkController.class); mStatusBarStateController = Dependency.get(StatusBarStateController.class); mStatusBarComponent = SysUiServiceProvider.getComponent(getContext(), StatusBar.class); @@ -207,8 +207,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue state |= DISABLE_CLOCK; } - if (!mKeyguardMonitor.isLaunchTransitionFadingAway() - && !mKeyguardMonitor.isKeyguardFadingAway() + if (!mKeyguardStateController.isLaunchTransitionFadingAway() + && !mKeyguardStateController.isKeyguardFadingAway() && shouldHideNotificationIcons() && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD && headsUpVisible)) { @@ -268,7 +268,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue * don't set the clock GONE otherwise it'll mess up the animation. */ private int clockHiddenMode() { - if (!mStatusBar.isClosed() && !mKeyguardMonitor.isShowing() + if (!mStatusBar.isClosed() && !mKeyguardStateController.isShowing() && !mStatusBarStateController.isDozing()) { return View.INVISIBLE; } @@ -345,11 +345,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue .withEndAction(null); // Synchronize the motion with the Keyguard fading if necessary. - if (mKeyguardMonitor.isKeyguardFadingAway()) { + if (mKeyguardStateController.isKeyguardFadingAway()) { v.animate() - .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration()) + .setDuration(mKeyguardStateController.getKeyguardFadingAwayDuration()) .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) - .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) + .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay()) .start(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java index 0d62703cbced..78ea5c03a7b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java @@ -100,6 +100,7 @@ public class DoubleTapHelper { event.getY() - mActivationY); } if (withinDoubleTapSlop) { + makeInactive(); if (!mDoubleTapListener.onDoubleTap()) { return false; } @@ -134,6 +135,7 @@ public class DoubleTapHelper { if (mActivated) { mActivated = false; mActivationListener.onActiveChanged(false); + mView.removeCallbacks(mTapTimeoutRunnable); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index bb6a38e1dcf5..bc482353753d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.phone; -import android.content.Context; +import android.content.res.Resources; import android.hardware.display.AmbientDisplayConfiguration; import android.os.PowerManager; import android.os.SystemProperties; @@ -24,18 +24,21 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.MathUtils; -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.MainResources; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.doze.DozeScreenState; import com.android.systemui.tuner.TunerService; import java.io.PrintWriter; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Retrieve doze information */ +@Singleton public class DozeParameters implements TunerService.Tunable, com.android.systemui.plugins.statusbar.DozeParameters { private static final int MAX_DURATION = 60 * 1000; @@ -44,35 +47,33 @@ public class DozeParameters implements TunerService.Tunable, public static final boolean FORCE_BLANKING = SystemProperties.getBoolean("debug.force_blanking", false); - private static DozeParameters sInstance; - - private final Context mContext; private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; private final PowerManager mPowerManager; private final AlwaysOnDisplayPolicy mAlwaysOnPolicy; + private final Resources mResources; private boolean mDozeAlwaysOn; private boolean mControlScreenOffAnimation; - public static DozeParameters getInstance(Context context) { - if (sInstance == null) { - sInstance = new DozeParameters(context); - } - return sInstance; - } - - @VisibleForTesting - protected DozeParameters(Context context) { - mContext = context.getApplicationContext(); - mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext); - mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(mContext); + @Inject + protected DozeParameters( + @MainResources Resources resources, + AmbientDisplayConfiguration ambientDisplayConfiguration, + AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, + PowerManager powerManager, + TunerService tunerService) { + mResources = resources; + mAmbientDisplayConfiguration = ambientDisplayConfiguration; + mAlwaysOnPolicy = alwaysOnDisplayPolicy; mControlScreenOffAnimation = !getDisplayNeedsBlanking(); - mPowerManager = mContext.getSystemService(PowerManager.class); + mPowerManager = powerManager; mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation); - Dependency.get(TunerService.class).addTunable(this, Settings.Secure.DOZE_ALWAYS_ON, + tunerService.addTunable( + this, + Settings.Secure.DOZE_ALWAYS_ON, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); } @@ -95,7 +96,7 @@ public class DozeParameters implements TunerService.Tunable, } public boolean getDozeSuspendDisplayStateSupported() { - return mContext.getResources().getBoolean(R.bool.doze_suspend_display_state_supported); + return mResources.getBoolean(R.bool.doze_suspend_display_state_supported); } public int getPulseDuration() { @@ -103,7 +104,7 @@ public class DozeParameters implements TunerService.Tunable, } public float getScreenBrightnessDoze() { - return mContext.getResources().getInteger( + return mResources.getInteger( com.android.internal.R.integer.config_screenBrightnessDoze) / 255f; } @@ -173,7 +174,7 @@ public class DozeParameters implements TunerService.Tunable, * @return {@code true} if screen needs to be completely black before a power transition. */ public boolean getDisplayNeedsBlanking() { - return FORCE_BLANKING || !FORCE_NO_BLANKING && mContext.getResources().getBoolean( + return FORCE_BLANKING || !FORCE_NO_BLANKING && mResources.getBoolean( com.android.internal.R.bool.config_displayBlanksAfterDoze); } @@ -186,33 +187,24 @@ public class DozeParameters implements TunerService.Tunable, return; } mControlScreenOffAnimation = controlScreenOffAnimation; - getPowerManager().setDozeAfterScreenOff(!controlScreenOffAnimation); - } - - @VisibleForTesting - protected PowerManager getPowerManager() { - return mPowerManager; + mPowerManager.setDozeAfterScreenOff(!controlScreenOffAnimation); } private boolean getBoolean(String propName, int resId) { - return SystemProperties.getBoolean(propName, mContext.getResources().getBoolean(resId)); + return SystemProperties.getBoolean(propName, mResources.getBoolean(resId)); } private int getInt(String propName, int resId) { - int value = SystemProperties.getInt(propName, mContext.getResources().getInteger(resId)); + int value = SystemProperties.getInt(propName, mResources.getInteger(resId)); return MathUtils.constrain(value, 0, MAX_DURATION); } - private String getString(String propName, int resId) { - return SystemProperties.get(propName, mContext.getString(resId)); - } - public int getPulseVisibleDurationExtended() { return 2 * getPulseVisibleDuration(); } public boolean doubleTapReportsTouchCoordinates() { - return mContext.getResources().getBoolean(R.bool.doze_double_tap_reports_touch_coordinates); + return mResources.getBoolean(R.bool.doze_double_tap_reports_touch_coordinates); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index 60e381a776d2..1ecc4899d5e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -22,18 +22,24 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; +import com.android.systemui.doze.DozeEvent; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Controller which handles all the doze animations of the scrims. */ +@Singleton public class DozeScrimController implements StateListener { private static final String TAG = "DozeScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private final DozeLog mDozeLog; private final DozeParameters mDozeParameters; private final Handler mHandler = new Handler(); @@ -47,7 +53,7 @@ public class DozeScrimController implements StateListener { public void onDisplayBlanked() { if (DEBUG) { Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason=" - + DozeLog.reasonToString(mPulseReason)); + + DozeEvent.reasonToString(mPulseReason)); } if (!mDozing) { return; @@ -68,8 +74,8 @@ public class DozeScrimController implements StateListener { // Notifications should time out on their own. Pulses due to notifications should // instead be managed externally based off the notification's lifetime. // Dock also controls the time out by self. - if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION - && mPulseReason != DozeLog.PULSE_REASON_DOCKING) { + if (mPulseReason != DozeEvent.PULSE_REASON_NOTIFICATION + && mPulseReason != DozeEvent.PULSE_REASON_DOCKING) { mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); mHandler.postDelayed(mPulseOutExtended, mDozeParameters.getPulseVisibleDurationExtended()); @@ -90,14 +96,16 @@ public class DozeScrimController implements StateListener { */ @Override public boolean shouldTimeoutWallpaper() { - return mPulseReason == DozeLog.PULSE_REASON_DOCKING; + return mPulseReason == DozeEvent.PULSE_REASON_DOCKING; } }; - public DozeScrimController(DozeParameters dozeParameters) { + @Inject + public DozeScrimController(DozeParameters dozeParameters, DozeLog dozeLog) { mDozeParameters = dozeParameters; //Never expected to be destroyed Dependency.get(StatusBarStateController.class).addCallback(this); + mDozeLog = dozeLog; } @VisibleForTesting @@ -168,14 +176,14 @@ public class DozeScrimController implements StateListener { } private void pulseStarted() { - DozeLog.tracePulseStart(mPulseReason); + mDozeLog.tracePulseStart(mPulseReason); if (mPulseCallback != null) { mPulseCallback.onPulseStarted(); } } private void pulseFinished() { - DozeLog.tracePulseFinish(); + mDozeLog.tracePulseFinish(); if (mPulseCallback != null) { mPulseCallback.onPulseFinished(); mPulseCallback = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java new file mode 100644 index 000000000000..28543555bf4d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; + +import android.annotation.NonNull; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.doze.DozeEvent; +import com.android.systemui.doze.DozeHost; +import com.android.systemui.doze.DozeLog; +import com.android.systemui.doze.DozeReceiver; +import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; + +import java.util.ArrayList; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Lazy; + +/** + * Implementation of DozeHost for SystemUI. + */ +@Singleton +public final class DozeServiceHost implements DozeHost { + private static final String TAG = "DozeServiceHost"; + private final ArrayList<Callback> mCallbacks = new ArrayList<>(); + private final DozeLog mDozeLog; + private final PowerManager mPowerManager; + private boolean mAnimateWakeup; + private boolean mAnimateScreenOff; + private boolean mIgnoreTouchWhilePulsing; + private Runnable mPendingScreenOffCallback; + @VisibleForTesting + boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean( + "persist.sysui.wake_performs_auth", true); + private boolean mDozingRequested; + private boolean mDozing; + private boolean mPulsing; + private WakefulnessLifecycle mWakefulnessLifecycle; + private final SysuiStatusBarStateController mStatusBarStateController; + private final DeviceProvisionedController mDeviceProvisionedController; + private final HeadsUpManagerPhone mHeadsUpManagerPhone; + private final BatteryController mBatteryController; + private final ScrimController mScrimController; + private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; + private BiometricUnlockController mBiometricUnlockController; + private final KeyguardViewMediator mKeyguardViewMediator; + private final AssistManager mAssistManager; + private final DozeScrimController mDozeScrimController; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final VisualStabilityManager mVisualStabilityManager; + private final PulseExpansionHandler mPulseExpansionHandler; + private final StatusBarWindowController mStatusBarWindowController; + private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; + private NotificationIconAreaController mNotificationIconAreaController; + private StatusBarWindowViewController mStatusBarWindowViewController; + private StatusBarWindowView mStatusBarWindow; + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private NotificationPanelView mNotificationPanel; + private View mAmbientIndicationContainer; + private StatusBar mStatusBar; + + @Inject + public DozeServiceHost(DozeLog dozeLog, PowerManager powerManager, + WakefulnessLifecycle wakefulnessLifecycle, + SysuiStatusBarStateController statusBarStateController, + DeviceProvisionedController deviceProvisionedController, + HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController, + ScrimController scrimController, + Lazy<BiometricUnlockController> biometricUnlockControllerLazy, + KeyguardViewMediator keyguardViewMediator, + AssistManager assistManager, + DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor, + VisualStabilityManager visualStabilityManager, + PulseExpansionHandler pulseExpansionHandler, + StatusBarWindowController statusBarWindowController, + NotificationWakeUpCoordinator notificationWakeUpCoordinator) { + super(); + mDozeLog = dozeLog; + mPowerManager = powerManager; + mWakefulnessLifecycle = wakefulnessLifecycle; + mStatusBarStateController = statusBarStateController; + mDeviceProvisionedController = deviceProvisionedController; + mHeadsUpManagerPhone = headsUpManagerPhone; + mBatteryController = batteryController; + mScrimController = scrimController; + mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; + mKeyguardViewMediator = keyguardViewMediator; + mAssistManager = assistManager; + mDozeScrimController = dozeScrimController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mVisualStabilityManager = visualStabilityManager; + mPulseExpansionHandler = pulseExpansionHandler; + mStatusBarWindowController = statusBarWindowController; + mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; + } + + // TODO: we should try to not pass status bar in here if we can avoid it. + + /** + * Initialize instance with objects only available later during execution. + */ + public void initialize(StatusBar statusBar, + NotificationIconAreaController notificationIconAreaController, + StatusBarWindowViewController statusBarWindowViewController, + StatusBarWindowView statusBarWindow, + StatusBarKeyguardViewManager statusBarKeyguardViewManager, + NotificationPanelView notificationPanel, View ambientIndicationContainer) { + mStatusBar = statusBar; + mNotificationIconAreaController = notificationIconAreaController; + mStatusBarWindowViewController = statusBarWindowViewController; + mStatusBarWindow = statusBarWindow; + mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; + mNotificationPanel = notificationPanel; + mAmbientIndicationContainer = ambientIndicationContainer; + mBiometricUnlockController = mBiometricUnlockControllerLazy.get(); + } + + @Override + public String toString() { + return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]"; + } + + void firePowerSaveChanged(boolean active) { + for (Callback callback : mCallbacks) { + callback.onPowerSaveChanged(active); + } + } + + void fireNotificationPulse(NotificationEntry entry) { + Runnable pulseSuppressedListener = () -> { + entry.setPulseSuppressed(true); + mNotificationIconAreaController.updateAodNotificationIcons(); + }; + for (Callback callback : mCallbacks) { + callback.onNotificationAlerted(pulseSuppressedListener); + } + } + + boolean getDozingRequested() { + return mDozingRequested; + } + + boolean isPulsing() { + return mPulsing; + } + + + @Override + public void addCallback(@NonNull Callback callback) { + mCallbacks.add(callback); + } + + @Override + public void removeCallback(@NonNull Callback callback) { + mCallbacks.remove(callback); + } + + @Override + public void startDozing() { + if (!mDozingRequested) { + mDozingRequested = true; + mDozeLog.traceDozing(mDozing); + updateDozing(); + mStatusBar.updateIsKeyguard(); + } + } + + void updateDozing() { + // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked. + boolean + dozing = + mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD + || mBiometricUnlockController.getMode() + == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; + // When in wake-and-unlock we may not have received a change to StatusBarState + // but we still should not be dozing, manually set to false. + if (mBiometricUnlockController.getMode() + == BiometricUnlockController.MODE_WAKE_AND_UNLOCK) { + dozing = false; + } + + + mStatusBarStateController.setIsDozing(dozing); + } + + @Override + public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { + if (reason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS) { + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, + "com.android.systemui:LONG_PRESS"); + mAssistManager.startAssist(new Bundle()); + return; + } + + if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { + mScrimController.setWakeLockScreenSensorActive(true); + } + + if (reason == DozeEvent.PULSE_REASON_DOCKING && mStatusBarWindow != null) { + mStatusBarWindowViewController.suppressWakeUpGesture(true); + } + + boolean passiveAuthInterrupt = reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN + && mWakeLockScreenPerformsAuth; + // Set the state to pulsing, so ScrimController will know what to do once we ask it to + // execute the transition. The pulse callback will then be invoked when the scrims + // are black, indicating that StatusBar is ready to present the rest of the UI. + mPulsing = true; + mDozeScrimController.pulse(new PulseCallback() { + @Override + public void onPulseStarted() { + callback.onPulseStarted(); + mStatusBar.updateNotificationPanelTouchState(); + setPulsing(true); + } + + @Override + public void onPulseFinished() { + mPulsing = false; + callback.onPulseFinished(); + mStatusBar.updateNotificationPanelTouchState(); + mScrimController.setWakeLockScreenSensorActive(false); + if (mStatusBarWindow != null) { + mStatusBarWindowViewController.suppressWakeUpGesture(false); + } + setPulsing(false); + } + + private void setPulsing(boolean pulsing) { + mStatusBarStateController.setPulsing(pulsing); + mStatusBarKeyguardViewManager.setPulsing(pulsing); + mKeyguardViewMediator.setPulsing(pulsing); + mNotificationPanel.setPulsing(pulsing); + mVisualStabilityManager.setPulsing(pulsing); + mStatusBarWindowViewController.setPulsing(pulsing); + mIgnoreTouchWhilePulsing = false; + if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) { + mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */); + } + mStatusBar.updateScrimController(); + mPulseExpansionHandler.setPulsing(pulsing); + mNotificationWakeUpCoordinator.setPulsing(pulsing); + } + }, reason); + // DozeScrimController is in pulse state, now let's ask ScrimController to start + // pulsing and draw the black frame, if necessary. + mStatusBar.updateScrimController(); + } + + @Override + public void stopDozing() { + if (mDozingRequested) { + mDozingRequested = false; + mDozeLog.traceDozing(mDozing); + updateDozing(); + } + } + + @Override + public void onIgnoreTouchWhilePulsing(boolean ignore) { + if (ignore != mIgnoreTouchWhilePulsing) { + mDozeLog.tracePulseTouchDisabledByProx(ignore); + } + mIgnoreTouchWhilePulsing = ignore; + if (mDozing && ignore) { + mStatusBarWindowViewController.cancelCurrentTouch(); + } + } + + @Override + public void dozeTimeTick() { + mNotificationPanel.dozeTimeTick(); + if (mAmbientIndicationContainer instanceof DozeReceiver) { + ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick(); + } + } + + @Override + public boolean isPowerSaveActive() { + return mBatteryController.isAodPowerSave(); + } + + @Override + public boolean isPulsingBlocked() { + return mBiometricUnlockController.getMode() + == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; + } + + @Override + public boolean isProvisioned() { + return mDeviceProvisionedController.isDeviceProvisioned() + && mDeviceProvisionedController.isCurrentUserSetup(); + } + + @Override + public boolean isBlockingDoze() { + if (mBiometricUnlockController.hasPendingAuthentication()) { + Log.i(StatusBar.TAG, "Blocking AOD because fingerprint has authenticated"); + return true; + } + return false; + } + + @Override + public void extendPulse(int reason) { + if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { + mScrimController.setWakeLockScreenSensorActive(true); + } + if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) { + mHeadsUpManagerPhone.extendHeadsUp(); + } else { + mDozeScrimController.extendPulse(); + } + } + + @Override + public void stopPulsing() { + if (mDozeScrimController.isPulsing()) { + mDozeScrimController.pulseOutNow(); + } + } + + @Override + public void setAnimateWakeup(boolean animateWakeup) { + if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE + || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) { + // Too late to change the wakeup animation. + return; + } + mAnimateWakeup = animateWakeup; + } + + @Override + public void setAnimateScreenOff(boolean animateScreenOff) { + mAnimateScreenOff = animateScreenOff; + } + + @Override + public void onSlpiTap(float screenX, float screenY) { + if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null + && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) { + int[] locationOnScreen = new int[2]; + mAmbientIndicationContainer.getLocationOnScreen(locationOnScreen); + float viewX = screenX - locationOnScreen[0]; + float viewY = screenY - locationOnScreen[1]; + if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth() + && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) { + + // Dispatch a tap + long now = SystemClock.elapsedRealtime(); + MotionEvent ev = MotionEvent.obtain( + now, now, MotionEvent.ACTION_DOWN, screenX, screenY, 0); + mAmbientIndicationContainer.dispatchTouchEvent(ev); + ev.recycle(); + ev = MotionEvent.obtain( + now, now, MotionEvent.ACTION_UP, screenX, screenY, 0); + mAmbientIndicationContainer.dispatchTouchEvent(ev); + ev.recycle(); + } + } + } + + @Override + public void setDozeScreenBrightness(int value) { + mStatusBarWindowController.setDozeScreenBrightness(value); + } + + @Override + public void setAodDimmingScrim(float scrimOpacity) { + mScrimController.setAodFrontScrimAlpha(scrimOpacity); + } + + + + @Override + public void prepareForGentleSleep(Runnable onDisplayOffCallback) { + if (mPendingScreenOffCallback != null) { + Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one."); + } + mPendingScreenOffCallback = onDisplayOffCallback; + mStatusBar.updateScrimController(); + } + + @Override + public void cancelGentleSleep() { + mPendingScreenOffCallback = null; + if (mScrimController.getState() == ScrimState.OFF) { + mStatusBar.updateScrimController(); + } + } + + /** + * When the dozing host is waiting for scrims to fade out to change the display state. + */ + boolean hasPendingScreenOffCallback() { + return mPendingScreenOffCallback != null; + } + + /** + * Executes an nullifies the pending display state callback. + * + * @see #hasPendingScreenOffCallback() + * @see #prepareForGentleSleep(Runnable) + */ + void executePendingScreenOffCallback() { + if (mPendingScreenOffCallback == null) { + return; + } + mPendingScreenOffCallback.run(); + mPendingScreenOffCallback = null; + } + + boolean shouldAnimateWakeup() { + return mAnimateWakeup; + } + + boolean shouldAnimateScreenOff() { + return mAnimateScreenOff; + } + + public void setDozing(boolean dozing) { + mDozing = dozing; + } + + boolean getIgnoreTouchWhilePulsing() { + return mIgnoreTouchWhilePulsing; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 4cd3ad27ab34..1c8e832d03d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -54,9 +54,7 @@ import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; -import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.shared.system.WindowManagerWrapper; import java.io.PrintWriter; import java.util.concurrent.Executor; @@ -70,15 +68,6 @@ public class EdgeBackGestureHandler implements DisplayListener { private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( "gestures.back_timeout", 250); - private final PinnedStackListener mImeChangedListener = new PinnedStackListener() { - @Override - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - // No need to thread jump, assignments are atomic - mImeHeight = imeVisible ? imeHeight : 0; - // TODO: Probably cancel any existing gesture - } - }; - private ISystemGestureExclusionListener mGestureExclusionListener = new ISystemGestureExclusionListener.Stub() { @Override @@ -126,11 +115,10 @@ public class EdgeBackGestureHandler implements DisplayListener { private boolean mInRejectedExclusion = false; private boolean mIsOnLeftEdge; - private int mImeHeight = 0; - private boolean mIsAttached; private boolean mIsGesturalModeEnabled; private boolean mIsEnabled; + private boolean mIsNavBarShownTransiently; private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; @@ -195,6 +183,10 @@ public class EdgeBackGestureHandler implements DisplayListener { updateCurrentUserResources(currentUserContext.getResources()); } + public void onNavBarTransientStateChanged(boolean isTransient) { + mIsNavBarShownTransiently = isTransient; + } + private void disposeInputChannel() { if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); @@ -222,7 +214,6 @@ public class EdgeBackGestureHandler implements DisplayListener { } if (!mIsEnabled) { - WindowManagerWrapper.getInstance().removePinnedStackListener(mImeChangedListener); mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); try { @@ -239,7 +230,6 @@ public class EdgeBackGestureHandler implements DisplayListener { mContext.getMainThreadHandler()); try { - WindowManagerWrapper.getInstance().addPinnedStackListener(mImeChangedListener); WindowManagerGlobal.getWindowManagerService() .registerSystemGestureExclusionListener( mGestureExclusionListener, mDisplayId); @@ -267,7 +257,7 @@ public class EdgeBackGestureHandler implements DisplayListener { | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT); mEdgePanelLp.privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; mEdgePanelLp.setTitle(TAG + mDisplayId); mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel); mEdgePanelLp.windowAnimations = 0; @@ -296,13 +286,16 @@ public class EdgeBackGestureHandler implements DisplayListener { } private boolean isWithinTouchRegion(int x, int y) { - if (y > (mDisplaySize.y - Math.max(mImeHeight, mNavBarHeight))) { + // Disallow if too far from the edge + if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) { return false; } - if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) { - return false; + // Always allow if the user is in a transient sticky immersive state + if (mIsNavBarShownTransiently) { + return true; } + boolean isInExcludedRegion = mExcludeRegion.contains(x, y); if (isInExcludedRegion) { mOverviewProxyService.notifyBackAction(false /* completed */, -1, -1, @@ -470,7 +463,6 @@ public class EdgeBackGestureHandler implements DisplayListener { pw.println(" mInRejectedExclusion" + mInRejectedExclusion); pw.println(" mExcludeRegion=" + mExcludeRegion); pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion); - pw.println(" mImeHeight=" + mImeHeight); pw.println(" mIsAttached=" + mIsAttached); pw.println(" mEdgeWidth=" + mEdgeWidth); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java index deb314bae5c9..a7849847387d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java @@ -82,7 +82,7 @@ public class FloatingRotationButton implements RotationButton { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter, mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("FloatingRotationButton"); switch (mWindowManager.getDefaultDisplay().getRotation()) { case Surface.ROTATION_0: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index f53c4e8c818e..ce96005aa306 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -39,7 +39,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import java.util.function.BiConsumer; @@ -89,7 +89,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, }; private boolean mAnimationsEnabled = true; Point mPoint; - private KeyguardMonitor mKeyguardMonitor; + private KeyguardStateController mKeyguardStateController; public HeadsUpAppearanceController( @@ -98,9 +98,10 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, View statusbarView, SysuiStatusBarStateController statusBarStateController, KeyguardBypassController keyguardBypassController, + KeyguardStateController keyguardStateController, NotificationWakeUpCoordinator wakeUpCoordinator) { this(notificationIconAreaController, headsUpManager, statusBarStateController, - keyguardBypassController, wakeUpCoordinator, + keyguardBypassController, wakeUpCoordinator, keyguardStateController, statusbarView.findViewById(R.id.heads_up_status_bar_view), statusbarView.findViewById(R.id.notification_stack_scroller), statusbarView.findViewById(R.id.notification_panel), @@ -116,6 +117,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, StatusBarStateController stateController, KeyguardBypassController bypassController, NotificationWakeUpCoordinator wakeUpCoordinator, + KeyguardStateController keyguardStateController, HeadsUpStatusBarView headsUpStatusBarView, NotificationStackScrollLayout stackScroller, NotificationPanelView panelView, @@ -160,7 +162,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mWakeUpCoordinator = wakeUpCoordinator; wakeUpCoordinator.addListener(this); mCommandQueue = getComponent(headsUpStatusBarView.getContext(), CommandQueue.class); - mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + mKeyguardStateController = keyguardStateController; } @@ -378,7 +380,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, boolean canShow = !mIsExpanded && notificationsShown; if (mBypassController.getBypassEnabled() && (mStatusBarStateController.getState() == StatusBarState.KEYGUARD - || mKeyguardMonitor.isKeyguardGoingAway()) + || mKeyguardStateController.isKeyguardGoingAway()) && notificationsShown) { canShow = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index a7e7f085ffd7..4e06c84c30be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -29,6 +29,7 @@ import android.view.DisplayCutout; import android.view.Gravity; import android.view.View; import android.view.ViewTreeObserver; +import android.view.WindowInsets; import androidx.collection.ArraySet; @@ -196,9 +197,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, mReleaseOnExpandFinish = false; } else { for (NotificationEntry entry : mEntriesToRemoveAfterExpand) { - if (isAlerting(entry.key)) { + if (isAlerting(entry.getKey())) { // Maybe the heads-up was removed already - removeAlertEntry(entry.key); + removeAlertEntry(entry.getKey()); } } } @@ -290,7 +291,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, */ public void setRemoteInputActive( @NonNull NotificationEntry entry, boolean remoteInputActive) { - HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key); + HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey()); if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) { headsUpEntry.remoteInputActive = remoteInputActive; if (remoteInputActive) { @@ -306,7 +307,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * area if it's pinned until it's hidden again. */ public void setMenuShown(@NonNull NotificationEntry entry, boolean menuShown) { - HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key); + HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey()); if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) { ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown); } @@ -374,7 +375,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } else { if (topEntry.isChildInGroup()) { final NotificationEntry groupSummary = - mGroupManager.getGroupSummary(topEntry.notification); + mGroupManager.getGroupSummary(topEntry.getSbn()); if (groupSummary != null) { topEntry = groupSummary; } @@ -390,7 +391,12 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } private void updateRegionForNotch(Region region) { - DisplayCutout cutout = mStatusBarWindowView.getRootWindowInsets().getDisplayCutout(); + WindowInsets windowInsets = mStatusBarWindowView.getRootWindowInsets(); + if (windowInsets == null) { + Log.w(TAG, "StatusBarWindowView is not attached."); + return; + } + DisplayCutout cutout = windowInsets.getDisplayCutout(); if (cutout == null) { return; } @@ -423,9 +429,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, public void onReorderingAllowed() { mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false); for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) { - if (isAlerting(entry.key)) { + if (isAlerting(entry.getKey())) { // Maybe the heads-up was removed already - removeAlertEntry(entry.key); + removeAlertEntry(entry.getKey()); } } mEntriesToRemoveWhenReorderingAllowed.clear(); @@ -442,7 +448,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, @Override protected void onAlertEntryRemoved(AlertEntry alertEntry) { - mKeysToRemoveWhenLeavingKeyguard.remove(alertEntry.mEntry.key); + mKeysToRemoveWhenLeavingKeyguard.remove(alertEntry.mEntry.getKey()); super.onAlertEntryRemoved(alertEntry); mEntryPool.release((HeadsUpEntryPhone) alertEntry); } @@ -527,9 +533,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } else if (mTrackingHeadsUp) { mEntriesToRemoveAfterExpand.add(entry); } else if (mIsAutoHeadsUp && mStatusBarState == StatusBarState.KEYGUARD) { - mKeysToRemoveWhenLeavingKeyguard.add(entry.key); + mKeysToRemoveWhenLeavingKeyguard.add(entry.getKey()); } else { - removeAlertEntry(entry.key); + removeAlertEntry(entry.getKey()); } }; @@ -547,7 +553,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) { mEntriesToRemoveWhenReorderingAllowed.remove(mEntry); } - mKeysToRemoveWhenLeavingKeyguard.remove(mEntry.key); + mKeysToRemoveWhenLeavingKeyguard.remove(mEntry.getKey()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 2bcbc026d561..99da47a00c0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -81,6 +81,7 @@ import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.ExtensionController.Extension; import com.android.systemui.statusbar.policy.FlashlightController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.PreviewInflater; import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory; import com.android.systemui.tuner.TunerService; @@ -90,7 +91,7 @@ import com.android.systemui.tuner.TunerService; * text. */ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener, - UnlockMethodCache.OnUnlockMethodChangedListener, + KeyguardStateController.Callback, AccessibilityController.AccessibilityStateChangedCallback { final static String TAG = "StatusBar/KeyguardBottomAreaView"; @@ -118,7 +119,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250; private EmergencyCarrierArea mEmergencyCarrierArea; - private final UnlockMethodCache mUnlockMethodCache; + + private final boolean mShowLeftAffordance; + private final boolean mShowCameraAffordance; + private KeyguardAffordanceView mRightAffordanceView; private KeyguardAffordanceView mLeftAffordanceView; private ViewGroup mIndicationArea; @@ -130,7 +134,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private View mCameraPreview; private ActivityStarter mActivityStarter; - private LockPatternUtils mLockPatternUtils; + private KeyguardStateController mKeyguardStateController; private FlashlightController mFlashlightController; private PreviewInflater mPreviewInflater; private AccessibilityController mAccessibilityController; @@ -185,7 +189,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mUnlockMethodCache = UnlockMethodCache.getInstance(getContext()); + mShowLeftAffordance = getResources().getBoolean(R.bool.config_keyguardShowLeftAffordance); + mShowCameraAffordance = getResources() + .getBoolean(R.bool.config_keyguardShowCameraAffordance); } private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() { @@ -227,7 +233,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override protected void onFinishInflate() { super.onFinishInflate(); - mLockPatternUtils = new LockPatternUtils(mContext); mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext), new ActivityIntentHelper(mContext)); mPreviewContainer = findViewById(R.id.preview_container); @@ -242,6 +247,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mBurnInYOffset = getResources().getDimensionPixelSize( R.dimen.default_burn_in_prevention_offset); updateCameraVisibility(); + mKeyguardStateController = Dependency.get(KeyguardStateController.class); + mKeyguardStateController.addCallback(this); setClipChildren(false); setClipToPadding(false); inflateCameraPreview(); @@ -279,13 +286,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL getContext().registerReceiverAsUser(mDevicePolicyReceiver, UserHandle.ALL, filter, null, null); Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback); - mUnlockMethodCache.addListener(this); + mKeyguardStateController.addCallback(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - mUnlockMethodCache.removeListener(this); + mKeyguardStateController.removeCallback(this); mAccessibilityController.removeStateChangedCallback(this); mRightExtension.destroy(); mLeftExtension.destroy(); @@ -372,8 +379,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL // Things are not set up yet; reply hazy, ask again later return; } - mRightAffordanceView.setVisibility(!mDozing && mRightButton.getIcon().isVisible - ? View.VISIBLE : View.GONE); + mRightAffordanceView.setVisibility(!mDozing && mShowCameraAffordance + && mRightButton.getIcon().isVisible ? View.VISIBLE : View.GONE); } /** @@ -385,8 +392,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void updateLeftAffordanceIcon() { + if (!mShowLeftAffordance || mDozing) { + mLeftAffordanceView.setVisibility(GONE); + return; + } IconState state = mLeftButton.getIcon(); - mLeftAffordanceView.setVisibility(!mDozing && state.isVisible ? View.VISIBLE : View.GONE); + mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE); if (state.drawable != mLeftAffordanceView.getDrawable() || state.tint != mLeftAffordanceView.shouldTint()) { mLeftAffordanceView.setImageDrawable(state.drawable, state.tint); @@ -547,7 +558,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mAssistManager.launchVoiceAssistFromKeyguard(); } }; - if (mStatusBar.isKeyguardCurrentlySecure()) { + if (!mKeyguardStateController.canDismissLockScreen()) { AsyncTask.execute(runnable); } else { boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr) @@ -612,7 +623,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } @Override - public void onUnlockMethodStateChanged() { + public void onUnlockedChanged() { updateCameraVisibility(); } @@ -771,10 +782,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public IconState getIcon() { mLeftIsVoiceAssist = canLaunchVoiceAssist(); - final boolean showAffordance = - getResources().getBoolean(R.bool.config_keyguardShowLeftAffordance); if (mLeftIsVoiceAssist) { - mIconState.isVisible = mUserSetupComplete && showAffordance; + mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance; if (mLeftAssistIcon == null) { mIconState.drawable = mContext.getDrawable(R.drawable.ic_mic_26dp); } else { @@ -783,7 +792,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mIconState.contentDescription = mContext.getString( R.string.accessibility_voice_assist_button); } else { - mIconState.isVisible = mUserSetupComplete && showAffordance && isPhoneVisible(); + mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance + && isPhoneVisible(); mIconState.drawable = mContext.getDrawable( com.android.internal.R.drawable.ic_phone); mIconState.contentDescription = mContext.getString( @@ -806,7 +816,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public IconState getIcon() { boolean isCameraDisabled = (mStatusBar != null) && !mStatusBar.isCameraAllowedByAdmin(); mIconState.isVisible = !isCameraDisabled - && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance) + && mShowCameraAffordance && mUserSetupComplete && resolveCameraIntent() != null; mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp); @@ -817,11 +827,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public Intent getIntent() { - KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - boolean canSkipBouncer = updateMonitor.getUserCanSkipBouncer( - KeyguardUpdateMonitor.getCurrentUser()); - boolean secure = mUnlockMethodCache.isMethodSecure(); - return (secure && !canSkipBouncer) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; + boolean canDismissLs = mKeyguardStateController.canDismissLockScreen(); + boolean secure = mKeyguardStateController.isMethodSecure(); + return (secure && !canDismissLs) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index d7f67cef033e..f34c15c7099d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import static com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; import android.content.Context; @@ -38,14 +37,17 @@ import android.view.WindowInsets; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardHostView; +import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardSecurityView; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.DejankUtils; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.PrintWriter; @@ -69,7 +71,7 @@ public class KeyguardBouncer { private final Handler mHandler; private final BouncerExpansionCallback mExpansionCallback; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final UnlockMethodCache mUnlockMethodCache; + private final KeyguardStateController mKeyguardStateController; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -97,7 +99,8 @@ public class KeyguardBouncer { public KeyguardBouncer(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils, ViewGroup container, DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager, - BouncerExpansionCallback expansionCallback, UnlockMethodCache unlockMethodCache, + BouncerExpansionCallback expansionCallback, + KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, KeyguardBypassController keyguardBypassController, Handler handler) { mContext = context; @@ -109,7 +112,7 @@ public class KeyguardBouncer { mDismissCallbackRegistry = dismissCallbackRegistry; mExpansionCallback = expansionCallback; mHandler = handler; - mUnlockMethodCache = unlockMethodCache; + mKeyguardStateController = keyguardStateController; mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); mKeyguardBypassController = keyguardBypassController; } @@ -173,7 +176,7 @@ public class KeyguardBouncer { // Split up the work over multiple frames. DejankUtils.removeCallbacks(mResetRunnable); - if (mUnlockMethodCache.isFaceAuthEnabled() && !needsFullscreenBouncer() + if (mKeyguardStateController.isFaceAuthEnabled() && !needsFullscreenBouncer() && !mKeyguardUpdateMonitor.userNeedsStrongAuth() && !mKeyguardBypassController.getBypassEnabled()) { mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY); @@ -459,15 +462,9 @@ public class KeyguardBouncer { * notifications on Keyguard, like SIM PIN/PUK. */ public boolean needsFullscreenBouncer() { - // TODO(b/140059518) - return whitelistIpcs(() -> { - ensureView(); - if (mKeyguardView != null) { - SecurityMode mode = mKeyguardView.getSecurityMode(); - return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk; - } - return false; - }); + SecurityMode mode = Dependency.get(KeyguardSecurityModel.class).getSecurityMode( + KeyguardUpdateMonitor.getCurrentUser()); + return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index 832ea9e3d72e..aca7f443ea0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -23,6 +23,7 @@ import android.provider.Settings import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.tuner.TunerService import java.io.PrintWriter import javax.inject.Inject @@ -31,7 +32,7 @@ import javax.inject.Singleton @Singleton class KeyguardBypassController { - private val unlockMethodCache: UnlockMethodCache + private val mKeyguardStateController: KeyguardStateController private val statusBarStateController: StatusBarStateController private var hasFaceFeature: Boolean @@ -47,7 +48,7 @@ class KeyguardBypassController { * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. */ var bypassEnabled: Boolean = false - get() = field && unlockMethodCache.isFaceAuthEnabled + get() = field && mKeyguardStateController.isFaceAuthEnabled private set var bouncerShowing: Boolean = false @@ -66,9 +67,10 @@ class KeyguardBypassController { context: Context, tunerService: TunerService, statusBarStateController: StatusBarStateController, - lockscreenUserManager: NotificationLockscreenUserManager + lockscreenUserManager: NotificationLockscreenUserManager, + keyguardStateController: KeyguardStateController ) { - unlockMethodCache = UnlockMethodCache.getInstance(context) + this.mKeyguardStateController = keyguardStateController this.statusBarStateController = statusBarStateController hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index 00b764bfbe9b..bf887044e05b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -21,17 +21,16 @@ import android.hardware.TriggerEvent import android.hardware.TriggerEventListener import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.Dependency import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.util.Assert import com.android.systemui.util.sensors.AsyncSensorManager class KeyguardLiftController constructor( private val statusBarStateController: StatusBarStateController, - private val asyncSensorManager: AsyncSensorManager + private val asyncSensorManager: AsyncSensorManager, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor ) : StatusBarStateController.StateListener, KeyguardUpdateMonitorCallback() { - private val keyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor::class.java) private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) private var isListening = false private var bouncerVisible = false @@ -66,6 +65,9 @@ class KeyguardLiftController constructor( } private fun updateListeningState() { + if (pickupSensor == null) { + return + } val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible && !statusBarStateController.isDozing diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index b0b656a1a951..2e776e39b54c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -16,15 +16,20 @@ package com.android.systemui.statusbar.phone; +import static android.view.WindowInsetsController.APPEARANCE_LIGHT_SIDE_BARS; +import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR; + import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import android.content.Context; import android.graphics.Color; -import android.graphics.Rect; -import android.view.View; +import android.view.InsetsFlags; +import android.view.ViewDebug; +import android.view.WindowInsetsController.Appearance; import com.android.internal.colorextraction.ColorExtractor.GradientColors; +import com.android.internal.view.AppearanceRegion; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; @@ -49,13 +54,10 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private BiometricUnlockController mBiometricUnlockController; private LightBarTransitionsController mNavigationBarController; - private int mSystemUiVisibility; - private int mFullscreenStackVisibility; - private int mDockedStackVisibility; - private boolean mFullscreenLight; - private boolean mDockedLight; - private int mLastStatusBarMode; - private int mLastNavigationBarMode; + private @Appearance int mAppearance; + private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0]; + private int mStatusBarMode; + private int mNavigationBarMode; private final Color mDarkModeColor; /** @@ -75,8 +77,6 @@ public class LightBarController implements BatteryController.BatteryStateChangeC */ private boolean mForceDarkForScrim; - private final Rect mLastFullscreenBounds = new Rect(); - private final Rect mLastDockedBounds = new Rect(); private boolean mQsCustomizing; private boolean mDirectReplying; @@ -101,45 +101,32 @@ public class LightBarController implements BatteryController.BatteryStateChangeC mBiometricUnlockController = biometricUnlockController; } - public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, - int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged, + void onStatusBarAppearanceChanged(AppearanceRegion[] appearanceRegions, boolean sbModeChanged, int statusBarMode, boolean navbarColorManagedByIme) { - int oldFullscreen = mFullscreenStackVisibility; - int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask); - int diffFullscreen = newFullscreen ^ oldFullscreen; - int oldDocked = mDockedStackVisibility; - int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask); - int diffDocked = newDocked ^ oldDocked; - if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 - || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 - || sbModeChanged - || !mLastFullscreenBounds.equals(fullscreenStackBounds) - || !mLastDockedBounds.equals(dockedStackBounds)) { - - mFullscreenLight = isLight(newFullscreen, statusBarMode, - View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - mDockedLight = isLight(newDocked, statusBarMode, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - updateStatus(fullscreenStackBounds, dockedStackBounds); + final int numStacks = appearanceRegions.length; + boolean stackAppearancesChanged = mAppearanceRegions.length != numStacks; + for (int i = 0; i < numStacks && !stackAppearancesChanged; i++) { + stackAppearancesChanged |= !appearanceRegions[i].equals(mAppearanceRegions[i]); + } + if (stackAppearancesChanged || sbModeChanged) { + mAppearanceRegions = appearanceRegions; + onStatusBarModeChanged(statusBarMode); } - - mFullscreenStackVisibility = newFullscreen; - mDockedStackVisibility = newDocked; - mLastStatusBarMode = statusBarMode; mNavbarColorManagedByIme = navbarColorManagedByIme; - mLastFullscreenBounds.set(fullscreenStackBounds); - mLastDockedBounds.set(dockedStackBounds); } - public void onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged, + void onStatusBarModeChanged(int newBarMode) { + mStatusBarMode = newBarMode; + updateStatus(); + } + + void onNavigationBarAppearanceChanged(@Appearance int appearance, boolean nbModeChanged, int navigationBarMode, boolean navbarColorManagedByIme) { - int oldVis = mSystemUiVisibility; - int newVis = (oldVis & ~mask) | (vis & mask); - int diffVis = newVis ^ oldVis; - if ((diffVis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0 - || nbModeChanged) { - boolean last = mNavigationLight; - mHasLightNavigationBar = isLight(vis, navigationBarMode, - View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); + int diff = appearance ^ mAppearance; + if ((diff & APPEARANCE_LIGHT_SIDE_BARS) != 0 || nbModeChanged) { + final boolean last = mNavigationLight; + mHasLightNavigationBar = isLight(appearance, navigationBarMode, + APPEARANCE_LIGHT_SIDE_BARS); mNavigationLight = mHasLightNavigationBar && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim) && !mQsCustomizing; @@ -147,17 +134,20 @@ public class LightBarController implements BatteryController.BatteryStateChangeC updateNavigation(); } } - mSystemUiVisibility = newVis; - mLastNavigationBarMode = navigationBarMode; + mAppearance = appearance; + mNavigationBarMode = navigationBarMode; mNavbarColorManagedByIme = navbarColorManagedByIme; } + void onNavigationBarModeChanged(int newBarMode) { + mHasLightNavigationBar = isLight(mAppearance, newBarMode, APPEARANCE_LIGHT_SIDE_BARS); + } + private void reevaluate() { - onSystemUiVisibilityChanged(mFullscreenStackVisibility, - mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds, - true /* sbModeChange*/, mLastStatusBarMode, mNavbarColorManagedByIme); - onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, true /* nbModeChanged */, - mLastNavigationBarMode, mNavbarColorManagedByIme); + onStatusBarAppearanceChanged(mAppearanceRegions, true /* sbModeChange */, mStatusBarMode, + mNavbarColorManagedByIme); + onNavigationBarAppearanceChanged(mAppearance, true /* nbModeChanged */, + mNavigationBarMode, mNavbarColorManagedByIme); } public void setQsCustomizing(boolean customizing) { @@ -191,10 +181,10 @@ public class LightBarController implements BatteryController.BatteryStateChangeC } } - private boolean isLight(int vis, int barMode, int flag) { - boolean isTransparentBar = (barMode == MODE_TRANSPARENT + private static boolean isLight(int appearance, int barMode, int flag) { + final boolean isTransparentBar = (barMode == MODE_TRANSPARENT || barMode == MODE_LIGHTS_OUT_TRANSPARENT); - boolean light = (vis & flag) != 0; + final boolean light = (appearance & flag) != 0; return isTransparentBar && light; } @@ -207,49 +197,49 @@ public class LightBarController implements BatteryController.BatteryStateChangeC && unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK; } - private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) { - boolean hasDockedStack = !dockedStackBounds.isEmpty(); + private void updateStatus() { + final int numStacks = mAppearanceRegions.length; + int numLightStacks = 0; + + // We can only have maximum one light stack. + int indexLightStack = -1; - // If both are light or fullscreen is light and there is no docked stack, all icons get - // dark. - if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) { + for (int i = 0; i < numStacks; i++) { + if (isLight(mAppearanceRegions[i].getAppearance(), mStatusBarMode, + APPEARANCE_LIGHT_TOP_BAR)) { + numLightStacks++; + indexLightStack = i; + } + } + + // If all stacks are light, all icons get dark. + if (numLightStacks == numStacks) { mStatusBarIconController.setIconsDarkArea(null); mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange()); } - // If no one is light or the fullscreen is not light and there is no docked stack, - // all icons become white. - else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) { + // If no one is light, all icons become white. + else if (numLightStacks == 0) { mStatusBarIconController.getTransitionsController().setIconsDark( false, animateChange()); } // Not the same for every stack, magic! else { - Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds; - if (bounds.isEmpty()) { - mStatusBarIconController.setIconsDarkArea(null); - } else { - mStatusBarIconController.setIconsDarkArea(bounds); - } + mStatusBarIconController.setIconsDarkArea( + mAppearanceRegions[indexLightStack].getBounds()); mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange()); } } private void updateNavigation() { if (mNavigationBarController != null) { - mNavigationBarController.setIconsDark( - mNavigationLight, animateChange()); + mNavigationBarController.setIconsDark(mNavigationLight, animateChange()); } } @Override - public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { - - } - - @Override public void onPowerSaveChanged(boolean isPowerSave) { reevaluate(); } @@ -257,24 +247,21 @@ public class LightBarController implements BatteryController.BatteryStateChangeC @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("LightBarController: "); - pw.print(" mSystemUiVisibility=0x"); pw.print( - Integer.toHexString(mSystemUiVisibility)); - pw.print(" mFullscreenStackVisibility=0x"); pw.print( - Integer.toHexString(mFullscreenStackVisibility)); - pw.print(" mDockedStackVisibility=0x"); pw.println( - Integer.toHexString(mDockedStackVisibility)); - - pw.print(" mFullscreenLight="); pw.print(mFullscreenLight); - pw.print(" mDockedLight="); pw.println(mDockedLight); - - pw.print(" mLastFullscreenBounds="); pw.print(mLastFullscreenBounds); - pw.print(" mLastDockedBounds="); pw.println(mLastDockedBounds); + pw.print(" mAppearance=0x"); pw.println(ViewDebug.flagsToString( + InsetsFlags.class, "appearance", mAppearance)); + final int numStacks = mAppearanceRegions.length; + for (int i = 0; i < numStacks; i++) { + final boolean isLight = isLight(mAppearanceRegions[i].getAppearance(), mStatusBarMode, + APPEARANCE_LIGHT_TOP_BAR); + pw.print(" stack #"); pw.print(i); pw.print(": "); + pw.print(mAppearanceRegions[i].toString()); pw.print(" isLight="); pw.println(isLight); + } pw.print(" mNavigationLight="); pw.print(mNavigationLight); pw.print(" mHasLightNavigationBar="); pw.println(mHasLightNavigationBar); - pw.print(" mLastStatusBarMode="); pw.print(mLastStatusBarMode); - pw.print(" mLastNavigationBarMode="); pw.println(mLastNavigationBarMode); + pw.print(" mStatusBarMode="); pw.print(mStatusBarMode); + pw.print(" mNavigationBarMode="); pw.println(mNavigationBarMode); pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim); pw.print(" mQsCustomizing="); pw.print(mQsCustomizing); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index d7097309ce20..de660ce18263 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -31,7 +31,7 @@ import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -47,7 +47,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, private final Handler mHandler; private final DarkIntensityApplier mApplier; - private final KeyguardMonitor mKeyguardMonitor; + private final KeyguardStateController mKeyguardStateController; private final StatusBarStateController mStatusBarStateController; private boolean mTransitionDeferring; @@ -73,7 +73,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, public LightBarTransitionsController(Context context, DarkIntensityApplier applier) { mApplier = applier; mHandler = new Handler(); - mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + mKeyguardStateController = Dependency.get(KeyguardStateController.class); mStatusBarStateController = Dependency.get(StatusBarStateController.class); SysUiServiceProvider.getComponent(context, CommandQueue.class) .addCallback(this); @@ -101,7 +101,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, @Override public void appTransitionPending(int displayId, boolean forced) { - if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) { + if (mDisplayId != displayId || mKeyguardStateController.isKeyguardGoingAway() && !forced) { return; } mTransitionPending = true; @@ -123,7 +123,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, @Override public void appTransitionStarting(int displayId, long startTime, long duration, boolean forced) { - if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) { + if (mDisplayId != displayId || mKeyguardStateController.isKeyguardGoingAway() && !forced) { return; } if (mTransitionPending && mTintChangePending) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 67810738b17e..4927ec8a9a47 100755 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -53,7 +53,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener; @@ -68,9 +68,8 @@ import javax.inject.Named; */ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChangedListener, StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener, - UnlockMethodCache.OnUnlockMethodChangedListener, - NotificationWakeUpCoordinator.WakeUpListener, ViewTreeObserver.OnPreDrawListener, - OnHeadsUpChangedListener { + KeyguardStateController.Callback, NotificationWakeUpCoordinator.WakeUpListener, + ViewTreeObserver.OnPreDrawListener, OnHeadsUpChangedListener { private static final int STATE_LOCKED = 0; private static final int STATE_LOCK_OPEN = 1; @@ -78,11 +77,10 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private static final int STATE_BIOMETRICS_ERROR = 3; private final ConfigurationController mConfigurationController; private final StatusBarStateController mStatusBarStateController; - private final UnlockMethodCache mUnlockMethodCache; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final AccessibilityController mAccessibilityController; private final DockManager mDockManager; - private final KeyguardMonitor mKeyguardMonitor; + private final KeyguardStateController mKeyguardStateController; private final KeyguardBypassController mBypassController; private final NotificationWakeUpCoordinator mWakeUpCoordinator; private final HeadsUpManagerPhone mHeadsUpManager; @@ -107,13 +105,13 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private boolean mUpdatePending; private boolean mBouncerPreHideAnimation; - private final KeyguardMonitor.Callback mKeyguardMonitorCallback = - new KeyguardMonitor.Callback() { + private final KeyguardStateController.Callback mKeyguardMonitorCallback = + new KeyguardStateController.Callback() { @Override public void onKeyguardShowingChanged() { boolean force = false; boolean wasShowing = mKeyguardShowing; - mKeyguardShowing = mKeyguardMonitor.isShowing(); + mKeyguardShowing = mKeyguardStateController.isShowing(); if (!wasShowing && mKeyguardShowing && mBlockUpdates) { mBlockUpdates = false; force = true; @@ -126,7 +124,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange @Override public void onKeyguardFadingAwayChanged() { - if (!mKeyguardMonitor.isKeyguardFadingAway()) { + if (!mKeyguardStateController.isKeyguardFadingAway()) { mBouncerPreHideAnimation = false; if (mBlockUpdates) { mBlockUpdates = false; @@ -134,6 +132,11 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange } } } + + @Override + public void onUnlockedChanged() { + update(); + } }; private final DockManager.DockEventListener mDockEventListener = new DockManager.DockEventListener() { @@ -181,19 +184,18 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange AccessibilityController accessibilityController, KeyguardBypassController bypassController, NotificationWakeUpCoordinator wakeUpCoordinator, - KeyguardMonitor keyguardMonitor, + KeyguardStateController keyguardStateController, @Nullable DockManager dockManager, HeadsUpManagerPhone headsUpManager) { super(context, attrs); mContext = context; - mUnlockMethodCache = UnlockMethodCache.getInstance(context); mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mAccessibilityController = accessibilityController; mConfigurationController = configurationController; mStatusBarStateController = statusBarStateController; mBypassController = bypassController; mWakeUpCoordinator = wakeUpCoordinator; - mKeyguardMonitor = keyguardMonitor; + mKeyguardStateController = keyguardStateController; mDockManager = dockManager; mHeadsUpManager = headsUpManager; } @@ -203,9 +205,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange super.onAttachedToWindow(); mStatusBarStateController.addCallback(this); mConfigurationController.addCallback(this); - mKeyguardMonitor.addCallback(mKeyguardMonitorCallback); + mKeyguardStateController.addCallback(mKeyguardMonitorCallback); mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); - mUnlockMethodCache.addListener(this); mWakeUpCoordinator.addListener(this); mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); if (mDockManager != null) { @@ -221,9 +222,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mStatusBarStateController.removeCallback(this); mConfigurationController.removeCallback(this); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); - mKeyguardMonitor.removeCallback(mKeyguardMonitorCallback); + mKeyguardStateController.removeCallback(mKeyguardMonitorCallback); mWakeUpCoordinator.removeListener(this); - mUnlockMethodCache.removeListener(this); if (mDockManager != null) { mDockManager.removeListener(mDockEventListener); } @@ -370,15 +370,15 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange } private boolean canBlockUpdates() { - return mKeyguardShowing || mKeyguardMonitor.isKeyguardFadingAway(); + return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway(); } private void updateClickability() { if (mAccessibilityController == null) { return; } - boolean canLock = mUnlockMethodCache.isMethodSecure() - && mUnlockMethodCache.canSkipBouncer(); + boolean canLock = mKeyguardStateController.isMethodSecure() + && mKeyguardStateController.canDismissLockScreen(); boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled(); setClickable(clickToUnlock); setLongClickable(canLock && !clickToUnlock); @@ -523,8 +523,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private int getState() { KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - if ((mUnlockMethodCache.canSkipBouncer() || !mKeyguardShowing || mBouncerPreHideAnimation - || mKeyguardMonitor.isKeyguardGoingAway()) && !mSimLocked) { + if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing + || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) { return STATE_LOCK_OPEN; } else if (mTransientBiometricsError) { return STATE_BIOMETRICS_ERROR; @@ -582,11 +582,6 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange update(true /* force */); } - @Override - public void onUnlockMethodStateChanged() { - update(); - } - /** * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the * icon on top of the black front scrim. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index e34c639a23c2..4d6b54ccfaff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -22,7 +22,6 @@ import android.app.IWallpaperManager; import android.app.IWallpaperManagerCallback; import android.app.WallpaperColors; import android.app.WallpaperManager; -import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -35,7 +34,6 @@ import android.os.AsyncTask; import android.os.Handler; import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; @@ -47,9 +45,13 @@ import libcore.io.IoUtils; import java.util.Objects; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Manages the lockscreen wallpaper. */ +@Singleton public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable { private static final String TAG = "LockscreenWallpaper"; @@ -57,9 +59,8 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen private final NotificationMediaManager mMediaManager = Dependency.get(NotificationMediaManager.class); - private final StatusBar mBar; private final WallpaperManager mWallpaperManager; - private final Handler mH; + private Handler mH; private final KeyguardUpdateMonitor mUpdateMonitor; private boolean mCached; @@ -70,25 +71,32 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen private UserHandle mSelectedUser; private AsyncTask<Void, Void, LoaderResult> mLoader; - public LockscreenWallpaper(Context ctx, StatusBar bar, Handler h) { - mBar = bar; - mH = h; - mWallpaperManager = (WallpaperManager) ctx.getSystemService(Context.WALLPAPER_SERVICE); + @Inject + public LockscreenWallpaper(WallpaperManager wallpaperManager, + @Nullable IWallpaperManager iWallpaperManager, + KeyguardUpdateMonitor keyguardUpdateMonitor) { + mWallpaperManager = wallpaperManager; mCurrentUserId = ActivityManager.getCurrentUser(); - mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); + mUpdateMonitor = keyguardUpdateMonitor; - IWallpaperManager service = IWallpaperManager.Stub.asInterface( - ServiceManager.getService(Context.WALLPAPER_SERVICE)); - if (service != null) { + if (iWallpaperManager != null) { // Service is disabled on some devices like Automotive try { - service.setLockWallpaperCallback(this); + iWallpaperManager.setLockWallpaperCallback(this); } catch (RemoteException e) { Log.e(TAG, "System dead?" + e); } } } + void setHandler(Handler handler) { + if (mH != null) { + Log.wtfStack(TAG, "Handler has already been set. Trying to double initialize?"); + return; + } + mH = handler; + } + public Bitmap getBitmap() { if (mCached) { return mCache; @@ -176,6 +184,10 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen } private void postUpdateWallpaper() { + if (mH == null) { + Log.wtfStack(TAG, "Trying to use LockscreenWallpaper before initialization."); + return; + } mH.removeCallbacks(this); mH.post(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt index 7dcc2fcfe2b2..53601babfd56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone import android.content.Context import android.view.View +import android.view.ViewGroup.MarginLayoutParams import android.widget.FrameLayout import com.android.systemui.plugins.NPVPlugin import com.android.systemui.plugins.PluginListener @@ -36,6 +37,7 @@ class NPVPluginManager( private var plugin: NPVPlugin? = null private var animator = createAnimator() + private var yOffset = 0f private fun createAnimator() = TouchAnimator.Builder() .addFloat(parent, "alpha", 1f, 0f) @@ -76,7 +78,7 @@ class NPVPluginManager( } fun setExpansion(expansion: Float, headerTranslation: Float, heightDiff: Float) { - parent.setTranslationY(expansion * heightDiff + headerTranslation) + parent.setTranslationY(expansion * heightDiff + headerTranslation + yOffset) if (!expansion.isNaN()) animator.setPosition(expansion) } @@ -88,5 +90,13 @@ class NPVPluginManager( animator = createAnimator() } - fun getHeight() = if (plugin != null) parent.height else 0 + fun getHeight() = + if (plugin != null) { + parent.height + (parent.getLayoutParams() as MarginLayoutParams).topMargin + } else 0 + + fun setYOffset(y: Float) { + yOffset = y + parent.setTranslationY(yOffset) + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 2b8c86b8c549..7030dfc4c33b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -21,6 +21,10 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.containsType; +import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; +import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_SIDE_BARS; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; @@ -31,7 +35,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OU import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; -import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE; @@ -52,7 +55,6 @@ import android.content.IntentFilter; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.PixelFormat; -import android.graphics.Rect; import android.inputmethodservice.InputMethodService; import android.net.Uri; import android.os.Binder; @@ -67,12 +69,14 @@ import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.Log; import android.view.Display; +import android.view.InsetsState.InternalInsetType; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.ViewGroup; +import android.view.WindowInsetsController.Appearance; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; @@ -84,11 +88,13 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.LatencyTracker; +import com.android.internal.view.AppearanceRegion; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.ScreenDecorations; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.assist.AssistHandleViewController; import com.android.systemui.assist.AssistManager; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.model.SysUiState; @@ -127,7 +133,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private static final boolean DEBUG = false; private static final String EXTRA_DISABLE_STATE = "disabled_state"; private static final String EXTRA_DISABLE2_STATE = "disabled2_state"; - private static final String EXTRA_SYSTEM_UI_VISIBILITY = "system_ui_visibility"; + private static final String EXTRA_APPEARANCE = "appearance"; + private static final String EXTRA_TRANSIENT_STATE = "transient_state"; /** Allow some time inbetween the long press for back and recents. */ private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; @@ -139,6 +146,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private final MetricsLogger mMetricsLogger; private final DeviceProvisionedController mDeviceProvisionedController; private final StatusBarStateController mStatusBarStateController; + private final NavigationModeController mNavigationModeController; protected NavigationBarView mNavigationBarView = null; @@ -163,18 +171,26 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private Locale mLocale; private int mLayoutDirection; - private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; + /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */ + private @Appearance int mAppearance; + + private boolean mTransientShown; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; private LightBarController mLightBarController; private AutoHideController mAutoHideController; private OverviewProxyService mOverviewProxyService; + private final BroadcastDispatcher mBroadcastDispatcher; + @VisibleForTesting public int mDisplayId; private boolean mIsOnDefaultDisplay; public boolean mHomeBlockedThisTouch; - private ScreenDecorations mScreenDecorations; + + /** Only for default display */ + @Nullable + private AssistHandleViewController mAssistHandlerViewController; private Handler mHandler = Dependency.get(Dependency.MAIN_HANDLER); @@ -248,7 +264,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback AssistManager assistManager, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, StatusBarStateController statusBarStateController, - SysUiState sysUiFlagsContainer) { + SysUiState sysUiFlagsContainer, + BroadcastDispatcher broadcastDispatcher) { mAccessibilityManagerWrapper = accessibilityManagerWrapper; mDeviceProvisionedController = deviceProvisionedController; mStatusBarStateController = statusBarStateController; @@ -257,7 +274,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mSysUiFlagsContainer = sysUiFlagsContainer; mAssistantAvailable = mAssistManager.getAssistInfoForUser(UserHandle.USER_CURRENT) != null; mOverviewProxyService = overviewProxyService; + mNavigationModeController = navigationModeController; mNavBarMode = navigationModeController.addListener(this); + mBroadcastDispatcher = broadcastDispatcher; } // ----- Fragment Lifecycle Callbacks ----- @@ -285,7 +304,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (savedInstanceState != null) { mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0); mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0); - mSystemUiVisibility = savedInstanceState.getInt(EXTRA_SYSTEM_UI_VISIBILITY, 0); + mAppearance = savedInstanceState.getInt(EXTRA_APPEARANCE, 0); + mTransientShown = savedInstanceState.getBoolean(EXTRA_TRANSIENT_STATE, false); } mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); @@ -296,6 +316,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback @Override public void onDestroy() { super.onDestroy(); + mNavigationModeController.removeListener(this); mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); mContentResolver.unregisterContentObserver(mMagnificationObserver); mContentResolver.unregisterContentObserver(mAssistContentObserver); @@ -334,7 +355,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_USER_SWITCHED); - getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); + mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, Handler.getMain(), + UserHandle.ALL); notifyNavigationBarScreenOn(); mOverviewProxyService.addCallback(mOverviewProxyListener); @@ -357,22 +379,27 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS; } setDisabled2Flags(mDisabledFlags2); - - mScreenDecorations = SysUiServiceProvider.getComponent(getContext(), - ScreenDecorations.class); - getBarTransitions().addDarkIntensityListener(mScreenDecorations); + if (mIsOnDefaultDisplay) { + mAssistHandlerViewController = + new AssistHandleViewController(mHandler, mNavigationBarView); + getBarTransitions().addDarkIntensityListener(mAssistHandlerViewController); + } } @Override public void onDestroyView() { super.onDestroyView(); if (mNavigationBarView != null) { - mNavigationBarView.getBarTransitions().removeDarkIntensityListener(mScreenDecorations); + if (mIsOnDefaultDisplay) { + mNavigationBarView.getBarTransitions() + .removeDarkIntensityListener(mAssistHandlerViewController); + mAssistHandlerViewController = null; + } mNavigationBarView.getBarTransitions().destroy(); mNavigationBarView.getLightTransitionsController().destroy(getContext()); } mOverviewProxyService.removeCallback(mOverviewProxyListener); - getContext().unregisterReceiver(mBroadcastReceiver); + mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); } @Override @@ -380,7 +407,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback super.onSaveInstanceState(outState); outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2); - outState.putInt(EXTRA_SYSTEM_UI_VISIBILITY, mSystemUiVisibility); + outState.putInt(EXTRA_APPEARANCE, mAppearance); + outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown); if (mNavigationBarView != null) { mNavigationBarView.getLightTransitionsController().saveState(outState); } @@ -501,77 +529,107 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback rotationButtonController.onRotationProposal(rotation, winRotation, isValid); } - /** Restores the System UI flags saved state to {@link NavigationBarFragment}. */ - public void restoreSystemUiVisibilityState() { - final int barMode = computeBarMode(0, mSystemUiVisibility); - if (barMode != -1) { - mNavigationBarMode = barMode; - } + /** Restores the appearance and the transient saved state to {@link NavigationBarFragment}. */ + public void restoreAppearanceAndTransientState() { + final int barMode = barMode(mTransientShown, mAppearance); + mNavigationBarMode = barMode; checkNavBarModes(); mAutoHideController.touchAutoHide(); - mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, - true /* nbModeChanged */, mNavigationBarMode, false /* navbarColorManagedByIme */); + mLightBarController.onNavigationBarAppearanceChanged(mAppearance, true /* nbModeChanged */, + barMode, false /* navbarColorManagedByIme */); } @Override - public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, - boolean navbarColorManagedByIme) { + public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { if (displayId != mDisplayId) { return; } - final int oldVal = mSystemUiVisibility; - final int newVal = (oldVal & ~mask) | (vis & mask); - final int diff = newVal ^ oldVal; boolean nbModeChanged = false; - if (diff != 0) { - mSystemUiVisibility = newVal; - - // update navigation bar mode - final int nbMode = getView() == null - ? -1 : computeBarMode(oldVal, newVal); - nbModeChanged = nbMode != -1; - if (nbModeChanged) { - if (mNavigationBarMode != nbMode) { - if (mNavigationBarMode == MODE_TRANSPARENT - || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) { - mNavigationBarView.hideRecentsOnboarding(); - } - mNavigationBarMode = nbMode; - checkNavBarModes(); - } - mAutoHideController.touchAutoHide(); + if (mAppearance != appearance) { + mAppearance = appearance; + if (getView() == null) { + return; } + nbModeChanged = updateBarMode(barMode(mTransientShown, appearance)); + } + mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged, + mNavigationBarMode, navbarColorManagedByIme); + } + + @Override + public void showTransient(int displayId, @InternalInsetType int[] types) { + if (displayId != mDisplayId) { + return; + } + if (!containsType(types, TYPE_NAVIGATION_BAR)) { + return; + } + if (!mTransientShown) { + mTransientShown = true; + handleTransientChanged(); + } + } + + @Override + public void abortTransient(int displayId, @InternalInsetType int[] types) { + if (displayId != mDisplayId) { + return; } - mLightBarController.onNavigationVisibilityChanged( - vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme); + if (!containsType(types, TYPE_NAVIGATION_BAR)) { + return; + } + clearTransient(); } - private @TransitionMode int computeBarMode(int oldVis, int newVis) { - final int oldMode = barMode(oldVis); - final int newMode = barMode(newVis); - if (oldMode == newMode) { - return -1; // no mode change + void clearTransient() { + if (mTransientShown) { + mTransientShown = false; + handleTransientChanged(); + } + } + + private void handleTransientChanged() { + if (getView() == null) { + return; + } + if (mNavigationBarView != null) { + mNavigationBarView.onTransientStateChanged(mTransientShown); + } + final int barMode = barMode(mTransientShown, mAppearance); + if (updateBarMode(barMode)) { + mLightBarController.onNavigationBarModeChanged(barMode); + } + } + + // Returns true if the bar mode is changed. + private boolean updateBarMode(int barMode) { + if (mNavigationBarMode != barMode) { + if (mNavigationBarMode == MODE_TRANSPARENT + || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) { + mNavigationBarView.hideRecentsOnboarding(); + } + mNavigationBarMode = barMode; + checkNavBarModes(); + mAutoHideController.touchAutoHide(); + return true; } - return newMode; + return false; } - private @TransitionMode int barMode(int vis) { - final int lightsOutTransparent = - View.SYSTEM_UI_FLAG_LOW_PROFILE | View.NAVIGATION_BAR_TRANSIENT; - if ((vis & View.NAVIGATION_BAR_TRANSIENT) != 0) { + private static @TransitionMode int barMode(boolean isTransient, int appearance) { + final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_SIDE_BARS; + if (isTransient) { return MODE_SEMI_TRANSPARENT; - } else if ((vis & View.NAVIGATION_BAR_TRANSLUCENT) != 0) { - return MODE_TRANSLUCENT; - } else if ((vis & lightsOutTransparent) == lightsOutTransparent) { - return MODE_LIGHTS_OUT_TRANSPARENT; - } else if ((vis & View.NAVIGATION_BAR_TRANSPARENT) != 0) { - return MODE_TRANSPARENT; - } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { + } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) { return MODE_LIGHTS_OUT; - } else { + } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) { + return MODE_LIGHTS_OUT_TRANSPARENT; + } else if ((appearance & APPEARANCE_OPAQUE_SIDE_BARS) != 0) { return MODE_OPAQUE; + } else { + return MODE_TRANSPARENT; } } @@ -970,8 +1028,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mAutoHideController.setNavigationBar(this); } - public boolean isSemiTransparent() { - return mNavigationBarMode == MODE_SEMI_TRANSPARENT; + boolean isTransientShown() { + return mTransientShown; } private void checkBarModes() { @@ -1019,6 +1077,11 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); } + @Nullable + public AssistHandleViewController getAssistHandlerViewController() { + return mAssistHandlerViewController; + } + /** * Performs transitions on navigation bar. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index fa4812dc4876..4b4a35bae05d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -67,6 +67,7 @@ import com.android.systemui.DockedStackExistsListener; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.assist.AssistHandleViewController; import com.android.systemui.assist.AssistManager; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; @@ -75,6 +76,7 @@ import com.android.systemui.recents.RecentsOnboarding; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.policy.DeadZone; import com.android.systemui.statusbar.policy.KeyButtonDrawable; @@ -364,6 +366,10 @@ public class NavigationBarView extends FrameLayout implements return super.onTouchEvent(event); } + void onTransientStateChanged(boolean isTransient) { + mEdgeBackGestureHandler.onNavBarTransientStateChanged(isTransient); + } + void onBarTransition(int newMode) { if (newMode == MODE_OPAQUE) { // If the nav bar background is opaque, stop auto tinting since we know the icons are @@ -829,7 +835,11 @@ public class NavigationBarView extends FrameLayout implements mRecentsOnboarding.onNavigationModeChanged(mNavBarMode); getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode); - mRegionSamplingHelper.start(mSamplingBounds); + if (isGesturalMode(mNavBarMode)) { + mRegionSamplingHelper.start(mSamplingBounds); + } else { + mRegionSamplingHelper.stop(); + } } public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) { @@ -1194,6 +1204,22 @@ public class NavigationBarView extends FrameLayout implements // we're passing the insets onto the gesture handler since the back arrow is only // conditionally added and doesn't always get all the insets. mEdgeBackGestureHandler.setInsets(leftInset, rightInset); + + // this allows assist handle to be drawn outside its bound so that it can align screen + // bottom by translating its y position. + final boolean shouldClip = + !isGesturalMode(mNavBarMode) || insets.getSystemWindowInsetBottom() == 0; + setClipChildren(shouldClip); + setClipToPadding(shouldClip); + + NavigationBarController navigationBarController = + Dependency.get(NavigationBarController.class); + AssistHandleViewController controller = + navigationBarController == null + ? null : navigationBarController.getAssistHandlerViewController(); + if (controller != null) { + controller.setBottomOffset(insets.getSystemWindowInsetBottom()); + } return super.onApplyWindowInsets(insets); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java index 4d69f77e744d..2798285c073d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java @@ -105,7 +105,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis * @return true if the entry was transferred to and should inflate + alert */ public boolean isAlertTransferPending(@NonNull NotificationEntry entry) { - PendingAlertInfo alertInfo = mPendingAlerts.get(entry.key); + PendingAlertInfo alertInfo = mPendingAlerts.get(entry.getKey()); return alertInfo != null && alertInfo.isStillValid(); } @@ -141,7 +141,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis @Override public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) { if (suppressed) { - if (mHeadsUpManager.isAlerting(group.summary.key)) { + if (mHeadsUpManager.isAlerting(group.summary.getKey())) { handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager); } } else { @@ -151,11 +151,11 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis return; } GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey( - group.summary.notification)); + group.summary.getSbn())); // Group is no longer suppressed. We should check if we need to transfer the alert // back to the summary now that it's no longer suppressed. if (groupAlertEntry.mAlertSummaryOnNextAddition) { - if (!mHeadsUpManager.isAlerting(group.summary.key)) { + if (!mHeadsUpManager.isAlerting(group.summary.getKey())) { alertNotificationWhenPossible(group.summary, mHeadsUpManager); } groupAlertEntry.mAlertSummaryOnNextAddition = false; @@ -173,7 +173,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting, AlertingNotificationManager alertManager) { - if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.notification)) { + if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.getSbn())) { handleSuppressedSummaryAlerted(entry, alertManager); } } @@ -184,7 +184,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis // see as early as we can if we need to abort a transfer. @Override public void onPendingEntryAdded(NotificationEntry entry) { - String groupKey = mGroupManager.getGroupKey(entry.notification); + String groupKey = mGroupManager.getGroupKey(entry.getSbn()); GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey); if (groupAlertEntry != null) { checkShouldTransferBack(groupAlertEntry); @@ -195,7 +195,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis // then show the alert. @Override public void onEntryReinflated(NotificationEntry entry) { - PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.key); + PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey()); if (alertInfo != null) { if (alertInfo.isStillValid()) { alertNotificationWhenPossible(entry, mHeadsUpManager); @@ -214,7 +214,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis // Removes any alerts pending on this entry. Note that this will not stop any inflation // tasks started by a transfer, so this should only be used as clean-up for when // inflation is stopped and the pending alert no longer needs to happen. - mPendingAlerts.remove(entry.key); + mPendingAlerts.remove(entry.getKey()); } }; @@ -267,10 +267,10 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis */ private boolean isPendingNotificationInGroup(@NonNull NotificationEntry entry, @NonNull NotificationGroup group) { - String groupKey = mGroupManager.getGroupKey(group.summary.notification); - return mGroupManager.isGroupChild(entry.notification) - && Objects.equals(mGroupManager.getGroupKey(entry.notification), groupKey) - && !group.children.containsKey(entry.key); + String groupKey = mGroupManager.getGroupKey(group.summary.getSbn()); + return mGroupManager.isGroupChild(entry.getSbn()) + && Objects.equals(mGroupManager.getGroupKey(entry.getSbn()), groupKey) + && !group.children.containsKey(entry.getKey()); } /** @@ -284,10 +284,10 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis */ private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary, @NonNull AlertingNotificationManager alertManager) { - StatusBarNotification sbn = summary.notification; + StatusBarNotification sbn = summary.getSbn(); GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(sbn)); - if (!mGroupManager.isSummaryOfSuppressedGroup(summary.notification) + if (!mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn()) || !alertManager.isAlerting(sbn.getKey()) || groupAlertEntry == null) { return; @@ -298,7 +298,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis return; } - NotificationEntry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next(); + NotificationEntry child = + mGroupManager.getLogicalChildren(summary.getSbn()).iterator().next(); if (child != null) { if (child.getRow().keepInParent() || child.isRowRemoved() @@ -306,7 +307,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis // The notification is actually already removed. No need to alert it. return; } - if (!alertManager.isAlerting(child.key) && onlySummaryAlerts(summary)) { + if (!alertManager.isAlerting(child.getKey()) && onlySummaryAlerts(summary)) { groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime(); } transferAlertState(summary, child, alertManager); @@ -324,7 +325,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis */ private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry, @NonNull AlertingNotificationManager alertManager) { - alertManager.removeNotification(fromEntry.key, true /* releaseImmediately */); + alertManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */); alertNotificationWhenPossible(toEntry, alertManager); } @@ -347,7 +348,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis if (!onlySummaryAlerts(summary)) { return; } - ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.notification); + ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren( + summary.getSbn()); int numChildren = children.size(); int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup); numChildren += numPendingChildren; @@ -357,17 +359,18 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis boolean releasedChild = false; for (int i = 0; i < children.size(); i++) { NotificationEntry entry = children.get(i); - if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.key)) { + if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) { releasedChild = true; - mHeadsUpManager.removeNotification(entry.key, true /* releaseImmediately */); + mHeadsUpManager.removeNotification( + entry.getKey(), true /* releaseImmediately */); } - if (mPendingAlerts.containsKey(entry.key)) { + if (mPendingAlerts.containsKey(entry.getKey())) { // This is the child that would've been removed if it was inflated. releasedChild = true; - mPendingAlerts.get(entry.key).mAbortOnInflation = true; + mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true; } } - if (releasedChild && !mHeadsUpManager.isAlerting(summary.key)) { + if (releasedChild && !mHeadsUpManager.isAlerting(summary.getKey())) { boolean notifyImmediately = (numChildren - numPendingChildren) > 1; if (notifyImmediately) { alertNotificationWhenPossible(summary, mHeadsUpManager); @@ -391,20 +394,20 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis @NonNull AlertingNotificationManager alertManager) { @InflationFlag int contentFlag = alertManager.getContentFlag(); if (!entry.getRow().isInflationFlagSet(contentFlag)) { - mPendingAlerts.put(entry.key, new PendingAlertInfo(entry)); + mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry)); entry.getRow().updateInflationFlag(contentFlag, true /* shouldInflate */); entry.getRow().inflateViews(); return; } - if (alertManager.isAlerting(entry.key)) { - alertManager.updateNotification(entry.key, true /* alert */); + if (alertManager.isAlerting(entry.getKey())) { + alertManager.updateNotification(entry.getKey(), true /* alert */); } else { alertManager.showNotification(entry); } } private boolean onlySummaryAlerts(NotificationEntry entry) { - return entry.notification.getNotification().getGroupAlertBehavior() + return entry.getSbn().getNotification().getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY; } @@ -431,7 +434,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis boolean mAbortOnInflation; PendingAlertInfo(NotificationEntry entry) { - mOriginalNotification = entry.notification; + mOriginalNotification = entry.getSbn(); mEntry = entry; } @@ -445,11 +448,11 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis // Notification is aborted due to the transfer being explicitly cancelled return false; } - if (mEntry.notification.getGroupKey() != mOriginalNotification.getGroupKey()) { + if (mEntry.getSbn().getGroupKey() != mOriginalNotification.getGroupKey()) { // Groups have changed return false; } - if (mEntry.notification.getNotification().isGroupSummary() + if (mEntry.getSbn().getNotification().isGroupSummary() != mOriginalNotification.getNotification().isGroupSummary()) { // Notification has changed from group summary to not or vice versa return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index adaea9379c71..e11fc1b46a5b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -103,8 +103,8 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } public void onEntryRemoved(NotificationEntry removed) { - onEntryRemovedInternal(removed, removed.notification); - mIsolatedEntries.remove(removed.key); + onEntryRemovedInternal(removed, removed.getSbn()); + mIsolatedEntries.remove(removed.getKey()); } /** @@ -126,7 +126,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return; } if (isGroupChild(sbn)) { - group.children.remove(removed.key); + group.children.remove(removed.getKey()); } else { group.summary = null; } @@ -145,7 +145,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State if (added.isRowRemoved()) { added.setDebugThrowable(new Throwable()); } - final StatusBarNotification sbn = added.notification; + final StatusBarNotification sbn = added.getSbn(); boolean isGroupChild = isGroupChild(sbn); String groupKey = getGroupKey(sbn); NotificationGroup group = mGroupMap.get(groupKey); @@ -157,17 +157,17 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } } if (isGroupChild) { - NotificationEntry existing = group.children.get(added.key); + NotificationEntry existing = group.children.get(added.getKey()); if (existing != null && existing != added) { Throwable existingThrowable = existing.getDebugThrowable(); - Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key + Log.wtf(TAG, "Inconsistent entries found with the same key " + added.getKey() + "existing removed: " + existing.isRowRemoved() + (existingThrowable != null ? Log.getStackTraceString(existingThrowable) + "\n": "") + " added removed" + added.isRowRemoved() , new Throwable()); } - group.children.put(added.key, added); + group.children.put(added.getKey(), added); updateSuppression(group); } else { group.summary = added; @@ -210,7 +210,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State group.suppressed = group.summary != null && !group.expanded && (childCount == 1 || (childCount == 0 - && group.summary.notification.getNotification().isGroupSummary() + && group.summary.getSbn().getNotification().isGroupSummary() && (hasIsolatedChildren(group) || hasBubbles))); if (prevSuppressed != group.suppressed) { for (OnGroupChangeListener listener : mListeners) { @@ -223,7 +223,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } private boolean hasIsolatedChildren(NotificationGroup group) { - return getNumberOfIsolatedChildren(group.summary.notification.getGroupKey()) != 0; + return getNumberOfIsolatedChildren(group.summary.getSbn().getGroupKey()) != 0; } private int getNumberOfIsolatedChildren(String groupKey) { @@ -248,18 +248,18 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) { String oldKey = oldNotification.getGroupKey(); - String newKey = entry.notification.getGroupKey(); + String newKey = entry.getSbn().getGroupKey(); boolean groupKeysChanged = !oldKey.equals(newKey); boolean wasGroupChild = isGroupChild(oldNotification); - boolean isGroupChild = isGroupChild(entry.notification); + boolean isGroupChild = isGroupChild(entry.getSbn()); mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild; if (mGroupMap.get(getGroupKey(oldNotification)) != null) { onEntryRemovedInternal(entry, oldNotification); } onEntryAdded(entry); mIsUpdatingUnchangedGroup = false; - if (isIsolated(entry.notification)) { - mIsolatedEntries.put(entry.key, entry.notification); + if (isIsolated(entry.getSbn())) { + mIsolatedEntries.put(entry.getKey(), entry.getSbn()); if (groupKeysChanged) { updateSuppression(mGroupMap.get(oldKey)); updateSuppression(mGroupMap.get(newKey)); @@ -284,7 +284,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } NotificationEntry logicalGroupSummary = getLogicalGroupSummary(sbn); return logicalGroupSummary != null - && !logicalGroupSummary.notification.equals(sbn); + && !logicalGroupSummary.getSbn().equals(sbn); } private int getTotalNumberOfChildren(StatusBarNotification sbn) { @@ -351,7 +351,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State if (group == null || group.summary == null) { return false; } - return !group.children.isEmpty() && Objects.equals(group.summary.notification, sbn); + return !group.children.isEmpty() && Objects.equals(group.summary.getSbn(), sbn); } /** @@ -404,7 +404,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State * will update the suppression of that group. */ public void updateSuppression(NotificationEntry entry) { - NotificationGroup group = mGroupMap.get(getGroupKey(entry.notification)); + NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn())); if (group != null) { updateSuppression(group); } @@ -489,12 +489,12 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State */ private boolean shouldIsolate(NotificationEntry entry) { - StatusBarNotification sbn = entry.notification; + StatusBarNotification sbn = entry.getSbn(); NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) { return false; } - if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.key)) { + if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) { return false; } return (sbn.getNotification().fullScreenIntent != null @@ -509,10 +509,10 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State * @param entry the notification to isolate */ private void isolateNotification(NotificationEntry entry) { - StatusBarNotification sbn = entry.notification; + StatusBarNotification sbn = entry.getSbn(); // We will be isolated now, so lets update the groups - onEntryRemovedInternal(entry, entry.notification); + onEntryRemovedInternal(entry, entry.getSbn()); mIsolatedEntries.put(sbn.getKey(), sbn); @@ -521,7 +521,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State // even before the groupManager knows about the notification at all. // When the notification gets added afterwards it is already isolated and therefore // it doesn't lead to an update. - updateSuppression(mGroupMap.get(entry.notification.getGroupKey())); + updateSuppression(mGroupMap.get(entry.getSbn().getGroupKey())); for (OnGroupChangeListener listener : mListeners) { listener.onGroupsChanged(); } @@ -533,10 +533,10 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State * @param entry the notification to un-isolate */ private void stopIsolatingNotification(NotificationEntry entry) { - StatusBarNotification sbn = entry.notification; + StatusBarNotification sbn = entry.getSbn(); if (mIsolatedEntries.containsKey(sbn.getKey())) { // not isolated anymore, we need to update the groups - onEntryRemovedInternal(entry, entry.notification); + onEntryRemovedInternal(entry, entry.getSbn()); mIsolatedEntries.remove(sbn.getKey()); onEntryAdded(entry); for (OnGroupChangeListener listener : mListeners) { @@ -584,13 +584,13 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State @Override public String toString() { String result = " summary:\n " - + (summary != null ? summary.notification : "null") + + (summary != null ? summary.getSbn() : "null") + (summary != null && summary.getDebugThrowable() != null ? Log.getStackTraceString(summary.getDebugThrowable()) : ""); result += "\n children size: " + children.size(); for (NotificationEntry child : children.values()) { - result += "\n " + child.notification + result += "\n " + child.getSbn() + (child.getDebugThrowable() != null ? Log.getStackTraceString(child.getDebugThrowable()) : ""); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 1a3560ece1d7..1e10b6f025f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -80,11 +80,14 @@ public class NotificationIconAreaController implements DarkReceiver, private boolean mAodIconsVisible; private boolean mIsPulsing; - public NotificationIconAreaController(Context context, StatusBar statusBar, + public NotificationIconAreaController( + Context context, + StatusBar statusBar, StatusBarStateController statusBarStateController, NotificationWakeUpCoordinator wakeUpCoordinator, KeyguardBypassController keyguardBypassController, - NotificationMediaManager notificationMediaManager) { + NotificationMediaManager notificationMediaManager, + DozeParameters dozeParameters) { mStatusBar = statusBar; mContrastColorUtil = ContrastColorUtil.getInstance(context); mContext = context; @@ -92,7 +95,7 @@ public class NotificationIconAreaController implements DarkReceiver, mStatusBarStateController = statusBarStateController; mStatusBarStateController.addCallback(this); mMediaManager = notificationMediaManager; - mDozeParameters = DozeParameters.getInstance(mContext); + mDozeParameters = dozeParameters; mWakeUpCoordinator = wakeUpCoordinator; wakeUpCoordinator.addListener(this); mBypassController = keyguardBypassController; @@ -244,10 +247,10 @@ public class NotificationIconAreaController implements DarkReceiver, if (hideCenteredIcon && isCenteredNotificationIcon && !entry.isRowHeadsUp()) { return false; } - if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) { + if (mEntryManager.getNotificationData().isAmbient(entry.getKey()) && !showAmbient) { return false; } - if (hideCurrentMedia && entry.key.equals(mMediaManager.getMediaNotificationKey())) { + if (hideCurrentMedia && entry.getKey().equals(mMediaManager.getMediaNotificationKey())) { return false; } if (!entry.isTopLevelChild()) { @@ -533,8 +536,7 @@ public class NotificationIconAreaController implements DarkReceiver, } public void appearAodIcons() { - DozeParameters dozeParameters = DozeParameters.getInstance(mContext); - if (dozeParameters.shouldControlScreenOff()) { + if (mDozeParameters.shouldControlScreenOff()) { mAodIcons.setTranslationY(-mAodIconAppearTranslation); mAodIcons.setAlpha(0); animateInAodIconTranslation(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index ea113dffa658..30fe68a28ef2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -43,6 +43,7 @@ import android.hardware.biometrics.BiometricSourceType; import android.os.PowerManager; import android.os.SystemClock; import android.provider.DeviceConfig; +import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; @@ -68,6 +69,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.doze.DozeLog; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.plugins.FalsingManager; @@ -88,6 +90,7 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.DynamicPrivacyController; @@ -102,7 +105,7 @@ import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.ZenModeController; @@ -143,6 +146,7 @@ public class NotificationPanelView extends PanelView implements * Fling until QS is completely hidden. */ public static final int FLING_HIDE = 2; + private final DozeParameters mDozeParameters; private double mQqsSplitFraction; @@ -205,11 +209,11 @@ public class NotificationPanelView extends PanelView implements mDelayShowingKeyguardStatusBar = false; } }; - private final KeyguardMonitor.Callback mKeyguardMonitorCallback = - new KeyguardMonitor.Callback() { + private final KeyguardStateController.Callback mKeyguardMonitorCallback = + new KeyguardStateController.Callback() { @Override public void onKeyguardFadingAwayChanged() { - if (!mKeyguardMonitor.isKeyguardFadingAway()) { + if (!mKeyguardStateController.isKeyguardFadingAway()) { mFirstBypassAttempt = false; mDelayShowingKeyguardStatusBar = false; } @@ -407,14 +411,11 @@ public class NotificationPanelView extends PanelView implements .setDuration(200) .setAnimationFinishListener(mAnimatorListenerAdapter) .setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_IN); - private final NotificationEntryManager mEntryManager = - Dependency.get(NotificationEntryManager.class); + private final NotificationEntryManager mEntryManager; private final CommandQueue mCommandQueue; - private final NotificationLockscreenUserManager mLockscreenUserManager = - Dependency.get(NotificationLockscreenUserManager.class); - private final ShadeController mShadeController = - Dependency.get(ShadeController.class); + private final NotificationLockscreenUserManager mLockscreenUserManager; + private final ShadeController mShadeController; private int mDisplayId; /** @@ -454,13 +455,17 @@ public class NotificationPanelView extends PanelView implements @Inject public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, InjectionInflationController injectionInflationController, - NotificationWakeUpCoordinator coordinator, - PulseExpansionHandler pulseExpansionHandler, + NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, - KeyguardBypassController bypassController, - FalsingManager falsingManager, - PluginManager pluginManager) { - super(context, attrs); + KeyguardBypassController bypassController, FalsingManager falsingManager, + PluginManager pluginManager, ShadeController shadeController, + NotificationLockscreenUserManager notificationLockscreenUserManager, + NotificationEntryManager notificationEntryManager, + KeyguardStateController keyguardStateController, + StatusBarStateController statusBarStateController, DozeLog dozeLog, + DozeParameters dozeParameters) { + super(context, attrs, falsingManager, dozeLog, keyguardStateController, + (SysuiStatusBarStateController) statusBarStateController); setWillNotDraw(!DEBUG); mInjectionInflationController = injectionInflationController; mFalsingManager = falsingManager; @@ -473,6 +478,7 @@ public class NotificationPanelView extends PanelView implements mCommandQueue = getComponent(context, CommandQueue.class); mDisplayId = context.getDisplayId(); mPulseExpansionHandler = pulseExpansionHandler; + mDozeParameters = dozeParameters; pulseExpansionHandler.setPulseExpandAbortListener(() -> { if (mQs != null) { mQs.animateHeaderSlidingOut(); @@ -482,7 +488,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardBypassController = bypassController; mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); - mKeyguardMonitor.addCallback(mKeyguardMonitorCallback); + mKeyguardStateController.addCallback(mKeyguardMonitorCallback); dynamicPrivacyController.addListener(this); mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0); @@ -493,6 +499,11 @@ public class NotificationPanelView extends PanelView implements mBottomAreaShadeAlphaAnimator.setDuration(160); mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); mPluginManager = pluginManager; + mShadeController = shadeController; + mLockscreenUserManager = notificationLockscreenUserManager; + mEntryManager = notificationEntryManager; + + setBackgroundColor(Color.TRANSPARENT); } /** @@ -507,9 +518,11 @@ public class NotificationPanelView extends PanelView implements mKeyguardBottomArea.setStatusBar(mStatusBar); } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); + /** + * Call after this view has been fully inflated and had its children attached. + */ + public void onChildrenAttached() { + loadDimens(); mKeyguardStatusBar = findViewById(R.id.keyguard_header); mKeyguardStatusView = findViewById(R.id.keyguard_status_view); @@ -528,7 +541,10 @@ public class NotificationPanelView extends PanelView implements mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim); mLastOrientation = getResources().getConfiguration().orientation; mPluginFrame = findViewById(R.id.plugin_frame); - mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager); + if (Settings.System.getInt( + mContext.getContentResolver(), "npv_plugin_flag", 0) == 1) { + mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager); + } initBottomArea(); @@ -552,7 +568,7 @@ public class NotificationPanelView extends PanelView implements } }); - Dependency.get(PluginManager.class).addPluginListener( + mPluginManager.addPluginListener( new PluginListener<HomeControlsPlugin>() { @Override @@ -652,8 +668,7 @@ public class NotificationPanelView extends PanelView implements mNotificationStackScroller.setLayoutParams(lp); } int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings); - int topMargin = - res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height); + int topMargin = sideMargin; lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams(); if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin || lp.rightMargin != sideMargin || lp.topMargin != topMargin) { @@ -755,6 +770,11 @@ public class NotificationPanelView extends PanelView implements int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings); int topMargin = res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height); + int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); + if (flag == 1) { + topMargin = res.getDimensionPixelOffset( + com.android.internal.R.dimen.quick_qs_total_height_with_media); + } lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams(); if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin || lp.rightMargin != sideMargin || lp.topMargin != topMargin) { @@ -766,7 +786,7 @@ public class NotificationPanelView extends PanelView implements mPluginFrame.setLayoutParams(lp); } - mNPVPluginManager.replaceFrameLayout(mPluginFrame); + if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame); } private void initBottomArea() { @@ -796,7 +816,10 @@ public class NotificationPanelView extends PanelView implements int oldMaxHeight = mQsMaxExpansionHeight; if (mQs != null) { mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); - mQsMinExpansionHeight += mNPVPluginManager.getHeight(); + if (mNPVPluginManager != null) { + mNPVPluginManager.setYOffset(mQsMinExpansionHeight); + mQsMinExpansionHeight += mNPVPluginManager.getHeight(); + } mQsMaxExpansionHeight = mQs.getDesiredHeight(); mNotificationStackScroller.setMaxTopPadding( mQsMaxExpansionHeight + mQsNotificationTopPadding); @@ -1683,7 +1706,7 @@ public class NotificationPanelView extends PanelView implements @Override public void onStateChanged(int statusBarState) { boolean goingToFullShade = mStatusBarStateController.goingToFullShade(); - boolean keyguardFadingAway = mKeyguardMonitor.isKeyguardFadingAway(); + boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway(); int oldState = mBarState; boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD; setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade); @@ -1701,7 +1724,7 @@ public class NotificationPanelView extends PanelView implements && (goingToFullShade || statusBarState == StatusBarState.SHADE_LOCKED)) { animateKeyguardStatusBarOut(); long delay = mBarState == StatusBarState.SHADE_LOCKED - ? 0 : mKeyguardMonitor.calculateGoingToFullShadeDelay(); + ? 0 : mKeyguardStateController.calculateGoingToFullShadeDelay(); mQs.animateHeaderSlidingIn(delay); } else if (oldState == StatusBarState.SHADE_LOCKED && statusBarState == StatusBarState.KEYGUARD) { @@ -1778,13 +1801,13 @@ public class NotificationPanelView extends PanelView implements private void animateKeyguardStatusBarOut() { ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f); anim.addUpdateListener(mStatusBarAnimateAlphaListener); - anim.setStartDelay(mKeyguardMonitor.isKeyguardFadingAway() - ? mKeyguardMonitor.getKeyguardFadingAwayDelay() + anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway() + ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0); long duration; - if (mKeyguardMonitor.isKeyguardFadingAway()) { - duration = mKeyguardMonitor.getShortenedFadingAwayDuration(); + if (mKeyguardStateController.isKeyguardFadingAway()) { + duration = mKeyguardStateController.getShortenedFadingAwayDuration(); } else { duration = StackStateAnimator.ANIMATION_DURATION_STANDARD; } @@ -1831,8 +1854,8 @@ public class NotificationPanelView extends PanelView implements if (goingToFullShade) { mKeyguardBottomArea.animate() .alpha(0f) - .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) - .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration()) + .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay()) + .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration()) .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable) .start(); @@ -1860,8 +1883,8 @@ public class NotificationPanelView extends PanelView implements .withEndAction(mAnimateKeyguardStatusViewGoneEndRunnable); if (keyguardFadingAway) { mKeyguardStatusView.animate() - .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) - .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration()) + .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay()) + .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration()) .start(); } } else if (mBarState == StatusBarState.SHADE_LOCKED @@ -1902,9 +1925,11 @@ public class NotificationPanelView extends PanelView implements mBarState != StatusBarState.KEYGUARD && (!mQsExpanded || mQsExpansionFromOverscroll)); updateEmptyShadeView(); - mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD) - ? View.VISIBLE - : View.INVISIBLE); + if (mNPVPluginManager != null) { + mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD) + ? View.VISIBLE + : View.INVISIBLE); + } mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling && mQsScrimEnabled ? View.VISIBLE @@ -1962,7 +1987,9 @@ public class NotificationPanelView extends PanelView implements float qsExpansionFraction = getQsExpansionFraction(); mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight(); - mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff); + if (mNPVPluginManager != null) { + mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff); + } mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction); } @@ -2383,7 +2410,7 @@ public class NotificationPanelView extends PanelView implements appearAmount = mNotificationStackScroller.calculateAppearFractionBypass(); } startHeight = -mQs.getQsMinExpansionHeight(); - startHeight -= mNPVPluginManager.getHeight(); + if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight(); } float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount)) @@ -2527,7 +2554,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusBar.setListening(listening); if (mQs == null) return; mQs.setListening(listening); - mNPVPluginManager.setListening(listening); + if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening); } @Override @@ -2556,7 +2583,7 @@ public class NotificationPanelView extends PanelView implements @Override protected void onTrackingStarted() { - mFalsingManager.onTrackingStarted(mStatusBar.isKeyguardCurrentlySecure()); + mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); super.onTrackingStarted(); if (mQsFullyExpanded) { mQsExpandImmediate = true; @@ -2846,7 +2873,7 @@ public class NotificationPanelView extends PanelView implements @Override protected boolean shouldUseDismissingAnimation() { return mBarState != StatusBarState.SHADE - && (!mStatusBar.isKeyguardCurrentlySecure() || !isTracking()); + && (mKeyguardStateController.canDismissLockScreen() || !isTracking()); } @Override @@ -3412,9 +3439,8 @@ public class NotificationPanelView extends PanelView implements public void setPulsing(boolean pulsing) { mPulsing = pulsing; - DozeParameters dozeParameters = DozeParameters.getInstance(mContext); - final boolean animatePulse = !dozeParameters.getDisplayNeedsBlanking() - && dozeParameters.getAlwaysOn(); + final boolean animatePulse = !mDozeParameters.getDisplayNeedsBlanking() + && mDozeParameters.getAlwaysOn(); if (animatePulse) { mAnimateNextPositionUpdate = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 938eeafb6a8c..b3051b3dd26e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -44,13 +44,12 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.doze.DozeLog; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import android.util.BoostFramework; import java.io.FileDescriptor; @@ -113,6 +112,7 @@ public abstract class PanelView extends FrameLayout { private FlingAnimationUtils mFlingAnimationUtilsClosing; private FlingAnimationUtils mFlingAnimationUtilsDismissing; private final FalsingManager mFalsingManager; + private final DozeLog mDozeLog; private final VibratorHelper mVibratorHelper; /** @@ -150,9 +150,8 @@ public abstract class PanelView extends FrameLayout { private boolean mGestureWaitForTouchSlop; private boolean mIgnoreXTouchSlop; private boolean mExpandLatencyTracking; - protected final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); - protected final SysuiStatusBarStateController mStatusBarStateController = - (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); + protected final KeyguardStateController mKeyguardStateController; + protected final SysuiStatusBarStateController mStatusBarStateController; protected void onExpandingFinished() { mBar.onExpandingFinished(); @@ -210,8 +209,12 @@ public abstract class PanelView extends FrameLayout { mJustPeeked = true; } - public PanelView(Context context, AttributeSet attrs) { + public PanelView(Context context, AttributeSet attrs, FalsingManager falsingManager, + DozeLog dozeLog, KeyguardStateController keyguardStateController, + SysuiStatusBarStateController statusBarStateController) { super(context, attrs); + mKeyguardStateController = keyguardStateController; + mStatusBarStateController = statusBarStateController; mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f /* maxLengthSeconds */, 0.6f /* speedUpFactor */); mFlingAnimationUtilsClosing = new FlingAnimationUtils(context, 0.5f /* maxLengthSeconds */, @@ -220,7 +223,8 @@ public abstract class PanelView extends FrameLayout { 0.5f /* maxLengthSeconds */, 0.2f /* speedUpFactor */, 0.6f /* x2 */, 0.84f /* y2 */); mBounceInterpolator = new BounceInterpolator(); - mFalsingManager = Dependency.get(FalsingManager.class); // TODO: inject into a controller. + mFalsingManager = falsingManager; + mDozeLog = dozeLog; mNotificationsDragEnabled = getResources().getBoolean(R.bool.config_enableNotificationShadeDrag); mVibratorHelper = Dependency.get(VibratorHelper.class); @@ -485,7 +489,7 @@ public abstract class PanelView extends FrameLayout { boolean expand = flingExpands(vel, vectorVel, x, y) || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel; - DozeLog.traceFling(expand, mTouchAboveFalsingThreshold, + mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold, mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch()); // Log collapse gesture if on lock screen. @@ -504,7 +508,8 @@ public abstract class PanelView extends FrameLayout { mUpdateFlingVelocity = vel; } } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking - && !mStatusBar.isBouncerShowing() && !mKeyguardMonitor.isKeyguardFadingAway()) { + && !mStatusBar.isBouncerShowing() + && !mKeyguardStateController.isKeyguardFadingAway()) { long timePassed = SystemClock.uptimeMillis() - mDownTime; if (timePassed < ViewConfiguration.getLongPressTimeout()) { // Lets show the user that he can actually expand the panel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 6c27a04752a3..fabd67e9ab5d 100755 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -58,7 +58,7 @@ import com.android.systemui.statusbar.policy.DataSaverController.Listener; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.HotspotController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.RotationLockController; @@ -83,7 +83,7 @@ public class PhoneStatusBarPolicy Listener, ZenModeController.Callback, DeviceProvisionedListener, - KeyguardMonitor.Callback, + KeyguardStateController.Callback, PrivacyItemController.Callback, LocationController.LocationChangeCallback { private static final String TAG = "PhoneStatusBarPolicy"; @@ -120,7 +120,7 @@ public class PhoneStatusBarPolicy private final DataSaverController mDataSaver; private final ZenModeController mZenController; private final DeviceProvisionedController mProvisionedController; - private final KeyguardMonitor mKeyguardMonitor; + private final KeyguardStateController mKeyguardStateController; private final LocationController mLocationController; private final PrivacyItemController mPrivacyItemController; private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); @@ -154,7 +154,7 @@ public class PhoneStatusBarPolicy mDataSaver = Dependency.get(DataSaverController.class); mZenController = Dependency.get(ZenModeController.class); mProvisionedController = Dependency.get(DeviceProvisionedController.class); - mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + mKeyguardStateController = Dependency.get(KeyguardStateController.class); mLocationController = Dependency.get(LocationController.class); mPrivacyItemController = Dependency.get(PrivacyItemController.class); mSensorPrivacyController = Dependency.get(SensorPrivacyController.class); @@ -254,7 +254,7 @@ public class PhoneStatusBarPolicy mHotspot.addCallback(mHotspotCallback); mNextAlarmController.addCallback(mNextAlarmCallback); mDataSaver.addCallback(this); - mKeyguardMonitor.addCallback(this); + mKeyguardStateController.addCallback(this); mPrivacyItemController.addCallback(this); mSensorPrivacyController.addCallback(mSensorPrivacyListener); mLocationController.addCallback(this); @@ -470,8 +470,8 @@ public class PhoneStatusBarPolicy boolean isManagedProfile = mUserManager.isManagedProfile(userId); mHandler.post(() -> { final boolean showIcon; - if (isManagedProfile && - (!mKeyguardMonitor.isShowing() || mKeyguardMonitor.isOccluded())) { + if (isManagedProfile && (!mKeyguardStateController.isShowing() + || mKeyguardStateController.isOccluded())) { showIcon = true; mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java index c1ff572bb210..1a6b415f87db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java @@ -127,6 +127,11 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, updateSamplingListener(); } + void stopAndDestroy() { + stop(); + mSamplingListener.destroy(); + } + @Override public void onViewAttachedToWindow(View view) { updateSamplingListener(); @@ -134,9 +139,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, @Override public void onViewDetachedFromWindow(View view) { - // isAttachedToWindow is only changed after this call to the listeners, so let's post it - // instead - postUpdateSamplingListener(); + stopAndDestroy(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 8c927799c31c..35039a0d74f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -21,7 +21,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.app.AlarmManager; -import android.content.Context; +import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -41,13 +41,14 @@ import com.android.internal.graphics.ColorUtils; import com.android.internal.util.function.TriConsumer; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.Dependency; +import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.qualifiers.MainResources; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.stack.ViewState; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -58,10 +59,14 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.function.Consumer; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Controls both the scrim behind the notifications and in front of the notifications (when a * security method gets shown). */ +@Singleton public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener, Dumpable { @@ -123,13 +128,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private static final float NOT_INITIALIZED = -1; private ScrimState mState = ScrimState.UNINITIALIZED; - private final Context mContext; - protected final ScrimView mScrimInFront; - protected final ScrimView mScrimBehind; - protected final ScrimView mScrimForBubble; + private ScrimView mScrimInFront; + private ScrimView mScrimBehind; + private ScrimView mScrimForBubble; - private final UnlockMethodCache mUnlockMethodCache; + private final KeyguardStateController mKeyguardStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final DozeParameters mDozeParameters; private final AlarmTimeout mTimeTicker; @@ -140,22 +144,21 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private GradientColors mColors; private boolean mNeedsDrawableColorUpdate; - protected float mScrimBehindAlpha; - protected float mScrimBehindAlphaResValue; - protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD; + private float mScrimBehindAlpha; + private float mScrimBehindAlphaResValue; + private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD; // Assuming the shade is expanded during initialization private float mExpansionFraction = 1f; private boolean mDarkenWhileDragging; private boolean mExpansionAffectsAlpha = true; - protected boolean mAnimateChange; + private boolean mAnimateChange; private boolean mUpdatePending; private boolean mTracking; - protected long mAnimationDuration = -1; + private long mAnimationDuration = -1; private long mAnimationDelay; - private Runnable mOnAnimationFinished; - private boolean mDeferFinishedListener; + private Animator.AnimatorListener mAnimatorListener; private final Interpolator mInterpolator = new DecelerateInterpolator(); private float mInFrontAlpha = NOT_INITIALIZED; @@ -169,7 +172,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private boolean mWallpaperVisibilityTimedOut; private int mScrimsVisibility; private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener; - private final Consumer<Integer> mScrimVisibleListener; + private Consumer<Integer> mScrimVisibleListener; private boolean mBlankScreen; private boolean mScreenBlankingCallbackCalled; private Callback mCallback; @@ -184,44 +187,50 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private boolean mWakeLockHeld; private boolean mKeyguardOccluded; - public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, ScrimView scrimForBubble, - TriConsumer<ScrimState, Float, GradientColors> scrimStateListener, - Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters, - AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) { - mScrimBehind = scrimBehind; - mScrimInFront = scrimInFront; - mScrimForBubble = scrimForBubble; + @Inject + public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters, + AlarmManager alarmManager, KeyguardStateController keyguardStateController, + @MainResources Resources resources, + DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, + KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor) { - mScrimStateListener = scrimStateListener; - mScrimVisibleListener = scrimVisibleListener; + mScrimStateListener = lightBarController::setScrimState; - mContext = scrimBehind.getContext(); - mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); - mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer(); - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); + mKeyguardStateController = keyguardStateController; + mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); + mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardVisibilityCallback = new KeyguardVisibilityCallback(); - mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback); - mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha); - mHandler = getHandler(); + mScrimBehindAlphaResValue = resources.getFloat(R.dimen.scrim_behind_alpha); + mHandler = handler; mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout, "hide_aod_wallpaper", mHandler); - mWakeLock = createWakeLock(); + mWakeLock = delayedWakeLockBuilder.setHandler(mHandler).setTag("Scrims").build(); // Scrim alpha is initially set to the value on the resource but might be changed // to make sure that text on top of it is legible. mScrimBehindAlpha = mScrimBehindAlphaResValue; mDozeParameters = dozeParameters; - keyguardMonitor.addCallback(new KeyguardMonitor.Callback() { + keyguardStateController.addCallback(new KeyguardStateController.Callback() { @Override public void onKeyguardFadingAwayChanged() { - setKeyguardFadingAway(keyguardMonitor.isKeyguardFadingAway(), - keyguardMonitor.getKeyguardFadingAwayDuration()); + setKeyguardFadingAway(keyguardStateController.isKeyguardFadingAway(), + keyguardStateController.getKeyguardFadingAwayDuration()); } }); - mColorExtractor = Dependency.get(SysuiColorExtractor.class); + mColorExtractor = sysuiColorExtractor; mColorExtractor.addOnColorsChangedListener(this); mColors = mColorExtractor.getNeutralColors(); mNeedsDrawableColorUpdate = true; + } + + /** + * Attach the controller to the supplied views. + */ + public void attachViews( + ScrimView scrimBehind, ScrimView scrimInFront, ScrimView scrimForBubble) { + mScrimBehind = scrimBehind; + mScrimInFront = scrimInFront; + mScrimForBubble = scrimForBubble; final ScrimState[] states = ScrimState.values(); for (int i = 0; i < states.length; i++) { @@ -232,8 +241,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mScrimBehind.setDefaultFocusHighlightEnabled(false); mScrimInFront.setDefaultFocusHighlightEnabled(false); mScrimForBubble.setDefaultFocusHighlightEnabled(false); - updateScrims(); + mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback); + } + + void setScrimVisibleListener(Consumer<Integer> listener) { + mScrimVisibleListener = listener; } public void transitionTo(ScrimState state) { @@ -257,7 +270,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo final ScrimState oldState = mState; mState = state; - Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.getIndex()); + Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.ordinal()); if (mCallback != null) { mCallback.onCancelled(); @@ -311,10 +324,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // Docking pulses may take a long time, wallpapers should also fade away after a while. mWallpaperVisibilityTimedOut = false; if (shouldFadeAwayWallpaper()) { - mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(), - AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); + DejankUtils.postAfterTraversal(() -> { + mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(), + AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); + }); } else { - mTimeTicker.cancel(); + DejankUtils.postAfterTraversal(mTimeTicker::cancel); } if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) { @@ -367,7 +382,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo public void onTrackingStarted() { mTracking = true; - mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer(); + mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); } public void onExpandingFinished() { @@ -430,8 +445,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // and docking. if (mWallpaperVisibilityTimedOut) { mWallpaperVisibilityTimedOut = false; - mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(), - AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); + DejankUtils.postAfterTraversal(() -> { + mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(), + AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); + }); } } } @@ -514,22 +531,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } /** - * Set front scrim to black, cancelling animations, in order to prepare to fade them - * away once the display turns on. - */ - public void prepareForGentleWakeUp() { - if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) { - mInFrontAlpha = 1f; - mInFrontTint = Color.BLACK; - mBehindTint = Color.BLACK; - mAnimateChange = false; - updateScrims(); - mAnimateChange = true; - mAnimationDuration = ANIMATION_DURATION_LONG; - } - } - - /** * If the lock screen sensor is active. */ public void setWakeLockScreenSensorActive(boolean active) { @@ -590,6 +591,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo setScrimAlpha(mScrimInFront, mInFrontAlpha); setScrimAlpha(mScrimBehind, mBehindAlpha); setScrimAlpha(mScrimForBubble, mBubbleAlpha); + // The animation could have all already finished, let's call onFinished just in case + onFinished(); dispatchScrimsVisible(); } @@ -667,6 +670,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private void startScrimAnimation(final View scrim, float current) { ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + if (mAnimatorListener != null) { + anim.addListener(mAnimatorListener); + } final int initialScrimTint = scrim instanceof ScrimView ? ((ScrimView) scrim).getTint() : Color.TRANSPARENT; anim.addUpdateListener(animation -> { @@ -688,15 +694,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo @Override public void onAnimationEnd(Animator animation) { + scrim.setTag(TAG_KEY_ANIM, null); onFinished(lastCallback); - scrim.setTag(TAG_KEY_ANIM, null); dispatchScrimsVisible(); - - if (!mDeferFinishedListener && mOnAnimationFinished != null) { - mOnAnimationFinished.run(); - mOnAnimationFinished = null; - } } }); @@ -741,11 +742,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mCallback.onStart(); } updateScrims(); - if (mOnAnimationFinished != null && !isAnimating(mScrimInFront) - && !isAnimating(mScrimBehind)) { - mOnAnimationFinished.run(); - mOnAnimationFinished = null; - } return true; } @@ -754,6 +750,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } private void onFinished(Callback callback) { + if (isAnimating(mScrimBehind) + || isAnimating(mScrimInFront) + || isAnimating(mScrimForBubble)) { + if (callback != null && callback != mCallback) { + // Since we only notify the callback that we're finished once everything has + // finished, we need to make sure that any changing callbacks are also invoked + callback.onFinished(); + } + return; + } if (mWakeLockHeld) { mWakeLock.release(TAG); mWakeLockHeld = false; @@ -773,6 +779,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mInFrontTint = Color.TRANSPARENT; mBehindTint = Color.TRANSPARENT; mBubbleTint = Color.TRANSPARENT; + updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint); + updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint); + updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint); } } @@ -781,8 +790,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } @VisibleForTesting - void setOnAnimationFinished(Runnable onAnimationFinished) { - mOnAnimationFinished = onAnimationFinished; + void setAnimatorListener(Animator.AnimatorListener animatorListener) { + mAnimatorListener = animatorListener; } private void updateScrim(ScrimView scrim, float alpha) { @@ -790,16 +799,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo ValueAnimator previousAnimator = ViewState.getChildTag(scrim, TAG_KEY_ANIM); if (previousAnimator != null) { - if (mAnimateChange) { - // We are not done yet! Defer calling the finished listener. - mDeferFinishedListener = true; - } // Previous animators should always be cancelled. Not doing so would cause // overlap, especially on states that don't animate, leading to flickering, // and in the worst case, an internal state that doesn't represent what // transitionTo requested. cancelAnimator(previousAnimator); - mDeferFinishedListener = false; } if (mPendingFrameCallback != null) { @@ -831,15 +835,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } else { // update the alpha directly updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim)); - onFinished(); } - } else { - onFinished(); } } - @VisibleForTesting - protected void cancelAnimator(ValueAnimator previousAnimator) { + private void cancelAnimator(ValueAnimator previousAnimator) { if (previousAnimator != null) { previousAnimator.cancel(); } @@ -887,11 +887,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mScrimBehind.postOnAnimationDelayed(callback, 32 /* delayMillis */); } - @VisibleForTesting - protected Handler getHandler() { - return new Handler(); - } - public int getBackgroundColor() { int color = mColors.getMainColor(); return Color.argb((int) (mScrimBehind.getViewAlpha() * Color.alpha(color)), @@ -913,11 +908,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo scheduleUpdate(); } - @VisibleForTesting - protected WakeLock createWakeLock() { - return new DelayedWakeLock(mHandler, WakeLock.createPartial(mContext, "Scrims")); - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(" ScrimController: "); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 7463c7c232bd..13055ffb2f77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -30,12 +30,35 @@ public enum ScrimState { /** * Initial state. */ - UNINITIALIZED(-1), + UNINITIALIZED, + + /** + * When turned off by sensors (prox, presence.) + */ + OFF { + @Override + public void prepare(ScrimState previousState) { + mFrontTint = Color.BLACK; + mBehindTint = Color.BLACK; + mBubbleTint = previousState.mBubbleTint; + + mFrontAlpha = 1f; + mBehindAlpha = 1f; + mBubbleAlpha = previousState.mBubbleAlpha; + + mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG; + } + + @Override + public boolean isLowPowerState() { + return true; + } + }, /** * On the lock screen. */ - KEYGUARD(0) { + KEYGUARD { @Override public void prepare(ScrimState previousState) { mBlankScreen = false; @@ -65,7 +88,7 @@ public enum ScrimState { /** * Showing password challenge on the keyguard. */ - BOUNCER(1) { + BOUNCER { @Override public void prepare(ScrimState previousState) { mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY; @@ -77,7 +100,7 @@ public enum ScrimState { /** * Showing password challenge on top of a FLAG_SHOW_WHEN_LOCKED activity. */ - BOUNCER_SCRIMMED(2) { + BOUNCER_SCRIMMED { @Override public void prepare(ScrimState previousState) { mBehindAlpha = 0; @@ -89,7 +112,7 @@ public enum ScrimState { /** * Changing screen brightness from quick settings. */ - BRIGHTNESS_MIRROR(3) { + BRIGHTNESS_MIRROR { @Override public void prepare(ScrimState previousState) { mBehindAlpha = 0; @@ -101,7 +124,7 @@ public enum ScrimState { /** * Always on display or screen off. */ - AOD(4) { + AOD { @Override public void prepare(ScrimState previousState) { final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn(); @@ -136,7 +159,7 @@ public enum ScrimState { /** * When phone wakes up because you received a notification. */ - PULSING(5) { + PULSING { @Override public void prepare(ScrimState previousState) { mFrontAlpha = mAodFrontScrimAlpha; @@ -164,7 +187,7 @@ public enum ScrimState { /** * Unlocked on top of an app (launcher or any other activity.) */ - UNLOCKED(6) { + UNLOCKED { @Override public void prepare(ScrimState previousState) { // State that UI will sync to. @@ -201,7 +224,7 @@ public enum ScrimState { /** * Unlocked with a bubble expanded. */ - BUBBLE_EXPANDED(7) { + BUBBLE_EXPANDED { @Override public void prepare(ScrimState previousState) { mFrontTint = Color.TRANSPARENT; @@ -237,17 +260,12 @@ public enum ScrimState { DozeParameters mDozeParameters; boolean mDisplayRequiresBlanking; boolean mWallpaperSupportsAmbientMode; - int mIndex; boolean mHasBackdrop; boolean mLaunchingAffordanceWithPreview; boolean mWakeLockScreenSensorActive; boolean mKeyguardFadingAway; long mKeyguardFadingAwayDuration; - ScrimState(int index) { - mIndex = index; - } - public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble, DozeParameters dozeParameters) { mScrimInFront = scrimInFront; @@ -262,10 +280,6 @@ public enum ScrimState { public void prepare(ScrimState previousState) { } - public int getIndex() { - return mIndex; - } - public float getFrontAlpha() { return mFrontAlpha; } 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 90894b08dc7f..11080619a5d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -24,8 +24,12 @@ import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; +import static android.view.InsetsFlags.getAppearance; +import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.InsetsState.containsType; +import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; +import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_TOP_BAR; -import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.Dependency.BG_HANDLER; import static com.android.systemui.Dependency.MAIN_HANDLER; @@ -46,12 +50,10 @@ import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ActivityTaskManager; -import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.IWallpaperManager; import android.app.KeyguardManager; @@ -76,7 +78,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; -import android.graphics.Rect; import android.media.AudioAttributes; import android.metrics.LogMaker; import android.net.Uri; @@ -105,6 +106,7 @@ import android.util.Log; import android.util.Slog; import android.view.Display; import android.view.IWindowManager; +import android.view.InsetsState.InternalInsetType; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -113,6 +115,7 @@ import android.view.ThreadedRenderer; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.view.WindowInsetsController.Appearance; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; @@ -125,6 +128,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.RegisterStatusBarResult; +import com.android.internal.view.AppearanceRegion; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; @@ -147,13 +151,13 @@ import com.android.systemui.SystemUIFactory; import com.android.systemui.UiOffloadThread; import com.android.systemui.appops.AppOpsController; import com.android.systemui.assist.AssistManager; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingLog; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; -import com.android.systemui.doze.DozeReceiver; import com.android.systemui.fragments.ExtensionFragmentListener; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.keyguard.KeyguardSliceProvider; @@ -178,6 +182,7 @@ import com.android.systemui.statusbar.BackDropView; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.EmptyShadeView; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyboardShortcuts; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -197,6 +202,7 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NewNotifPipeline; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationClicker; @@ -208,11 +214,11 @@ import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; +import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import com.android.systemui.statusbar.policy.BrightnessMirrorController; @@ -222,7 +228,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -242,11 +248,14 @@ import java.util.Map; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Singleton; +import dagger.Lazy; import dagger.Subcomponent; +@Singleton public class StatusBar extends SystemUI implements DemoMode, - ActivityStarter, OnUnlockMethodChangedListener, + ActivityStarter, KeyguardStateController.Callback, OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback, ColorExtractor.OnColorsChangedListener, ConfigurationListener, StatusBarStateController.StateListener, ShadeController, @@ -335,7 +344,7 @@ public class StatusBar extends SystemUI implements DemoMode, /** * The {@link StatusBarState} of the status bar. */ - protected int mState; + protected int mState; // TODO: remove this. Just use StatusBarStateController protected boolean mBouncerShowing; private PhoneStatusBarPolicy mIconPolicy; @@ -344,51 +353,49 @@ public class StatusBar extends SystemUI implements DemoMode, private VolumeComponent mVolumeComponent; private BrightnessMirrorController mBrightnessMirrorController; private boolean mBrightnessMirrorVisible; - protected BiometricUnlockController mBiometricUnlockController; - private LightBarController mLightBarController; + private BiometricUnlockController mBiometricUnlockController; + private final LightBarController mLightBarController; + private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy; protected LockscreenWallpaper mLockscreenWallpaper; - @VisibleForTesting - protected AutoHideController mAutoHideController; + private final AutoHideController mAutoHideController; private int mNaturalBarHeight = -1; private final Point mCurrentDisplaySize = new Point(); + protected StatusBarWindowViewController mStatusBarWindowViewController; protected StatusBarWindowView mStatusBarWindow; protected PhoneStatusBarView mStatusBarView; private int mStatusBarWindowState = WINDOW_STATE_SHOWING; protected StatusBarWindowController mStatusBarWindowController; - protected UnlockMethodCache mUnlockMethodCache; - @VisibleForTesting - KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @VisibleForTesting - DozeServiceHost mDozeServiceHost = new DozeServiceHost(); + DozeServiceHost mDozeServiceHost; private boolean mWakeUpComingFromTouch; private PointF mWakeUpTouchLocation; private final Object mQueueLock = new Object(); - protected StatusBarIconController mIconController; - @Inject - InjectionInflationController mInjectionInflater; - @Inject - PulseExpansionHandler mPulseExpansionHandler; - @Inject - NotificationWakeUpCoordinator mWakeUpCoordinator; - @Inject - KeyguardBypassController mKeyguardBypassController; - @Inject - protected HeadsUpManagerPhone mHeadsUpManager; - @Inject - DynamicPrivacyController mDynamicPrivacyController; - @Inject - BypassHeadsUpNotifier mBypassHeadsUpNotifier; - @Nullable - @Inject - protected KeyguardLiftController mKeyguardLiftController; - @Inject - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) - boolean mAllowNotificationLongPress; + private final FeatureFlags mFeatureFlags; + private final StatusBarIconController mIconController; + private final DozeLog mDozeLog; + private final InjectionInflationController mInjectionInflater; + private final PulseExpansionHandler mPulseExpansionHandler; + private final NotificationWakeUpCoordinator mWakeUpCoordinator; + private final KeyguardBypassController mKeyguardBypassController; + private final KeyguardStateController mKeyguardStateController; + private final HeadsUpManagerPhone mHeadsUpManager; + private final DynamicPrivacyController mDynamicPrivacyController; + private final BypassHeadsUpNotifier mBypassHeadsUpNotifier; + private final boolean mAllowNotificationLongPress; + private final Lazy<NewNotifPipeline> mNewNotifPipeline; + private final FalsingManager mFalsingManager; + private final BroadcastDispatcher mBroadcastDispatcher; + private final ConfigurationController mConfigurationController; + private final StatusBarWindowViewController.Builder mStatusBarWindowViewControllerBuilder; + private final NotifLog mNotifLog; + private final DozeParameters mDozeParameters; + private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; // expanded notifications protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window @@ -401,8 +408,7 @@ public class StatusBar extends SystemUI implements DemoMode, // RemoteInputView to be activated after unlock private View mPendingRemoteInputView; - private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler = - Dependency.get(RemoteInputQuickSettingsDisabler.class); + private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; private View mReportRejectedTouch; @@ -411,29 +417,31 @@ public class StatusBar extends SystemUI implements DemoMode, private final int[] mAbsPos = new int[2]; private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); - private NotificationGutsManager mGutsManager; - protected NotificationLogger mNotificationLogger; - protected NotificationEntryManager mEntryManager; + private final NotificationGutsManager mGutsManager; + private final NotificationLogger mNotificationLogger; + private final NotificationEntryManager mEntryManager; private NotificationListController mNotificationListController; - private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; - protected NotificationViewHierarchyManager mViewHierarchyManager; - protected ForegroundServiceController mForegroundServiceController; - protected AppOpsController mAppOpsController; - protected KeyguardViewMediator mKeyguardViewMediator; - private ZenModeController mZenController; - private final NotificationAlertingManager mNotificationAlertingManager = - Dependency.get(NotificationAlertingManager.class); + private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; + private final NotificationViewHierarchyManager mViewHierarchyManager; + private final ForegroundServiceController mForegroundServiceController; + private final AppOpsController mAppOpsController; + private final KeyguardViewMediator mKeyguardViewMediator; + private final ZenModeController mZenController; + private final NotificationAlertingManager mNotificationAlertingManager; // for disabling the status bar private int mDisabled1 = 0; private int mDisabled2 = 0; - // tracking calls to View.setSystemUiVisibility() - private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; - private final Rect mLastFullscreenStackBounds = new Rect(); - private final Rect mLastDockedStackBounds = new Rect(); + /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */ + private @Appearance int mAppearance; - private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class); + private boolean mTransientShown; + + private boolean mAppFullscreen; + private boolean mAppImmersive; + + private final DisplayMetrics mDisplayMetrics; // XXX: gesture research private final GestureRecorder mGestureRec = DEBUG_GESTURES @@ -442,7 +450,7 @@ public class StatusBar extends SystemUI implements DemoMode, private ScreenPinningRequest mScreenPinningRequest; - private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); + private final MetricsLogger mMetricsLogger; // ensure quick settings is disabled until the current user makes it through the setup wizard @VisibleForTesting @@ -477,16 +485,15 @@ public class StatusBar extends SystemUI implements DemoMode, private @TransitionMode int mStatusBarMode; private ViewMediatorCallback mKeyguardViewMediatorCallback; - protected ScrimController mScrimController; + private final ScrimController mScrimController; protected DozeScrimController mDozeScrimController; - private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); + private final UiOffloadThread mUiOffloadThread; protected boolean mDozing; - private boolean mDozingRequested; - private NotificationMediaManager mMediaManager; - protected NotificationLockscreenUserManager mLockscreenUserManager; - protected NotificationRemoteInputManager mRemoteInputManager; + private final NotificationMediaManager mMediaManager; + private final NotificationLockscreenUserManager mLockscreenUserManager; + private final NotificationRemoteInputManager mRemoteInputManager; private boolean mWallpaperSupported; private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() { @@ -501,8 +508,7 @@ public class StatusBar extends SystemUI implements DemoMode, WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT); final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean( com.android.internal.R.bool.config_dozeSupportsAodWallpaper); - final boolean imageWallpaperInAmbient = - !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking(); + final boolean imageWallpaperInAmbient = !mDozeParameters.getDisplayNeedsBlanking(); // If WallpaperInfo is null, it must be ImageWallpaper. final boolean supportsAmbientMode = deviceSupportsAodWallpaper && ((info == null && imageWallpaperInAmbient) @@ -545,7 +551,7 @@ public class StatusBar extends SystemUI implements DemoMode, + "mStatusBarKeyguardViewManager was null"); return; } - if (mKeyguardMonitor.isKeyguardFadingAway()) { + if (mKeyguardStateController.isKeyguardFadingAway()) { mStatusBarKeyguardViewManager.onKeyguardFadedAway(); } } @@ -557,19 +563,18 @@ public class StatusBar extends SystemUI implements DemoMode, }; private KeyguardUserSwitcher mKeyguardUserSwitcher; - protected UserSwitcherController mUserSwitcherController; - private NetworkController mNetworkController; - private KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); - private BatteryController mBatteryController; + private final UserSwitcherController mUserSwitcherController; + private final NetworkController mNetworkController; + private final BatteryController mBatteryController; protected boolean mPanelExpanded; private UiModeManager mUiModeManager; protected boolean mIsKeyguard; private LogMaker mStatusBarStateLog; protected NotificationIconAreaController mNotificationIconAreaController; @Nullable private View mAmbientIndicationContainer; - private SysuiColorExtractor mColorExtractor; - private ScreenLifecycle mScreenLifecycle; - @VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle; + private final SysuiColorExtractor mColorExtractor; + private final ScreenLifecycle mScreenLifecycle; + private final WakefulnessLifecycle mWakefulnessLifecycle; private final View.OnClickListener mGoToLockedShadeListener = v -> { if (mState == StatusBarState.KEYGUARD) { @@ -578,9 +583,7 @@ public class StatusBar extends SystemUI implements DemoMode, } }; private boolean mNoAnimationOnNextBarModeChange; - protected FalsingManager mFalsingManager; - private final SysuiStatusBarStateController mStatusBarStateController = - (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); + private final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @@ -594,26 +597,21 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onStrongAuthStateChanged(int userId) { super.onStrongAuthStateChanged(userId); - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("onStrongAuthStateChanged"); } }; private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); private HeadsUpAppearanceController mHeadsUpAppearanceController; private boolean mVibrateOnOpening; - private VibratorHelper mVibratorHelper; + private final VibratorHelper mVibratorHelper; private ActivityLaunchAnimator mActivityLaunchAnimator; protected StatusBarNotificationPresenter mPresenter; private NotificationActivityStarter mNotificationActivityStarter; - private boolean mPulsing; - protected BubbleController mBubbleController; - private final BubbleController.BubbleExpandListener mBubbleExpandListener = - (isExpanding, key) -> { - mEntryManager.updateNotifications(); - updateScrimController(); - }; + private final BubbleController mBubbleController; + private final BubbleController.BubbleExpandListener mBubbleExpandListener; + private ActivityIntentHelper mActivityIntentHelper; - private ShadeController mShadeController; @Override public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { @@ -629,46 +627,155 @@ public class StatusBar extends SystemUI implements DemoMode, AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION}; + @Inject + public StatusBar( + Context context, + FeatureFlags featureFlags, + LightBarController lightBarController, + AutoHideController autoHideController, + KeyguardUpdateMonitor keyguardUpdateMonitor, + StatusBarIconController statusBarIconController, + DozeLog dozeLog, + InjectionInflationController injectionInflationController, + PulseExpansionHandler pulseExpansionHandler, + NotificationWakeUpCoordinator notificationWakeUpCoordinator, + KeyguardBypassController keyguardBypassController, + KeyguardStateController keyguardStateController, + HeadsUpManagerPhone headsUpManagerPhone, + DynamicPrivacyController dynamicPrivacyController, + BypassHeadsUpNotifier bypassHeadsUpNotifier, + @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress, + Lazy<NewNotifPipeline> newNotifPipeline, + FalsingManager falsingManager, + BroadcastDispatcher broadcastDispatcher, + RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, + NotificationGutsManager notificationGutsManager, + NotificationLogger notificationLogger, + NotificationEntryManager notificationEntryManager, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, + NotificationViewHierarchyManager notificationViewHierarchyManager, + ForegroundServiceController foregroundServiceController, + AppOpsController appOpsController, + KeyguardViewMediator keyguardViewMediator, + ZenModeController zenModeController, + NotificationAlertingManager notificationAlertingManager, + DisplayMetrics displayMetrics, + MetricsLogger metricsLogger, + UiOffloadThread uiOffloadThread, + NotificationMediaManager notificationMediaManager, + NotificationLockscreenUserManager lockScreenUserManager, + NotificationRemoteInputManager remoteInputManager, + UserSwitcherController userSwitcherController, + NetworkController networkController, + BatteryController batteryController, + SysuiColorExtractor colorExtractor, + ScreenLifecycle screenLifecycle, + WakefulnessLifecycle wakefulnessLifecycle, + SysuiStatusBarStateController statusBarStateController, + VibratorHelper vibratorHelper, + BubbleController bubbleController, + NotificationGroupManager groupManager, + NotificationGroupAlertTransferHelper groupAlertTransferHelper, + VisualStabilityManager visualStabilityManager, + DeviceProvisionedController deviceProvisionedController, + NavigationBarController navigationBarController, + AssistManager assistManager, + NotificationListener notificationListener, + ConfigurationController configurationController, + StatusBarWindowController statusBarWindowController, + StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder, + NotifLog notifLog, + DozeParameters dozeParameters, + ScrimController scrimController, + Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, + Lazy<BiometricUnlockController> biometricUnlockControllerLazy, + DozeServiceHost dozeServiceHost, + PowerManager powerManager, + DozeScrimController dozeScrimController) { + super(context); + mFeatureFlags = featureFlags; + mLightBarController = lightBarController; + mAutoHideController = autoHideController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mIconController = statusBarIconController; + mDozeLog = dozeLog; + mInjectionInflater = injectionInflationController; + mPulseExpansionHandler = pulseExpansionHandler; + mWakeUpCoordinator = notificationWakeUpCoordinator; + mKeyguardBypassController = keyguardBypassController; + mKeyguardStateController = keyguardStateController; + mHeadsUpManager = headsUpManagerPhone; + mDynamicPrivacyController = dynamicPrivacyController; + mBypassHeadsUpNotifier = bypassHeadsUpNotifier; + mAllowNotificationLongPress = allowNotificationLongPress; + mNewNotifPipeline = newNotifPipeline; + mFalsingManager = falsingManager; + mBroadcastDispatcher = broadcastDispatcher; + mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler; + mGutsManager = notificationGutsManager; + mNotificationLogger = notificationLogger; + mEntryManager = notificationEntryManager; + mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; + mViewHierarchyManager = notificationViewHierarchyManager; + mForegroundServiceController = foregroundServiceController; + mAppOpsController = appOpsController; + mKeyguardViewMediator = keyguardViewMediator; + mZenController = zenModeController; + mNotificationAlertingManager = notificationAlertingManager; + mDisplayMetrics = displayMetrics; + mMetricsLogger = metricsLogger; + mUiOffloadThread = uiOffloadThread; + mMediaManager = notificationMediaManager; + mLockscreenUserManager = lockScreenUserManager; + mRemoteInputManager = remoteInputManager; + mUserSwitcherController = userSwitcherController; + mNetworkController = networkController; + mBatteryController = batteryController; + mColorExtractor = colorExtractor; + mScreenLifecycle = screenLifecycle; + mWakefulnessLifecycle = wakefulnessLifecycle; + mStatusBarStateController = statusBarStateController; + mVibratorHelper = vibratorHelper; + mBubbleController = bubbleController; + mGroupManager = groupManager; + mGroupAlertTransferHelper = groupAlertTransferHelper; + mVisualStabilityManager = visualStabilityManager; + mDeviceProvisionedController = deviceProvisionedController; + mNavigationBarController = navigationBarController; + mAssistManager = assistManager; + mNotificationListener = notificationListener; + mConfigurationController = configurationController; + mStatusBarWindowController = statusBarWindowController; + mStatusBarWindowViewControllerBuilder = statusBarWindowViewControllerBuilder; + mNotifLog = notifLog; + mDozeServiceHost = dozeServiceHost; + mPowerManager = powerManager; + mDozeParameters = dozeParameters; + mScrimController = scrimController; + mLockscreenWallpaperLazy = lockscreenWallpaperLazy; + mDozeScrimController = dozeScrimController; + mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; + + mBubbleExpandListener = + (isExpanding, key) -> { + mEntryManager.updateNotifications("onBubbleExpandChanged"); + updateScrimController(); + }; + } + @Override public void start() { - mGroupManager = Dependency.get(NotificationGroupManager.class); - mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class); - mVisualStabilityManager = Dependency.get(VisualStabilityManager.class); - mNotificationLogger = Dependency.get(NotificationLogger.class); - mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); - mNotificationListener = Dependency.get(NotificationListener.class); mNotificationListener.registerAsSystemService(); - mNetworkController = Dependency.get(NetworkController.class); - mUserSwitcherController = Dependency.get(UserSwitcherController.class); - mScreenLifecycle = Dependency.get(ScreenLifecycle.class); mScreenLifecycle.addObserver(mScreenObserver); - mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); mWakefulnessLifecycle.addObserver(mWakefulnessObserver); - mBatteryController = Dependency.get(BatteryController.class); - mAssistManager = Dependency.get(AssistManager.class); mUiModeManager = mContext.getSystemService(UiModeManager.class); - mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class); - mGutsManager = Dependency.get(NotificationGutsManager.class); - mMediaManager = Dependency.get(NotificationMediaManager.class); - mEntryManager = Dependency.get(NotificationEntryManager.class); mBypassHeadsUpNotifier.setUp(mEntryManager); - mNotificationInterruptionStateProvider = - Dependency.get(NotificationInterruptionStateProvider.class); - mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); - mForegroundServiceController = Dependency.get(ForegroundServiceController.class); - mAppOpsController = Dependency.get(AppOpsController.class); - mZenController = Dependency.get(ZenModeController.class); - mKeyguardViewMediator = getComponent(KeyguardViewMediator.class); - mColorExtractor = Dependency.get(SysuiColorExtractor.class); - mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); - mNavigationBarController = Dependency.get(NavigationBarController.class); - mBubbleController = Dependency.get(BubbleController.class); mBubbleController.setExpandListener(mBubbleExpandListener); mActivityIntentHelper = new ActivityIntentHelper(mContext); KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance(); if (sliceProvider != null) { sliceProvider.initDependencies(mMediaManager, mStatusBarStateController, - mKeyguardBypassController, DozeParameters.getInstance(mContext)); + mKeyguardBypassController, mDozeParameters); } else { Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies"); } @@ -687,7 +794,6 @@ public class StatusBar extends SystemUI implements DemoMode, mVibrateOnOpening = mContext.getResources().getBoolean( R.bool.config_vibrateOnIconAnimation); - mVibratorHelper = Dependency.get(VibratorHelper.class); DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER)); putComponent(StatusBar.class, this); @@ -700,8 +806,6 @@ public class StatusBar extends SystemUI implements DemoMode, mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); - mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController); mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @@ -709,7 +813,6 @@ public class StatusBar extends SystemUI implements DemoMode, mRecents = getComponent(Recents.class); mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - mFalsingManager = Dependency.get(FalsingManager.class); // Connect in to the status bar manager service mCommandQueue = getComponent(CommandQueue.class); @@ -740,10 +843,22 @@ public class StatusBar extends SystemUI implements DemoMode, // Set up the initial notification state. This needs to happen before CommandQueue.disable() setUpPresenter(); - setSystemUiVisibility(mDisplayId, result.mSystemUiVisibility, - result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility, - 0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds, - result.mNavbarColorManagedByIme); + if ((result.mSystemUiVisibility & View.STATUS_BAR_TRANSIENT) != 0) { + showTransientUnchecked(); + } + final int fullscreenAppearance = getAppearance(result.mFullscreenStackSysUiVisibility); + final int dockedAppearance = getAppearance(result.mDockedStackSysUiVisibility); + final AppearanceRegion[] appearanceRegions = result.mDockedStackBounds.isEmpty() + ? new AppearanceRegion[]{ + new AppearanceRegion(fullscreenAppearance, result.mFullscreenStackBounds)} + : new AppearanceRegion[]{ + new AppearanceRegion(fullscreenAppearance, result.mFullscreenStackBounds), + new AppearanceRegion(dockedAppearance, result.mDockedStackBounds)}; + onSystemBarAppearanceChanged(mDisplayId, getAppearance(result.mSystemUiVisibility), + appearanceRegions, result.mNavbarColorManagedByIme); + mAppFullscreen = result.mAppFullscreen; + mAppImmersive = result.mAppImmersive; + // StatusBarManagerService has a back up of IME token and it's restored here. setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher); @@ -786,18 +901,20 @@ public class StatusBar extends SystemUI implements DemoMode, mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController); mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController); - mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); - mUnlockMethodCache.addListener(this); + mKeyguardStateController.addCallback(this); startKeyguard(); mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); + mDozeServiceHost.initialize(this, mNotificationIconAreaController, + mStatusBarWindowViewController, mStatusBarWindow, mStatusBarKeyguardViewManager, + mNotificationPanel, mAmbientIndicationContainer); putComponent(DozeHost.class, mDozeServiceHost); mScreenPinningRequest = new ScreenPinningRequest(mContext); Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this); - Dependency.get(ConfigurationController.class).addCallback(this); + mConfigurationController.addCallback(this); // set the initial view visibility Dependency.get(InitController.class).addPostInitTask(this::updateAreThereNotifications); @@ -817,8 +934,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateTheme(); inflateStatusBarWindow(context); - mStatusBarWindow.setService(this); - mStatusBarWindow.setBypassController(mKeyguardBypassController); + mStatusBarWindowViewController.setService(this); mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener()); // TODO: Deal with the ugliness that comes from having some of the statusbar broken out @@ -830,6 +946,8 @@ public class StatusBar extends SystemUI implements DemoMode, NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller; mNotificationLogger.setUpWithContainer(notifListContainer); + // TODO: make this injectable. Currently that would create a circular dependency between + // NotificationIconAreaController and StatusBar. mNotificationIconAreaController = SystemUIFactory.getInstance() .createNotificationIconAreaController(context, this, mWakeUpCoordinator, mKeyguardBypassController, @@ -882,9 +1000,9 @@ public class StatusBar extends SystemUI implements DemoMode, mHeadsUpAppearanceController = new HeadsUpAppearanceController( mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow, mStatusBarStateController, mKeyguardBypassController, - mWakeUpCoordinator); + mKeyguardStateController, mWakeUpCoordinator); mHeadsUpAppearanceController.readFrom(oldController); - mStatusBarWindow.setStatusBarView(mStatusBarView); + mStatusBarWindowViewController.setStatusBarView(mStatusBarView); updateAreThereNotifications(); checkBarModes(); }).getFragmentManager() @@ -892,10 +1010,9 @@ public class StatusBar extends SystemUI implements DemoMode, .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(), CollapsedStatusBarFragment.TAG) .commit(); - mIconController = Dependency.get(StatusBarIconController.class); mHeadsUpManager.setUp(mStatusBarWindow, mGroupManager, this, mVisualStabilityManager); - Dependency.get(ConfigurationController.class).addCallback(mHeadsUpManager); + mConfigurationController.addCallback(mHeadsUpManager); mHeadsUpManager.addListener(this); mHeadsUpManager.addListener(mNotificationPanel); mHeadsUpManager.addListener(mGroupManager); @@ -910,7 +1027,8 @@ public class StatusBar extends SystemUI implements DemoMode, createNavigationBar(result); if (ENABLE_LOCKSCREEN_WALLPAPER) { - mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler); + mLockscreenWallpaper = mLockscreenWallpaperLazy.get(); + mLockscreenWallpaper.setHandler(mHandler); } mKeyguardIndicationController = @@ -938,31 +1056,22 @@ public class StatusBar extends SystemUI implements DemoMode, } }); - mAutoHideController = Dependency.get(AutoHideController.class); mAutoHideController.setStatusBar(this); - mLightBarController = Dependency.get(LightBarController.class); - ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind); ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front); ScrimView scrimForBubble = mStatusBarWindow.findViewById(R.id.scrim_for_bubble); - mScrimController = SystemUIFactory.getInstance().createScrimController( - scrimBehind, scrimInFront, scrimForBubble, mLockscreenWallpaper, - (state, alpha, color) -> mLightBarController.setScrimState(state, alpha, color), - scrimsVisible -> { - if (mStatusBarWindowController != null) { - mStatusBarWindowController.setScrimsVisibility(scrimsVisible); - } - if (mStatusBarWindow != null) { - mStatusBarWindow.onScrimVisibilityChanged(scrimsVisible); - } - }, DozeParameters.getInstance(mContext), - mContext.getSystemService(AlarmManager.class), - mKeyguardMonitor); + mScrimController.setScrimVisibleListener(scrimsVisible -> { + mStatusBarWindowController.setScrimsVisibility(scrimsVisible); + if (mStatusBarWindow != null) { + mStatusBarWindowViewController.onScrimVisibilityChanged(scrimsVisible); + } + }); + mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble); + mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf, mHeadsUpManager, mNotificationIconAreaController, mScrimController); - mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context)); BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop); mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front), @@ -977,7 +1086,7 @@ public class StatusBar extends SystemUI implements DemoMode, } mNotificationPanel.setLaunchAffordanceListener( - mStatusBarWindow::onShowingLaunchAffordanceChanged); + mStatusBarWindowViewController::onShowingLaunchAffordanceChanged); // Set up the quick settings tile panel View container = mStatusBarWindow.findViewById(R.id.qs_frame); @@ -1032,11 +1141,10 @@ public class StatusBar extends SystemUI implements DemoMode, }); } - PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - if (!pm.isScreenOn()) { + if (!mPowerManager.isScreenOn()) { mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); } - mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, + mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "GestureWakeLock"); mVibrator = mContext.getSystemService(Vibrator.class); int[] pattern = mContext.getResources().getIntArray( @@ -1047,11 +1155,7 @@ public class StatusBar extends SystemUI implements DemoMode, } // receive broadcasts - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - filter.addAction(Intent.ACTION_SCREEN_OFF); - filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG); - context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); + registerBroadcastReceiver(); IntentFilter demoFilter = new IntentFilter(); if (DEBUG_MEDIA_FAKE_ARTWORK) { @@ -1072,6 +1176,15 @@ public class StatusBar extends SystemUI implements DemoMode, ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); } + @VisibleForTesting + protected void registerBroadcastReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG); + mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, null, UserHandle.ALL); + } + protected QS createDefaultQSFragment() { return FragmentHostManager.get(mStatusBarWindow).create(QSFragment.class); } @@ -1079,7 +1192,7 @@ public class StatusBar extends SystemUI implements DemoMode, private void setUpPresenter() { // Set up the initial notification state. mActivityLaunchAnimator = new ActivityLaunchAnimator( - mStatusBarWindow, this, mNotificationPanel, + mStatusBarWindowViewController, this, mNotificationPanel, (NotificationListContainer) mStackScroller); final NotificationRowBinderImpl rowBinder = @@ -1087,12 +1200,13 @@ public class StatusBar extends SystemUI implements DemoMode, mContext, mAllowNotificationLongPress, mKeyguardBypassController, - mStatusBarStateController); + mStatusBarStateController, + mNotifLog); mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel, mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController, - mNotificationAlertingManager, rowBinder); + mNotificationAlertingManager, rowBinder, mKeyguardStateController); mNotificationListController = new NotificationListController( @@ -1108,7 +1222,6 @@ public class StatusBar extends SystemUI implements DemoMode, final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback = (StatusBarRemoteInputCallback) Dependency.get( NotificationRemoteInputManager.Callback.class); - mShadeController = Dependency.get(ShadeController.class); final ActivityStarter activityStarter = Dependency.get(ActivityStarter.class); mNotificationActivityStarter = new StatusBarNotificationActivityStarter(mContext, @@ -1116,7 +1229,7 @@ public class StatusBar extends SystemUI implements DemoMode, mHeadsUpManager, activityStarter, mActivityLaunchAnimator, mBarService, mStatusBarStateController, mKeyguardManager, mDreamManager, mRemoteInputManager, mStatusBarRemoteInputCallback, mGroupManager, - mLockscreenUserManager, mShadeController, mKeyguardMonitor, + mLockscreenUserManager, this, mKeyguardStateController, mNotificationInterruptionStateProvider, mMetricsLogger, new LockPatternUtils(mContext), Dependency.get(MAIN_HANDLER), Dependency.get(BG_HANDLER), mActivityIntentHelper, mBubbleController); @@ -1125,10 +1238,14 @@ public class StatusBar extends SystemUI implements DemoMode, mEntryManager.setRowBinder(rowBinder); rowBinder.setNotificationClicker(new NotificationClicker( - this, Dependency.get(BubbleController.class), mNotificationActivityStarter)); + this, mBubbleController, mNotificationActivityStarter)); mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager); mNotificationListController.bind(); + + if (mFeatureFlags.isNewNotifPipelineEnabled()) { + mNewNotifPipeline.get().initialize(mNotificationListener); + } } /** @@ -1153,8 +1270,8 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void wakeUpIfDozing(long time, View where, String why) { if (mDozing) { - PowerManager pm = mContext.getSystemService(PowerManager.class); - pm.wakeUp(time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why); + mPowerManager.wakeUp( + time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why); mWakeUpComingFromTouch = true; where.getLocationInWindow(mTmpInt2); mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, @@ -1250,17 +1367,16 @@ public class StatusBar extends SystemUI implements DemoMode, protected void inflateStatusBarWindow(Context context) { mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable( LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null); + mStatusBarWindowViewController = mStatusBarWindowViewControllerBuilder + .setStatusBarWindowView(mStatusBarWindow) + .setShadeController(this) + .build(); } protected void startKeyguard() { Trace.beginSection("StatusBar#startKeyguard"); - KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class); - mBiometricUnlockController = new BiometricUnlockController(mContext, - mDozeScrimController, keyguardViewMediator, - mScrimController, this, UnlockMethodCache.getInstance(mContext), - new Handler(), mKeyguardUpdateMonitor, mKeyguardBypassController); - putComponent(BiometricUnlockController.class, mBiometricUnlockController); - mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, + mBiometricUnlockController = mBiometricUnlockControllerLazy.get(); + mStatusBarKeyguardViewManager = mKeyguardViewMediator.registerStatusBar(this, getBouncerContainer(), mNotificationPanel, mBiometricUnlockController, mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller, mKeyguardBypassController); @@ -1270,7 +1386,7 @@ public class StatusBar extends SystemUI implements DemoMode, mRemoteInputManager.getController().addCallback(mStatusBarKeyguardViewManager); mDynamicPrivacyController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); - mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); + mKeyguardViewMediatorCallback = mKeyguardViewMediator.getViewMediatorCallback(); mLightBarController.setBiometricUnlockController(mBiometricUnlockController); mMediaManager.setBiometricUnlockController(mBiometricUnlockController); Dependency.get(KeyguardDismissUtil.class).setDismissHandler(this::executeWhenUnlocked); @@ -1366,15 +1482,19 @@ public class StatusBar extends SystemUI implements DemoMode, return mZenController.areNotificationsHiddenInShade(); } - public void requestNotificationUpdate() { - mEntryManager.updateNotifications(); + /** + * Request a notification update + * @param reason why we're requesting a notification update + */ + public void requestNotificationUpdate(String reason) { + mEntryManager.updateNotifications(reason); } /** * Asks {@link KeyguardUpdateMonitor} to run face auth. */ public void requestFaceAuth() { - if (!mUnlockMethodCache.canSkipBouncer()) { + if (!mKeyguardStateController.canDismissLockScreen()) { mKeyguardUpdateMonitor.requestFaceAuth(); } } @@ -1558,9 +1678,8 @@ public class StatusBar extends SystemUI implements DemoMode, logStateToEventlog(); } - @Override // UnlockMethodCache.OnUnlockMethodChangedListener - public void onUnlockMethodStateChanged() { - // Unlock method state changed. Notify KeguardMonitor + @Override + public void onUnlockedChanged() { updateKeyguardState(); logStateToEventlog(); } @@ -1609,11 +1728,11 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - mEntryManager.updateNotifications(); + mEntryManager.updateNotifications("onHeadsUpStateChanged"); if (isDozing() && isHeadsUp) { entry.setPulseSuppressed(false); mDozeServiceHost.fireNotificationPulse(entry); - if (mPulsing) { + if (mDozeServiceHost.isPulsing()) { mDozeScrimController.cancelPendingPulseTimeout(); } } @@ -1623,10 +1742,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - public boolean isKeyguardCurrentlySecure() { - return !mUnlockMethodCache.canSkipBouncer(); - } - public void setPanelExpanded(boolean isExpanded) { mPanelExpanded = isExpanded; updateHideIconsForBouncer(false /* animate */); @@ -1649,7 +1764,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean isPulsing() { - return mPulsing; + return mDozeServiceHost.isPulsing(); } public boolean hideStatusBarIconsWhenExpanded() { @@ -1761,6 +1876,16 @@ public class StatusBar extends SystemUI implements DemoMode, return mPresenter; } + @VisibleForTesting + void setBarStateForTest(int state) { + mState = state; + } + + @VisibleForTesting + void setUserSetupForTest(boolean userSetup) { + mUserSetup = userSetup; + } + /** * All changes to the status bar and notifications funnel through here and are batched. */ @@ -1793,7 +1918,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void maybeEscalateHeadsUp() { mHeadsUpManager.getAllEntries().forEach(entry -> { - final StatusBarNotification sbn = entry.notification; + final StatusBarNotification sbn = entry.getSbn(); final Notification notification = sbn.getNotification(); if (notification.fullScreenIntent != null) { if (DEBUG) { @@ -1818,8 +1943,8 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void handleSystemKey(int key) { if (SPEW) Log.d(TAG, "handleNavigationKey: " + key); - if (!mCommandQueue.panelsEnabled() || !mKeyguardMonitor.isDeviceInteractive() - || mKeyguardMonitor.isShowing() && !mKeyguardMonitor.isOccluded()) { + if (!mCommandQueue.panelsEnabled() || !mKeyguardUpdateMonitor.isDeviceInteractive() + || mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded()) { return; } @@ -1948,7 +2073,7 @@ public class StatusBar extends SystemUI implements DemoMode, // release focus immediately to kick off focus change transition mStatusBarWindowController.setStatusBarFocusable(false); - mStatusBarWindow.cancelExpandHelper(); + mStatusBarWindowViewController.cancelExpandHelper(); mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor); } else { mBubbleController.collapseStack(); @@ -2128,49 +2253,104 @@ public class StatusBar extends SystemUI implements DemoMode, } } - @Override // CommandQueue - public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, - boolean navbarColorManagedByIme) { + @Override + public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { if (displayId != mDisplayId) { return; } - final int oldVal = mSystemUiVisibility; - final int newVal = (oldVal&~mask) | (vis&mask); - final int diff = newVal ^ oldVal; - if (DEBUG) Log.d(TAG, String.format( - "setSystemUiVisibility displayId=%d vis=%s mask=%s oldVal=%s newVal=%s diff=%s", - displayId, Integer.toHexString(vis), Integer.toHexString(mask), - Integer.toHexString(oldVal), Integer.toHexString(newVal), - Integer.toHexString(diff))); - boolean sbModeChanged = false; - if (diff != 0) { - mSystemUiVisibility = newVal; + boolean barModeChanged = false; + final int diff = mAppearance ^ appearance; + if (mAppearance != appearance) { + mAppearance = appearance; // update low profile - if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { + if ((diff & APPEARANCE_LOW_PROFILE_BARS) != 0) { updateAreThereNotifications(); } + barModeChanged = updateBarMode(barMode(mTransientShown, appearance)); + } + mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged, + mStatusBarMode, navbarColorManagedByIme); + } - // ready to unhide - if ((vis & View.STATUS_BAR_UNHIDE) != 0) { - mNoAnimationOnNextBarModeChange = true; - } + @Override + public void showTransient(int displayId, @InternalInsetType int[] types) { + if (displayId != mDisplayId) { + return; + } + if (!containsType(types, TYPE_TOP_BAR)) { + return; + } + showTransientUnchecked(); + } - // update status bar mode - final int sbMode = computeStatusBarMode(oldVal, newVal); + private void showTransientUnchecked() { + if (!mTransientShown) { + mTransientShown = true; + mNoAnimationOnNextBarModeChange = true; + handleTransientChanged(); + } + } - sbModeChanged = sbMode != -1; - if (sbModeChanged && sbMode != mStatusBarMode) { - mStatusBarMode = sbMode; - checkBarModes(); - mAutoHideController.touchAutoHide(); - } - mStatusBarStateController.setSystemUiVisibility(mSystemUiVisibility); + @Override + public void abortTransient(int displayId, @InternalInsetType int[] types) { + if (displayId != mDisplayId) { + return; + } + if (!containsType(types, TYPE_TOP_BAR)) { + return; } - mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis, - mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode, - navbarColorManagedByIme); + clearTransient(); + } + + void clearTransient() { + if (mTransientShown) { + mTransientShown = false; + handleTransientChanged(); + } + } + + private void handleTransientChanged() { + final int barMode = barMode(mTransientShown, mAppearance); + if (updateBarMode(barMode)) { + mLightBarController.onStatusBarModeChanged(barMode); + } + } + + private boolean updateBarMode(int barMode) { + if (mStatusBarMode != barMode) { + mStatusBarMode = barMode; + checkBarModes(); + mAutoHideController.touchAutoHide(); + return true; + } + return false; + } + + private static @TransitionMode int barMode(boolean isTransient, int appearance) { + final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_TOP_BAR; + if (isTransient) { + return MODE_SEMI_TRANSPARENT; + } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) { + return MODE_LIGHTS_OUT; + } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) { + return MODE_LIGHTS_OUT_TRANSPARENT; + } else if ((appearance & APPEARANCE_OPAQUE_TOP_BAR) != 0) { + return MODE_OPAQUE; + } else { + return MODE_TRANSPARENT; + } + } + + @Override + public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { + if (displayId != mDisplayId) { + return; + } + mAppFullscreen = isFullscreen; + mAppImmersive = isImmersive; + mStatusBarStateController.setFullscreenState(isFullscreen, isImmersive); } @Override @@ -2201,40 +2381,10 @@ public class StatusBar extends SystemUI implements DemoMode, setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running); } - protected @TransitionMode int computeStatusBarMode(int oldVal, int newVal) { - return computeBarMode(oldVal, newVal); - } - protected BarTransitions getStatusBarTransitions() { return mStatusBarView.getBarTransitions(); } - protected @TransitionMode int computeBarMode(int oldVis, int newVis) { - final int oldMode = barMode(oldVis); - final int newMode = barMode(newVis); - if (oldMode == newMode) { - return -1; // no mode change - } - return newMode; - } - - private @TransitionMode int barMode(int vis) { - int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.STATUS_BAR_TRANSPARENT; - if ((vis & View.STATUS_BAR_TRANSIENT) != 0) { - return MODE_SEMI_TRANSPARENT; - } else if ((vis & View.STATUS_BAR_TRANSLUCENT) != 0) { - return MODE_TRANSLUCENT; - } else if ((vis & lightsOutTransparent) == lightsOutTransparent) { - return MODE_LIGHTS_OUT_TRANSPARENT; - } else if ((vis & View.STATUS_BAR_TRANSPARENT) != 0) { - return MODE_TRANSPARENT; - } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { - return MODE_LIGHTS_OUT; - } else { - return MODE_OPAQUE; - } - } - void checkBarModes() { if (mDemoMode) return; if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState, @@ -2290,20 +2440,16 @@ public class StatusBar extends SystemUI implements DemoMode, /** Returns whether the top activity is in fullscreen mode. */ public boolean inFullscreenMode() { - return 0 - != (mSystemUiVisibility - & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)); + return mAppFullscreen; } /** Returns whether the top activity is in immersive mode. */ public boolean inImmersiveMode() { - return 0 - != (mSystemUiVisibility - & (View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)); + return mAppImmersive; } private boolean areLightsOn() { - return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); + return 0 == (mAppearance & APPEARANCE_LOW_PROFILE_BARS); } public static String viewInfo(View v) { @@ -2339,8 +2485,8 @@ public class StatusBar extends SystemUI implements DemoMode, dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); } pw.println(" StatusBarWindowView: "); - if (mStatusBarWindow != null) { - mStatusBarWindow.dump(fd, pw, args); + if (mStatusBarWindowViewController != null) { + mStatusBarWindowViewController.dump(fd, pw, args); } pw.println(" mMediaManager: "); @@ -2369,7 +2515,7 @@ public class StatusBar extends SystemUI implements DemoMode, final boolean lightWpTheme = mContext.getThemeResId() == R.style.Theme_SystemUI_Light; pw.println(" light wallpaper theme: " + lightWpTheme); - DozeLog.dump(pw); + mDozeLog.dump(pw); if (mBiometricUnlockController != null) { mBiometricUnlockController.dump(pw); @@ -2428,10 +2574,6 @@ public class StatusBar extends SystemUI implements DemoMode, mLightBarController.dump(fd, pw, args); } - if (mUnlockMethodCache != null) { - mUnlockMethodCache.dump(pw); - } - if (mKeyguardBypassController != null) { mKeyguardBypassController.dump(pw); } @@ -2440,7 +2582,7 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardUpdateMonitor.dump(fd, pw, args); } - Dependency.get(FalsingManager.class).dump(pw); + mFalsingManager.dump(pw); FalsingLog.dump(pw); pw.println("SharedPreferences:"); @@ -2456,7 +2598,6 @@ public class StatusBar extends SystemUI implements DemoMode, public void createAndAddWindows(@Nullable RegisterStatusBarResult result) { makeStatusBarView(result); - mStatusBarWindowController = Dependency.get(StatusBarWindowController.class); mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight()); } @@ -2684,9 +2825,9 @@ public class StatusBar extends SystemUI implements DemoMode, public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) { if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP - && mUnlockMethodCache.canSkipBouncer() + && mKeyguardStateController.canDismissLockScreen() && !mStatusBarStateController.leaveOpenOnKeyguardHide() - && isPulsing()) { + && mDozeServiceHost.isPulsing()) { // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse. // TODO: Factor this transition out of BiometricUnlockController. mBiometricUnlockController.startWakeAndUnlock( @@ -2700,7 +2841,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - // SystemUIService notifies SystemBars of configuration changes, which then calls down here @Override public void onConfigChanged(Configuration newConfig) { updateResources(); @@ -2827,14 +2967,14 @@ public class StatusBar extends SystemUI implements DemoMode, boolean isShowing = mStatusBarKeyguardViewManager.isShowing(); boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded(); boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing(); - boolean isSecure = mUnlockMethodCache.isMethodSecure(); - boolean canSkipBouncer = mUnlockMethodCache.canSkipBouncer(); + boolean isSecure = mKeyguardStateController.isMethodSecure(); + boolean unlocked = mKeyguardStateController.canDismissLockScreen(); int stateFingerprint = getLoggingFingerprint(mState, isShowing, isOccluded, isBouncerShowing, isSecure, - canSkipBouncer); + unlocked); if (stateFingerprint != mLastLoggedStateFingerprint) { if (mStatusBarStateLog == null) { mStatusBarStateLog = new LogMaker(MetricsEvent.VIEW_UNKNOWN); @@ -2848,7 +2988,7 @@ public class StatusBar extends SystemUI implements DemoMode, isOccluded ? 1 : 0, isBouncerShowing ? 1 : 0, isSecure ? 1 : 0, - canSkipBouncer ? 1 : 0); + unlocked ? 1 : 0); mLastLoggedStateFingerprint = stateFingerprint; } } @@ -3018,7 +3158,7 @@ public class StatusBar extends SystemUI implements DemoMode, return mState == StatusBarState.FULLSCREEN_USER_SWITCHER; } - private boolean updateIsKeyguard() { + boolean updateIsKeyguard() { boolean wakeAndUnlocking = mBiometricUnlockController.getMode() == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; @@ -3026,8 +3166,8 @@ public class StatusBar extends SystemUI implements DemoMode, // there's no surface we can show to the user. Note that the device goes fully interactive // late in the transition, so we also allow the device to start dozing once the screen has // turned off fully. - boolean keyguardForDozing = mDozingRequested && - (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard)); + boolean keyguardForDozing = mDozeServiceHost.getDozingRequested() + && (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard)); boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested() || keyguardForDozing) && !wakeAndUnlocking; if (keyguardForDozing) { @@ -3048,7 +3188,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void showKeyguardImpl() { mIsKeyguard = true; - if (mKeyguardMonitor.isLaunchTransitionFadingAway()) { + if (mKeyguardStateController.isLaunchTransitionFadingAway()) { mNotificationPanel.animate().cancel(); onLaunchTransitionFadingEnded(); } @@ -3080,7 +3220,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanel.onAffordanceLaunchEnded(); releaseGestureWakeLock(); runLaunchTransitionEndRunnable(); - mKeyguardMonitor.setLaunchTransitionFadingAway(false); + mKeyguardStateController.setLaunchTransitionFadingAway(false); mPresenter.updateMediaMetaData(true /* metaDataChanged */, true); } @@ -3105,7 +3245,7 @@ public class StatusBar extends SystemUI implements DemoMode, mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); mLaunchTransitionEndRunnable = endRunnable; Runnable hideRunnable = () -> { - mKeyguardMonitor.setLaunchTransitionFadingAway(true); + mKeyguardStateController.setLaunchTransitionFadingAway(true); if (beforeFading != null) { beforeFading.run(); } @@ -3198,7 +3338,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (!mStatusBarStateController.isKeyguardRequested()) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); } - long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay(); + long delay = mKeyguardStateController.calculateGoingToFullShadeDelay(); mNotificationPanel.animateToFullShade(delay); if (mDraggedDownEntry != null) { mDraggedDownEntry.setUserLocked(false); @@ -3240,7 +3380,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void keyguardGoingAway() { // Treat Keyguard exit animation as an app transition to achieve nice transition for status // bar. - mKeyguardMonitor.notifyKeyguardGoingAway(true); + mKeyguardStateController.notifyKeyguardGoingAway(true); mCommandQueue.appTransitionPending(mDisplayId, true /* forced */); } @@ -3260,14 +3400,14 @@ public class StatusBar extends SystemUI implements DemoMode, mCommandQueue.appTransitionStarting(mDisplayId, startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); - mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration, isBypassFading); + mKeyguardStateController.notifyKeyguardFadingAway(delay, fadeoutDuration, isBypassFading); } /** * Notifies that the Keyguard fading away animation is done. */ public void finishKeyguardFadingAway() { - mKeyguardMonitor.notifyKeyguardDoneFading(); + mKeyguardStateController.notifyKeyguardDoneFading(); mScrimController.setExpansionAffectsAlpha(true); } @@ -3282,7 +3422,7 @@ public class StatusBar extends SystemUI implements DemoMode, final int themeResId = lockDarkText ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI; if (mContext.getThemeResId() != themeResId) { mContext.setTheme(themeResId); - Dependency.get(ConfigurationController.class).notifyThemeChanged(); + mConfigurationController.notifyThemeChanged(); } } @@ -3443,7 +3583,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onStateChanged(int newState) { mState = newState; updateReportRejectedTouchVisibility(); - updateDozing(); + mDozeServiceHost.updateDozing(); updateTheme(); mNavigationBarController.touchAutoDim(mDisplayId); Trace.beginSection("StatusBar#updateKeyguardState"); @@ -3481,40 +3621,25 @@ public class StatusBar extends SystemUI implements DemoMode, public void onDozingChanged(boolean isDozing) { Trace.beginSection("StatusBar#updateDozing"); mDozing = isDozing; + mDozeServiceHost.setDozing(mDozing); // Collapse the notification panel if open - boolean dozingAnimated = mDozingRequested - && DozeParameters.getInstance(mContext).shouldControlScreenOff(); + boolean dozingAnimated = mDozeServiceHost.getDozingRequested() + && mDozeParameters.shouldControlScreenOff(); mNotificationPanel.resetViews(dozingAnimated); updateQsExpansionEnabled(); mKeyguardViewMediator.setDozing(mDozing); - mEntryManager.updateNotifications(); - updateDozingState(); + mEntryManager.updateNotifications("onDozingChanged"); + mDozeServiceHost.updateDozing(); updateScrimController(); updateReportRejectedTouchVisibility(); Trace.endSection(); } - private void updateDozing() { - // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked. - boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD - || mBiometricUnlockController.getMode() - == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; - // When in wake-and-unlock we may not have received a change to mState - // but we still should not be dozing, manually set to false. - if (mBiometricUnlockController.getMode() == - BiometricUnlockController.MODE_WAKE_AND_UNLOCK) { - dozing = false; - } - - mStatusBarStateController.setIsDozing(dozing); - } - private void updateKeyguardState() { - mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), - mUnlockMethodCache.isMethodSecure(), + mKeyguardStateController.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), mStatusBarKeyguardViewManager.isOccluded()); } @@ -3562,7 +3687,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onTrackingStopped(boolean expand) { if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { - if (!expand && !mUnlockMethodCache.canSkipBouncer()) { + if (!expand && !mKeyguardStateController.canDismissLockScreen()) { showBouncer(false /* scrimmed */); } } @@ -3602,9 +3727,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Indicate that the group expansion is changing at this time -- this way the group // and children backgrounds / divider animations will look correct. entry.setGroupExpansionChanging(true); - if (entry.notification != null) { - userId = entry.notification.getUserId(); - } + userId = entry.getSbn().getUserId(); } boolean fullShadeNeedsBouncer = !mLockscreenUserManager. userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId()) @@ -3640,7 +3763,7 @@ public class StatusBar extends SystemUI implements DemoMode, mBouncerShowing = bouncerShowing; mKeyguardBypassController.setBouncerShowing(bouncerShowing); mPulseExpansionHandler.setBouncerShowing(bouncerShowing); - mStatusBarWindow.setBouncerShowingScrimmed(isBouncerShowingScrimmed()); + mStatusBarWindowViewController.setBouncerShowingScrimmed(isBouncerShowingScrimmed()); if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing); updateHideIconsForBouncer(true /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); @@ -3655,7 +3778,7 @@ public class StatusBar extends SystemUI implements DemoMode, */ public void collapseShade() { if (mNotificationPanel.isTracking()) { - mStatusBarWindow.cancelCurrentTouch(); + mStatusBarWindowViewController.cancelCurrentTouch(); } if (mPanelExpanded && mState == StatusBarState.SHADE) { animateCollapsePanels(); @@ -3676,7 +3799,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateVisibleToUser(); updateNotificationPanelTouchState(); - mStatusBarWindow.cancelCurrentTouch(); + mStatusBarWindowViewController.cancelCurrentTouch(); if (mLaunchCameraOnFinishedGoingToSleep) { mLaunchCameraOnFinishedGoingToSleep = false; @@ -3739,10 +3862,11 @@ public class StatusBar extends SystemUI implements DemoMode, * collapse the panel after we expanded it, and thus we would end up with a blank * Keyguard. */ - private void updateNotificationPanelTouchState() { + void updateNotificationPanelTouchState() { boolean goingToSleepWithoutAnimation = isGoingToSleep() - && !DozeParameters.getInstance(mContext).shouldControlScreenOff(); - boolean disabled = (!mDeviceInteractive && !mPulsing) || goingToSleepWithoutAnimation; + && !mDozeParameters.shouldControlScreenOff(); + boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing()) + || goingToSleepWithoutAnimation; mNotificationPanel.setTouchAndAnimationDisabled(disabled); mNotificationIconAreaController.setAnimationsEnabled(!disabled); } @@ -3787,7 +3911,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void showScreenPinningRequest(int taskId) { - if (mKeyguardMonitor.isShowing()) { + if (mKeyguardStateController.isShowing()) { // Don't allow apps to trigger this from keyguard. return; } @@ -3830,8 +3954,7 @@ public class StatusBar extends SystemUI implements DemoMode, return; } if (!mDeviceInteractive) { - PowerManager pm = mContext.getSystemService(PowerManager.class); - pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH, + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH, "com.android.systemui:CAMERA_GESTURE"); } vibrateForCameraGesture(); @@ -3870,20 +3993,17 @@ public class StatusBar extends SystemUI implements DemoMode, } boolean isCameraAllowedByAdmin() { - // TODO(b/140060745) - return whitelistIpcs(() -> { - if (mDevicePolicyManager.getCameraDisabled(null, - mLockscreenUserManager.getCurrentUserId())) { - return false; - } else if (mStatusBarKeyguardViewManager == null - || (isKeyguardShowing() && isKeyguardSecure())) { - // Check if the admin has disabled the camera specifically for the keyguard - return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, - mLockscreenUserManager.getCurrentUserId()) - & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0; - } - return true; - }); + if (mDevicePolicyManager.getCameraDisabled(null, + mLockscreenUserManager.getCurrentUserId())) { + return false; + } else if (mStatusBarKeyguardViewManager == null + || (isKeyguardShowing() && isKeyguardSecure())) { + // Check if the admin has disabled the camera specifically for the keyguard + return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, + mLockscreenUserManager.getCurrentUserId()) + & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0; + } + return true; } private boolean isGoingToSleep() { @@ -3897,9 +4017,10 @@ public class StatusBar extends SystemUI implements DemoMode, } public void notifyBiometricAuthModeChanged() { - updateDozing(); + mDozeServiceHost.updateDozing(); updateScrimController(); - mStatusBarWindow.onBiometricAuthModeChanged(mBiometricUnlockController.isWakeAndUnlock(), + mStatusBarWindowViewController.onBiometricAuthModeChanged( + mBiometricUnlockController.isWakeAndUnlock(), mBiometricUnlockController.isBiometricUnlock()); } @@ -3910,7 +4031,7 @@ public class StatusBar extends SystemUI implements DemoMode, // We don't want to end up in KEYGUARD state when we're unlocking with // fingerprint from doze. We should cross fade directly from black. boolean unlocking = mBiometricUnlockController.isWakeAndUnlock() - || mKeyguardMonitor.isKeyguardFadingAway(); + || mKeyguardStateController.isKeyguardFadingAway(); // Do not animate the scrim expansion when triggered by the fingerprint sensor. mScrimController.setExpansionAffectsAlpha( @@ -3932,9 +4053,16 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } else if (mBrightnessMirrorVisible) { mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR); - } else if (isPulsing()) { + } else if (mDozeServiceHost.isPulsing()) { mScrimController.transitionTo(ScrimState.PULSING, mDozeScrimController.getScrimCallback()); + } else if (mDozeServiceHost.hasPendingScreenOffCallback()) { + mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() { + @Override + public void onFinished() { + mDozeServiceHost.executePendingScreenOffCallback(); + } + }); } else if (mDozing && !unlocking) { mScrimController.transitionTo(ScrimState.AOD); } else if (mIsKeyguard && !unlocking) { @@ -3955,263 +4083,8 @@ public class StatusBar extends SystemUI implements DemoMode, return mStatusBarKeyguardViewManager.isShowing(); } - @VisibleForTesting - final class DozeServiceHost implements DozeHost { - private final ArrayList<Callback> mCallbacks = new ArrayList<>(); - private boolean mAnimateWakeup; - private boolean mAnimateScreenOff; - private boolean mIgnoreTouchWhilePulsing; - @VisibleForTesting - boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean( - "persist.sysui.wake_performs_auth", true); - - @Override - public String toString() { - return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]"; - } - - public void firePowerSaveChanged(boolean active) { - for (Callback callback : mCallbacks) { - callback.onPowerSaveChanged(active); - } - } - - public void fireNotificationPulse(NotificationEntry entry) { - Runnable pulseSupressedListener = () -> { - entry.setPulseSuppressed(true); - mNotificationIconAreaController.updateAodNotificationIcons(); - }; - for (Callback callback : mCallbacks) { - callback.onNotificationAlerted(pulseSupressedListener); - } - } - - @Override - public void addCallback(@NonNull Callback callback) { - mCallbacks.add(callback); - } - - @Override - public void removeCallback(@NonNull Callback callback) { - mCallbacks.remove(callback); - } - - @Override - public void startDozing() { - if (!mDozingRequested) { - mDozingRequested = true; - DozeLog.traceDozing(mContext, mDozing); - updateDozing(); - updateIsKeyguard(); - }else{ - mDozingRequested = true; - } - } - - @Override - public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { - if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) { - mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, - "com.android.systemui:LONG_PRESS"); - startAssist(new Bundle()); - return; - } - - if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { - mScrimController.setWakeLockScreenSensorActive(true); - } - - if (reason == DozeLog.PULSE_REASON_DOCKING && mStatusBarWindow != null) { - mStatusBarWindow.suppressWakeUpGesture(true); - } - - boolean passiveAuthInterrupt = reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN - && mWakeLockScreenPerformsAuth; - // Set the state to pulsing, so ScrimController will know what to do once we ask it to - // execute the transition. The pulse callback will then be invoked when the scrims - // are black, indicating that StatusBar is ready to present the rest of the UI. - mPulsing = true; - mDozeScrimController.pulse(new PulseCallback() { - @Override - public void onPulseStarted() { - callback.onPulseStarted(); - updateNotificationPanelTouchState(); - setPulsing(true); - } - - @Override - public void onPulseFinished() { - mPulsing = false; - callback.onPulseFinished(); - updateNotificationPanelTouchState(); - mScrimController.setWakeLockScreenSensorActive(false); - if (mStatusBarWindow != null) { - mStatusBarWindow.suppressWakeUpGesture(false); - } - setPulsing(false); - } - - private void setPulsing(boolean pulsing) { - mStatusBarStateController.setPulsing(pulsing); - mStatusBarKeyguardViewManager.setPulsing(pulsing); - mKeyguardViewMediator.setPulsing(pulsing); - mNotificationPanel.setPulsing(pulsing); - mVisualStabilityManager.setPulsing(pulsing); - mStatusBarWindow.setPulsing(pulsing); - mIgnoreTouchWhilePulsing = false; - if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) { - mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */); - } - updateScrimController(); - mPulseExpansionHandler.setPulsing(pulsing); - mWakeUpCoordinator.setPulsing(pulsing); - } - }, reason); - // DozeScrimController is in pulse state, now let's ask ScrimController to start - // pulsing and draw the black frame, if necessary. - updateScrimController(); - } - - @Override - public void stopDozing() { - if (mDozingRequested) { - mDozingRequested = false; - DozeLog.traceDozing(mContext, mDozing); - updateDozing(); - } - } - - @Override - public void onIgnoreTouchWhilePulsing(boolean ignore) { - if (ignore != mIgnoreTouchWhilePulsing) { - DozeLog.tracePulseTouchDisabledByProx(mContext, ignore); - } - mIgnoreTouchWhilePulsing = ignore; - if (isDozing() && ignore) { - mStatusBarWindow.cancelCurrentTouch(); - } - } - - @Override - public void dozeTimeTick() { - mNotificationPanel.dozeTimeTick(); - if (mAmbientIndicationContainer instanceof DozeReceiver) { - ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick(); - } - } - - @Override - public boolean isPowerSaveActive() { - return mBatteryController.isAodPowerSave(); - } - - @Override - public boolean isPulsingBlocked() { - return mBiometricUnlockController.getMode() - == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; - } - - @Override - public boolean isProvisioned() { - return mDeviceProvisionedController.isDeviceProvisioned() - && mDeviceProvisionedController.isCurrentUserSetup(); - } - - @Override - public boolean isBlockingDoze() { - if (mBiometricUnlockController.hasPendingAuthentication()) { - Log.i(TAG, "Blocking AOD because fingerprint has authenticated"); - return true; - } - return false; - } - - @Override - public void extendPulse(int reason) { - if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { - mScrimController.setWakeLockScreenSensorActive(true); - } - if (mDozeScrimController.isPulsing() && mHeadsUpManager.hasNotifications()) { - mHeadsUpManager.extendHeadsUp(); - } else { - mDozeScrimController.extendPulse(); - } - } - - @Override - public void stopPulsing() { - if (mDozeScrimController.isPulsing()) { - mDozeScrimController.pulseOutNow(); - } - } - - @Override - public void setAnimateWakeup(boolean animateWakeup) { - if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE - || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) { - // Too late to change the wakeup animation. - return; - } - mAnimateWakeup = animateWakeup; - } - - @Override - public void setAnimateScreenOff(boolean animateScreenOff) { - mAnimateScreenOff = animateScreenOff; - } - - @Override - public void onSlpiTap(float screenX, float screenY) { - if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null - && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) { - mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2); - float viewX = screenX - mTmpInt2[0]; - float viewY = screenY - mTmpInt2[1]; - if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth() - && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) { - dispatchTap(mAmbientIndicationContainer, viewX, viewY); - } - } - } - - @Override - public void setDozeScreenBrightness(int value) { - mStatusBarWindowController.setDozeScreenBrightness(value); - } - - @Override - public void setAodDimmingScrim(float scrimOpacity) { - mScrimController.setAodFrontScrimAlpha(scrimOpacity); - } - - @Override - public void prepareForGentleWakeUp() { - mScrimController.prepareForGentleWakeUp(); - } - - private void dispatchTap(View view, float x, float y) { - long now = SystemClock.elapsedRealtime(); - dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN); - dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP); - } - - private void dispatchTouchEvent(View view, float x, float y, long now, int action) { - MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */); - view.dispatchTouchEvent(ev); - ev.recycle(); - } - - private boolean shouldAnimateWakeup() { - return mAnimateWakeup; - } - - public boolean shouldAnimateScreenOff() { - return mAnimateScreenOff; - } - } - public boolean shouldIgnoreTouch() { - return isDozing() && mDozeServiceHost.mIgnoreTouchWhilePulsing; + return isDozing() && mDozeServiceHost.getIgnoreTouchWhilePulsing(); } // Begin Extra BaseStatusBar methods. @@ -4222,12 +4095,11 @@ public class StatusBar extends SystemUI implements DemoMode, // all notifications protected ViewGroup mStackScroller; - protected NotificationGroupManager mGroupManager; - - protected NotificationGroupAlertTransferHelper mGroupAlertTransferHelper; + private final NotificationGroupManager mGroupManager; + private final NotificationGroupAlertTransferHelper mGroupAlertTransferHelper; // handling reordering - protected VisualStabilityManager mVisualStabilityManager; + private final VisualStabilityManager mVisualStabilityManager; protected AccessibilityManager mAccessibilityManager; @@ -4239,14 +4111,13 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mVisibleToUser; protected DevicePolicyManager mDevicePolicyManager; - protected PowerManager mPowerManager; + private final PowerManager mPowerManager; protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; protected KeyguardManager mKeyguardManager; - private DeviceProvisionedController mDeviceProvisionedController - = Dependency.get(DeviceProvisionedController.class); + private final DeviceProvisionedController mDeviceProvisionedController; - protected NavigationBarController mNavigationBarController; + private final NavigationBarController mNavigationBarController; // UI-specific methods @@ -4262,7 +4133,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationShelf mNotificationShelf; protected EmptyShadeView mEmptyShadeView; - protected AssistManager mAssistManager; + private final AssistManager mAssistManager; public boolean isDeviceInteractive() { return mDeviceInteractive; @@ -4321,7 +4192,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } - protected NotificationListener mNotificationListener; + private final NotificationListener mNotificationListener; public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) { if (snoozeOption.getSnoozeCriterion() != null) { @@ -4457,8 +4328,7 @@ public class StatusBar extends SystemUI implements DemoMode, executeActionDismissingKeyguard(() -> { try { intent.send(null, 0, null, null, null, null, getActivityOptions( - mActivityLaunchAnimator.getLaunchAnimation(associatedView, - mShadeController.isOccluded()))); + mActivityLaunchAnimator.getLaunchAnimation(associatedView, isOccluded()))); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -4546,7 +4416,7 @@ public class StatusBar extends SystemUI implements DemoMode, */ public void onBouncerPreHideAnimation() { mNotificationPanel.onBouncerPreHideAnimation(); - mStatusBarWindow.onBouncerPreHideAnimation(); + mStatusBarWindowViewController.onBouncerPreHideAnimation(); } /** @@ -4611,8 +4481,8 @@ public class StatusBar extends SystemUI implements DemoMode, void createStatusBar(StatusBar statusbar); } - public @TransitionMode int getStatusBarMode() { - return mStatusBarMode; + boolean isTransientShown() { + return mTransientShown; } } 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 bb8ba055276a..8683586326d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -33,6 +33,8 @@ import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; +import androidx.annotation.VisibleForTesting; + import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; @@ -56,8 +58,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; -import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.PrintWriter; import java.util.ArrayList; @@ -161,12 +162,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mLastLockVisible; private OnDismissAction mAfterKeyguardGoneAction; + private Runnable mKeyguardGoneCancelAction; private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>(); // Dismiss action to be launched when we stop dozing or the keyguard is gone. private DismissWithActionRequest mPendingWakeupAction; - private final KeyguardMonitorImpl mKeyguardMonitor = - (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class); + private final KeyguardStateController mKeyguardStateController = Dependency.get( + KeyguardStateController.class); private final NotificationMediaManager mMediaManager = Dependency.get(NotificationMediaManager.class); private final SysuiStatusBarStateController mStatusBarStateController = @@ -221,7 +223,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBiometricUnlockController = biometricUnlockController; mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry, - mExpansionCallback, falsingManager, bypassController); + mExpansionCallback, mKeyguardStateController, falsingManager, bypassController); mNotificationPanelView = notificationPanelView; notificationPanelView.addExpansionListener(this); mBypassController = bypassController; @@ -245,7 +247,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncer.setExpansion(expansion); } if (expansion != KeyguardBouncer.EXPANSION_HIDDEN && tracking - && mStatusBar.isKeyguardCurrentlySecure() + && !mKeyguardStateController.canDismissLockScreen() && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) { mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */); } @@ -269,7 +271,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !mNotificationPanelView.isQsExpanded(); boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs) - && !mBouncer.isAnimatingAway() && !mKeyguardMonitor.isKeyguardFadingAway(); + && !mBouncer.isAnimatingAway() && !mKeyguardStateController.isKeyguardFadingAway(); if (mLastLockVisible != lockVisible) { mLastLockVisible = lockVisible; @@ -299,8 +301,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void show(Bundle options) { mShowing = true; mStatusBarWindowController.setKeyguardShowing(true); - mKeyguardMonitor.notifyKeyguardState( - mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded()); + mKeyguardStateController.notifyKeyguardState(mShowing, + mKeyguardStateController.isOccluded()); reset(true /* hideBouncerWhenShowing */); StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED, StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN); @@ -329,10 +331,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return false; } - private void hideBouncer(boolean destroyView) { + @VisibleForTesting + void hideBouncer(boolean destroyView) { if (mBouncer == null) { return; } + if (mShowing) { + // If we were showing the bouncer and then aborting, we need to also clear out any + // potential actions unless we actually unlocked. + mAfterKeyguardGoneAction = null; + if (mKeyguardGoneCancelAction != null) { + mKeyguardGoneCancelAction.run(); + mKeyguardGoneCancelAction = null; + } + } mBouncer.hide(destroyView); cancelPendingWakeupAction(); } @@ -365,6 +377,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncer.showWithDismissAction(r, cancelAction); } else { mAfterKeyguardGoneAction = r; + mKeyguardGoneCancelAction = cancelAction; mBouncer.show(false /* resetSecuritySelection */); } } @@ -530,8 +543,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ public void hide(long startTime, long fadeoutDuration) { mShowing = false; - mKeyguardMonitor.notifyKeyguardState( - mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded()); + mKeyguardStateController.notifyKeyguardState(mShowing, + mKeyguardStateController.isOccluded()); launchPendingWakeupAction(); if (Dependency.get(KeyguardUpdateMonitor.class).needsSlowUnlockTransition()) { @@ -672,6 +685,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mAfterKeyguardGoneAction.onDismiss(); mAfterKeyguardGoneAction = null; } + mKeyguardGoneCancelAction = null; for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) { mAfterKeyguardGoneRunnables.get(i).run(); } @@ -739,8 +753,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } private long getNavBarShowDelay() { - if (mKeyguardMonitor.isKeyguardFadingAway()) { - return mKeyguardMonitor.getKeyguardFadingAwayDelay(); + if (mKeyguardStateController.isKeyguardFadingAway()) { + return mKeyguardStateController.getKeyguardFadingAwayDelay(); } else if (mBouncer.isShowing()) { return NAV_BAR_SHOW_DELAY_BOUNCER; } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 320243b3ee22..64a45e16d749 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -68,7 +68,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.HeadsUpUtil; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; /** * Status bar implementation of {@link NotificationActivityStarter}. @@ -84,7 +84,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotificationRemoteInputManager mRemoteInputManager; private final NotificationLockscreenUserManager mLockscreenUserManager; private final ShadeController mShadeController; - private final KeyguardMonitor mKeyguardMonitor; + private final KeyguardStateController mKeyguardStateController; private final ActivityStarter mActivityStarter; private final NotificationEntryManager mEntryManager; private final StatusBarStateController mStatusBarStateController; @@ -125,7 +125,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit NotificationGroupManager groupManager, NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, - KeyguardMonitor keyguardMonitor, + KeyguardStateController keyguardStateController, NotificationInterruptionStateProvider notificationInterruptionStateProvider, MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, @@ -145,7 +145,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mRemoteInputManager = remoteInputManager; mLockscreenUserManager = lockscreenUserManager; mShadeController = shadeController; - mKeyguardMonitor = keyguardMonitor; + mKeyguardStateController = keyguardStateController; mActivityStarter = activityStarter; mEntryManager = entryManager; mStatusBarStateController = statusBarStateController; @@ -204,7 +204,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); final boolean wasOccluded = mShadeController.isOccluded(); - boolean showOverLockscreen = mKeyguardMonitor.isShowing() && intent != null + boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null && mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); ActivityStarter.OnDismissAction postKeyguardAction = @@ -245,7 +245,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit StatusBarNotification parentToCancel = null; if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) { StatusBarNotification summarySbn = - mGroupManager.getLogicalGroupSummary(sbn).notification; + mGroupManager.getLogicalGroupSummary(sbn).getSbn(); if (shouldAutoCancel(summarySbn)) { parentToCancel = summarySbn; } @@ -258,7 +258,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit if (showOverLockscreen) { mShadeController.addPostCollapseAction(runnable); mShadeController.collapsePanel(true /* animate */); - } else if (mKeyguardMonitor.isShowing() + } else if (mKeyguardStateController.isShowing() && mShadeController.isOccluded()) { mShadeController.addAfterKeyguardGoneRunnable(runnable); mShadeController.collapsePanel(); @@ -310,7 +310,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit if (!TextUtils.isEmpty(entry.remoteInputText)) { remoteInputText = entry.remoteInputText; } - if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(entry.key)) { + if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(entry.getKey())) { fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT, remoteInputText.toString()); } @@ -409,11 +409,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit if (mNotificationInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) { if (shouldSuppressFullScreenIntent(entry)) { if (DEBUG) { - Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key); + Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.getKey()); } } else if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) { if (DEBUG) { - Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.key); + Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.getKey()); } } else { // Stop screensaver if the notification has a fullscreen intent. @@ -432,8 +432,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } try { EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, - entry.key); - entry.notification.getNotification().fullScreenIntent.send(); + entry.getKey()); + entry.getSbn().getNotification().fullScreenIntent.send(); entry.notifyFullScreenIntentLaunched(); mMetricsLogger.count("note_fullscreen", 1); } catch (PendingIntent.CanceledException e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 4732049d635e..b01a8d4b82e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -74,7 +74,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager.O import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.ArrayList; @@ -89,7 +89,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, private final ShadeController mShadeController = Dependency.get(ShadeController.class); private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); - private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + private final KeyguardStateController mKeyguardStateController; private final NotificationViewHierarchyManager mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); private final NotificationLockscreenUserManager mLockscreenUserManager = @@ -123,7 +123,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, private final DynamicPrivacyController mDynamicPrivacyController; private boolean mReinflateNotificationsOnUserSwitched; private boolean mDispatchUiModeChangeOnUserSwitched; - private final UnlockMethodCache mUnlockMethodCache; private TextView mNotificationPanelDebugText; protected boolean mVrMode; @@ -139,8 +138,10 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, ActivityLaunchAnimator activityLaunchAnimator, DynamicPrivacyController dynamicPrivacyController, NotificationAlertingManager notificationAlertingManager, - NotificationRowBinderImpl notificationRowBinder) { + NotificationRowBinderImpl notificationRowBinder, + KeyguardStateController keyguardStateController) { mContext = context; + mKeyguardStateController = keyguardStateController; mNotificationPanel = panel; mHeadsUpManager = headsUp; mDynamicPrivacyController = dynamicPrivacyController; @@ -152,7 +153,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mDozeScrimController = dozeScrimController; mScrimController = scrimController; - mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); mKeyguardManager = context.getSystemService(KeyguardManager.class); mMaxAllowedKeyguardNotifications = context.getResources().getInteger( R.integer.keyguard_max_notification_count); @@ -201,7 +201,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, NotificationVisibility visibility, boolean removedByUser) { StatusBarNotificationPresenter.this.onNotificationRemoved( - entry.key, entry.notification); + entry.getKey(), entry.getSbn()); if (removedByUser) { maybeEndAmbientPulse(); } @@ -366,7 +366,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, return false; } else { // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent - return !mKeyguardMonitor.isShowing() + return !mKeyguardStateController.isShowing() || mShadeController.isOccluded(); } } @@ -398,7 +398,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, public void onBindRow(NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row) { row.setAboveShelfChangedListener(mAboveShelfObserver); - row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer); + row.setSecureStateProvider(mKeyguardStateController::canDismissLockScreen); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 13d4b8edb8d4..1def89b3af54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -35,7 +35,6 @@ import android.view.View; import android.view.ViewParent; import com.android.systemui.ActivityIntentHelper; -import com.android.systemui.Dependency; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; @@ -47,8 +46,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; -import com.android.systemui.statusbar.policy.KeyguardMonitor; -import com.android.systemui.statusbar.policy.RemoteInputView; +import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; import javax.inject.Singleton; @@ -59,18 +57,16 @@ import javax.inject.Singleton; public class StatusBarRemoteInputCallback implements Callback, Callbacks, StatusBarStateController.StateListener { - private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); - private final SysuiStatusBarStateController mStatusBarStateController = - (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); - private final NotificationLockscreenUserManager mLockscreenUserManager = - Dependency.get(NotificationLockscreenUserManager.class); - private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); + private final KeyguardStateController mKeyguardStateController; + private final SysuiStatusBarStateController mStatusBarStateController; + private final NotificationLockscreenUserManager mLockscreenUserManager; + private final ActivityStarter mActivityStarter; + private final ShadeController mShadeController; private final Context mContext; private final ActivityIntentHelper mActivityIntentHelper; private final NotificationGroupManager mGroupManager; private View mPendingWorkRemoteInputView; private View mPendingRemoteInputView; - private final ShadeController mShadeController = Dependency.get(ShadeController.class); private KeyguardManager mKeyguardManager; private final CommandQueue mCommandQueue; private int mDisabled2; @@ -80,10 +76,19 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, /** */ @Inject - public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager) { + public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager, + NotificationLockscreenUserManager notificationLockscreenUserManager, + KeyguardStateController keyguardStateController, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, ShadeController shadeController) { mContext = context; mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL, new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null); + mLockscreenUserManager = notificationLockscreenUserManager; + mKeyguardStateController = keyguardStateController; + mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController; + mShadeController = shadeController; + mActivityStarter = activityStarter; mStatusBarStateController.addCallback(this); mKeyguardManager = context.getSystemService(KeyguardManager.class); mCommandQueue = getComponent(context, CommandQueue.class); @@ -165,7 +170,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, @Override public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView) { - if (mKeyguardMonitor.isShowing()) { + if (mKeyguardStateController.isShowing()) { onLockedRemoteInput(row, clickedView); } else { if (row.isChildInGroup() && !row.areChildrenExpanded()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 724b46297e75..ca7a936d58f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -21,7 +21,6 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; -import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; import android.content.pm.ActivityInfo; @@ -39,8 +38,6 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -94,24 +91,14 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat private final ArrayList<WeakReference<StatusBarWindowCallback>> mCallbacks = Lists.newArrayList(); - private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class); + private final SysuiColorExtractor mColorExtractor; @Inject - public StatusBarWindowController(Context context, - StatusBarStateController statusBarStateController, - ConfigurationController configurationController, - KeyguardBypassController keyguardBypassController) { - this(context, context.getSystemService(WindowManager.class), ActivityManager.getService(), - DozeParameters.getInstance(context), statusBarStateController, - configurationController, keyguardBypassController); - } - - @VisibleForTesting public StatusBarWindowController(Context context, WindowManager windowManager, IActivityManager activityManager, DozeParameters dozeParameters, StatusBarStateController statusBarStateController, ConfigurationController configurationController, - KeyguardBypassController keyguardBypassController) { + KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor) { mContext = context; mWindowManager = windowManager; mActivityManager = activityManager; @@ -120,6 +107,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze(); mLpChanged = new LayoutParams(); mKeyguardBypassController = keyguardBypassController; + mColorExtractor = colorExtractor; mLockScreenDisplayTimeout = context.getResources() .getInteger(R.integer.config_lockScreenDisplayTimeout); ((SysuiStatusBarStateController) statusBarStateController) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index a9e818df6bc3..6b513919e912 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -19,29 +19,19 @@ package com.android.systemui.statusbar.phone; import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.LayoutRes; -import android.app.StatusBarManager; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.hardware.display.AmbientDisplayConfiguration; -import android.media.AudioManager; -import android.media.session.MediaSessionLegacyHelper; import android.net.Uri; import android.os.Bundle; -import android.os.SystemClock; -import android.os.UserHandle; -import android.provider.Settings; import android.util.AttributeSet; import android.view.ActionMode; import android.view.DisplayCutout; -import android.view.GestureDetector; -import android.view.InputDevice; import android.view.InputQueue; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -53,24 +43,13 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.Window; +import android.view.WindowInsets; import android.view.WindowInsetsController; import android.widget.FrameLayout; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.view.FloatingActionMode; import com.android.internal.widget.FloatingToolbar; -import com.android.systemui.Dependency; -import com.android.systemui.ExpandHelper; import com.android.systemui.R; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.DragDownHelper; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; -import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility; -import com.android.systemui.tuner.TunerService; - -import java.io.FileDescriptor; -import java.io.PrintWriter; /** * Combined status bar and notification panel view. Also holding backdrop and scrims. @@ -79,91 +58,26 @@ public class StatusBarWindowView extends FrameLayout { public static final String TAG = "StatusBarWindowView"; public static final boolean DEBUG = StatusBar.DEBUG; - private final GestureDetector mGestureDetector; - private final StatusBarStateController mStatusBarStateController; - private boolean mDoubleTapEnabled; - private boolean mSingleTapEnabled; - private DragDownHelper mDragDownHelper; - private NotificationStackScrollLayout mStackScrollLayout; - private NotificationPanelView mNotificationPanel; - private View mBrightnessMirror; - private LockIcon mLockIcon; - private PhoneStatusBarView mStatusBarView; - private int mRightInset = 0; private int mLeftInset = 0; - private StatusBar mService; - private final Paint mTransparentSrcPaint = new Paint(); - private FalsingManager mFalsingManager; - // Implements the floating action mode for TextView's Cut/Copy/Past menu. Normally provided by // DecorView, but since this is a special window we have to roll our own. private View mFloatingActionModeOriginatingView; private ActionMode mFloatingActionMode; private FloatingToolbar mFloatingToolbar; private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener; - private boolean mTouchCancelled; - private boolean mTouchActive; - private boolean mExpandAnimationRunning; - private boolean mExpandAnimationPending; - private boolean mSuppressingWakeUpGesture; - - private final GestureDetector.SimpleOnGestureListener mGestureListener = - new GestureDetector.SimpleOnGestureListener() { - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - if (mSingleTapEnabled && !mSuppressingWakeUpGesture) { - mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this, - "SINGLE_TAP"); - return true; - } - return false; - } - @Override - public boolean onDoubleTap(MotionEvent e) { - if (mDoubleTapEnabled || mSingleTapEnabled) { - mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this, - "DOUBLE_TAP"); - return true; - } - return false; - } - }; - private final TunerService.Tunable mTunable = (key, newValue) -> { - AmbientDisplayConfiguration configuration = new AmbientDisplayConfiguration(mContext); - switch (key) { - case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE: - mDoubleTapEnabled = configuration.doubleTapGestureEnabled(UserHandle.USER_CURRENT); - break; - case Settings.Secure.DOZE_TAP_SCREEN_GESTURE: - mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT); - } - }; - - /** - * If set to true, the current gesture started below the notch and we need to dispatch touch - * events manually as it's outside of the regular view bounds. - */ - private boolean mExpandingBelowNotch; - private KeyguardBypassController mBypassController; + private InteractionEventHandler mInteractionEventHandler; public StatusBarWindowView(Context context, AttributeSet attrs) { super(context, attrs); setMotionEventSplittingEnabled(false); - mTransparentSrcPaint.setColor(0); - mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); - mFalsingManager = Dependency.get(FalsingManager.class); // TODO: inject into a controller. - mGestureDetector = new GestureDetector(context, mGestureListener); - mStatusBarStateController = Dependency.get(StatusBarStateController.class); - Dependency.get(TunerService.class).addTunable(mTunable, - Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, - Settings.Secure.DOZE_TAP_SCREEN_GESTURE); } @Override - protected boolean fitSystemWindows(Rect insets) { + public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) { + final Insets insets = windowInsets.getMaxInsets(WindowInsets.Type.systemBars()); if (getFitsSystemWindows()) { boolean paddingChanged = insets.top != getPaddingTop() || insets.bottom != getPaddingBottom(); @@ -189,9 +103,6 @@ public class StatusBarWindowView extends FrameLayout { if (paddingChanged) { setPadding(0, 0, 0, 0); } - insets.left = 0; - insets.top = 0; - insets.right = 0; } else { if (mRightInset != 0 || mLeftInset != 0) { mRightInset = 0; @@ -205,9 +116,8 @@ public class StatusBarWindowView extends FrameLayout { if (changed) { setPadding(0, 0, 0, 0); } - insets.top = 0; } - return false; + return windowInsets; } private void applyMargins() { @@ -226,11 +136,6 @@ public class StatusBarWindowView extends FrameLayout { } } - @VisibleForTesting - protected NotificationStackScrollLayout getStackScrollLayout() { - return mStackScrollLayout; - } - @Override public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); @@ -242,61 +147,6 @@ public class StatusBarWindowView extends FrameLayout { } @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mStackScrollLayout = findViewById(R.id.notification_stack_scroller); - mNotificationPanel = findViewById(R.id.notification_panel); - mBrightnessMirror = findViewById(R.id.brightness_mirror); - mLockIcon = findViewById(R.id.lock_icon); - } - - @Override - public void onViewAdded(View child) { - super.onViewAdded(child); - if (child.getId() == R.id.brightness_mirror) { - mBrightnessMirror = child; - } - } - - /** - * Propagate {@link StatusBar} pulsing state. - */ - public void setPulsing(boolean pulsing) { - if (mLockIcon != null) { - mLockIcon.setPulsing(pulsing); - } - } - - /** - * Called when the biometric authentication mode changes. - * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()} - * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} () - */ - public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) { - if (mLockIcon != null) { - mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock); - } - } - - public void setStatusBarView(PhoneStatusBarView statusBarView) { - mStatusBarView = statusBarView; - } - - public void setService(StatusBar service) { - mService = service; - NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); - ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback(); - DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback(); - setDragDownHelper(new DragDownHelper(getContext(), this, expandHelperCallback, - dragDownCallback, mFalsingManager)); - } - - @VisibleForTesting - void setDragDownHelper(DragDownHelper dragDownHelper) { - mDragDownHelper = dragDownHelper; - } - - @Override protected void onAttachedToWindow () { super.onAttachedToWindow(); setWillNotDraw(!DEBUG); @@ -304,152 +154,53 @@ public class StatusBarWindowView extends FrameLayout { @Override public boolean dispatchKeyEvent(KeyEvent event) { - if (mService.interceptMediaKey(event)) { + if (mInteractionEventHandler.interceptMediaKey(event)) { return true; } + if (super.dispatchKeyEvent(event)) { return true; } - boolean down = event.getAction() == KeyEvent.ACTION_DOWN; - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_BACK: - if (!down) { - mService.onBackPressed(); - } - return true; - case KeyEvent.KEYCODE_MENU: - if (!down) { - return mService.onMenuPressed(); - } - case KeyEvent.KEYCODE_SPACE: - if (!down) { - return mService.onSpacePressed(); - } - break; - case KeyEvent.KEYCODE_VOLUME_DOWN: - case KeyEvent.KEYCODE_VOLUME_UP: - if (mService.isDozing()) { - MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent( - event, AudioManager.USE_DEFAULT_STREAM_TYPE, true); - return true; - } - break; - } - return false; - } - public void setTouchActive(boolean touchActive) { - mTouchActive = touchActive; + return mInteractionEventHandler.dispatchKeyEvent(event); } - void suppressWakeUpGesture(boolean suppress) { - mSuppressingWakeUpGesture = suppress; + protected void setInteractionEventHandler(InteractionEventHandler listener) { + mInteractionEventHandler = listener; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { - boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN; - boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; - boolean isCancel = ev.getActionMasked() == MotionEvent.ACTION_CANCEL; - - // Reset manual touch dispatch state here but make sure the UP/CANCEL event still gets - // delivered. - boolean expandingBelowNotch = mExpandingBelowNotch; - if (isUp || isCancel) { - mExpandingBelowNotch = false; - } - - if (!isCancel && mService.shouldIgnoreTouch()) { - return false; - } - if (isDown && mNotificationPanel.isFullyCollapsed()) { - mNotificationPanel.startExpandLatencyTracking(); - } - if (isDown) { - setTouchActive(true); - mTouchCancelled = false; - } else if (ev.getActionMasked() == MotionEvent.ACTION_UP - || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { - setTouchActive(false); - } - if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) { - return false; - } - mFalsingManager.onTouchEvent(ev, getWidth(), getHeight()); - mGestureDetector.onTouchEvent(ev); - if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) { - // Disallow new pointers while the brightness mirror is visible. This is so that you - // can't touch anything other than the brightness slider while the mirror is showing - // and the rest of the panel is transparent. - if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { - return false; - } - } - if (isDown) { - getStackScrollLayout().closeControlsIfOutsideTouch(ev); - } - if (mService.isDozing()) { - mService.mDozeScrimController.extendPulse(); - } + Boolean result = mInteractionEventHandler.handleDispatchTouchEvent(ev); - // In case we start outside of the view bounds (below the status bar), we need to dispatch - // the touch manually as the view system can't accomodate for touches outside of the - // regular view bounds. - if (isDown && ev.getY() >= mBottom) { - mExpandingBelowNotch = true; - expandingBelowNotch = true; - } - if (expandingBelowNotch) { - return mStatusBarView.dispatchTouchEvent(ev); - } - - return super.dispatchTouchEvent(ev); + return result != null ? result : super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); - if (mService.isDozing() && !mService.isPulsing()) { - // Capture all touch events in always-on. - return true; - } - boolean intercept = false; - if (mNotificationPanel.isFullyExpanded() - && mDragDownHelper.isDragDownEnabled() - && !mService.isBouncerShowing() - && !mService.isDozing()) { - intercept = mDragDownHelper.onInterceptTouchEvent(ev); - } + boolean intercept = mInteractionEventHandler.shouldInterceptTouchEvent(ev); if (!intercept) { - super.onInterceptTouchEvent(ev); + intercept = super.onInterceptTouchEvent(ev); } if (intercept) { - MotionEvent cancellation = MotionEvent.obtain(ev); - cancellation.setAction(MotionEvent.ACTION_CANCEL); - stackScrollLayout.onInterceptTouchEvent(cancellation); - mNotificationPanel.onInterceptTouchEvent(cancellation); - cancellation.recycle(); + mInteractionEventHandler.didIntercept(ev); } + return intercept; } @Override public boolean onTouchEvent(MotionEvent ev) { - boolean handled = false; - if (mService.isDozing()) { - handled = !mService.isPulsing(); - } - if ((mDragDownHelper.isDragDownEnabled() && !handled) || mDragDownHelper.isDraggingDown()) { - // we still want to finish our drag down gesture when locking the screen - handled = mDragDownHelper.onTouchEvent(ev); - } + boolean handled = mInteractionEventHandler.handleTouchEvent(ev); + if (!handled) { handled = super.onTouchEvent(ev); } - final int action = ev.getAction(); - if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) { - mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); + + if (!handled) { + mInteractionEventHandler.didNotHandleTouchEvent(ev); } + return handled; } @@ -465,77 +216,6 @@ public class StatusBarWindowView extends FrameLayout { } } - public void cancelExpandHelper() { - NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); - if (stackScrollLayout != null) { - stackScrollLayout.cancelExpandHelper(); - } - } - - public void cancelCurrentTouch() { - if (mTouchActive) { - final long now = SystemClock.uptimeMillis(); - MotionEvent event = MotionEvent.obtain(now, now, - MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); - event.setSource(InputDevice.SOURCE_TOUCHSCREEN); - dispatchTouchEvent(event); - event.recycle(); - mTouchCancelled = true; - } - } - - public void setExpandAnimationRunning(boolean expandAnimationRunning) { - mExpandAnimationRunning = expandAnimationRunning; - } - - public void setExpandAnimationPending(boolean pending) { - mExpandAnimationPending = pending; - } - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.print(" mExpandAnimationPending="); pw.println(mExpandAnimationPending); - pw.print(" mExpandAnimationRunning="); pw.println(mExpandAnimationRunning); - pw.print(" mTouchCancelled="); pw.println(mTouchCancelled); - pw.print(" mTouchActive="); pw.println(mTouchActive); - } - - /** - * Called whenever the scrims become opaque, transparent or semi-transparent. - */ - public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) { - if (mLockIcon != null) { - mLockIcon.onScrimVisibilityChanged(scrimsVisible); - } - } - - /** - * When we're launching an affordance, like double pressing power to open camera. - */ - public void onShowingLaunchAffordanceChanged(boolean showing) { - if (mLockIcon != null) { - mLockIcon.onShowingLaunchAffordanceChanged(showing); - } - } - - public void setBypassController(KeyguardBypassController bypassController) { - mBypassController = bypassController; - } - - public void setBouncerShowingScrimmed(boolean bouncerShowing) { - if (mLockIcon != null) { - mLockIcon.setBouncerShowingScrimmed(bouncerShowing); - } - } - - /** - * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation. - */ - public void onBouncerPreHideAnimation() { - if (mLockIcon != null) { - mLockIcon.onBouncerPreHideAnimation(); - } - } - public class LayoutParams extends FrameLayout.LayoutParams { public boolean ignoreRightInset; @@ -657,6 +337,35 @@ public class StatusBarWindowView extends FrameLayout { } } + interface InteractionEventHandler { + /** + * Returns a result for {@link ViewGroup#dispatchTouchEvent(MotionEvent)} or null to defer + * to the super method. + */ + Boolean handleDispatchTouchEvent(MotionEvent ev); + + /** + * Returns if the view should intercept the touch event. + * + * The touch event may still be interecepted if + * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)} decides to do so. + */ + boolean shouldInterceptTouchEvent(MotionEvent ev); + + /** + * Called when the view decides to intercept the touch event. + */ + void didIntercept(MotionEvent ev); + + boolean handleTouchEvent(MotionEvent ev); + + void didNotHandleTouchEvent(MotionEvent ev); + + boolean interceptMediaKey(KeyEvent event); + + boolean dispatchKeyEvent(KeyEvent event); + } + /** * Minimal window to satisfy FloatingToolbar. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java new file mode 100644 index 000000000000..fd3f9c803c12 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.app.StatusBarManager; +import android.hardware.display.AmbientDisplayConfiguration; +import android.media.AudioManager; +import android.media.session.MediaSessionLegacyHelper; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.view.GestureDetector; +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStub; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.ExpandHelper; +import com.android.systemui.R; +import com.android.systemui.doze.DozeLog; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.DragDownHelper; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.InjectionInflationController; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +import javax.inject.Inject; + +/** + * Controller for {@link StatusBarWindowView}. + */ +public class StatusBarWindowViewController { + private final StatusBarWindowView mView; + private final FalsingManager mFalsingManager; + private final GestureDetector mGestureDetector; + private View mBrightnessMirror; + private boolean mTouchActive; + private boolean mTouchCancelled; + private boolean mExpandAnimationPending; + private boolean mExpandAnimationRunning; + private NotificationStackScrollLayout mStackScrollLayout; + private LockIcon mLockIcon; + private PhoneStatusBarView mStatusBarView; + private StatusBar mService; + private DragDownHelper mDragDownHelper; + private boolean mSuppressingWakeUpGesture; + private boolean mDoubleTapEnabled; + private boolean mSingleTapEnabled; + private boolean mExpandingBelowNotch; + + private StatusBarWindowViewController( + StatusBarWindowView view, + InjectionInflationController injectionInflationController, + NotificationWakeUpCoordinator coordinator, + PulseExpansionHandler pulseExpansionHandler, + DynamicPrivacyController dynamicPrivacyController, + KeyguardBypassController bypassController, + FalsingManager falsingManager, + PluginManager pluginManager, + TunerService tunerService, + ShadeController shadeController, + NotificationLockscreenUserManager notificationLockscreenUserManager, + NotificationEntryManager notificationEntryManager, + KeyguardStateController keyguardStateController, + SysuiStatusBarStateController statusBarStateController, + DozeLog dozeLog, + DozeParameters dozeParameters) { + mView = view; + mFalsingManager = falsingManager; + + // TODO: create controller for NotificationPanelView + NotificationPanelView notificationPanelView = new NotificationPanelView( + view.getContext(), + null, + injectionInflationController, + coordinator, + pulseExpansionHandler, + dynamicPrivacyController, + bypassController, + falsingManager, + pluginManager, + shadeController, + notificationLockscreenUserManager, + notificationEntryManager, + keyguardStateController, + statusBarStateController, + dozeLog, + dozeParameters); + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + notificationPanelView.setVisibility(View.INVISIBLE); + notificationPanelView.setId(R.id.notification_panel); + LayoutInflater li = injectionInflationController.injectable( + LayoutInflater.from(mView.getContext())); + + li.inflate(R.layout.status_bar_expanded, notificationPanelView); + notificationPanelView.onChildrenAttached(); + + ViewStub statusBarExpanded = view.findViewById(R.id.status_bar_expanded); + mView.addView(notificationPanelView, mView.indexOfChild(statusBarExpanded), lp); + mView.removeView(statusBarExpanded); + + mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller); + mLockIcon = mView.findViewById(R.id.lock_icon); + mBrightnessMirror = mView.findViewById(R.id.brightness_mirror); + + TunerService.Tunable tunable = (key, newValue) -> { + AmbientDisplayConfiguration configuration = + new AmbientDisplayConfiguration(mView.getContext()); + switch (key) { + case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE: + mDoubleTapEnabled = configuration.doubleTapGestureEnabled( + UserHandle.USER_CURRENT); + break; + case Settings.Secure.DOZE_TAP_SCREEN_GESTURE: + mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT); + } + }; + tunerService.addTunable(tunable, + Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, + Settings.Secure.DOZE_TAP_SCREEN_GESTURE); + + GestureDetector.SimpleOnGestureListener gestureListener = + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + if (mSingleTapEnabled && !mSuppressingWakeUpGesture) { + mService.wakeUpIfDozing( + SystemClock.uptimeMillis(), mView, "SINGLE_TAP"); + return true; + } + return false; + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + if (mDoubleTapEnabled || mSingleTapEnabled) { + mService.wakeUpIfDozing( + SystemClock.uptimeMillis(), mView, "DOUBLE_TAP"); + return true; + } + return false; + } + }; + mGestureDetector = new GestureDetector(mView.getContext(), gestureListener); + + mView.setInteractionEventHandler(new StatusBarWindowView.InteractionEventHandler() { + @Override + public Boolean handleDispatchTouchEvent(MotionEvent ev) { + boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN; + boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; + boolean isCancel = ev.getActionMasked() == MotionEvent.ACTION_CANCEL; + + if (isUp || isCancel) { + mExpandingBelowNotch = false; + } + + // Reset manual touch dispatch state here but make sure the UP/CANCEL event still + // gets + // delivered. + + if (!isCancel && mService.shouldIgnoreTouch()) { + return false; + } + if (isDown && notificationPanelView.isFullyCollapsed()) { + notificationPanelView.startExpandLatencyTracking(); + } + if (isDown) { + setTouchActive(true); + mTouchCancelled = false; + } else if (ev.getActionMasked() == MotionEvent.ACTION_UP + || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { + setTouchActive(false); + } + if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) { + return false; + } + mFalsingManager.onTouchEvent(ev, mView.getWidth(), mView.getHeight()); + mGestureDetector.onTouchEvent(ev); + if (mBrightnessMirror != null + && mBrightnessMirror.getVisibility() == View.VISIBLE) { + // Disallow new pointers while the brightness mirror is visible. This is so that + // you can't touch anything other than the brightness slider while the mirror is + // showing and the rest of the panel is transparent. + if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { + return false; + } + } + if (isDown) { + getStackScrollLayout().closeControlsIfOutsideTouch(ev); + } + if (mService.isDozing()) { + mService.mDozeScrimController.extendPulse(); + } + // In case we start outside of the view bounds (below the status bar), we need to + // dispatch + // the touch manually as the view system can't accommodate for touches outside of + // the + // regular view bounds. + if (isDown && ev.getY() >= mView.getBottom()) { + mExpandingBelowNotch = true; + } + if (mExpandingBelowNotch) { + return mStatusBarView.dispatchTouchEvent(ev); + } + + return null; + } + + @Override + public boolean shouldInterceptTouchEvent(MotionEvent ev) { + if (mService.isDozing() && !mService.isPulsing()) { + // Capture all touch events in always-on. + return true; + } + boolean intercept = false; + if (notificationPanelView.isFullyExpanded() + && mDragDownHelper.isDragDownEnabled() + && !mService.isBouncerShowing() + && !mService.isDozing()) { + intercept = mDragDownHelper.onInterceptTouchEvent(ev); + } + + return intercept; + + } + + @Override + public void didIntercept(MotionEvent ev) { + NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); + MotionEvent cancellation = MotionEvent.obtain(ev); + cancellation.setAction(MotionEvent.ACTION_CANCEL); + stackScrollLayout.onInterceptTouchEvent(cancellation); + notificationPanelView.onInterceptTouchEvent(cancellation); + cancellation.recycle(); + } + + @Override + public boolean handleTouchEvent(MotionEvent ev) { + boolean handled = false; + if (mService.isDozing()) { + handled = !mService.isPulsing(); + } + if ((mDragDownHelper.isDragDownEnabled() && !handled) + || mDragDownHelper.isDraggingDown()) { + // we still want to finish our drag down gesture when locking the screen + handled = mDragDownHelper.onTouchEvent(ev); + } + + return handled; + } + + @Override + public void didNotHandleTouchEvent(MotionEvent ev) { + final int action = ev.getActionMasked(); + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); + } + } + + @Override + public boolean interceptMediaKey(KeyEvent event) { + return mService.interceptMediaKey(event); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + boolean down = event.getAction() == KeyEvent.ACTION_DOWN; + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_BACK: + if (!down) { + mService.onBackPressed(); + } + return true; + case KeyEvent.KEYCODE_MENU: + if (!down) { + return mService.onMenuPressed(); + } + break; + case KeyEvent.KEYCODE_SPACE: + if (!down) { + return mService.onSpacePressed(); + } + break; + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_UP: + if (mService.isDozing()) { + MediaSessionLegacyHelper.getHelper(mView.getContext()) + .sendVolumeKeyEvent( + event, AudioManager.USE_DEFAULT_STREAM_TYPE, true); + return true; + } + break; + } + return false; + } + }); + + mView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { + @Override + public void onChildViewAdded(View parent, View child) { + if (child.getId() == R.id.brightness_mirror) { + mBrightnessMirror = child; + } + } + + @Override + public void onChildViewRemoved(View parent, View child) { + } + }); + } + + public StatusBarWindowView getView() { + return mView; + } + + public void setTouchActive(boolean touchActive) { + mTouchActive = touchActive; + } + + public void cancelCurrentTouch() { + if (mTouchActive) { + final long now = SystemClock.uptimeMillis(); + MotionEvent event = MotionEvent.obtain(now, now, + MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + mView.dispatchTouchEvent(event); + event.recycle(); + mTouchCancelled = true; + } + } + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.print(" mExpandAnimationPending="); + pw.println(mExpandAnimationPending); + pw.print(" mExpandAnimationRunning="); + pw.println(mExpandAnimationRunning); + pw.print(" mTouchCancelled="); + pw.println(mTouchCancelled); + pw.print(" mTouchActive="); + pw.println(mTouchActive); + } + + public void setExpandAnimationPending(boolean pending) { + mExpandAnimationPending = pending; + } + + public void setExpandAnimationRunning(boolean running) { + mExpandAnimationRunning = running; + } + + public void cancelExpandHelper() { + NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); + if (stackScrollLayout != null) { + stackScrollLayout.cancelExpandHelper(); + } + } + + @VisibleForTesting + protected NotificationStackScrollLayout getStackScrollLayout() { + return mStackScrollLayout; + } + + /** + * Called whenever the scrims become opaque, transparent or semi-transparent. + */ + public void onScrimVisibilityChanged(Integer scrimsVisible) { + if (mLockIcon != null) { + mLockIcon.onScrimVisibilityChanged(scrimsVisible); + } + } + + /** + * Propagate {@link StatusBar} pulsing state. + */ + public void setPulsing(boolean pulsing) { + if (mLockIcon != null) { + mLockIcon.setPulsing(pulsing); + } + } + + /** + * Called when the biometric authentication mode changes. + * + * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()} + * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} () + */ + public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) { + if (mLockIcon != null) { + mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock); + } + } + + public void setStatusBarView(PhoneStatusBarView statusBarView) { + mStatusBarView = statusBarView; + } + + public void setService(StatusBar statusBar) { + mService = statusBar; + NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); + ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback(); + DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback(); + setDragDownHelper( + new DragDownHelper( + mView.getContext(), mView, expandHelperCallback, + dragDownCallback, mFalsingManager)); + } + + @VisibleForTesting + void setDragDownHelper(DragDownHelper dragDownHelper) { + mDragDownHelper = dragDownHelper; + } + + public void suppressWakeUpGesture(boolean suppress) { + mSuppressingWakeUpGesture = suppress; + } + + /** + * When we're launching an affordance, like double pressing power to open camera. + */ + public void onShowingLaunchAffordanceChanged(Boolean showing) { + if (mLockIcon != null) { + mLockIcon.onShowingLaunchAffordanceChanged(showing); + } + } + + public void setBouncerShowingScrimmed(boolean bouncerShowing) { + if (mLockIcon != null) { + mLockIcon.setBouncerShowingScrimmed(bouncerShowing); + } + } + + /** + * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation. + */ + public void onBouncerPreHideAnimation() { + if (mLockIcon != null) { + mLockIcon.onBouncerPreHideAnimation(); + } + } + + /** + * Builder for {@link StatusBarWindowViewController}. + */ + public static class Builder { + private final InjectionInflationController mInjectionInflationController; + private final NotificationWakeUpCoordinator mCoordinator; + private final PulseExpansionHandler mPulseExpansionHandler; + private final DynamicPrivacyController mDynamicPrivacyController; + private final KeyguardBypassController mBypassController; + private final FalsingManager mFalsingManager; + private final PluginManager mPluginManager; + private final TunerService mTunerService; + private final KeyguardStateController mKeyguardStateController; + private final SysuiStatusBarStateController mStatusBarStateController; + private ShadeController mShadeController; + private final NotificationLockscreenUserManager mNotificationLockScreenUserManager; + private final NotificationEntryManager mNotificationEntryManager; + private final DozeLog mDozeLog; + private final DozeParameters mDozeParameters; + private StatusBarWindowView mView; + + @Inject + public Builder( + InjectionInflationController injectionInflationController, + NotificationWakeUpCoordinator coordinator, + PulseExpansionHandler pulseExpansionHandler, + DynamicPrivacyController dynamicPrivacyController, + KeyguardBypassController bypassController, + FalsingManager falsingManager, + PluginManager pluginManager, + TunerService tunerService, + NotificationLockscreenUserManager notificationLockscreenUserManager, + NotificationEntryManager notificationEntryManager, + KeyguardStateController keyguardStateController, + StatusBarStateController statusBarStateController, + DozeLog dozeLog, + DozeParameters dozeParameters) { + mInjectionInflationController = injectionInflationController; + mCoordinator = coordinator; + mPulseExpansionHandler = pulseExpansionHandler; + mDynamicPrivacyController = dynamicPrivacyController; + mBypassController = bypassController; + mFalsingManager = falsingManager; + mPluginManager = pluginManager; + mTunerService = tunerService; + mNotificationLockScreenUserManager = notificationLockscreenUserManager; + mNotificationEntryManager = notificationEntryManager; + mKeyguardStateController = keyguardStateController; + mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController; + mDozeLog = dozeLog; + mDozeParameters = dozeParameters; + } + + /** + * Provide {@link StatusBarWindowView} to attach this controller to. + */ + public Builder setStatusBarWindowView(StatusBarWindowView view) { + mView = view; + return this; + } + + /** + * Provide {@link ShadeController} that this view needs. + */ + public Builder setShadeController(ShadeController shadeController) { + mShadeController = shadeController; + return this; + } + + /** + * Build a {@link StatusBarWindowView}. + */ + public StatusBarWindowViewController build() { + return new StatusBarWindowViewController( + mView, + mInjectionInflationController, + mCoordinator, + mPulseExpansionHandler, + mDynamicPrivacyController, + mBypassController, + mFalsingManager, + mPluginManager, + mTunerService, + mShadeController, + mNotificationLockScreenUserManager, + mNotificationEntryManager, + mKeyguardStateController, + mStatusBarStateController, + mDozeLog, + mDozeParameters); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java index e61a67c77602..44be6bc282a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java @@ -28,7 +28,8 @@ import android.view.WindowManager.LayoutParams; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.statusbar.policy.KeyguardStateController; /** @@ -92,15 +93,15 @@ public class SystemUIDialog extends AlertDialog { public static void setShowForAllUsers(Dialog dialog, boolean show) { if (show) { dialog.getWindow().getAttributes().privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; } else { dialog.getWindow().getAttributes().privateFlags &= - ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + ~WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; } } public static void setWindowOnTop(Dialog dialog) { - if (Dependency.get(KeyguardMonitor.class).isShowing()) { + if (Dependency.get(KeyguardStateController.class).isShowing()) { dialog.getWindow().setType(LayoutParams.TYPE_STATUS_BAR_PANEL); } else { dialog.getWindow().setType(LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); @@ -138,20 +139,21 @@ public class SystemUIDialog extends AlertDialog { private final Dialog mDialog; private boolean mRegistered; + private final BroadcastDispatcher mBroadcastDispatcher; DismissReceiver(Dialog dialog) { mDialog = dialog; + mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class); } void register() { - mDialog.getContext() - .registerReceiverAsUser(this, UserHandle.CURRENT, INTENT_FILTER, null, null); + mBroadcastDispatcher.registerReceiver(this, INTENT_FILTER, null, UserHandle.CURRENT); mRegistered = true; } void unregister() { if (mRegistered) { - mDialog.getContext().unregisterReceiver(this); + mBroadcastDispatcher.unregisterReceiver(this); mRegistered = false; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java deleted file mode 100644 index c76f93e5ebaa..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.statusbar.phone; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.hardware.biometrics.BiometricSourceType; -import android.os.Build; -import android.os.Trace; - -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.DejankUtils; -import com.android.systemui.Dependency; - -import java.io.PrintWriter; -import java.util.ArrayList; - -/** - * Caches whether the current unlock method is insecure, taking trust into account. This information - * might be a little bit out of date and should not be used for actual security decisions; it should - * be only used for visual indications. - */ -public class UnlockMethodCache { - - private static UnlockMethodCache sInstance; - private static final boolean DEBUG_AUTH_WITH_ADB = false; - private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth"; - - private final LockPatternUtils mLockPatternUtils; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final ArrayList<OnUnlockMethodChangedListener> mListeners = new ArrayList<>(); - /** Whether the user configured a secure unlock method (PIN, password, etc.) */ - private boolean mSecure; - /** Whether the unlock method is currently insecure (insecure method or trusted environment) */ - private boolean mCanSkipBouncer; - private boolean mTrustManaged; - private boolean mTrusted; - private boolean mDebugUnlocked = false; - private boolean mFaceAuthEnabled; - - private UnlockMethodCache(Context ctx) { - mLockPatternUtils = new LockPatternUtils(ctx); - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mCallback); - update(true /* updateAlways */); - if (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB) { - // Watch for interesting updates - final IntentFilter filter = new IntentFilter(); - filter.addAction(AUTH_BROADCAST_KEY); - ctx.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (DEBUG_AUTH_WITH_ADB && AUTH_BROADCAST_KEY.equals(intent.getAction())) { - mDebugUnlocked = !mDebugUnlocked; - update(true /* updateAlways */); - } - } - }, filter, null, null); - } - } - - public static UnlockMethodCache getInstance(Context context) { - if (sInstance == null) { - sInstance = new UnlockMethodCache(context); - } - return sInstance; - } - - /** - * @return whether the user configured a secure unlock method like PIN, password, etc. - */ - public boolean isMethodSecure() { - return mSecure; - } - - public boolean isTrusted() { - return mTrusted; - } - - /** - * @return whether the lockscreen is currently insecure, and the bouncer won't be shown - */ - public boolean canSkipBouncer() { - return mCanSkipBouncer; - } - - public void addListener(OnUnlockMethodChangedListener listener) { - mListeners.add(listener); - } - - public void removeListener(OnUnlockMethodChangedListener listener) { - mListeners.remove(listener); - } - - /** - * If there are faces enrolled and user enabled face auth on keyguard. - */ - public boolean isFaceAuthEnabled() { - return mFaceAuthEnabled; - } - - private void update(boolean updateAlways) { - Trace.beginSection("UnlockMethodCache#update"); - int user = KeyguardUpdateMonitor.getCurrentUser(); - boolean secure = mLockPatternUtils.isSecure(user); - boolean canSkipBouncer = !secure || mKeyguardUpdateMonitor.getUserCanSkipBouncer(user) - || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked); - boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user); - boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user); - boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user); - boolean changed = secure != mSecure || canSkipBouncer != mCanSkipBouncer - || trustManaged != mTrustManaged - || mFaceAuthEnabled != faceAuthEnabled; - if (changed || updateAlways) { - mSecure = secure; - mCanSkipBouncer = canSkipBouncer; - mTrusted = trusted; - mTrustManaged = trustManaged; - mFaceAuthEnabled = faceAuthEnabled; - Trace.endSection(); - notifyListeners(); - } else { - Trace.endSection(); - } - } - - private void notifyListeners() { - String tag = "UnlockMethodCache#notifyListeners"; - DejankUtils.startDetectingBlockingIpcs(tag); - for (OnUnlockMethodChangedListener listener : mListeners) { - listener.onUnlockMethodStateChanged(); - } - DejankUtils.stopDetectingBlockingIpcs(tag); - } - - public void dump(PrintWriter pw) { - pw.println("UnlockMethodCache"); - pw.println(" mSecure: " + mSecure); - pw.println(" mCanSkipBouncer: " + mCanSkipBouncer); - pw.println(" mTrustManaged: " + mTrustManaged); - pw.println(" mTrusted: " + mTrusted); - pw.println(" mDebugUnlocked: " + mDebugUnlocked); - pw.println(" mFaceAuthEnabled: " + mFaceAuthEnabled); - } - - private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onUserSwitchComplete(int userId) { - update(false /* updateAlways */); - } - - @Override - public void onTrustChanged(int userId) { - update(false /* updateAlways */); - } - - @Override - public void onTrustManagedChanged(int userId) { - update(false /* updateAlways */); - } - - @Override - public void onStartedWakingUp() { - update(false /* updateAlways */); - } - - @Override - public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { - Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated"); - if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) { - Trace.endSection(); - return; - } - update(false /* updateAlways */); - Trace.endSection(); - } - - @Override - public void onFaceUnlockStateChanged(boolean running, int userId) { - update(false /* updateAlways */); - } - - @Override - public void onStrongAuthStateChanged(int userId) { - update(false /* updateAlways */); - } - - @Override - public void onScreenTurnedOff() { - update(false /* updateAlways */); - } - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - update(false /* updateAlways */); - } - - @Override - public void onBiometricsCleared() { - update(false /* alwaysUpdate */); - } - }; - - public boolean isTrustManaged() { - return mTrustManaged; - } - - public static interface OnUnlockMethodChangedListener { - void onUnlockMethodStateChanged(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 111cdd2cc32a..738d076e13c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -45,9 +45,7 @@ public interface BatteryController extends DemoMode, Dumpable, /** * Returns {@code true} if AOD was disabled by power saving policies. */ - default boolean isAodPowerSave() { - return isPowerSave(); - } + boolean isAodPowerSave(); /** * A listener that will be notified whenever a change in battery level or power save mode has diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 0a2fb2e783a9..76683b6a5e6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; -import static com.android.systemui.Dependency.MAIN_LOOPER_NAME; - import android.annotation.Nullable; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; @@ -36,6 +33,8 @@ import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.MainLooper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -46,7 +45,6 @@ import java.util.List; import java.util.WeakHashMap; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -74,9 +72,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa /** */ @Inject - public BluetoothControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper, - @Named(MAIN_LOOPER_NAME) Looper mainLooper, - @Nullable LocalBluetoothManager localBluetoothManager) { + public BluetoothControllerImpl(Context context, @BgLooper Looper bgLooper, + @MainLooper Looper mainLooper, @Nullable LocalBluetoothManager localBluetoothManager) { mLocalBluetoothManager = localBluetoothManager; mBgHandler = new Handler(bgLooper); mHandler = new H(mainLooper); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index c2c3f81527e8..b331fc3bf0ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; + import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -44,6 +46,7 @@ import com.android.systemui.Dependency; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.settings.CurrentUserTracker; @@ -60,6 +63,9 @@ import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; +import javax.inject.Inject; +import javax.inject.Named; + /** * Digital clock for the status bar. */ @@ -107,15 +113,20 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C */ private int mNonAdaptedColor; - public Clock(Context context) { - this(context, null); - } + private final BroadcastDispatcher mBroadcastDispatcher; public Clock(Context context, AttributeSet attrs) { - this(context, attrs, 0); + this(context, attrs, null); + } + + @Inject + public Clock(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, + BroadcastDispatcher broadcastDispatcher) { + this(context, attrs, 0, broadcastDispatcher); } - public Clock(Context context, AttributeSet attrs, int defStyle) { + public Clock(Context context, AttributeSet attrs, int defStyle, + BroadcastDispatcher broadcastDispatcher) { super(context, attrs, defStyle); TypedArray a = context.getTheme().obtainStyledAttributes( attrs, @@ -134,6 +145,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C mCurrentUserId = newUserId; } }; + mBroadcastDispatcher = broadcastDispatcher; } @Override @@ -358,11 +370,11 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C } IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); - mContext.registerReceiver(mScreenReceiver, filter); + mBroadcastDispatcher.registerReceiver(mScreenReceiver, filter); } } else { if (mSecondsHandler != null) { - mContext.unregisterReceiver(mScreenReceiver); + mBroadcastDispatcher.unregisterReceiver(mScreenReceiver); mSecondsHandler.removeCallbacks(mSecondTick); mSecondsHandler = null; updateClock(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java index 089d5c924de8..0a40b3c4831c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java @@ -14,8 +14,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; @@ -26,12 +24,12 @@ import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.util.Log; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.settings.CurrentUserTracker; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -51,8 +49,7 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen /** */ @Inject - public DeviceProvisionedControllerImpl(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { + public DeviceProvisionedControllerImpl(Context context, @MainHandler Handler mainHandler) { super(context); mContext = context; mContentResolver = context.getContentResolver(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java index cade5dc46388..3ce62396fbb5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java @@ -57,7 +57,7 @@ public interface ExtensionController { ExtensionBuilder<T> withCallback(Consumer<T> callback); ExtensionBuilder<T> withUiMode(int mode, Supplier<T> def); ExtensionBuilder<T> withFeature(String feature, Supplier<T> def); - Extension build(); + Extension<T> build(); } public interface PluginConverter<T, P> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java index fd030d133be0..eeef726ace75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java @@ -135,7 +135,7 @@ public class ExtensionControllerImpl implements ExtensionController { } @Override - public ExtensionController.Extension build() { + public ExtensionController.Extension<T> build() { // Sort items in ascending order Collections.sort(mExtension.mProducers, Comparator.comparingInt(Item::sortOrder)); mExtension.notifyChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index b84dc476dd6f..6828c322dea2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -117,7 +117,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) { - return entry.notification.getNotification().fullScreenIntent != null; + return entry.getSbn().getNotification().fullScreenIntent != null; } protected void setEntryPinned( @@ -206,7 +206,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { public void snooze() { for (String key : mAlertEntries.keySet()) { AlertEntry entry = getHeadsUpEntry(key); - String packageName = entry.mEntry.notification.getPackageName(); + String packageName = entry.mEntry.getSbn().getPackageName(); mSnoozedPackages.put(snoozeKey(packageName, mUser), mClock.currentTimeMillis() + mSnoozeLengthMs); } @@ -328,8 +328,8 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * one should be ranked higher and 0 if they are equal. */ public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) { - AlertEntry aEntry = getHeadsUpEntry(a.key); - AlertEntry bEntry = getHeadsUpEntry(b.key); + AlertEntry aEntry = getHeadsUpEntry(a.getKey()); + AlertEntry bEntry = getHeadsUpEntry(b.getKey()); if (aEntry == null || bEntry == null) { return aEntry == null ? 1 : -1; } @@ -341,7 +341,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * until it's collapsed again. */ public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) { - HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key); + HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey()); if (headsUpEntry != null && entry.isRowPinned()) { headsUpEntry.setExpanded(expanded); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index 4df0c6051f42..e8bc7db6d5c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.ActivityManager; import android.content.Context; import android.net.ConnectivityManager; @@ -26,12 +24,13 @@ import android.os.Handler; import android.os.UserManager; import android.util.Log; +import com.android.systemui.dagger.qualifiers.MainHandler; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -56,7 +55,7 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof /** */ @Inject - public HotspotControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler mainHandler) { + public HotspotControllerImpl(Context context, @MainHandler Handler mainHandler) { mContext = context; mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java index be27741b4dc8..c6ae669d5d08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java @@ -144,13 +144,13 @@ public class InflatedSmartReplies { return false; } // If we are showing the spinner we don't want to add the buttons. - boolean showingSpinner = entry.notification.getNotification() + boolean showingSpinner = entry.getSbn().getNotification() .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); if (showingSpinner) { return false; } // If we are keeping the notification around while sending we don't want to add the buttons. - boolean hideSmartReplies = entry.notification.getNotification() + boolean hideSmartReplies = entry.getSbn().getNotification() .extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false); if (hideSmartReplies) { return false; @@ -168,7 +168,7 @@ public class InflatedSmartReplies { public static SmartRepliesAndActions chooseSmartRepliesAndActions( SmartReplyConstants smartReplyConstants, final NotificationEntry entry) { - Notification notification = entry.notification.getNotification(); + Notification notification = entry.getSbn().getNotification(); Pair<RemoteInput, Notification.Action> remoteInputActionPair = notification.findRemoteInputActionPair(false /* freeform */); Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair = @@ -177,7 +177,7 @@ public class InflatedSmartReplies { if (!smartReplyConstants.isEnabled()) { if (DEBUG) { Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for " - + entry.notification.getKey()); + + entry.getSbn().getKey()); } return new SmartRepliesAndActions(null, null); } @@ -271,7 +271,7 @@ public class InflatedSmartReplies { * through the remote input. */ public static boolean hasFreeformRemoteInput(NotificationEntry entry) { - Notification notification = entry.notification.getNotification(); + Notification notification = entry.getSbn().getNotification(); return null != notification.findRemoteInputActionPair(true /* freeform */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java deleted file mode 100644 index 6dc90b9028fa..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.android.systemui.statusbar.policy; - -import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback; - -public interface KeyguardMonitor extends CallbackController<Callback> { - - boolean isSecure(); - boolean isShowing(); - boolean isOccluded(); - boolean isKeyguardFadingAway(); - boolean isKeyguardGoingAway(); - boolean isLaunchTransitionFadingAway(); - long getKeyguardFadingAwayDuration(); - long getKeyguardFadingAwayDelay(); - long calculateGoingToFullShadeDelay(); - - /** - * @return a shortened fading away duration similar to - * {{@link #getKeyguardFadingAwayDuration()}} which may only span half of the duration, unless - * we're bypassing - */ - default long getShortenedFadingAwayDuration() { - if (isBypassFadingAnimation()) { - return getKeyguardFadingAwayDuration(); - } else { - return getKeyguardFadingAwayDuration() / 2; - } - } - - default boolean isDeviceInteractive() { - return false; - } - - default void setLaunchTransitionFadingAway(boolean b) { - } - - default void notifyKeyguardGoingAway(boolean b) { - } - - /** - * @return {@code true} if the current fading away animation is the fast bypass fading. - */ - default boolean isBypassFadingAnimation() { - return false; - } - - /** - * Notifies that the Keyguard is fading away with the specified timings. - * @param delay the precalculated animation delay in milliseconds - * @param fadeoutDuration the duration of the exit animation, in milliseconds - * @param isBypassFading is this a fading away animation while bypassing - */ - default void notifyKeyguardFadingAway(long delay, long fadeoutDuration, - boolean isBypassFading) { - } - - default void notifyKeyguardDoneFading() { - } - - default void notifyKeyguardState(boolean showing, boolean methodSecure, boolean occluded) { - } - - interface Callback { - default void onKeyguardShowingChanged() {} - default void onKeyguardFadingAwayChanged() {} - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java deleted file mode 100644 index e8b0f9b2864e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.policy; - -import android.annotation.NonNull; -import android.content.Context; - -import com.android.internal.util.Preconditions; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.Dependency; - -import java.util.ArrayList; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - */ -@Singleton -public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback - implements KeyguardMonitor { - - private final ArrayList<Callback> mCallbacks = new ArrayList<>(); - - private final Context mContext; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - - private boolean mShowing; - private boolean mSecure; - private boolean mOccluded; - - private boolean mListening; - private boolean mKeyguardFadingAway; - private long mKeyguardFadingAwayDelay; - private long mKeyguardFadingAwayDuration; - private boolean mKeyguardGoingAway; - private boolean mLaunchTransitionFadingAway; - private boolean mBypassFadingAnimation; - - /** - */ - @Inject - public KeyguardMonitorImpl(Context context) { - mContext = context; - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - } - - @Override - public void addCallback(@NonNull Callback callback) { - Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449"); - mCallbacks.add(callback); - if (mCallbacks.size() != 0 && !mListening) { - mListening = true; - mKeyguardUpdateMonitor.registerCallback(this); - } - } - - @Override - public void removeCallback(@NonNull Callback callback) { - Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449"); - if (mCallbacks.remove(callback) && mCallbacks.size() == 0 && mListening) { - mListening = false; - mKeyguardUpdateMonitor.removeCallback(this); - } - } - - @Override - public boolean isShowing() { - return mShowing; - } - - @Override - public boolean isSecure() { - return mSecure; - } - - @Override - public boolean isOccluded() { - return mOccluded; - } - - public void notifyKeyguardState(boolean showing, boolean secure, boolean occluded) { - if (mShowing == showing && mSecure == secure && mOccluded == occluded) return; - mShowing = showing; - mSecure = secure; - mOccluded = occluded; - notifyKeyguardChanged(); - } - - @Override - public void onTrustChanged(int userId) { - notifyKeyguardChanged(); - } - - public boolean isDeviceInteractive() { - return mKeyguardUpdateMonitor.isDeviceInteractive(); - } - - private void notifyKeyguardChanged() { - // Copy the list to allow removal during callback. - new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged); - } - - public void notifyKeyguardFadingAway(long delay, long fadeoutDuration, boolean isBypassFading) { - mKeyguardFadingAwayDelay = delay; - mKeyguardFadingAwayDuration = fadeoutDuration; - mBypassFadingAnimation = isBypassFading; - setKeyguardFadingAway(true); - } - - private void setKeyguardFadingAway(boolean keyguardFadingAway) { - if (mKeyguardFadingAway != keyguardFadingAway) { - mKeyguardFadingAway = keyguardFadingAway; - ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks); - for (int i = 0; i < callbacks.size(); i++) { - callbacks.get(i).onKeyguardFadingAwayChanged(); - } - } - } - - public void notifyKeyguardDoneFading() { - mKeyguardGoingAway = false; - setKeyguardFadingAway(false); - } - - @Override - public boolean isKeyguardFadingAway() { - return mKeyguardFadingAway; - } - - @Override - public boolean isKeyguardGoingAway() { - return mKeyguardGoingAway; - } - - @Override - public boolean isBypassFadingAnimation() { - return mBypassFadingAnimation; - } - - @Override - public long getKeyguardFadingAwayDelay() { - return mKeyguardFadingAwayDelay; - } - - @Override - public long getKeyguardFadingAwayDuration() { - return mKeyguardFadingAwayDuration; - } - - @Override - public long calculateGoingToFullShadeDelay() { - return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration; - } - - public void notifyKeyguardGoingAway(boolean keyguardGoingAway) { - mKeyguardGoingAway = keyguardGoingAway; - } - - public void setLaunchTransitionFadingAway(boolean fadingAway) { - mLaunchTransitionFadingAway = fadingAway; - } - - @Override - public boolean isLaunchTransitionFadingAway() { - return mLaunchTransitionFadingAway; - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java new file mode 100644 index 000000000000..692c34c62dd8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.policy.KeyguardStateController.Callback; + +/** + * Source of truth for keyguard state: If locked, occluded, has password, trusted etc. + */ +public interface KeyguardStateController extends CallbackController<Callback> { + + /** + * If the device is locked or unlocked. + */ + default boolean isUnlocked() { + return !isShowing() || canDismissLockScreen(); + } + + /** + * If the lock screen is visible. + * The keyguard is also visible when the device is asleep or in always on mode, except when + * the screen timed out and the user can unlock by quickly pressing power. + * + * This is unrelated to being locked or not. + * + * @see #isUnlocked() + * @see #canDismissLockScreen() + */ + boolean isShowing(); + + /** + * If swiping up will unlock without asking for a password. + * @see #isUnlocked() + */ + boolean canDismissLockScreen(); + + /** + * If the device has PIN/pattern/password or a lock screen at all. + */ + boolean isMethodSecure(); + + /** + * When there's an {@link android.app.Activity} on top of the keyguard, where + * {@link android.app.Activity#setShowWhenLocked(boolean)} is true. + */ + boolean isOccluded(); + + /** + * If a {@link android.service.trust.TrustAgentService} is keeping the device unlocked. + * {@link #canDismissLockScreen()} is better source of truth that also considers this state. + */ + boolean isTrusted(); + + /** + * If the keyguard dismissal animation is running. + * @see #isKeyguardGoingAway() + */ + boolean isKeyguardFadingAway(); + + /** + * When the keyguard challenge was successfully solved, and {@link android.app.ActivityManager} + * is launching the activity that will be revealed. + * + * This also includes the animation of the keyguard being dismissed, meaning that this will + * return {@code true} whenever {@link #isKeyguardFadingAway()} also returns {@code true}. + */ + boolean isKeyguardGoingAway(); + + /** + * @return a shortened fading away duration similar to + * {{@link #getKeyguardFadingAwayDuration()}} which may only span half of the duration, unless + * we're bypassing + */ + default long getShortenedFadingAwayDuration() { + if (isBypassFadingAnimation()) { + return getKeyguardFadingAwayDuration(); + } else { + return getKeyguardFadingAwayDuration() / 2; + } + } + + /** + * @return {@code true} if the current fading away animation is the fast bypass fading. + */ + default boolean isBypassFadingAnimation() { + return false; + } + + /** + * Notifies that the Keyguard is fading away with the specified timings. + * @param delay the precalculated animation delay in milliseconds + * @param fadeoutDuration the duration of the exit animation, in milliseconds + * @param isBypassFading is this a fading away animation while bypassing + */ + default void notifyKeyguardFadingAway(long delay, long fadeoutDuration, + boolean isBypassFading) { + } + + /** + * If there are faces enrolled and user enabled face auth on keyguard. + */ + default boolean isFaceAuthEnabled() { + return false; + } + + /** + * If the animation that morphs a notification into an app window is playing. + */ + boolean isLaunchTransitionFadingAway(); + + /** + * How long the keyguard dismissal animation should take when unlocking. + */ + long getKeyguardFadingAwayDuration(); + + /** + * Delay for {@link #getKeyguardFadingAwayDuration()}. + */ + long getKeyguardFadingAwayDelay(); + + /** + * Delay when going from {@link StatusBarState#KEYGUARD} to {@link StatusBarState#SHADE} or + * {@link StatusBarState#SHADE_LOCKED}. + */ + long calculateGoingToFullShadeDelay(); + + /** **/ + default void setLaunchTransitionFadingAway(boolean b) {} + /** **/ + default void notifyKeyguardGoingAway(boolean b) {} + /** **/ + default void notifyKeyguardDoneFading() {} + /** **/ + default void notifyKeyguardState(boolean showing, boolean occluded) {} + + /** + * Callback for authentication events. + */ + interface Callback { + /** + * Called when the locked state of the device changes. The lock screen might still be + * showing on some cases, like when a {@link android.service.trust.TrustAgentService} is + * active, or face auth was triggered but the user didn't swipe up to dismiss the lock + * screen yet. + */ + default void onUnlockedChanged() {} + + /** + * If the lock screen is active or not. This is different from being locked, since the lock + * screen can be visible but unlocked by {@link android.service.trust.TrustAgentService} or + * face unlock. + * + * @see #isShowing() + */ + default void onKeyguardShowingChanged() {} + + /** + * Triggered when the device was just unlocked and the lock screen is being dismissed. + */ + default void onKeyguardFadingAwayChanged() {} + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java new file mode 100644 index 000000000000..1cb2bd430199 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.annotation.NonNull; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.biometrics.BiometricSourceType; +import android.os.Build; +import android.os.Trace; + +import androidx.annotation.VisibleForTesting; + +import com.android.internal.util.Preconditions; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.Dumpable; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + */ +@Singleton +public class KeyguardStateControllerImpl implements KeyguardStateController, Dumpable { + + private static final boolean DEBUG_AUTH_WITH_ADB = false; + private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth"; + + private final ArrayList<Callback> mCallbacks = new ArrayList<>(); + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final LockPatternUtils mLockPatternUtils; + private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = + new UpdateMonitorCallback(); + + private boolean mCanDismissLockScreen; + private boolean mShowing; + private boolean mSecure; + private boolean mOccluded; + + private boolean mKeyguardFadingAway; + private long mKeyguardFadingAwayDelay; + private long mKeyguardFadingAwayDuration; + private boolean mKeyguardGoingAway; + private boolean mLaunchTransitionFadingAway; + private boolean mBypassFadingAnimation; + private boolean mTrustManaged; + private boolean mTrusted; + private boolean mDebugUnlocked = false; + private boolean mFaceAuthEnabled; + + /** + */ + @Inject + public KeyguardStateControllerImpl(Context context, + KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) { + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mLockPatternUtils = lockPatternUtils; + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); + + update(true /* updateAlways */); + if (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB) { + // Watch for interesting updates + final IntentFilter filter = new IntentFilter(); + filter.addAction(AUTH_BROADCAST_KEY); + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG_AUTH_WITH_ADB && AUTH_BROADCAST_KEY.equals(intent.getAction())) { + mDebugUnlocked = !mDebugUnlocked; + update(true /* updateAlways */); + } + } + }, filter, null, null); + } + } + + @Override + public void addCallback(@NonNull Callback callback) { + Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449"); + if (!mCallbacks.contains(callback)) { + mCallbacks.add(callback); + } + } + + @Override + public void removeCallback(@NonNull Callback callback) { + Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449"); + mCallbacks.remove(callback); + } + + @Override + public boolean isShowing() { + return mShowing; + } + + @Override + public boolean isMethodSecure() { + return mSecure; + } + + @Override + public boolean isOccluded() { + return mOccluded; + } + + @Override + public boolean isTrusted() { + return mTrusted; + } + + @Override + public void notifyKeyguardState(boolean showing, boolean occluded) { + if (mShowing == showing && mOccluded == occluded) return; + mShowing = showing; + mOccluded = occluded; + notifyKeyguardChanged(); + } + + private void notifyKeyguardChanged() { + Trace.beginSection("KeyguardStateController#notifyKeyguardChanged"); + // Copy the list to allow removal during callback. + new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged); + Trace.endSection(); + } + + private void notifyUnlockedChanged() { + Trace.beginSection("KeyguardStateController#notifyUnlockedChanged"); + // Copy the list to allow removal during callback. + new ArrayList<>(mCallbacks).forEach(Callback::onUnlockedChanged); + Trace.endSection(); + } + + @Override + public void notifyKeyguardFadingAway(long delay, long fadeoutDuration, boolean isBypassFading) { + mKeyguardFadingAwayDelay = delay; + mKeyguardFadingAwayDuration = fadeoutDuration; + mBypassFadingAnimation = isBypassFading; + setKeyguardFadingAway(true); + } + + private void setKeyguardFadingAway(boolean keyguardFadingAway) { + if (mKeyguardFadingAway != keyguardFadingAway) { + mKeyguardFadingAway = keyguardFadingAway; + ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks); + for (int i = 0; i < callbacks.size(); i++) { + callbacks.get(i).onKeyguardFadingAwayChanged(); + } + } + } + + @Override + public void notifyKeyguardDoneFading() { + mKeyguardGoingAway = false; + setKeyguardFadingAway(false); + } + + @VisibleForTesting + void update(boolean updateAlways) { + Trace.beginSection("KeyguardStateController#update"); + int user = KeyguardUpdateMonitor.getCurrentUser(); + boolean secure = mLockPatternUtils.isSecure(user); + boolean canDismissLockScreen = !secure || mKeyguardUpdateMonitor.getUserCanSkipBouncer(user) + || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked); + boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user); + boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user); + boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user); + boolean changed = secure != mSecure || canDismissLockScreen != mCanDismissLockScreen + || trustManaged != mTrustManaged || mTrusted != trusted + || mFaceAuthEnabled != faceAuthEnabled; + if (changed || updateAlways) { + mSecure = secure; + mCanDismissLockScreen = canDismissLockScreen; + mTrusted = trusted; + mTrustManaged = trustManaged; + mFaceAuthEnabled = faceAuthEnabled; + notifyUnlockedChanged(); + } + Trace.endSection(); + } + + @Override + public boolean canDismissLockScreen() { + return mCanDismissLockScreen; + } + + @Override + public boolean isFaceAuthEnabled() { + return mFaceAuthEnabled; + } + + @Override + public boolean isKeyguardFadingAway() { + return mKeyguardFadingAway; + } + + @Override + public boolean isKeyguardGoingAway() { + return mKeyguardGoingAway; + } + + @Override + public boolean isBypassFadingAnimation() { + return mBypassFadingAnimation; + } + + @Override + public long getKeyguardFadingAwayDelay() { + return mKeyguardFadingAwayDelay; + } + + @Override + public long getKeyguardFadingAwayDuration() { + return mKeyguardFadingAwayDuration; + } + + @Override + public long calculateGoingToFullShadeDelay() { + return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration; + } + + @Override + public void notifyKeyguardGoingAway(boolean keyguardGoingAway) { + mKeyguardGoingAway = keyguardGoingAway; + } + + @Override + public void setLaunchTransitionFadingAway(boolean fadingAway) { + mLaunchTransitionFadingAway = fadingAway; + } + + @Override + public boolean isLaunchTransitionFadingAway() { + return mLaunchTransitionFadingAway; + } + + /** + * Dumps internal state for debugging. + * @param pw Where to dump. + */ + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("KeyguardStateController:"); + pw.println(" mSecure: " + mSecure); + pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen); + pw.println(" mTrustManaged: " + mTrustManaged); + pw.println(" mTrusted: " + mTrusted); + pw.println(" mDebugUnlocked: " + mDebugUnlocked); + pw.println(" mFaceAuthEnabled: " + mFaceAuthEnabled); + } + + private class UpdateMonitorCallback extends KeyguardUpdateMonitorCallback { + @Override + public void onUserSwitchComplete(int userId) { + update(false /* updateAlways */); + } + + @Override + public void onTrustChanged(int userId) { + update(false /* updateAlways */); + notifyKeyguardChanged(); + } + + @Override + public void onTrustManagedChanged(int userId) { + update(false /* updateAlways */); + } + + @Override + public void onStartedWakingUp() { + update(false /* updateAlways */); + } + + @Override + public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { + Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated"); + if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) { + Trace.endSection(); + return; + } + update(false /* updateAlways */); + Trace.endSection(); + } + + @Override + public void onFaceUnlockStateChanged(boolean running, int userId) { + update(false /* updateAlways */); + } + + @Override + public void onStrongAuthStateChanged(int userId) { + update(false /* updateAlways */); + } + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + update(false /* updateAlways */); + } + + @Override + public void onBiometricsCleared() { + update(false /* alwaysUpdate */); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index 683cdbb326dc..5a97eedd9b2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.policy; import static com.android.settingslib.Utils.updateLocationEnabled; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.app.ActivityManager; import android.app.AppOpsManager; @@ -36,13 +35,13 @@ import android.provider.Settings; import androidx.annotation.VisibleForTesting; +import com.android.systemui.dagger.qualifiers.BgLooper; import com.android.systemui.util.Utils; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -66,7 +65,7 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio private final H mHandler = new H(); @Inject - public LocationControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) { + public LocationControllerImpl(Context context, @BgLooper Looper bgLooper) { mContext = context; // Register to listen for changes in location settings. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 67739835a3fd..e36d28383bda 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -24,7 +24,6 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OU import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.content.BroadcastReceiver; import android.content.Context; @@ -65,6 +64,7 @@ import com.android.systemui.ConfigurationChangedReceiver; import com.android.systemui.DemoMode; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.BgLooper; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup; @@ -81,7 +81,6 @@ import java.util.Locale; import java.util.Map; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** Platform implementation of the network controller. **/ @@ -172,7 +171,7 @@ public class NetworkControllerImpl extends BroadcastReceiver * Construct this controller object and register for updates. */ @Inject - public NetworkControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper, + public NetworkControllerImpl(Context context, @BgLooper Looper bgLooper, DeviceProvisionedController deviceProvisionedController) { this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 43795dc08c91..502a9bd34b12 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -23,12 +23,16 @@ import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; +import android.content.ClipDescription; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ShortcutManager; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.text.Editable; @@ -53,8 +57,13 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.core.view.inputmethod.EditorInfoCompat; +import androidx.core.view.inputmethod.InputConnectionCompat; +import androidx.core.view.inputmethod.InputContentInfoCompat; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -65,6 +74,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; +import java.util.HashMap; import java.util.function.Consumer; /** @@ -88,6 +98,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInputController mController; private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; + private IStatusBarService mStatusBarManagerService; + private NotificationEntry mEntry; private boolean mRemoved; @@ -103,6 +115,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene public RemoteInputView(Context context, AttributeSet attrs) { super(context, attrs); mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class); + mStatusBarManagerService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } @Override @@ -128,7 +142,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (isSoftImeEvent || isKeyboardEnterKey) { if (mEditText.length() > 0) { - sendRemoteInput(); + sendRemoteInput(prepareRemoteInputFromText()); } // Consume action to prevent IME from closing. return true; @@ -141,7 +155,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.mRemoteInputView = this; } - private void sendRemoteInput() { + protected Intent prepareRemoteInputFromText() { Bundle results = new Bundle(); results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString()); Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); @@ -153,12 +167,31 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE); } + return fillInIntent; + } + + protected Intent prepareRemoteInputFromData(String contentType, Uri data) { + HashMap<String, Uri> results = new HashMap<>(); + results.put(contentType, data); + try { + mStatusBarManagerService.grantInlineReplyUriPermission( + mEntry.getSbn().getKey(), data); + } catch (Exception e) { + Log.e(TAG, "Failed to grant URI permissions:" + e.getMessage(), e); + } + Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results); + + return fillInIntent; + } + + private void sendRemoteInput(Intent intent) { mEditText.setEnabled(false); mSendButton.setVisibility(INVISIBLE); mProgressBar.setVisibility(VISIBLE); mEntry.remoteInputText = mEditText.getText(); mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime(); - mController.addSpinning(mEntry.key, mToken); + mController.addSpinning(mEntry.getKey(), mToken); mController.removeRemoteInput(mEntry, mToken); mEditText.mShowImeOnInputConnection = false; mController.remoteInputSent(mEntry); @@ -170,17 +203,17 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene // but that's an edge case, and also because we can't always know which package will receive // an intent, so we just reset for the publisher. getContext().getSystemService(ShortcutManager.class).onApplicationActive( - mEntry.notification.getPackageName(), - mEntry.notification.getUser().getIdentifier()); + mEntry.getSbn().getPackageName(), + mEntry.getSbn().getUser().getIdentifier()); MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND, - mEntry.notification.getPackageName()); + mEntry.getSbn().getPackageName()); try { - mPendingIntent.send(mContext, 0, fillInIntent); + mPendingIntent.send(mContext, 0, intent); } catch (PendingIntent.CanceledException e) { Log.i(TAG, "Unable to send remote input result", e); MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL, - mEntry.notification.getPackageName()); + mEntry.getSbn().getPackageName()); } } @@ -195,7 +228,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene LayoutInflater.from(context).inflate(R.layout.remote_input, root, false); v.mController = controller; v.mEntry = entry; - v.mEditText.setTextOperationUser(computeTextOperationUser(entry.notification.getUser())); + UserHandle user = computeTextOperationUser(entry.getSbn().getUser()); + v.mEditText.mUser = user; + v.mEditText.setTextOperationUser(user); v.setTag(VIEW_TAG); return v; @@ -204,7 +239,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public void onClick(View v) { if (v == mSendButton) { - sendRemoteInput(); + sendRemoteInput(prepareRemoteInputFromText()); } } @@ -249,7 +284,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false); MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE, - mEntry.notification.getPackageName()); + mEntry.getSbn().getPackageName()); } @Override @@ -269,7 +304,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene return; } mController.removeRemoteInput(mEntry, mToken); - mController.removeSpinning(mEntry.key, mToken); + mController.removeSpinning(mEntry.getKey(), mToken); } public void setPendingIntent(PendingIntent pendingIntent) { @@ -314,7 +349,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene public void focus() { MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN, - mEntry.notification.getPackageName()); + mEntry.getSbn().getPackageName()); setVisibility(VISIBLE); if (mWrapper != null) { @@ -353,7 +388,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.setEnabled(true); mSendButton.setVisibility(VISIBLE); mProgressBar.setVisibility(INVISIBLE); - mController.removeSpinning(mEntry.key, mToken); + mController.removeSpinning(mEntry.getKey(), mToken); updateSendButton(); onDefocus(false /* animate */); @@ -505,7 +540,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } public boolean isSending() { - return getVisibility() == VISIBLE && mController.isSpinning(mEntry.key, mToken); + return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken); } /** @@ -518,6 +553,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInputView mRemoteInputView; boolean mShowImeOnInputConnection; private LightBarController mLightBarController; + UserHandle mUser; public RemoteEditText(Context context, AttributeSet attrs) { super(context, attrs); @@ -617,11 +653,47 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + String[] allowedDataTypes = mRemoteInputView.mRemoteInput.getAllowedDataTypes() + .toArray(new String[0]); + EditorInfoCompat.setContentMimeTypes(outAttrs, allowedDataTypes); final InputConnection inputConnection = super.onCreateInputConnection(outAttrs); - if (mShowImeOnInputConnection && inputConnection != null) { + final InputConnectionCompat.OnCommitContentListener callback = + new InputConnectionCompat.OnCommitContentListener() { + @Override + public boolean onCommitContent( + InputContentInfoCompat inputContentInfoCompat, int i, + Bundle bundle) { + Uri contentUri = inputContentInfoCompat.getContentUri(); + ClipDescription description = inputContentInfoCompat.getDescription(); + String mimeType = null; + if (description != null && description.getMimeTypeCount() > 0) { + mimeType = description.getMimeType(0); + } + if (mimeType != null) { + Intent dataIntent = mRemoteInputView.prepareRemoteInputFromData( + mimeType, contentUri); + mRemoteInputView.sendRemoteInput(dataIntent); + } + return true; + } + }; + + InputConnection ic = inputConnection == null ? null : + InputConnectionCompat.createWrapper(inputConnection, outAttrs, callback); + + Context userContext = null; + try { + userContext = mContext.createPackageContextAsUser( + mContext.getPackageName(), 0, mUser); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Unable to create user context:" + e.getMessage(), e); + } + + if (mShowImeOnInputConnection && ic != null) { + Context targetContext = userContext != null ? userContext : getContext(); final InputMethodManager imm = - getContext().getSystemService(InputMethodManager.class); + targetContext.getSystemService(InputMethodManager.class); if (imm != null) { // onCreateInputConnection is called by InputMethodManager in the middle of // setting up the connection to the IME; wait with requesting the IME until that @@ -636,7 +708,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } - return inputConnection; + return ic; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index d88ae78c5afb..6edd75b20fed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -15,8 +15,6 @@ */ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.BG_HANDLER_NAME; - import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; @@ -50,6 +48,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.BgHandler; import com.android.systemui.settings.CurrentUserTracker; import java.io.FileDescriptor; @@ -57,7 +56,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -102,7 +100,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi /** */ @Inject - public SecurityControllerImpl(Context context, @Named(BG_HANDLER_NAME) Handler bgHandler) { + public SecurityControllerImpl(Context context, @BgHandler Handler bgHandler) { this(context, bgHandler, null); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java index 655c29cbf687..347d3009c3ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.RemoteInput; import android.content.Context; import android.content.res.Resources; @@ -30,9 +28,9 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.MainHandler; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; @Singleton @@ -67,7 +65,7 @@ public final class SmartReplyConstants { private final KeyValueListParser mParser = new KeyValueListParser(','); @Inject - public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) { + public SmartReplyConstants(@MainHandler Handler handler, Context context) { mHandler = handler; mContext = context; final Resources resources = mContext.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index b5f660a84043..65bb28ffbe39 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -350,7 +350,7 @@ public class SmartReplyView extends ViewGroup { () -> { smartReplyController.smartActionClicked( entry, actionIndex, action, smartActions.fromAssistant); - headsUpManager.removeNotification(entry.key, true); + headsUpManager.removeNotification(entry.getKey(), true); }, entry.getRow()); if (useDelayedOnClickListener) { onClickListener = new DelayedOnClickListener(onClickListener, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 4fa4b6c456e2..f2d2faed6a42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.policy; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import static com.android.systemui.DejankUtils.whitelistIpcs; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import android.app.ActivityManager; import android.app.Dialog; @@ -58,11 +57,11 @@ import com.android.systemui.Prefs; import com.android.systemui.Prefs.Key; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.qs.tiles.UserDetailView; import com.android.systemui.statusbar.phone.SystemUIDialog; -import com.android.systemui.statusbar.phone.UnlockMethodCache; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -71,7 +70,6 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -93,7 +91,7 @@ public class UserSwitcherController implements Dumpable { private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>(); private final GuestResumeSessionReceiver mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(); - private final KeyguardMonitor mKeyguardMonitor; + private final KeyguardStateController mKeyguardStateController; protected final Handler mHandler; private final ActivityStarter mActivityStarter; @@ -110,13 +108,13 @@ public class UserSwitcherController implements Dumpable { private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2); @Inject - public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor, - @Named(MAIN_HANDLER_NAME) Handler handler, ActivityStarter activityStarter) { + public UserSwitcherController(Context context, KeyguardStateController keyguardStateController, + @MainHandler Handler handler, ActivityStarter activityStarter) { mContext = context; if (!UserManager.isGuestUserEphemeral()) { mGuestResumeSessionReceiver.register(context); } - mKeyguardMonitor = keyguardMonitor; + mKeyguardStateController = keyguardStateController; mHandler = handler; mActivityStarter = activityStarter; mUserManager = UserManager.get(context); @@ -149,7 +147,7 @@ public class UserSwitcherController implements Dumpable { // Fetch initial values. mSettingsObserver.onChange(false); - keyguardMonitor.addCallback(mCallback); + keyguardStateController.addCallback(mCallback); listenForCallState(); refreshUsers(UserHandle.USER_NULL); @@ -315,7 +313,6 @@ public class UserSwitcherController implements Dumpable { // adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on. // Restart SystemUI or adb reboot. final int DEFAULT = -1; - // TODO(b/140061064) final int overrideUseFullscreenUserSwitcher = whitelistIpcs(() -> Settings.System.getInt(mContext.getContentResolver(), "enable_fullscreen_user_switcher", DEFAULT)); @@ -597,20 +594,18 @@ public class UserSwitcherController implements Dumpable { public static abstract class BaseUserAdapter extends BaseAdapter { final UserSwitcherController mController; - private final KeyguardMonitor mKeyguardMonitor; - private final UnlockMethodCache mUnlockMethodCache; + private final KeyguardStateController mKeyguardStateController; protected BaseUserAdapter(UserSwitcherController controller) { mController = controller; - mKeyguardMonitor = controller.mKeyguardMonitor; - mUnlockMethodCache = UnlockMethodCache.getInstance(controller.mContext); + mKeyguardStateController = controller.mKeyguardStateController; controller.addAdapter(new WeakReference<>(this)); } public int getUserCount() { - boolean secureKeyguardShowing = mKeyguardMonitor.isShowing() - && mKeyguardMonitor.isSecure() - && !mUnlockMethodCache.canSkipBouncer(); + boolean secureKeyguardShowing = mKeyguardStateController.isShowing() + && mKeyguardStateController.isMethodSecure() + && !mKeyguardStateController.canDismissLockScreen(); if (!secureKeyguardShowing) { return mController.getUsers().size(); } @@ -630,9 +625,9 @@ public class UserSwitcherController implements Dumpable { @Override public int getCount() { - boolean secureKeyguardShowing = mKeyguardMonitor.isShowing() - && mKeyguardMonitor.isSecure() - && !mUnlockMethodCache.canSkipBouncer(); + boolean secureKeyguardShowing = mKeyguardStateController.isShowing() + && mKeyguardStateController.isMethodSecure() + && !mKeyguardStateController.canDismissLockScreen(); if (!secureKeyguardShowing) { return mController.getUsers().size(); } @@ -819,19 +814,21 @@ public class UserSwitcherController implements Dumpable { } }; - private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() { - @Override - public void onKeyguardShowingChanged() { + private final KeyguardStateController.Callback mCallback = + new KeyguardStateController.Callback() { + @Override + public void onKeyguardShowingChanged() { - // When Keyguard is going away, we don't need to update our items immediately which - // helps making the transition faster. - if (!mKeyguardMonitor.isShowing()) { - mHandler.post(UserSwitcherController.this::notifyAdapters); - } else { - notifyAdapters(); - } - } - }; + // When Keyguard is going away, we don't need to update our items immediately + // which + // helps making the transition faster. + if (!mKeyguardStateController.isShowing()) { + mHandler.post(UserSwitcherController.this::notifyAdapters); + } else { + notifyAdapters(); + } + } + }; private final class ExitGuestDialog extends SystemUIDialog implements DialogInterface.OnClickListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index 29c42d24b3b4..1c7a1951b27d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.ActivityManager; import android.app.AlarmManager; import android.app.NotificationManager; @@ -41,6 +39,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.qs.GlobalSetting; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.util.Utils; @@ -51,7 +50,6 @@ import java.util.ArrayList; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** Platform implementation of the zen mode controller. **/ @@ -79,7 +77,7 @@ public class ZenModeControllerImpl extends CurrentUserTracker private NotificationManager.Policy mConsolidatedNotificationPolicy; @Inject - public ZenModeControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) { + public ZenModeControllerImpl(Context context, @MainHandler Handler handler) { super(context); mContext = context; mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java new file mode 100644 index 000000000000..d6d0a3603c25 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.PixelFormat; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.systemui.R; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +class AudioRecordingDisclosureBar { + private static final String TAG = "AudioRecordingDisclosureBar"; + private static final boolean DEBUG = false; + + private static final String LAYOUT_PARAMS_TITLE = "AudioRecordingDisclosureBar"; + private static final int ANIM_DURATION_MS = 150; + + private final Context mContext; + private final List<String> mAudioRecordingApps = new ArrayList<>(); + private View mView; + private ViewGroup mAppsInfoContainer; + + AudioRecordingDisclosureBar(Context context) { + mContext = context; + } + + void start() { + // Inflate and add audio recording disclosure bar + createView(); + + // Register AppOpsManager callback + final AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService( + Context.APP_OPS_SERVICE); + appOpsManager.startWatchingActive( + new String[]{AppOpsManager.OPSTR_RECORD_AUDIO}, mContext.getMainExecutor(), + new OnActiveRecordingListener()); + } + + private void createView() { + mView = View.inflate(mContext, + R.layout.tv_status_bar_audio_recording, null); + mAppsInfoContainer = mView.findViewById(R.id.container); + + final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( + MATCH_PARENT, + WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT); + layoutParams.gravity = Gravity.BOTTOM; + layoutParams.setTitle(LAYOUT_PARAMS_TITLE); + layoutParams.packageName = mContext.getPackageName(); + + final WindowManager windowManager = (WindowManager) mContext.getSystemService( + Context.WINDOW_SERVICE); + windowManager.addView(mView, layoutParams); + + // Set invisible first util it gains its actual size and we are able to hide it by moving + // off the screen + mView.setVisibility(View.INVISIBLE); + mView.getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + // Now that we get the height, we can move the bar off ("below") the screen + final int height = mView.getHeight(); + mView.setTranslationY(height); + // ... and make it visible + mView.setVisibility(View.VISIBLE); + // Remove the observer + mView.getViewTreeObserver() + .removeOnGlobalLayoutListener(this); + } + }); + } + + private void showAudioRecordingDisclosureBar() { + mView.animate() + .translationY(0f) + .setDuration(ANIM_DURATION_MS) + .start(); + } + + private void addToAudioRecordingDisclosureBar(String packageName) { + final PackageManager pm = mContext.getPackageManager(); + final ApplicationInfo appInfo; + try { + appInfo = pm.getApplicationInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + return; + } + final CharSequence label = pm.getApplicationLabel(appInfo); + final Drawable icon = pm.getApplicationIcon(appInfo); + + final View view = LayoutInflater.from(mContext).inflate(R.layout.tv_item_app_info, + mAppsInfoContainer, false); + ((TextView) view.findViewById(R.id.title)).setText(label); + ((ImageView) view.findViewById(R.id.icon)).setImageDrawable(icon); + + mAppsInfoContainer.addView(view); + } + + private void removeFromAudioRecordingDisclosureBar(int index) { + mAppsInfoContainer.removeViewAt(index); + } + + private void hideAudioRecordingDisclosureBar() { + mView.animate() + .translationY(mView.getHeight()) + .setDuration(ANIM_DURATION_MS) + .start(); + } + + private class OnActiveRecordingListener implements AppOpsManager.OnOpActiveChangedListener { + private final List<String> mExemptApps; + + private OnActiveRecordingListener() { + mExemptApps = Arrays.asList(mContext.getResources().getStringArray( + R.array.audio_recording_disclosure_exempt_apps)); + } + + @Override + public void onOpActiveChanged(String op, int uid, String packageName, boolean active) { + if (DEBUG) { + Log.d(TAG, + "OP_RECORD_AUDIO active change, active" + active + ", app=" + packageName); + } + + if (mExemptApps.contains(packageName)) { + if (DEBUG) { + Log.d(TAG, "\t- exempt app"); + } + return; + } + + final boolean alreadyTracking = mAudioRecordingApps.contains(packageName); + if ((active && alreadyTracking) || (!active && !alreadyTracking)) { + if (DEBUG) { + Log.d(TAG, "\t- nothing changed"); + } + return; + } + + if (active) { + if (DEBUG) { + Log.d(TAG, "\t- new recording app"); + } + + if (mAudioRecordingApps.isEmpty()) { + showAudioRecordingDisclosureBar(); + } + + mAudioRecordingApps.add(packageName); + addToAudioRecordingDisclosureBar(packageName); + } else { + if (DEBUG) { + Log.d(TAG, "\t- not recording any more"); + } + + final int index = mAudioRecordingApps.indexOf(packageName); + removeFromAudioRecordingDisclosureBar(index); + mAudioRecordingApps.remove(index); + + if (mAudioRecordingApps.isEmpty()) { + hideAudioRecordingDisclosureBar(); + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS new file mode 100644 index 000000000000..a601e9b86ef4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS @@ -0,0 +1,8 @@ +# Android TV Core Framework +rgl@google.com +valiiftime@google.com +galinap@google.com +patrikf@google.com +robhor@google.com +sergeynv@google.com + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 17d9cbe3af07..c2ed7df7cb2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -23,28 +23,37 @@ import android.os.ServiceManager; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.CommandQueue.Callbacks; + /** - * Status bar implementation for "large screen" products that mostly present no on-screen nav + * Status bar implementation for "large screen" products that mostly present no on-screen nav. + * Serves as a collection of UI components, rather than showing its own UI. + * The following is the list of elements that constitute the TV-specific status bar: + * <ul> + * <li> {@link AudioRecordingDisclosureBar} - shown whenever applications are conducting audio + * recording, discloses the responsible applications </li> + * </ul> */ +public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { -public class TvStatusBar extends SystemUI implements Callbacks { - - private IStatusBarService mBarService; + public TvStatusBar(Context context) { + super(context); + } @Override public void start() { putComponent(TvStatusBar.class, this); - CommandQueue commandQueue = getComponent(CommandQueue.class); - commandQueue.addCallback(this); - mBarService = IStatusBarService.Stub.asInterface( + + final IStatusBarService barService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + final CommandQueue commandQueue = getComponent(CommandQueue.class); + commandQueue.addCallback(this); try { - mBarService.registerStatusBar(commandQueue); + barService.registerStatusBar(commandQueue); } catch (RemoteException ex) { // If the system process isn't there we're doomed anyway. } - } + new AudioRecordingDisclosureBar(mContext).start(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 89aa7979e7d8..9a58a355c586 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -61,6 +61,10 @@ public class ThemeOverlayController extends SystemUI { private ThemeOverlayManager mThemeManager; private UserManager mUserManager; + public ThemeOverlayController(Context context) { + super(context); + } + @Override public void start() { if (DEBUG) Log.d(TAG, "Start"); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index a55e2cf38e39..2d6027cb324d 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -15,8 +15,6 @@ */ package com.android.systemui.tuner; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; @@ -36,6 +34,7 @@ import android.util.ArraySet; import com.android.internal.util.ArrayUtils; import com.android.systemui.DejankUtils; import com.android.systemui.DemoMode; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.qs.QSTileHost; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -46,7 +45,6 @@ import java.util.HashSet; import java.util.Set; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; @@ -83,7 +81,7 @@ public class TunerServiceImpl extends TunerService { /** */ @Inject - public TunerServiceImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler mainHandler, + public TunerServiceImpl(Context context, @MainHandler Handler mainHandler, LeakDetector leakDetector) { mContext = context; mContentResolver = mContext.getContentResolver(); diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index ff5bd03740bd..11885c55b51d 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -60,6 +60,10 @@ public class StorageNotification extends SystemUI { private NotificationManager mNotificationManager; private StorageManager mStorageManager; + public StorageNotification(Context context) { + super(context); + } + private static class MoveInfo { public int moveId; public Bundle extras; diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java index 0a3e34ee951d..fd99ef389bec 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.hardware.usb.IUsbManager; @@ -63,6 +64,7 @@ public class UsbConfirmActivity extends AlertActivity mDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); mResolveInfo = (ResolveInfo) intent.getParcelableExtra("rinfo"); + String packageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE); PackageManager packageManager = getPackageManager(); String appName = mResolveInfo.loadLabel(packageManager).toString(); @@ -74,8 +76,20 @@ public class UsbConfirmActivity extends AlertActivity mAccessory.getDescription()); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory); } else { - ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName, - mDevice.getProductName()); + int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + boolean hasRecordPermission = + PermissionChecker.checkPermissionForPreflight( + this, android.Manifest.permission.RECORD_AUDIO, -1, uid, + packageName) + == android.content.pm.PackageManager.PERMISSION_GRANTED; + boolean isAudioCaptureDevice = mDevice.getHasAudioCapture(); + boolean 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()); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); } ap.mPositiveButtonText = getString(android.R.string.ok); diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java index a46f018af816..47b56e097ec9 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java @@ -21,6 +21,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.PermissionChecker; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.hardware.usb.IUsbManager; @@ -84,14 +85,27 @@ public class UsbPermissionActivity extends AlertActivity final AlertController.AlertParams ap = mAlertParams; ap.mTitle = appName; if (mDevice == null) { + // Accessory Case + ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName, mAccessory.getDescription()); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory); } else { - ap.mMessage = getString(R.string.usb_device_permission_prompt, appName, - mDevice.getProductName()); + boolean hasRecordPermission = + PermissionChecker.checkPermissionForPreflight( + this, android.Manifest.permission.RECORD_AUDIO, -1, aInfo.uid, + mPackageName) + == android.content.pm.PackageManager.PERMISSION_GRANTED; + boolean isAudioCaptureDevice = mDevice.getHasAudioCapture(); + boolean 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()); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); } + ap.mPositiveButtonText = getString(android.R.string.ok); ap.mNegativeButtonText = getString(android.R.string.cancel); ap.mPositiveButtonListener = this; diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index e44e58a84dc8..5ed027d37def 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -26,7 +26,7 @@ import android.view.View; import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardSliceView; -import com.android.systemui.SystemUIRootComponent; +import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.qs.QSCarrierGroup; import com.android.systemui.qs.QSFooterImpl; import com.android.systemui.qs.QSPanel; @@ -37,6 +37,7 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.policy.Clock; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -178,6 +179,11 @@ public class InjectionInflationController { * Creates the QSCustomizer. */ QSCustomizer createQSCustomizer(); + + /** + * Creates a Clock. + */ + Clock createClock(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java index f35af90edc3c..8c60747dffc7 100644 --- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java +++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java @@ -38,6 +38,10 @@ public class NotificationChannels extends SystemUI { public static String BATTERY = "BAT"; public static String HINTS = "HNT"; + public NotificationChannels(Context context) { + super(context); + } + public static void createAll(Context context) { final NotificationManager nm = context.getSystemService(NotificationManager.class); final NotificationChannel batteryChannel = new NotificationChannel(BATTERY, diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java b/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java index fa7af0be77f1..be5e0a0b12c5 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java @@ -36,6 +36,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -53,7 +54,7 @@ public class DumpTruck { private final Context context; private Uri hprofUri; - private long pss; + private long rss; final StringBuilder body = new StringBuilder(); public DumpTruck(Context context) { @@ -66,7 +67,7 @@ public class DumpTruck { * @param pids * @return this, for chaining */ - public DumpTruck captureHeaps(int[] pids) { + public DumpTruck captureHeaps(List<Long> pids) { final GarbageMonitor gm = Dependency.get(GarbageMonitor.class); final File dumpDir = new File(context.getCacheDir(), FILEPROVIDER_PATH); @@ -79,8 +80,8 @@ public class DumpTruck { final ArrayList<String> paths = new ArrayList<String>(); final int myPid = android.os.Process.myPid(); - final int[] pids_copy = Arrays.copyOf(pids, pids.length); - for (int pid : pids_copy) { + for (Long pidL : pids) { + final int pid = pidL.intValue(); body.append(" pid ").append(pid); if (gm != null) { GarbageMonitor.ProcessMemInfo info = gm.getMemInfo(pid); @@ -88,11 +89,9 @@ public class DumpTruck { body.append(":") .append(" up=") .append(info.getUptime()) - .append(" pss=") - .append(info.currentPss) - .append(" uss=") - .append(info.currentUss); - pss = info.currentPss; + .append(" rss=") + .append(info.currentRss); + rss = info.currentRss; } } if (pid == myPid) { @@ -147,7 +146,7 @@ public class DumpTruck { shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); shareIntent.putExtra(Intent.EXTRA_SUBJECT, - String.format("SystemUI memory dump (pss=%dM)", pss / 1024)); + String.format("SystemUI memory dump (rss=%dM)", rss / 1024)); shareIntent.putExtra(Intent.EXTRA_TEXT, body.toString()); diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java index 583f6b340d47..bff405c0bee6 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java @@ -20,7 +20,6 @@ import static android.service.quicksettings.Tile.STATE_ACTIVE; import static android.telephony.ims.feature.ImsFeature.STATE_UNAVAILABLE; import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.annotation.Nullable; import android.app.ActivityManager; @@ -35,7 +34,6 @@ import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; -import android.os.Debug; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -49,7 +47,8 @@ import android.util.LongSparseArray; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SystemUI; -import com.android.systemui.SystemUIFactory; +import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -57,9 +56,9 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -102,7 +101,6 @@ public class GarbageMonitor implements Dumpable { private final LongSparseArray<ProcessMemInfo> mData = new LongSparseArray<>(); private final ArrayList<Long> mPids = new ArrayList<>(); - private int[] mPidsArray = new int[1]; private long mHeapLimit; @@ -111,7 +109,7 @@ public class GarbageMonitor implements Dumpable { @Inject public GarbageMonitor( Context context, - @Named(BG_LOOPER_NAME) Looper bgLooper, + @BgLooper Looper bgLooper, LeakDetector leakDetector, LeakReporter leakReporter) { mContext = context.getApplicationContext(); @@ -164,8 +162,8 @@ public class GarbageMonitor implements Dumpable { return mData.get(pid); } - public int[] getTrackedProcesses() { - return mPidsArray; + public List<Long> getTrackedProcesses() { + return mPids; } public void startTrackingProcess(long pid, String name, long start) { @@ -173,43 +171,40 @@ public class GarbageMonitor implements Dumpable { if (mPids.contains(pid)) return; mPids.add(pid); - updatePidsArrayL(); + logPids(); mData.put(pid, new ProcessMemInfo(pid, name, start)); } } - private void updatePidsArrayL() { - final int N = mPids.size(); - mPidsArray = new int[N]; - StringBuffer sb = new StringBuffer("Now tracking processes: "); - for (int i = 0; i < N; i++) { - final int p = mPids.get(i).intValue(); - mPidsArray[i] = p; - sb.append(p); - sb.append(" "); - } - if (DEBUG) Log.v(TAG, sb.toString()); + private void logPids() { + if (DEBUG) { + StringBuffer sb = new StringBuffer("Now tracking processes: "); + for (int i = 0; i < mPids.size(); i++) { + final int p = mPids.get(i).intValue(); + sb.append(" "); + } + Log.v(TAG, sb.toString()); + } } private void update() { synchronized (mPids) { - Debug.MemoryInfo[] dinfos = mAm.getProcessMemoryInfo(mPidsArray); - for (int i = 0; i < dinfos.length; i++) { - Debug.MemoryInfo dinfo = dinfos[i]; - if (i > mPids.size()) { - if (DEBUG) Log.e(TAG, "update: unknown process info received: " + dinfo); + for (int i = 0; i < mPids.size(); i++) { + final int pid = mPids.get(i).intValue(); + // rssValues contains [VmRSS, RssFile, RssAnon, VmSwap]. + long[] rssValues = Process.getRss(pid); + if (rssValues == null && rssValues.length == 0) { + if (DEBUG) Log.e(TAG, "update: Process.getRss() didn't provide any values."); break; } - final long pid = mPids.get(i).intValue(); + long rss = rssValues[0]; final ProcessMemInfo info = mData.get(pid); - info.pss[info.head] = info.currentPss = dinfo.getTotalPss(); - info.uss[info.head] = info.currentUss = dinfo.getTotalPrivateDirty(); - info.head = (info.head + 1) % info.pss.length; - if (info.currentPss > info.max) info.max = info.currentPss; - if (info.currentUss > info.max) info.max = info.currentUss; - if (info.currentPss == 0) { - if (DEBUG) Log.v(TAG, "update: pid " + pid + " has pss=0, it probably died"); + info.rss[info.head] = info.currentRss = rss; + info.head = (info.head + 1) % info.rss.length; + if (info.currentRss > info.max) info.max = info.currentRss; + if (info.currentRss == 0) { + if (DEBUG) Log.v(TAG, "update: pid " + pid + " has rss=0, it probably died"); mData.remove(pid); } } @@ -217,7 +212,7 @@ public class GarbageMonitor implements Dumpable { final long pid = mPids.get(i).intValue(); if (mData.get(pid) == null) { mPids.remove(i); - updatePidsArrayL(); + logPids(); } } } @@ -270,7 +265,7 @@ public class GarbageMonitor implements Dumpable { private static class MemoryIconDrawable extends Drawable { - long pss, limit; + long rss, limit; final Drawable baseIcon; final Paint paint = new Paint(); final float dp; @@ -281,9 +276,9 @@ public class GarbageMonitor implements Dumpable { paint.setColor(QSTileImpl.getColorForState(context, STATE_ACTIVE)); } - public void setPss(long pss) { - if (pss != this.pss) { - this.pss = pss; + public void setRss(long rss) { + if (rss != this.rss) { + this.rss = rss; invalidateSelf(); } } @@ -299,8 +294,8 @@ public class GarbageMonitor implements Dumpable { public void draw(Canvas canvas) { baseIcon.draw(canvas); - if (limit > 0 && pss > 0) { - float frac = Math.min(1f, (float) pss / limit); + if (limit > 0 && rss > 0) { + float frac = Math.min(1f, (float) rss / limit); final Rect bounds = getBounds(); canvas.translate(bounds.left + 8 * dp, bounds.top + 5 * dp); @@ -361,10 +356,10 @@ public class GarbageMonitor implements Dumpable { } private static class MemoryGraphIcon extends QSTile.Icon { - long pss, limit; + long rss, limit; - public void setPss(long pss) { - this.pss = pss; + public void setRss(long rss) { + this.rss = rss; } public void setHeapLimit(long limit) { @@ -374,7 +369,7 @@ public class GarbageMonitor implements Dumpable { @Override public Drawable getDrawable(Context context) { final MemoryIconDrawable drawable = new MemoryIconDrawable(context); - drawable.setPss(pss); + drawable.setRss(rss); drawable.setLimit(limit); return drawable; } @@ -387,13 +382,15 @@ public class GarbageMonitor implements Dumpable { public static final boolean ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS = true; private final GarbageMonitor gm; + private final ActivityStarter mActivityStarter; private ProcessMemInfo pmi; private boolean dumpInProgress; @Inject - public MemoryTile(QSHost host) { + public MemoryTile(QSHost host, GarbageMonitor monitor, ActivityStarter starter) { super(host); - gm = SystemUIFactory.getInstance().getRootComponent().createGarbageMonitor(); + gm = monitor; + mActivityStarter = starter; } @Override @@ -424,7 +421,7 @@ public class GarbageMonitor implements Dumpable { dumpInProgress = false; refreshState(); getHost().collapsePanels(); - mContext.startActivity(shareIntent); + mActivityStarter.postStartActivityDismissingKeyguard(shareIntent, 0); }); } }.start(); @@ -462,14 +459,14 @@ public class GarbageMonitor implements Dumpable { ? "Dumping..." : mContext.getString(R.string.heap_dump_tile_name); if (pmi != null) { - icon.setPss(pmi.currentPss); + icon.setRss(pmi.currentRss); state.secondaryLabel = String.format( - "pss: %s / %s", - formatBytes(pmi.currentPss * 1024), + "rss: %s / %s", + formatBytes(pmi.currentRss * 1024), formatBytes(gm.mHeapLimit * 1024)); } else { - icon.setPss(0); + icon.setRss(0); state.secondaryLabel = null; } state.icon = icon; @@ -479,8 +476,8 @@ public class GarbageMonitor implements Dumpable { refreshState(); } - public long getPss() { - return pmi != null ? pmi.currentPss : 0; + public long getRss() { + return pmi != null ? pmi.currentRss : 0; } public long getHeapLimit() { @@ -493,9 +490,8 @@ public class GarbageMonitor implements Dumpable { public long pid; public String name; public long startTime; - public long currentPss, currentUss; - public long[] pss = new long[HEAP_TRACK_HISTORY_LEN]; - public long[] uss = new long[HEAP_TRACK_HISTORY_LEN]; + public long currentRss; + public long[] rss = new long[HEAP_TRACK_HISTORY_LEN]; public long max = 1; public int head = 0; @@ -517,25 +513,27 @@ public class GarbageMonitor implements Dumpable { pw.print(name.replace('"', '-')); pw.print("\", \"start\": "); pw.print(startTime); - pw.print(", \"pss\": ["); - // write pss values starting from the oldest, which is pss[head], wrapping around to - // pss[(head-1) % pss.length] - for (int i = 0; i < pss.length; i++) { - if (i > 0) pw.print(","); - pw.print(pss[(head + i) % pss.length]); - } - pw.print("], \"uss\": ["); - for (int i = 0; i < uss.length; i++) { + pw.print(", \"rss\": ["); + // write rss values starting from the oldest, which is rss[head], wrapping around to + // rss[(head-1) % rss.length] + for (int i = 0; i < rss.length; i++) { if (i > 0) pw.print(","); - pw.print(uss[(head + i) % uss.length]); + pw.print(rss[(head + i) % rss.length]); } pw.println("] }"); } } /** */ + @Singleton public static class Service extends SystemUI implements Dumpable { - private GarbageMonitor mGarbageMonitor; + private final GarbageMonitor mGarbageMonitor; + + @Inject + public Service(Context context, GarbageMonitor garbageMonitor) { + super(context); + mGarbageMonitor = garbageMonitor; + } @Override public void start() { @@ -543,8 +541,6 @@ public class GarbageMonitor implements Dumpable { Settings.Secure.getInt( mContext.getContentResolver(), FORCE_ENABLE_LEAK_REPORTING, 0) != 0; - mGarbageMonitor = SystemUIFactory.getInstance().getRootComponent() - .createGarbageMonitor(); if (LEAK_REPORTING_ENABLED || forceEnable) { mGarbageMonitor.startLeakMonitor(); } diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java index 9271bc282615..61de86698f1e 100755 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java @@ -146,9 +146,11 @@ public class AsyncSensorManager extends SensorManager @Override protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) { - if ( sensor == null ) { - Log.e(TAG, "sensor cannot be null \n" + Log.getStackTraceString(new Throwable())); - return false; + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + if (sensor == null) { + throw new IllegalArgumentException("sensor cannot be null"); } mHandler.post(() -> { if ( sensor == null ) { diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index c48bdde6adef..a96977a338a9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -16,7 +16,7 @@ package com.android.systemui.util.sensors; -import android.content.Context; +import android.content.res.Resources; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -24,8 +24,9 @@ import android.hardware.SensorManager; import android.os.Handler; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; -import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.dagger.qualifiers.MainResources; import java.util.ArrayList; import java.util.List; @@ -47,7 +48,7 @@ public class ProximitySensor { private final float mMaxRange; private List<ProximitySensorListener> mListeners = new ArrayList<>(); private String mTag = null; - private ProximityEvent mLastEvent; + @VisibleForTesting ProximityEvent mLastEvent; private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL; private boolean mPaused; private boolean mRegistered; @@ -64,10 +65,10 @@ public class ProximitySensor { }; @Inject - public ProximitySensor( - Context context, AsyncSensorManager sensorManager, PluginManager pluginManager) { + public ProximitySensor(@MainResources Resources resources, + AsyncSensorManager sensorManager) { mSensorManager = sensorManager; - Sensor sensor = findBrightnessSensor(context); + Sensor sensor = findBrightnessSensor(resources); if (sensor == null) { mUsingBrightnessSensor = false; @@ -107,8 +108,8 @@ public class ProximitySensor { registerInternal(); } - private Sensor findBrightnessSensor(Context context) { - String sensorType = context.getString(R.string.doze_brightness_sensor_type); + private Sensor findBrightnessSensor(Resources resources) { + String sensorType = resources.getString(R.string.doze_brightness_sensor_type); List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL); Sensor sensor = null; for (Sensor s : sensorList) { @@ -146,17 +147,17 @@ public class ProximitySensor { return false; } - logDebug("Using brightness sensor? " + mUsingBrightnessSensor); mListeners.add(listener); registerInternal(); return true; } - private void registerInternal() { + protected void registerInternal() { if (mRegistered || mPaused || mListeners.isEmpty()) { return; } + logDebug("Using brightness sensor? " + mUsingBrightnessSensor); logDebug("Registering sensor listener"); mRegistered = true; mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay); @@ -175,7 +176,7 @@ public class ProximitySensor { } } - private void unregisterInternal() { + protected void unregisterInternal() { if (!mRegistered) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java index 7991e388af7e..e4ebea9483c7 100644 --- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java +++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java @@ -16,8 +16,11 @@ package com.android.systemui.util.wakelock; +import android.content.Context; import android.os.Handler; +import javax.inject.Inject; + /** * A wake lock that has a built in delay when releasing to give the framebuffer time to update. */ @@ -53,4 +56,46 @@ public class DelayedWakeLock implements WakeLock { public String toString() { return TO_STRING_PREFIX + mInner; } + + /** + * An injectable builder for {@see DelayedWakeLock} that has the context already filled in. + */ + public static class Builder { + private final Context mContext; + private String mTag; + private Handler mHandler; + + /** + * Constructor for DelayedWakeLock.Builder + */ + @Inject + public Builder(Context context) { + mContext = context; + } + + /** + * Set the tag for the WakeLock. + */ + public Builder setTag(String tag) { + mTag = tag; + + return this; + } + + /** + * Set the handler for the DelayedWakeLock. + */ + public Builder setHandler(Handler handler) { + mHandler = handler; + + return this; + } + + /** + * Build the DelayedWakeLock. + */ + public DelayedWakeLock build() { + return new DelayedWakeLock(mHandler, WakeLock.createPartial(mContext, mTag)); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java index d2f185a88bfd..25a5139bf661 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java @@ -27,7 +27,6 @@ import android.view.WindowManager.LayoutParams; import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.Dependency; -import com.android.systemui.SystemUI; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.PluginDependencyProvider; @@ -40,9 +39,13 @@ import com.android.systemui.tuner.TunerService; import java.io.FileDescriptor; import java.io.PrintWriter; +import javax.inject.Inject; +import javax.inject.Singleton; + /** * Implementation of VolumeComponent backed by the new volume dialog. */ +@Singleton public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable, VolumeDialogControllerImpl.UserActivityListener{ @@ -54,12 +57,12 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false; public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false; - private final SystemUI mSysui; protected final Context mContext; private final VolumeDialogControllerImpl mController; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_ASSETS_PATHS | ActivityInfo.CONFIG_UI_MODE); + private final KeyguardViewMediator mKeyguardViewMediator; private VolumeDialog mDialog; private VolumePolicy mVolumePolicy = new VolumePolicy( DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent @@ -68,9 +71,10 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna 400 // vibrateToSilentDebounce ); - public VolumeDialogComponent(SystemUI sysui, Context context) { - mSysui = sysui; + @Inject + public VolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator) { mContext = context; + mKeyguardViewMediator = keyguardViewMediator; mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class); mController.setUserActivityListener(this); // Allow plugins to reference the VolumeDialogController. @@ -133,10 +137,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna @Override public void onUserActivity() { - final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class); - if (kvm != null) { - kvm.userActivity(); - } + mKeyguardViewMediator.userActivity(); } private void applyConfiguration() { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index a6b5b38fd728..2c70fb4c50ec 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -59,6 +59,7 @@ import com.android.settingslib.volume.MediaSessions; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.qs.tiles.DndTile; @@ -137,9 +138,10 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private UserActivityListener mUserActivityListener; protected final VC mVolumeController = new VC(); + protected final BroadcastDispatcher mBroadcastDispatcher; @Inject - public VolumeDialogControllerImpl(Context context) { + public VolumeDialogControllerImpl(Context context, BroadcastDispatcher broadcastDispatcher) { mContext = context.getApplicationContext(); mNotificationManager = (NotificationManager) mContext.getSystemService( Context.NOTIFICATION_SERVICE); @@ -152,6 +154,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mObserver = new SettingObserver(mWorker); + mBroadcastDispatcher = broadcastDispatcher; mObserver.init(); mReceiver.init(); mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); @@ -618,7 +621,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa .PRIORITY_CATEGORY_MEDIA) == 0; boolean disallowSystem = (policy.priorityCategories & NotificationManager.Policy .PRIORITY_CATEGORY_SYSTEM) == 0; - boolean disallowRinger = ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(policy); + // ringer controls notifications, ringer and system sounds, so only disallow ringer changes + // if all relevant (notifications + ringer + system) sounds are not allowed to bypass DND + boolean disallowRinger = ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(policy); if (mState.disallowAlarms == disallowAlarms && mState.disallowMedia == disallowMedia && mState.disallowRinger == disallowRinger @@ -1004,11 +1009,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - mContext.registerReceiver(this, filter, null, mWorker); + mBroadcastDispatcher.registerReceiver(this, filter, mWorker); } public void destroy() { - mContext.unregisterReceiver(this); + mBroadcastDispatcher.unregisterReceiver(this); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index f8cf79322b40..b74313975223 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -16,18 +16,22 @@ package com.android.systemui.volume; +import android.content.Context; import android.content.res.Configuration; import android.os.Handler; import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; -import com.android.systemui.SystemUIFactory; import com.android.systemui.qs.tiles.DndTile; import java.io.FileDescriptor; import java.io.PrintWriter; +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton public class VolumeUI extends SystemUI { private static final String TAG = "VolumeUI"; private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); @@ -37,6 +41,12 @@ public class VolumeUI extends SystemUI { private boolean mEnabled; private VolumeDialogComponent mVolumeComponent; + @Inject + public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent) { + super(context); + mVolumeComponent = volumeDialogComponent; + } + @Override public void start() { boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui); @@ -45,8 +55,6 @@ public class VolumeUI extends SystemUI { mEnabled = enableVolumeUi || enableSafetyWarning; if (!mEnabled) return; - mVolumeComponent = SystemUIFactory.getInstance() - .createVolumeDialogComponent(this, mContext); mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning); putComponent(VolumeComponent.class, getVolumeComponent()); setDefaultVolumeController(); |