diff options
| author | 2018-08-23 17:16:59 -0400 | |
|---|---|---|
| committer | 2018-10-23 11:19:15 -0400 | |
| commit | 297c04ee5376d8bca84bc8e876d6ecc88e84a999 (patch) | |
| tree | 01e9a2093ecfb34be59690e02c2eaae75c7902ca | |
| parent | c2896a27fadc416458e883282bb0d8a0f81ee13a (diff) | |
Make StatusBar not be a NotificationPresenter
Break the NotificationPresenter out of the StatusBar and most of
the logic with it.
- Break RemoteInput Callbacks out of NotificationPresenter
- Break Environment Callbacks out of NotificationPresenter
- Add ShadeController interface for StatusBar (abstraction
layer from StatusBar)
- Add InitController to allow for post-init tasks (dependency
resolution should not go here)
- Make some dependencies (ShadeController, NotificationEntryManager,
NotificationLockscreenUserManager usually) lazily-resolved to break
some dependency cycles
- Lots of other cleanup
Test: Existing tests do not pass
Change-Id: Ic043c6b15a4ffe551fc27f99b25d5c3caa1be582
72 files changed, 3088 insertions, 1958 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index b58ea00997ef..f4922088bb05 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -14,6 +14,7 @@ package com.android.systemui.plugins; +import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Intent; @@ -36,7 +37,17 @@ public interface ActivityStarter { void postStartActivityDismissingKeyguard(PendingIntent intent); void postQSRunnableDismissingKeyguard(Runnable runnable); + void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel, + boolean afterKeyguardGone); + interface Callback { void onActivityStarted(int resultCode); } + + interface OnDismissAction { + /** + * @return {@code true} if the dismiss should be deferred + */ + boolean onDismiss(); + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java index 265a961cd985..34df15f17869 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java @@ -29,13 +29,13 @@ import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; -import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.settingslib.Utils; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import java.io.File; @@ -50,13 +50,6 @@ import java.io.File; */ public class KeyguardHostView extends FrameLayout implements SecurityCallback { - public interface OnDismissAction { - /** - * @return true if the dismiss should be deferred - */ - boolean onDismiss(); - } - private AudioManager mAudioManager; private TelephonyManager mTelephonyManager = null; protected ViewMediatorCallback mViewMediatorCallback; diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java index e58538db9cea..e1b8dc839bde 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java @@ -29,46 +29,69 @@ public class ActivityStarterDelegate implements ActivityStarter { @Override public void startPendingIntentDismissingKeyguard(PendingIntent intent) { - if (mActualStarter == null) return; + if (mActualStarter == null) { + return; + } mActualStarter.startPendingIntentDismissingKeyguard(intent); } @Override public void startActivity(Intent intent, boolean dismissShade) { - if (mActualStarter == null) return; + if (mActualStarter == null) { + return; + } mActualStarter.startActivity(intent, dismissShade); } @Override public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) { - if (mActualStarter == null) return; + if (mActualStarter == null) { + return; + } mActualStarter.startActivity(intent, onlyProvisioned, dismissShade); } @Override public void startActivity(Intent intent, boolean dismissShade, Callback callback) { - if (mActualStarter == null) return; + if (mActualStarter == null) { + return; + } mActualStarter.startActivity(intent, dismissShade, callback); } @Override public void postStartActivityDismissingKeyguard(Intent intent, int delay) { - if (mActualStarter == null) return; + if (mActualStarter == null) { + return; + } mActualStarter.postStartActivityDismissingKeyguard(intent, delay); } @Override public void postStartActivityDismissingKeyguard(PendingIntent intent) { - if (mActualStarter == null) return; + if (mActualStarter == null) { + return; + } mActualStarter.postStartActivityDismissingKeyguard(intent); } @Override public void postQSRunnableDismissingKeyguard(Runnable runnable) { - if (mActualStarter == null) return; + if (mActualStarter == null) { + return; + } mActualStarter.postQSRunnableDismissingKeyguard(runnable); } + @Override + public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancel, + boolean afterKeyguardGone) { + if (mActualStarter == null) { + return; + } + mActualStarter.dismissKeyguardThenExecute(action, cancel, afterKeyguardGone); + } + public void setActivityStarterImpl(ActivityStarter starter) { mActualStarter = starter; } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 2c821b25f0a5..494880e96ff7 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -48,16 +48,22 @@ import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.power.EnhancedEstimatesImpl; import com.android.systemui.power.PowerNotificationWarnings; import com.android.systemui.power.PowerUI; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.AppOpsListener; import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; +import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; @@ -343,6 +349,14 @@ public class Dependency extends SystemUI { mProviders.put(LockscreenGestureLogger.class, () -> new LockscreenGestureLogger()); + mProviders.put(KeyguardEnvironment.class, () -> new KeyguardEnvironmentImpl()); + mProviders.put(ShadeController.class, () -> + SysUiServiceProvider.getComponent(mContext, StatusBar.class)); + mProviders.put(NotificationRemoteInputManager.Callback.class, + () -> new StatusBarRemoteInputCallback(mContext)); + + mProviders.put(InitController.class, InitController::new); + // Put all dependencies above here so the factory can override them if it wants. SystemUIFactory.getInstance().injectDependencies(mProviders, mContext); diff --git a/packages/SystemUI/src/com/android/systemui/InitController.java b/packages/SystemUI/src/com/android/systemui/InitController.java new file mode 100644 index 000000000000..52ba66a93524 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/InitController.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui; + +import java.util.ArrayList; + +/** + * Created by {@link Dependency} on SystemUI startup. Add tasks which need to be executed only + * after all other dependencies have been created. + */ +public class InitController { + + private final ArrayList<Runnable> mTasks = new ArrayList<>(); + + /** + * Add a task to be executed after {@link Dependency#start()} + * @param runnable the task to be executed + */ + public void addPostInitTask(Runnable runnable) { + mTasks.add(runnable); + } + + /** + * Run post-init tasks and remove them from the tasks list + */ + public void executePostInitTasks() { + while (!mTasks.isEmpty()) { + mTasks.remove(0).run(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 78053b28c4c3..92aa652131ba 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -194,6 +194,7 @@ public class SystemUIApplication extends Application implements SysUiServiceProv mServices[i].onBootCompleted(); } } + Dependency.get(InitController.class).executePostInitTasks(); log.traceEnd(); Dependency.get(PluginManager.class).addPluginListener( new PluginListener<OverlayPlugin>() { diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 258b6f61d4c0..c4bf27b5104a 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -32,6 +32,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -133,7 +134,7 @@ public class SystemUIFactory { Context context) { providers.put(StatusBarStateController.class, StatusBarStateController::new); providers.put(NotificationLockscreenUserManager.class, - () -> new NotificationLockscreenUserManager(context)); + () -> new NotificationLockscreenUserManagerImpl(context)); providers.put(VisualStabilityManager.class, VisualStabilityManager::new); providers.put(NotificationGroupManager.class, NotificationGroupManager::new); providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context)); diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 38a90cfd96a5..53cdee549536 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -202,7 +202,8 @@ public class AssistManager implements ConfigurationChangedReceiver { // Close Recent Apps if needed SysUiServiceProvider.getComponent(mContext, CommandQueue.class).animateCollapsePanels( - CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL | CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL); + CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL | CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, + false /* force */); boolean structureEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, UserHandle.USER_CURRENT) != 0; diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java index 5739c997d8bf..96af08b6bf6b 100644 --- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java @@ -44,7 +44,7 @@ public class CarNotificationEntryManager extends NotificationEntryManager { // Because space is usually constrained in the auto use-case, there should not be a // pinned notification when the shade has been expanded. Ensure this by not pinning any // notification if the shade is already opened. - if (!mPresenter.isPresenterFullyCollapsed()) { + if (!getPresenter().isPresenterFullyCollapsed()) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationMediaManager.java new file mode 100644 index 000000000000..f34d6b3e03e7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationMediaManager.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.car; + +import android.content.Context; + +import com.android.systemui.statusbar.NotificationMediaManager; + +public class CarNotificationMediaManager extends NotificationMediaManager { + public CarNotificationMediaManager(Context context) { + super(context); + } + + @Override + public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) { + // Do nothing, we don't want to display media art in the lock screen for a car. + } +} diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java index a015a18cd9a8..e4b2e07dc81e 100644 --- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java @@ -22,6 +22,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.Dependency.DependencyProvider; import com.android.systemui.SystemUIFactory; +import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.car.CarFacetButtonController; import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager; @@ -46,5 +47,7 @@ public class CarSystemUIFactory extends SystemUIFactory { () -> new CarNotificationEntryManager(context)); providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context)); providers.put(HvacController.class, () -> new HvacController(context)); + providers.put(NotificationMediaManager.class, + () -> new CarNotificationMediaManager(context)); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 5c0b3289c61a..daaefb9b2559 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS; + +import android.app.StatusBarManager; import android.content.ComponentName; import android.graphics.Rect; import android.hardware.biometrics.IBiometricPromptReceiver; @@ -24,7 +27,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.RemoteException; + import androidx.annotation.VisibleForTesting; import android.util.Pair; @@ -117,7 +120,7 @@ public class CommandQueue extends IStatusBar.Stub { default void removeIcon(String slot) { } default void disable(int state1, int state2, boolean animate) { } default void animateExpandNotificationsPanel() { } - default void animateCollapsePanels(int flags) { } + default void animateCollapsePanels(int flags, boolean force) { } default void togglePanel() { } default void animateExpandSettingsPanel(String obj) { } default void setSystemUiVisibility(int vis, int fullscreenStackVis, @@ -169,7 +172,13 @@ public class CommandQueue extends IStatusBar.Stub { } @VisibleForTesting - protected CommandQueue() { + public CommandQueue() { + } + + public boolean panelsEnabled() { + return (mDisable1 & StatusBarManager.DISABLE_EXPAND) == 0 + && (mDisable2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0 + && !ONLY_CORE_APPS; } public void addCallbacks(Callbacks callbacks) { @@ -234,10 +243,10 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void animateCollapsePanels(int flags) { + public void animateCollapsePanels(int flags, boolean force) { synchronized (mLock) { mHandler.removeMessages(MSG_COLLAPSE_PANELS); - mHandler.obtainMessage(MSG_COLLAPSE_PANELS, flags, 0).sendToTarget(); + mHandler.obtainMessage(MSG_COLLAPSE_PANELS, flags, force ? 1 : 0).sendToTarget(); } } @@ -592,7 +601,7 @@ public class CommandQueue extends IStatusBar.Stub { break; case MSG_COLLAPSE_PANELS: for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).animateCollapsePanels(msg.arg1); + mCallbacks.get(i).animateCollapsePanels(msg.arg1, msg.arg2 != 0); } break; case MSG_TOGGLE_PANEL: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index cfa09bc93adb..f3a46ce5778a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -29,6 +29,7 @@ import android.util.Log; import com.android.systemui.Dependency; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins; /** @@ -41,11 +42,14 @@ public class NotificationListener extends NotificationListenerWithPlugins { // Dependencies: private final NotificationRemoteInputManager mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); + private final NotificationEntryManager mEntryManager = + Dependency.get(NotificationEntryManager.class); + private final NotificationGroupManager mGroupManager = + Dependency.get(NotificationGroupManager.class); private final Context mContext; protected NotificationPresenter mPresenter; - protected NotificationEntryManager mEntryManager; public NotificationListener(Context context) { mContext = context; @@ -61,7 +65,7 @@ public class NotificationListener extends NotificationListenerWithPlugins { return; } final RankingMap currentRanking = getCurrentRanking(); - mPresenter.getHandler().post(() -> { + Dependency.get(Dependency.MAIN_HANDLER).post(() -> { for (StatusBarNotification sbn : notifications) { mEntryManager.addNotification(sbn, currentRanking); } @@ -73,7 +77,7 @@ public class NotificationListener extends NotificationListenerWithPlugins { final RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) { - mPresenter.getHandler().post(() -> { + Dependency.get(Dependency.MAIN_HANDLER).post(() -> { processForRemoteInput(sbn.getNotification(), mContext); String key = sbn.getKey(); boolean isUpdate = @@ -83,7 +87,7 @@ public class NotificationListener extends NotificationListenerWithPlugins { // anyway. This is true also when the summary is canceled, // because children are automatically canceled by NoMan in that case. if (!ENABLE_CHILD_NOTIFICATIONS - && mPresenter.getGroupManager().isChildInGroupWithSummary(sbn)) { + && mGroupManager.isChildInGroupWithSummary(sbn)) { if (DEBUG) { Log.d(TAG, "Ignoring group child due to existing summary: " + sbn); } @@ -112,7 +116,7 @@ public class NotificationListener extends NotificationListenerWithPlugins { if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) { final String key = sbn.getKey(); - mPresenter.getHandler().post(() -> { + Dependency.get(Dependency.MAIN_HANDLER).post(() -> { mEntryManager.removeNotification(key, rankingMap); }); } @@ -123,16 +127,14 @@ public class NotificationListener extends NotificationListenerWithPlugins { if (DEBUG) Log.d(TAG, "onRankingUpdate"); if (rankingMap != null) { RankingMap r = onPluginRankingUpdate(rankingMap); - mPresenter.getHandler().post(() -> { + Dependency.get(Dependency.MAIN_HANDLER).post(() -> { mEntryManager.updateNotificationRanking(r); }); } } - public void setUpWithPresenter(NotificationPresenter presenter, - NotificationEntryManager entryManager) { + public void setUpWithPresenter(NotificationPresenter presenter) { mPresenter = presenter; - mEntryManager = entryManager; try { registerAsSystemService(mContext, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index 89a842e11c68..bc662e3d8855 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -1,516 +1,61 @@ /* * 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 + * 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 + * 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.app.ActivityManager; -import android.app.KeyguardManager; -import android.app.Notification; -import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.IntentSender; import android.content.pm.UserInfo; -import android.database.ContentObserver; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; import android.service.notification.StatusBarNotification; -import android.util.Log; import android.util.SparseArray; -import android.util.SparseBooleanArray; - -import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.NotificationVisibility; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Dependency; -import com.android.systemui.Dumpable; -import com.android.systemui.OverviewProxyService; -import com.android.systemui.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import java.io.FileDescriptor; -import java.io.PrintWriter; +import com.android.systemui.statusbar.notification.NotificationData.Entry; -/** - * Handles keeping track of the current user, profiles, and various things related to hiding - * contents, redacting notifications, and the lockscreen. - */ -public class NotificationLockscreenUserManager implements Dumpable, StateListener { - private static final String TAG = "LockscreenUserManager"; - private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false; - public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; - public static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION +public interface NotificationLockscreenUserManager { + String PERMISSION_SELF = "com.android.systemui.permission.SELF"; + String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action"; - private final DevicePolicyManager mDevicePolicyManager; - private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray(); - private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); - private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray(); - private final DeviceProvisionedController mDeviceProvisionedController = - Dependency.get(DeviceProvisionedController.class); - private final UserManager mUserManager; - private final IStatusBarService mBarService; - private final LockPatternUtils mLockPatternUtils; - private final KeyguardManager mKeyguardManager; - private StatusBarKeyguardViewManager mKeyguardViewManager; - - private boolean mShowLockscreenNotifications; - private boolean mAllowLockscreenRemoteInput; - private int mState = StatusBarState.SHADE; - - protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - - if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) && - isCurrentProfile(getSendingUserId())) { - mUsersAllowingPrivateNotifications.clear(); - updateLockscreenNotificationSetting(); - mEntryManager.updateNotifications(); - } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) { - if (userId != mCurrentUserId && isCurrentProfile(userId)) { - updatePublicMode(); - mPresenter.onWorkChallengeChanged(); - mEntryManager.updateNotifications(); - } - } - } - }; - - protected final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_USER_SWITCHED.equals(action)) { - mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - updateCurrentProfilesCache(); - Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); - - updateLockscreenNotificationSetting(); - updatePublicMode(); - mPresenter.onUserSwitched(mCurrentUserId); - mEntryManager.getNotificationData().filterAndSort(); - } else if (Intent.ACTION_USER_ADDED.equals(action)) { - updateCurrentProfilesCache(); - } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { - // Start the overview connection to the launcher service - Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser(); - } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) { - final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT); - final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX); - if (intentSender != null) { - try { - mContext.startIntentSender(intentSender, null, 0, 0, 0); - } catch (IntentSender.SendIntentException e) { - /* ignore */ - } - } - if (notificationKey != null) { - final int count = - mEntryManager.getNotificationData().getActiveNotifications().size(); - final int rank = mEntryManager.getNotificationData().getRank(notificationKey); - final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey, - rank, count, true); - try { - mBarService.onNotificationClick(notificationKey, nv); - } catch (RemoteException e) { - /* ignore */ - } - } - } - } - }; - - protected final Context mContext; - protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>(); - - protected int mCurrentUserId = 0; - protected NotificationPresenter mPresenter; - protected NotificationEntryManager mEntryManager; - protected ContentObserver mLockscreenSettingsObserver; - protected ContentObserver mSettingsObserver; - - public NotificationLockscreenUserManager(Context context) { - mContext = context; - mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService( - Context.DEVICE_POLICY_SERVICE); - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mCurrentUserId = ActivityManager.getCurrentUser(); - mBarService = IStatusBarService.Stub.asInterface( - ServiceManager.getService(Context.STATUS_BAR_SERVICE)); - mLockPatternUtils = new LockPatternUtils(mContext); - mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - Dependency.get(StatusBarStateController.class).addListener(this); - } - - public void setUpWithPresenter(NotificationPresenter presenter, - NotificationEntryManager entryManager) { - mPresenter = presenter; - mEntryManager = entryManager; - - mLockscreenSettingsObserver = new ContentObserver(mPresenter.getHandler()) { - @Override - public void onChange(boolean selfChange) { - // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or - // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ... - mUsersAllowingPrivateNotifications.clear(); - mUsersAllowingNotifications.clear(); - // ... and refresh all the notifications - updateLockscreenNotificationSetting(); - mEntryManager.updateNotifications(); - } - }; - - mSettingsObserver = new ContentObserver(mPresenter.getHandler()) { - @Override - public void onChange(boolean selfChange) { - updateLockscreenNotificationSetting(); - if (mDeviceProvisionedController.isDeviceProvisioned()) { - mEntryManager.updateNotifications(); - } - } - }; - - mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, - mLockscreenSettingsObserver, - UserHandle.USER_ALL); - - mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), - true, - mLockscreenSettingsObserver, - UserHandle.USER_ALL); - - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, - mSettingsObserver); - - if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { - mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), - false, - mSettingsObserver, - UserHandle.USER_ALL); - } - - IntentFilter allUsersFilter = new IntentFilter(); - allUsersFilter.addAction( - DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); - allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED); - mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter, - null, null); - - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_USER_SWITCHED); - filter.addAction(Intent.ACTION_USER_ADDED); - filter.addAction(Intent.ACTION_USER_UNLOCKED); - mContext.registerReceiver(mBaseBroadcastReceiver, filter); - - IntentFilter internalFilter = new IntentFilter(); - internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION); - mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null); - - updateCurrentProfilesCache(); - - mSettingsObserver.onChange(false); // set up - } - - public boolean shouldShowLockscreenNotifications() { - return mShowLockscreenNotifications; - } - - public boolean shouldAllowLockscreenRemoteInput() { - return mAllowLockscreenRemoteInput; - } - - public boolean isCurrentProfile(int userId) { - synchronized (mCurrentProfiles) { - return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null; - } - } - - public void setKeyguardViewManager(StatusBarKeyguardViewManager sbkvm) { - mKeyguardViewManager = sbkvm; - } - - @Override - public void onStateChanged(int newState) { - mState = newState; - updatePublicMode(); - } - - public void updatePublicMode() { - //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns - // false when it should be true. Therefore, if we are not on the SHADE, don't even bother - // 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. - boolean showingKeyguard = mState != StatusBarState.SHADE - || mKeyguardViewManager.isShowing(); - boolean devicePublic = showingKeyguard && mKeyguardViewManager.isSecure(getCurrentUserId()); - - SparseArray<UserInfo> currentProfiles = getCurrentProfiles(); - for (int i = currentProfiles.size() - 1; i >= 0; i--) { - final int userId = currentProfiles.valueAt(i).id; - boolean isProfilePublic = devicePublic; - if (!devicePublic && userId != mCurrentUserId) { - // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge - // due to a race condition where this code could be called before - // TrustManagerService updates its internal records, resulting in an incorrect - // state being cached in mLockscreenPublicMode. (b/35951989) - if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) - && mKeyguardViewManager.isSecure(userId)) { - isProfilePublic = mKeyguardManager.isDeviceLocked(userId); - } - } - setLockscreenPublicMode(isProfilePublic, userId); - } - } + boolean shouldAllowLockscreenRemoteInput(); /** - * Returns true if notifications are temporarily disabled for this user for security reasons, - * regardless of the normal settings for that user. + * @param userId user Id + * @return true if we re on a secure lock screen */ - private boolean shouldTemporarilyHideNotifications(int userId) { - if (userId == UserHandle.USER_ALL) { - userId = mCurrentUserId; - } - return KeyguardUpdateMonitor.getInstance(mContext).isUserInLockdown(userId); - } + boolean isLockscreenPublicMode(int userId); - /** - * Returns true if we're on a secure lockscreen and the user wants to hide notification data. - * If so, notifications should be hidden. - */ - public boolean shouldHideNotifications(int userId) { - return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId) - || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId)) - || shouldTemporarilyHideNotifications(userId); - } - - /** - * Returns true if we're on a secure lockscreen and the user wants to hide notifications via - * package-specific override. - */ - public boolean shouldHideNotifications(String key) { - if (mEntryManager == null) { - Log.wtf(TAG, "mEntryManager was null!", new Throwable()); - return true; - } - return isLockscreenPublicMode(mCurrentUserId) - && mEntryManager.getNotificationData().getVisibilityOverride(key) == - Notification.VISIBILITY_SECRET; - } - - public boolean shouldShowOnKeyguard(StatusBarNotification sbn) { - if (mEntryManager == null) { - Log.wtf(TAG, "mEntryManager was null!", new Throwable()); - return false; - } - return mShowLockscreenNotifications - && !mEntryManager.getNotificationData().isAmbient(sbn.getKey()); - } + void setUpWithPresenter(NotificationPresenter presenter); - private void setShowLockscreenNotifications(boolean show) { - mShowLockscreenNotifications = show; - } + int getCurrentUserId(); - private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) { - mAllowLockscreenRemoteInput = allowLockscreenRemoteInput; - } + boolean isCurrentProfile(int userId); - protected void updateLockscreenNotificationSetting() { - final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, - 1, - mCurrentUserId) != 0; - final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures( - null /* admin */, mCurrentUserId); - final boolean allowedByDpm = (dpmFlags - & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; + void destroy(); - setShowLockscreenNotifications(show && allowedByDpm); + SparseArray<UserInfo> getCurrentProfiles(); - if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { - final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, - 0, - mCurrentUserId) != 0; - final boolean remoteInputDpm = - (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0; + void setLockscreenPublicMode(boolean isProfilePublic, int userId); - setLockscreenAllowRemoteInput(remoteInput && remoteInputDpm); - } else { - setLockscreenAllowRemoteInput(false); - } - } - - /** - * Has the given user chosen to allow their private (full) notifications to be shown even - * when the lockscreen is in "public" (secure & locked) mode? - */ - public boolean userAllowsPrivateNotificationsInPublic(int userHandle) { - if (userHandle == UserHandle.USER_ALL) { - return true; - } - - if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { - final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( - mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); - final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, - DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); - final boolean allowed = allowedByUser && allowedByDpm; - mUsersAllowingPrivateNotifications.append(userHandle, allowed); - return allowed; - } - - return mUsersAllowingPrivateNotifications.get(userHandle); - } - - private boolean adminAllowsKeyguardFeature(int userHandle, int feature) { - if (userHandle == UserHandle.USER_ALL) { - return true; - } - final int dpmFlags = - mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, userHandle); - return (dpmFlags & feature) == 0; - } - - /** - * Save the current "public" (locked and secure) state of the lockscreen. - */ - public void setLockscreenPublicMode(boolean publicMode, int userId) { - mLockscreenPublicMode.put(userId, publicMode); - } + boolean shouldShowLockscreenNotifications(); - public boolean isLockscreenPublicMode(int userId) { - if (userId == UserHandle.USER_ALL) { - return mLockscreenPublicMode.get(mCurrentUserId, false); - } - return mLockscreenPublicMode.get(userId, false); - } + boolean shouldHideNotifications(int userId); + boolean shouldHideNotifications(String key); + boolean shouldShowOnKeyguard(StatusBarNotification sbn); - /** - * Has the given user chosen to allow notifications to be shown even when the lockscreen is in - * "public" (secure & locked) mode? - */ - private boolean userAllowsNotificationsInPublic(int userHandle) { - if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) { - return true; - } - - if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { - final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( - mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); - final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, - DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); - final boolean allowed = allowedByUser && allowedByDpm; - mUsersAllowingNotifications.append(userHandle, allowed); - return allowed; - } - - return mUsersAllowingNotifications.get(userHandle); - } - - /** @return true if the entry needs redaction when on the lockscreen. */ - public boolean needsRedaction(NotificationData.Entry ent) { - int userId = ent.notification.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()); - - return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen; - } - - private boolean packageHasVisibilityOverride(String key) { - if (mEntryManager == null) { - Log.wtf(TAG, "mEntryManager was null!", new Throwable()); - return true; - } - return mEntryManager.getNotificationData().getVisibilityOverride(key) == - Notification.VISIBILITY_PRIVATE; - } - - private void updateCurrentProfilesCache() { - synchronized (mCurrentProfiles) { - mCurrentProfiles.clear(); - if (mUserManager != null) { - for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) { - mCurrentProfiles.put(user.id, user); - } - } - } - } - - public boolean isAnyProfilePublicMode() { - for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) { - if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) { - return true; - } - } - return false; - } - - /** - * Returns the current user id. This can change if the user is switched. - */ - public int getCurrentUserId() { - return mCurrentUserId; - } + boolean isAnyProfilePublicMode(); - public SparseArray<UserInfo> getCurrentProfiles() { - return mCurrentProfiles; - } + void updatePublicMode(); - public void destroy() { - mContext.unregisterReceiver(mBaseBroadcastReceiver); - mContext.unregisterReceiver(mAllUsersReceiver); - } + boolean needsRedaction(Entry entry); - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("NotificationLockscreenUserManager state:"); - pw.print(" mCurrentUserId="); - pw.println(mCurrentUserId); - pw.print(" mShowLockscreenNotifications="); - pw.println(mShowLockscreenNotifications); - pw.print(" mAllowLockscreenRemoteInput="); - pw.println(mAllowLockscreenRemoteInput); - pw.print(" mCurrentProfiles="); - for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) { - final int userId = mCurrentProfiles.valueAt(i).id; - pw.print("" + userId + " "); - } - pw.println(); - } + boolean userAllowsPrivateNotificationsInPublic(int currentUserId); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java new file mode 100644 index 000000000000..178c5c516e7b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.statusbar; + +import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; + +import android.app.ActivityManager; +import android.app.KeyguardManager; +import android.app.Notification; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.pm.UserInfo; +import android.database.ContentObserver; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.util.SparseArray; +import android.util.SparseBooleanArray; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.Dependency; +import com.android.systemui.Dumpable; +import com.android.systemui.OverviewProxyService; +import com.android.systemui.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.KeyguardMonitor; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Handles keeping track of the current user, profiles, and various things related to hiding + * contents, redacting notifications, and the lockscreen. + */ +public class NotificationLockscreenUserManagerImpl implements + Dumpable, NotificationLockscreenUserManager, StateListener { + private static final String TAG = "LockscreenUserManager"; + private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false; + + private final DeviceProvisionedController mDeviceProvisionedController = + Dependency.get(DeviceProvisionedController.class); + private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + + // Lazy + private NotificationEntryManager mEntryManager; + + private final DevicePolicyManager mDevicePolicyManager; + private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray(); + private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); + private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray(); + private final UserManager mUserManager; + private final IStatusBarService mBarService; + + private boolean mShowLockscreenNotifications; + private boolean mAllowLockscreenRemoteInput; + private LockPatternUtils mLockPatternUtils; + protected KeyguardManager mKeyguardManager; + private int mState = StatusBarState.SHADE; + + protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + + if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) && + isCurrentProfile(getSendingUserId())) { + mUsersAllowingPrivateNotifications.clear(); + updateLockscreenNotificationSetting(); + getEntryManager().updateNotifications(); + } + } + }; + + protected final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_USER_SWITCHED.equals(action)) { + mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + updateCurrentProfilesCache(); + Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); + + updateLockscreenNotificationSetting(); + updatePublicMode(); + mPresenter.onUserSwitched(mCurrentUserId); + getEntryManager().getNotificationData().filterAndSort(); + } else if (Intent.ACTION_USER_ADDED.equals(action)) { + updateCurrentProfilesCache(); + } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { + // Start the overview connection to the launcher service + Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser(); + } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) { + final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT); + final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX); + if (intentSender != null) { + try { + mContext.startIntentSender(intentSender, null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + /* ignore */ + } + } + if (notificationKey != null) { + final int count = + getEntryManager().getNotificationData().getActiveNotifications().size(); + final int rank = getEntryManager().getNotificationData().getRank(notificationKey); + final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey, + rank, count, true); + try { + mBarService.onNotificationClick(notificationKey, nv); + } catch (RemoteException e) { + /* ignore */ + } + } + } + } + }; + + protected final Context mContext; + protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>(); + + protected int mCurrentUserId = 0; + protected NotificationPresenter mPresenter; + protected ContentObserver mLockscreenSettingsObserver; + protected ContentObserver mSettingsObserver; + + private NotificationEntryManager getEntryManager() { + if (mEntryManager == null) { + mEntryManager = Dependency.get(NotificationEntryManager.class); + } + return mEntryManager; + } + + public NotificationLockscreenUserManagerImpl(Context context) { + mContext = context; + mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService( + Context.DEVICE_POLICY_SERVICE); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + mCurrentUserId = ActivityManager.getCurrentUser(); + mBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + Dependency.get(StatusBarStateController.class).addListener(this); + mLockPatternUtils = new LockPatternUtils(context); + mKeyguardManager = context.getSystemService(KeyguardManager.class); + } + + public void setUpWithPresenter(NotificationPresenter presenter) { + mPresenter = presenter; + + mLockscreenSettingsObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) { + @Override + public void onChange(boolean selfChange) { + // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or + // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ... + mUsersAllowingPrivateNotifications.clear(); + mUsersAllowingNotifications.clear(); + // ... and refresh all the notifications + updateLockscreenNotificationSetting(); + getEntryManager().updateNotifications(); + } + }; + + mSettingsObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) { + @Override + public void onChange(boolean selfChange) { + updateLockscreenNotificationSetting(); + if (mDeviceProvisionedController.isDeviceProvisioned()) { + getEntryManager().updateNotifications(); + } + } + }; + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, + mLockscreenSettingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + true, + mLockscreenSettingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, + mSettingsObserver); + + if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), + false, + mSettingsObserver, + UserHandle.USER_ALL); + } + + mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, + new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED), + null, null); + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_SWITCHED); + filter.addAction(Intent.ACTION_USER_ADDED); + filter.addAction(Intent.ACTION_USER_UNLOCKED); + mContext.registerReceiver(mBaseBroadcastReceiver, filter); + + IntentFilter internalFilter = new IntentFilter(); + internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION); + mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null); + + updateCurrentProfilesCache(); + + mSettingsObserver.onChange(false); // set up + } + + public boolean shouldShowLockscreenNotifications() { + return mShowLockscreenNotifications; + } + + public boolean shouldAllowLockscreenRemoteInput() { + return mAllowLockscreenRemoteInput; + } + + public boolean isCurrentProfile(int userId) { + synchronized (mCurrentProfiles) { + return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null; + } + } + + /** + * Returns true if notifications are temporarily disabled for this user for security reasons, + * regardless of the normal settings for that user. + */ + private boolean shouldTemporarilyHideNotifications(int userId) { + if (userId == UserHandle.USER_ALL) { + userId = mCurrentUserId; + } + return KeyguardUpdateMonitor.getInstance(mContext).isUserInLockdown(userId); + } + + /** + * Returns true if we're on a secure lockscreen and the user wants to hide notification data. + * If so, notifications should be hidden. + */ + public boolean shouldHideNotifications(int userId) { + return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId) + || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId)) + || shouldTemporarilyHideNotifications(userId); + } + + /** + * Returns true if we're on a secure lockscreen and the user wants to hide notifications via + * package-specific override. + */ + public boolean shouldHideNotifications(String key) { + if (getEntryManager() == null) { + Log.wtf(TAG, "mEntryManager was null!", new Throwable()); + return true; + } + return isLockscreenPublicMode(mCurrentUserId) + && getEntryManager().getNotificationData().getVisibilityOverride(key) == + Notification.VISIBILITY_SECRET; + } + + public boolean shouldShowOnKeyguard(StatusBarNotification sbn) { + if (getEntryManager() == null) { + Log.wtf(TAG, "mEntryManager was null!", new Throwable()); + return false; + } + return mShowLockscreenNotifications + && !getEntryManager().getNotificationData().isAmbient(sbn.getKey()); + } + + private void setShowLockscreenNotifications(boolean show) { + mShowLockscreenNotifications = show; + } + + private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) { + mAllowLockscreenRemoteInput = allowLockscreenRemoteInput; + } + + protected void updateLockscreenNotificationSetting() { + final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, + 1, + mCurrentUserId) != 0; + final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures( + null /* admin */, mCurrentUserId); + final boolean allowedByDpm = (dpmFlags + & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; + + setShowLockscreenNotifications(show && allowedByDpm); + + if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { + final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, + 0, + mCurrentUserId) != 0; + final boolean remoteInputDpm = + (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0; + + setLockscreenAllowRemoteInput(remoteInput && remoteInputDpm); + } else { + setLockscreenAllowRemoteInput(false); + } + } + + /** + * Has the given user chosen to allow their private (full) notifications to be shown even + * when the lockscreen is in "public" (secure & locked) mode? + */ + public boolean userAllowsPrivateNotificationsInPublic(int userHandle) { + if (userHandle == UserHandle.USER_ALL) { + return true; + } + + if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { + final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); + final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, + DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); + final boolean allowed = allowedByUser && allowedByDpm; + mUsersAllowingPrivateNotifications.append(userHandle, allowed); + return allowed; + } + + return mUsersAllowingPrivateNotifications.get(userHandle); + } + + private boolean adminAllowsKeyguardFeature(int userHandle, int feature) { + if (userHandle == UserHandle.USER_ALL) { + return true; + } + final int dpmFlags = + mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, userHandle); + return (dpmFlags & feature) == 0; + } + + /** + * Save the current "public" (locked and secure) state of the lockscreen. + */ + public void setLockscreenPublicMode(boolean publicMode, int userId) { + mLockscreenPublicMode.put(userId, publicMode); + } + + public boolean isLockscreenPublicMode(int userId) { + if (userId == UserHandle.USER_ALL) { + return mLockscreenPublicMode.get(mCurrentUserId, false); + } + return mLockscreenPublicMode.get(userId, false); + } + + /** + * Has the given user chosen to allow notifications to be shown even when the lockscreen is in + * "public" (secure & locked) mode? + */ + private boolean userAllowsNotificationsInPublic(int userHandle) { + if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) { + return true; + } + + if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { + final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); + final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, + DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); + final boolean allowed = allowedByUser && allowedByDpm; + mUsersAllowingNotifications.append(userHandle, allowed); + return allowed; + } + + return mUsersAllowingNotifications.get(userHandle); + } + + /** @return true if the entry needs redaction when on the lockscreen. */ + public boolean needsRedaction(NotificationData.Entry ent) { + int userId = ent.notification.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()); + + return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen; + } + + private boolean packageHasVisibilityOverride(String key) { + if (getEntryManager() == null) { + Log.wtf(TAG, "mEntryManager was null!", new Throwable()); + return true; + } + return getEntryManager().getNotificationData().getVisibilityOverride(key) == + Notification.VISIBILITY_PRIVATE; + } + + private void updateCurrentProfilesCache() { + synchronized (mCurrentProfiles) { + mCurrentProfiles.clear(); + if (mUserManager != null) { + for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) { + mCurrentProfiles.put(user.id, user); + } + } + } + } + + public boolean isAnyProfilePublicMode() { + for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) { + if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) { + return true; + } + } + return false; + } + + /** + * Returns the current user id. This can change if the user is switched. + */ + public int getCurrentUserId() { + return mCurrentUserId; + } + + public SparseArray<UserInfo> getCurrentProfiles() { + return mCurrentProfiles; + } + + @Override + public void onStateChanged(int newState) { + mState = newState; + updatePublicMode(); + } + + public void updatePublicMode() { + //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns + // false when it should be true. Therefore, if we are not on the SHADE, don't even bother + // 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()); + + + // Look for public mode users. Users are considered public in either case of: + // - device keyguard is shown in secure mode; + // - profile is locked with a work challenge. + SparseArray<UserInfo> currentProfiles = getCurrentProfiles(); + for (int i = currentProfiles.size() - 1; i >= 0; i--) { + final int userId = currentProfiles.valueAt(i).id; + boolean isProfilePublic = devicePublic; + if (!devicePublic && userId != getCurrentUserId()) { + // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge + // due to a race condition where this code could be called before + // TrustManagerService updates its internal records, resulting in an incorrect + // state being cached in mLockscreenPublicMode. (b/35951989) + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) + && isSecure(userId)) { + isProfilePublic = mKeyguardManager.isDeviceLocked(userId); + } + } + setLockscreenPublicMode(isProfilePublic, userId); + } + } + + +// public void updatePublicMode() { +// //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns +// // false when it should be true. Therefore, if we are not on the SHADE, don't even bother +// // 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()); +// +// +// // Look for public mode users. Users are considered public in either case of: +// // - device keyguard is shown in secure mode; +// // - profile is locked with a work challenge. +// SparseArray<UserInfo> currentProfiles = getCurrentProfiles(); +// for (int i = currentProfiles.size() - 1; i >= 0; i--) { +// final int userId = currentProfiles.valueAt(i).id; +// boolean isProfilePublic = devicePublic; +// if (!devicePublic && userId != getCurrentUserId()) { +// // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge +// // due to a race condition where this code could be called before +// // TrustManagerService updates its internal records, resulting in an incorrect +// // state being cached in mLockscreenPublicMode. (b/35951989) +// if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) +// && isSecure(userId)) { +// isProfilePublic = mKeyguardManager.isDeviceLocked(userId); +// } +// } +// setLockscreenPublicMode(isProfilePublic, userId); +// } +// } + + private boolean isSecure(int userId) { + return mKeyguardMonitor.isSecure() || mLockPatternUtils.isSecure(userId); + } + + public void destroy() { + mContext.unregisterReceiver(mBaseBroadcastReceiver); + mContext.unregisterReceiver(mAllUsersReceiver); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("NotificationLockscreenUserManager state:"); + pw.print(" mCurrentUserId="); + pw.println(mCurrentUserId); + pw.print(" mShowLockscreenNotifications="); + pw.println(mShowLockscreenNotifications); + pw.print(" mAllowLockscreenRemoteInput="); + pw.println(mAllowLockscreenRemoteInput); + pw.print(" mCurrentProfiles="); + for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) { + final int userId = mCurrentProfiles.valueAt(i).id; + pw.print("" + userId + " "); + } + pw.println(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 2db99453e36c..67b21e1c8752 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -15,19 +15,46 @@ */ 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.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; + +import android.annotation.Nullable; import android.app.Notification; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; +import android.os.Handler; +import android.os.Trace; import android.os.UserHandle; import android.util.Log; +import android.view.View; +import android.widget.ImageView; +import com.android.systemui.Dependency; import com.android.systemui.Dumpable; +import com.android.systemui.Interpolators; +import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.phone.BiometricUnlockController; +import com.android.systemui.statusbar.phone.LockscreenWallpaper; +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 java.io.FileDescriptor; import java.io.PrintWriter; @@ -42,15 +69,45 @@ public class NotificationMediaManager implements Dumpable { private static final String TAG = "NotificationMediaManager"; public static final boolean DEBUG_MEDIA = false; + private final StatusBarStateController mStatusBarStateController + = Dependency.get(StatusBarStateController.class); + private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class); + private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + + // Late binding + private NotificationEntryManager mEntryManager; + + // Late binding, also @Nullable due to being in com.android.systemui.statusbar.phone package + @Nullable + private ShadeController mShadeController; + @Nullable + private StatusBarWindowController mStatusBarWindowController; + + @Nullable + private BiometricUnlockController mBiometricUnlockController; + @Nullable + private ScrimController mScrimController; + @Nullable + private LockscreenWallpaper mLockscreenWallpaper; + + protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); + protected final PorterDuffXfermode mSrcOverXferMode = + new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER); + + private final Handler mHandler = Dependency.get(MAIN_HANDLER); + private final Context mContext; private final MediaSessionManager mMediaSessionManager; protected NotificationPresenter mPresenter; - protected NotificationEntryManager mEntryManager; private MediaController mMediaController; private String mMediaNotificationKey; private MediaMetadata mMediaMetadata; + private BackDropView mBackdrop; + private ImageView mBackdropFront; + private ImageView mBackdropBack; + private final MediaController.Callback mMediaListener = new MediaController.Callback() { @Override public void onPlaybackStateChanged(PlaybackState state) { @@ -77,6 +134,29 @@ public class NotificationMediaManager implements Dumpable { } }; + @Nullable + private ShadeController getShadeController() { + if (mShadeController == null) { + mShadeController = Dependency.get(ShadeController.class); + } + return mShadeController; + } + + @Nullable + private StatusBarWindowController getWindowController() { + if (mStatusBarWindowController == null) { + mStatusBarWindowController = Dependency.get(StatusBarWindowController.class); + } + return mStatusBarWindowController; + } + + private NotificationEntryManager getEntryManager() { + if (mEntryManager == null) { + mEntryManager = Dependency.get(NotificationEntryManager.class); + } + return mEntryManager; + } + public NotificationMediaManager(Context context) { mContext = context; mMediaSessionManager @@ -85,10 +165,8 @@ public class NotificationMediaManager implements Dumpable { // in session state } - public void setUpWithPresenter(NotificationPresenter presenter, - NotificationEntryManager entryManager) { + public void setUpWithPresenter(NotificationPresenter presenter) { mPresenter = presenter; - mEntryManager = entryManager; } public void onNotificationRemoved(String key) { @@ -109,8 +187,9 @@ public class NotificationMediaManager implements Dumpable { public void findAndUpdateMediaNotifications() { boolean metaDataChanged = false; - synchronized (mEntryManager.getNotificationData()) { - ArrayList<NotificationData.Entry> activeNotifications = mEntryManager + NotificationEntryManager manager = getEntryManager(); + synchronized (manager.getNotificationData()) { + ArrayList<NotificationData.Entry> activeNotifications = manager .getNotificationData().getActiveNotifications(); final int N = activeNotifications.size(); @@ -199,7 +278,7 @@ public class NotificationMediaManager implements Dumpable { } if (metaDataChanged) { - mEntryManager.updateNotifications(); + getEntryManager().updateNotifications(); } mPresenter.updateMediaMetaData(metaDataChanged, true); } @@ -272,4 +351,202 @@ public class NotificationMediaManager implements Dumpable { } mMediaController = null; } + + /** + * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper. + */ + public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) { + Trace.beginSection("StatusBar#updateMediaMetaData"); + if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) { + Trace.endSection(); + return; + } + + if (mBackdrop == null) { + Trace.endSection(); + return; // called too early + } + + boolean wakeAndUnlock = mBiometricUnlockController != null + && mBiometricUnlockController.isWakeAndUnlock(); + if (mKeyguardMonitor.isLaunchTransitionFadingAway() || wakeAndUnlock) { + mBackdrop.setVisibility(View.INVISIBLE); + Trace.endSection(); + return; + } + + MediaMetadata mediaMetadata = getMediaMetadata(); + + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + + getMediaNotificationKey() + + " metadata=" + mediaMetadata + + " metaDataChanged=" + metaDataChanged + + " state=" + mStatusBarStateController.getState()); + } + + Drawable artworkDrawable = null; + if (mediaMetadata != null) { + Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); + if (artworkBitmap == null) { + artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); + // might still be null + } + if (artworkBitmap != null) { + artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap); + } + } + boolean allowWhenShade = false; + if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) { + Bitmap lockWallpaper = + mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null; + if (lockWallpaper != null) { + artworkDrawable = new LockscreenWallpaper.WallpaperDrawable( + mBackdropBack.getResources(), lockWallpaper); + // We're in the SHADE mode on the SIM screen - yet we still need to show + // the lockscreen wallpaper in that mode. + allowWhenShade = mStatusBarStateController.getState() == KEYGUARD; + } + } + + boolean hideBecauseOccluded = getShadeController() != null + && getShadeController().isOccluded(); + + final boolean hasArtwork = artworkDrawable != null; + mColorExtractor.setHasBackdrop(hasArtwork); + if (mScrimController != null) { + mScrimController.setHasBackdrop(hasArtwork); + } + + if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) + && (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade) + && mBiometricUnlockController != null && mBiometricUnlockController.getMode() + != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING + && !hideBecauseOccluded) { + // time to show some art! + if (mBackdrop.getVisibility() != View.VISIBLE) { + mBackdrop.setVisibility(View.VISIBLE); + if (allowEnterAnimation) { + mBackdrop.setAlpha(0); + mBackdrop.animate().alpha(1f); + } else { + mBackdrop.animate().cancel(); + mBackdrop.setAlpha(1f); + } + if (getWindowController() != null) { + getWindowController().setBackdropShowing(true); + } + metaDataChanged = true; + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork"); + } + } + if (metaDataChanged) { + if (mBackdropBack.getDrawable() != null) { + Drawable drawable = + mBackdropBack.getDrawable().getConstantState() + .newDrawable(mBackdropFront.getResources()).mutate(); + mBackdropFront.setImageDrawable(drawable); + mBackdropFront.setAlpha(1f); + mBackdropFront.setVisibility(View.VISIBLE); + } else { + mBackdropFront.setVisibility(View.INVISIBLE); + } + + if (DEBUG_MEDIA_FAKE_ARTWORK) { + final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF); + Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c)); + mBackdropBack.setBackgroundColor(0xFFFFFFFF); + mBackdropBack.setImageDrawable(new ColorDrawable(c)); + } else { + mBackdropBack.setImageDrawable(artworkDrawable); + } + + if (mBackdropFront.getVisibility() == View.VISIBLE) { + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from " + + mBackdropFront.getDrawable() + + " to " + + mBackdropBack.getDrawable()); + } + mBackdropFront.animate() + .setDuration(250) + .alpha(0f).withEndAction(mHideBackdropFront); + } + } + } else { + // need to hide the album art, either because we are unlocked, on AOD + // or because the metadata isn't there to support it + if (mBackdrop.getVisibility() != View.GONE) { + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork"); + } + boolean cannotAnimateDoze = getShadeController() != null + && getShadeController().isDozing() + && !ScrimState.AOD.getAnimateChange(); + if (mBiometricUnlockController != null && mBiometricUnlockController.getMode() + == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING + || hideBecauseOccluded || cannotAnimateDoze) { + + // We are unlocking directly - no animation! + mBackdrop.setVisibility(View.GONE); + mBackdropBack.setImageDrawable(null); + if (getWindowController() != null) { + getWindowController().setBackdropShowing(false); + } + } else { + if (getWindowController() != null) { + getWindowController().setBackdropShowing(false); + } + mBackdrop.animate() + .alpha(0) + .setInterpolator(Interpolators.ACCELERATE_DECELERATE) + .setDuration(300) + .setStartDelay(0) + .withEndAction(() -> { + mBackdrop.setVisibility(View.GONE); + mBackdropFront.animate().cancel(); + mBackdropBack.setImageDrawable(null); + mHandler.post(mHideBackdropFront); + }); + if (mKeyguardMonitor.isKeyguardFadingAway()) { + mBackdrop.animate() + // Make it disappear faster, as the focus should be on the activity + // behind. + .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2) + .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) + .setInterpolator(Interpolators.LINEAR) + .start(); + } + } + } + } + Trace.endSection(); + } + + public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack, + BiometricUnlockController biometricUnlockController, ScrimController scrimController, + LockscreenWallpaper lockscreenWallpaper) { + mBackdrop = backdrop; + mBackdropFront = backdropFront; + mBackdropBack = backdropBack; + mBiometricUnlockController = biometricUnlockController; + mScrimController = scrimController; + mLockscreenWallpaper = lockscreenWallpaper; + } + + /** + * Hide the album artwork that is fading out and release its bitmap. + */ + protected final Runnable mHideBackdropFront = new Runnable() { + @Override + public void run() { + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: removing fade layer"); + } + mBackdropFront.setVisibility(View.INVISIBLE); + mBackdropFront.animate().cancel(); + mBackdropFront.setImageDrawable(null); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java index c58eb80efd25..5c8f4cbf6078 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java @@ -19,6 +19,7 @@ import android.content.Intent; import android.os.Handler; import android.view.View; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -31,9 +32,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow * for affecting the state of the system (e.g. starting an intent, given that the presenter may * want to perform some action before doing so). */ -public interface NotificationPresenter extends NotificationData.Environment, - NotificationRemoteInputManager.Callback, - ExpandableNotificationRow.OnExpandClickListener, +public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener, ActivatableNotificationView.OnActivatedListener, NotificationEntryManager.Callback { /** @@ -43,59 +42,23 @@ public interface NotificationPresenter extends NotificationData.Environment, boolean isPresenterFullyCollapsed(); /** - * Returns true if the presenter is locked. For example, if the keyguard is active. - */ - boolean isPresenterLocked(); - - /** * Runs the given intent. The presenter may want to run some animations or close itself when * this happens. */ void startNotificationGutsIntent(Intent intent, int appUid, ExpandableNotificationRow row); /** - * Returns the Handler for NotificationPresenter. - */ - Handler getHandler(); - - /** * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper. */ void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation); /** - * Called when the locked status of the device is changed for a work profile. - */ - void onWorkChallengeChanged(); - - /** * Called when the current user changes. * @param newUserId new user id */ void onUserSwitched(int newUserId); /** - * Gets the NotificationLockscreenUserManager for this Presenter. - */ - NotificationLockscreenUserManager getNotificationLockscreenUserManager(); - - /** - * Wakes the device up if dozing. - * - * @param time the time when the request to wake up was issued - * @param where which view caused this wake up request - */ - void wakeUpIfDozing(long time, View where); - - /** - * True if the device currently requires a PIN, pattern, or password to unlock. - * - * @param userId user id to query about - * @return true iff the device is locked - */ - boolean isDeviceLocked(int userId); - - /** * @return true iff the device is in vr mode */ boolean isDeviceInVrMode(); @@ -114,7 +77,36 @@ public interface NotificationPresenter extends NotificationData.Environment, int getMaxNotificationsWhileLocked(boolean recompute); /** - * Called when the row states are updated by NotificationViewHierarchyManager. + * True if the presenter + * @return + */ + default boolean isPresenterLocked() { return false; } + + /** + * Called when the row states are updated by {@link NotificationViewHierarchyManager}. */ void onUpdateRowStates(); + + /** + * @return true if the shade is collapsing. + */ + boolean isCollapsing(); + + /** + * @return true if the shade is collapsing to show an activity over the lock screen + */ + default public boolean isCollapsingToShowActivityOverLockscreen() { + return false; + } + + /** + * Get the {@link ActivityLaunchAnimator} from the presenter so it can be queried by + * {@link com.android.systemui.statusbar.phone.StatusBar} + * @return the current animator + * @deprecated This is only here for now because StatusBar is still the ActivityLaunchAnimator + * callback but shouldn't be. + */ + default public ActivityLaunchAnimator getActivityLaunchAnimator() { + return null; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index ea7e03e686b5..f30377ead957 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.KeyguardManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; @@ -46,9 +47,11 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; +import com.android.systemui.InitController; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.RemoteInputView; import java.io.FileDescriptor; @@ -97,13 +100,18 @@ public class NotificationRemoteInputManager implements Dumpable { Dependency.get(NotificationLockscreenUserManager.class); protected final SmartReplyController mSmartReplyController = Dependency.get(SmartReplyController.class); + private final NotificationEntryManager mEntryManager + = Dependency.get(NotificationEntryManager.class); + + // Lazy + private ShadeController mShadeController; protected final Context mContext; private final UserManager mUserManager; + private final KeyguardManager mKeyguardManager; protected RemoteInputController mRemoteInputController; protected NotificationPresenter mPresenter; - protected NotificationEntryManager mEntryManager; protected NotificationLifetimeExtender.NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback; protected IStatusBarService mBarService; @@ -115,7 +123,7 @@ public class NotificationRemoteInputManager implements Dumpable { @Override public boolean onClickHandler( final View view, final PendingIntent pendingIntent, final Intent fillInIntent) { - mPresenter.wakeUpIfDozing(SystemClock.uptimeMillis(), view); + getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), view); if (handleRemoteInput(view, pendingIntent)) { return true; @@ -240,7 +248,7 @@ public class NotificationRemoteInputManager implements Dumpable { return true; } if (mUserManager.getUserInfo(userId).isManagedProfile() - && mPresenter.isDeviceLocked(userId)) { + && mKeyguardManager.isDeviceLocked(userId)) { mCallback.onLockedWorkRemoteInput(userId, row, view); return true; } @@ -291,20 +299,26 @@ public class NotificationRemoteInputManager implements Dumpable { } }; + private ShadeController getShadeController() { + if (mShadeController == null) { + mShadeController = Dependency.get(ShadeController.class); + } + return mShadeController; + } + public NotificationRemoteInputManager(Context context) { mContext = context; mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); addLifetimeExtenders(); + mKeyguardManager = context.getSystemService(KeyguardManager.class); } public void setUpWithPresenter(NotificationPresenter presenter, - NotificationEntryManager entryManager, Callback callback, RemoteInputController.Delegate delegate) { mPresenter = presenter; - mEntryManager = entryManager; mCallback = callback; mRemoteInputController = new RemoteInputController(delegate); mRemoteInputController.addCallback(new RemoteInputController.Callback() { @@ -318,7 +332,7 @@ public class NotificationRemoteInputManager implements Dumpable { // view it is already canceled, so we'll need to cancel it on the apps behalf // after sending - unless the app posts an update in the mean time, so wait a // bit. - mPresenter.getHandler().postDelayed(() -> { + Dependency.get(Dependency.MAIN_HANDLER).postDelayed(() -> { if (mEntriesKeptForRemoteInputActive.remove(entry)) { mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 1495abf9310c..cd3da123ed32 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -34,6 +34,7 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.accessibility.AccessibilityNodeInfo; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -103,7 +104,8 @@ public class NotificationShelf extends ActivatableNotificationView implements } @Override - protected void onFinishInflate() { + @VisibleForTesting + public void onFinishInflate() { super.onFinishInflate(); mShelfIcons = findViewById(R.id.content); mShelfIcons.setClipChildren(false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 5b3082b04d58..92765bbec5b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -24,6 +24,7 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.Dependency; +import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -31,6 +32,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.ShadeController; import java.util.ArrayList; import java.util.HashMap; @@ -57,6 +59,13 @@ public class NotificationViewHierarchyManager { Dependency.get(NotificationGroupManager.class); protected final VisualStabilityManager mVisualStabilityManager = Dependency.get(VisualStabilityManager.class); + private final StatusBarStateController mStatusBarStateController = + Dependency.get(StatusBarStateController.class); + private final NotificationEntryManager mEntryManager = + Dependency.get(NotificationEntryManager.class); + + // Lazy + private ShadeController mShadeController; /** * {@code true} if notifications not part of a group should by default be rendered in their @@ -66,9 +75,15 @@ public class NotificationViewHierarchyManager { private final boolean mAlwaysExpandNonGroupedNotification; private NotificationPresenter mPresenter; - private NotificationEntryManager mEntryManager; private NotificationListContainer mListContainer; + private ShadeController getShadeController() { + if (mShadeController == null) { + mShadeController = Dependency.get(ShadeController.class); + } + return mShadeController; + } + public NotificationViewHierarchyManager(Context context) { Resources res = context.getResources(); mAlwaysExpandNonGroupedNotification = @@ -76,9 +91,8 @@ public class NotificationViewHierarchyManager { } public void setUpWithPresenter(NotificationPresenter presenter, - NotificationEntryManager entryManager, NotificationListContainer listContainer) { + NotificationListContainer listContainer) { mPresenter = presenter; - mEntryManager = entryManager; mListContainer = listContainer; } @@ -291,9 +305,9 @@ public class NotificationViewHierarchyManager { final int N = mListContainer.getContainerChildCount(); int visibleNotifications = 0; - boolean isLocked = mPresenter.isPresenterLocked(); + boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD; int maxNotifications = -1; - if (isLocked) { + if (onKeyguard) { maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */); } mListContainer.setMaxDisplayedNotifications(maxNotifications); @@ -311,9 +325,9 @@ public class NotificationViewHierarchyManager { boolean isChildNotification = mGroupManager.isChildInGroupWithSummary(entry.notification); - row.setOnKeyguard(isLocked); + row.setOnKeyguard(onKeyguard); - if (!isLocked) { + if (!onKeyguard) { // If mAlwaysExpandNonGroupedNotification is false, then only expand the // very first notification and if it's not a child of grouped notifications. row.setSystemExpanded(mAlwaysExpandNonGroupedNotification @@ -321,7 +335,7 @@ public class NotificationViewHierarchyManager { && !row.isLowPriority())); } - entry.row.setOnAmbient(mPresenter.isDozing()); + entry.row.setOnAmbient(getShadeController().isDozing()); int userId = entry.notification.getUserId(); boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup( entry.notification) && !entry.row.isRemoved(); @@ -340,7 +354,7 @@ public class NotificationViewHierarchyManager { } if (suppressedSummary || mLockscreenUserManager.shouldHideNotifications(userId) - || (isLocked && !showOnKeyguard)) { + || (onKeyguard && !showOnKeyguard)) { entry.row.setVisibility(View.GONE); } else { boolean wasGone = entry.row.getVisibility() == View.GONE; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java index 78a5817c32b2..12c0fcbed204 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java @@ -44,6 +44,7 @@ public class StatusBarStateController { private int mState; private int mLastState; private boolean mLeaveOpenOnKeyguardHide; + private boolean mKeyguardRequested; // TODO: b/115739177 (remove this explicit ordering if we can) @Retention(SOURCE) @@ -173,6 +174,14 @@ public class StatusBarStateController { } } + public void setKeyguardRequested(boolean keyguardRequested) { + mKeyguardRequested = keyguardRequested; + } + + public boolean isKeyguardRequested() { + return mKeyguardRequested; + } + public static String describe(int state) { return StatusBarState.toShortString(state); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 24665eac76a7..879934146ac0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -525,12 +525,6 @@ public class CarStatusBar extends StatusBar implements } @Override - public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) { - // Do nothing, we don't want to display media art in the lock screen for a car. - } - - - @Override public void animateExpandNotificationsPanel() { // Because space is usually constrained in the auto use-case, there should not be a // pinned notification when the shade has been expanded. Ensure this by removing all heads- diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java index 8cae80635e69..9e99fbb3afc0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java @@ -33,10 +33,11 @@ public class AppOpsListener implements AppOpsManager.OnOpActiveChangedListener { // Dependencies: private final ForegroundServiceController mFsc = Dependency.get(ForegroundServiceController.class); + private final NotificationEntryManager mEntryManager = + Dependency.get(NotificationEntryManager.class); private final Context mContext; protected NotificationPresenter mPresenter; - protected NotificationEntryManager mEntryManager; protected final AppOpsManager mAppOps; protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA, @@ -48,10 +49,8 @@ public class AppOpsListener implements AppOpsManager.OnOpActiveChangedListener { mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); } - public void setUpWithPresenter(NotificationPresenter presenter, - NotificationEntryManager entryManager) { + public void setUpWithPresenter(NotificationPresenter presenter) { mPresenter = presenter; - mEntryManager = entryManager; mAppOps.startWatchingActive(OPS, this); } @@ -62,7 +61,7 @@ public class AppOpsListener implements AppOpsManager.OnOpActiveChangedListener { @Override public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { mFsc.onAppOpChanged(code, uid, packageName, active); - mPresenter.getHandler().post(() -> { + Dependency.get(Dependency.MAIN_HANDLER).post(() -> { mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active); }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java index fbf12ed39561..3539fff8bb33 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java @@ -51,21 +51,22 @@ import android.util.ArraySet; import android.view.View; import android.widget.ImageView; -import androidx.annotation.Nullable; - import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; +import com.android.systemui.InitController; import com.android.systemui.statusbar.InflationTask; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.ZenModeController; import java.io.PrintWriter; import java.util.ArrayList; @@ -74,16 +75,23 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; +import androidx.annotation.Nullable; + /** * The list of currently displaying notifications. */ public class NotificationData { - private final Environment mEnvironment; - private HeadsUpManager mHeadsUpManager; + /** + * These dependencies are late init-ed + */ + private KeyguardEnvironment mEnvironment; + private ShadeController mShadeController; + private NotificationMediaManager mMediaManager; + private ForegroundServiceController mFsc; + private NotificationLockscreenUserManager mUserManager; - final ZenModeController mZen = Dependency.get(ZenModeController.class); - final ForegroundServiceController mFsc = Dependency.get(ForegroundServiceController.class); + private HeadsUpManager mHeadsUpManager; public static final class Entry { private static final long LAUNCH_COOLDOWN = 2000; @@ -375,7 +383,8 @@ public class NotificationData { private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>(); private final ArrayList<Entry> mFilteredForUser = new ArrayList<>(); - private NotificationGroupManager mGroupManager; + private final NotificationGroupManager mGroupManager + = Dependency.get(NotificationGroupManager.class); private RankingMap mRankingMap; private final Ranking mTmpRanking = new Ranking(); @@ -407,7 +416,7 @@ public class NotificationData { bRank = mRankingB.getRank(); } - String mediaNotification = mEnvironment.getCurrentMediaNotificationKey(); + String mediaNotification = getMediaManager().getMediaNotificationKey(); // IMPORTANCE_MIN media streams are allowed to drift to the bottom final boolean aMedia = a.key.equals(mediaNotification) @@ -442,13 +451,43 @@ public class NotificationData { } }; - public NotificationData(Environment environment) { - mEnvironment = environment; - mGroupManager = environment.getGroupManager(); + private KeyguardEnvironment getEnvironment() { + if (mEnvironment == null) { + mEnvironment = Dependency.get(KeyguardEnvironment.class); + } + return mEnvironment; + } + + private ShadeController getShadeController() { + if (mShadeController == null) { + mShadeController = Dependency.get(ShadeController.class); + } + return mShadeController; + } + + private NotificationMediaManager getMediaManager() { + if (mMediaManager == null) { + mMediaManager = Dependency.get(NotificationMediaManager.class); + } + return mMediaManager; + } + + private ForegroundServiceController getFsc() { + if (mFsc == null) { + mFsc = Dependency.get(ForegroundServiceController.class); + } + return mFsc; + } + + private NotificationLockscreenUserManager getUserManager() { + if (mUserManager == null) { + mUserManager = Dependency.get(NotificationLockscreenUserManager.class); + } + return mUserManager; } /** - * Returns the sorted list of active notifications (depending on {@link Environment} + * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment} * * <p> * This call doesn't update the list of active notifications. Call {@link #filterAndSort()} @@ -468,7 +507,7 @@ public class NotificationData { for (int i = 0; i < N; i++) { Entry entry = mEntries.valueAt(i); final StatusBarNotification sbn = entry.notification; - if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) { + if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) { continue; } mFilteredForUser.add(entry); @@ -719,27 +758,27 @@ public class NotificationData { */ public boolean shouldFilterOut(Entry entry) { final StatusBarNotification sbn = entry.notification; - if (!(mEnvironment.isDeviceProvisioned() || + if (!(getEnvironment().isDeviceProvisioned() || showNotificationEvenIfUnprovisioned(sbn))) { return true; } - if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) { + if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) { return true; } - if (mEnvironment.isSecurelyLocked(sbn.getUserId()) && + if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET - || mEnvironment.shouldHideNotifications(sbn.getUserId()) - || mEnvironment.shouldHideNotifications(sbn.getKey()))) { + || getUserManager().shouldHideNotifications(sbn.getUserId()) + || getUserManager().shouldHideNotifications(sbn.getKey()))) { return true; } - if (mEnvironment.isDozing() && shouldSuppressAmbient(entry)) { + if (getShadeController().isDozing() && shouldSuppressAmbient(entry)) { return true; } - if (!mEnvironment.isDozing() && shouldSuppressNotificationList(entry)) { + if (!getShadeController().isDozing() && shouldSuppressNotificationList(entry)) { return true; } @@ -752,15 +791,16 @@ public class NotificationData { return true; } - if (mFsc.isDungeonNotification(sbn) && !mFsc.isDungeonNeededForUser(sbn.getUserId())) { + if (getFsc().isDungeonNotification(sbn) + && !getFsc().isDungeonNeededForUser(sbn.getUserId())) { // this is a foreground-service disclosure for a user that does not need to show one return true; } - if (mFsc.isSystemAlertNotification(sbn)) { + if (getFsc().isSystemAlertNotification(sbn)) { final String[] apps = sbn.getNotification().extras.getStringArray( Notification.EXTRA_FOREGROUND_APPS); if (apps != null && apps.length >= 1) { - if (!mFsc.isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) { + if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) { return true; } } @@ -838,18 +878,8 @@ public class NotificationData { /** * Provides access to keyguard state and user settings dependent data. */ - public interface Environment { - public boolean isSecurelyLocked(int userId); - public boolean shouldHideNotifications(int userid); - public boolean shouldHideNotifications(String key); - public boolean isDeviceProvisioned(); - public boolean isNotificationForCurrentProfiles(StatusBarNotification sbn); - public String getCurrentMediaNotificationKey(); - public NotificationGroupManager getGroupManager(); - - /** - * @return true iff the device is dozing - */ - boolean isDozing(); + public interface KeyguardEnvironment { + boolean isDeviceProvisioned(); + boolean isNotificationForCurrentProfiles(StatusBarNotification sbn); } } 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 28d339aaeab2..d136c4a24fbd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -62,6 +62,7 @@ import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.EventLogTags; import com.android.systemui.ForegroundServiceController; +import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.statusbar.NotificationLifetimeExtender; @@ -74,13 +75,15 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.NotificationUpdateHandler; +import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.RowInflaterTask; -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.NotificationGroupManager; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -110,33 +113,31 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>(); protected final NotificationClicker mNotificationClicker = new NotificationClicker(); - // Dependencies: - protected final NotificationLockscreenUserManager mLockscreenUserManager = - Dependency.get(NotificationLockscreenUserManager.class); - protected final NotificationGroupManager mGroupManager = + private final NotificationGroupManager mGroupManager = Dependency.get(NotificationGroupManager.class); - protected final NotificationGutsManager mGutsManager = + private final NotificationGutsManager mGutsManager = Dependency.get(NotificationGutsManager.class); - protected final NotificationRemoteInputManager mRemoteInputManager = - Dependency.get(NotificationRemoteInputManager.class); - protected final NotificationMediaManager mMediaManager = - Dependency.get(NotificationMediaManager.class); - protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); - protected final DeviceProvisionedController mDeviceProvisionedController = + private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); + private final DeviceProvisionedController mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); - protected final VisualStabilityManager mVisualStabilityManager = + private final VisualStabilityManager mVisualStabilityManager = Dependency.get(VisualStabilityManager.class); - protected final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); - protected final ForegroundServiceController mForegroundServiceController = + private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); + private final ForegroundServiceController mForegroundServiceController = Dependency.get(ForegroundServiceController.class); - protected final NotificationListener mNotificationListener = - Dependency.get(NotificationListener.class); - protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); + private final AmbientPulseManager mAmbientPulseManager = + Dependency.get(AmbientPulseManager.class); + + // Lazily retrieved dependencies + private NotificationRemoteInputManager mRemoteInputManager; + private NotificationMediaManager mMediaManager; + private NotificationListener mNotificationListener; + private ShadeController mShadeController; protected IDreamManager mDreamManager; protected IStatusBarService mBarService; - protected NotificationPresenter mPresenter; - protected Callback mCallback; + private NotificationPresenter mPresenter; + private Callback mCallback; protected PowerManager mPowerManager; protected NotificationListenerService.RankingMap mLatestRankingMap; protected HeadsUpManager mHeadsUpManager; @@ -149,7 +150,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. = new ArrayList<>(); private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener; - private final class NotificationClicker implements View.OnClickListener { @Override @@ -159,7 +159,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. return; } - mPresenter.wakeUpIfDozing(SystemClock.uptimeMillis(), v); + getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), v); final ExpandableNotificationRow row = (ExpandableNotificationRow) v; final StatusBarNotification sbn = row.getStatusBarNotification(); @@ -232,20 +232,55 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mDreamManager = IDreamManager.Stub.asInterface( ServiceManager.checkService(DreamService.DREAM_SERVICE)); mMessagingUtil = new NotificationMessagingUtil(context); + mNotificationData = new NotificationData(); + Dependency.get(InitController.class).addPostInitTask(this::onPostInit); + } + + private void onPostInit() { mGroupManager.setPendingEntries(mPendingNotifications); } + /** + * Our dependencies can have cyclic references, so some need to be lazy + */ + private NotificationRemoteInputManager getRemoteInputManager() { + if (mRemoteInputManager == null) { + mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); + } + return mRemoteInputManager; + } + + private NotificationMediaManager getMediaManager() { + if (mMediaManager == null) { + mMediaManager = Dependency.get(NotificationMediaManager.class); + } + return mMediaManager; + } + + private NotificationListener getNotificationListener() { + if (mNotificationListener == null) { + mNotificationListener = Dependency.get(NotificationListener.class); + } + return mNotificationListener; + } + + private ShadeController getShadeController() { + if (mShadeController == null) { + mShadeController = Dependency.get(ShadeController.class); + } + return mShadeController; + } + public void setUpWithPresenter(NotificationPresenter presenter, NotificationListContainer listContainer, Callback callback, HeadsUpManager headsUpManager) { mPresenter = presenter; mCallback = callback; - mNotificationData = new NotificationData(presenter); mHeadsUpManager = headsUpManager; mNotificationData.setHeadsUpManager(mHeadsUpManager); mListContainer = listContainer; - mHeadsUpObserver = new ContentObserver(mPresenter.getHandler()) { + mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) { @Override public void onChange(boolean selfChange) { boolean wasUsing = mUseHeadsUp; @@ -278,7 +313,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mNotificationLifetimeExtenders.add(mHeadsUpManager); mNotificationLifetimeExtenders.add(mAmbientPulseManager); mNotificationLifetimeExtenders.add(mGutsManager); - mNotificationLifetimeExtenders.addAll(mRemoteInputManager.getLifetimeExtenders()); + mNotificationLifetimeExtenders.addAll(getRemoteInputManager().getLifetimeExtenders()); for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) { extender.setCallback(key -> removeNotification(key, mLatestRankingMap)); @@ -294,6 +329,14 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. return mNotificationData; } + protected Context getContext() { + return mContext; + } + + protected NotificationPresenter getPresenter() { + return mPresenter; + } + public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() { return mGutsManager::openGuts; } @@ -348,7 +391,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. row.setInflationCallback(this); row.setLongPressListener(getNotificationLongClicker()); mListContainer.bindRow(row); - mRemoteInputManager.bindRow(row); + getRemoteInputManager().bindRow(row); // Get the app name. // Note that Notification.Builder#bindHeaderAppName has similar logic @@ -387,7 +430,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. true); NotificationData.Entry entry = mNotificationData.get(n.getKey()); - mRemoteInputManager.onPerformRemoveNotification(n, entry); + getRemoteInputManager().onPerformRemoveNotification(n, entry); final String pkg = n.getPackageName(); final String tag = n.getTag(); final int id = n.getId(); @@ -512,7 +555,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. // sending look longer than it takes. // Also we should not defer the removal if reordering isn't allowed since otherwise // some notifications can't disappear before the panel is closed. - boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key) + boolean ignoreEarliestRemovalTime = getRemoteInputManager().getController().isSpinning(key) && !FORCE_REMOTE_INPUT_HISTORY || !mVisualStabilityManager.isReorderingAllowed(); mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime); @@ -547,7 +590,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. extender.setShouldManageLifetime(entry, false /* shouldManage */); } - mMediaManager.onNotificationRemoved(key); + getMediaManager().onNotificationRemoved(key); mForegroundServiceController.removeNotification(entry.notification); if (entry.row != null) { @@ -602,8 +645,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. boolean isForeground = (row.getStatusBarNotification().getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; boolean keepForReply = - mRemoteInputManager.shouldKeepForRemoteInputHistory(childEntry) - || mRemoteInputManager.shouldKeepForSmartReplyHistory(childEntry); + getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry) + || getRemoteInputManager().shouldKeepForSmartReplyHistory(childEntry); if (isForeground || keepForReply) { // the child is a foreground service notification which we can't remove or it's // a child we're keeping around for reply! @@ -633,7 +676,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row) { - row.setNeedsRedaction(mLockscreenUserManager.needsRedaction(entry)); + row.setNeedsRedaction( + Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry)); boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey()); boolean isUpdate = mNotificationData.get(entry.key) != null; boolean wasLowPriority = row.isLowPriority(); @@ -818,7 +862,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mNotificationData.getImportance(key)); boolean alertAgain = alertAgain(entry, entry.notification.getNotification()); - if (mPresenter.isDozing()) { + if (getShadeController().isDozing()) { updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager); } else { updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager); @@ -833,7 +877,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. if (DEBUG) { // Is this for you? - boolean isForCurrentUser = mPresenter.isNotificationForCurrentProfiles(notification); + boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class) + .isNotificationForCurrentProfiles(notification); Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you"); } @@ -917,7 +962,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. public boolean shouldHeadsUp(NotificationData.Entry entry) { StatusBarNotification sbn = entry.notification; - if (mPresenter.isDozing()) { + if (getShadeController().isDozing()) { if (DEBUG) { Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey()); } @@ -998,7 +1043,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. protected boolean shouldPulse(NotificationData.Entry entry) { StatusBarNotification sbn = entry.notification; - if (!mPresenter.isDozing()) { + if (!getShadeController().isDozing()) { if (DEBUG) { Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey()); } @@ -1076,7 +1121,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. protected void setNotificationsShown(String[] keys) { try { - mNotificationListener.setNotificationsShown(keys); + getNotificationListener().setNotificationsShown(keys); } catch (RuntimeException e) { Log.d(TAG, "failed setNotificationsShown: ", e); } 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 e96e176cc503..b5fbde136c87 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 @@ -56,8 +56,9 @@ public class NotificationLogger { private final NotificationListenerService mNotificationListener = Dependency.get(NotificationListener.class); private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); + protected NotificationEntryManager mEntryManager + = Dependency.get(NotificationEntryManager.class); - protected NotificationEntryManager mEntryManager; protected Handler mHandler = new Handler(); protected IStatusBarService mBarService; private long mLastVisibilityReportUptimeMs; @@ -147,9 +148,7 @@ public class NotificationLogger { ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } - public void setUpWithEntryManager(NotificationEntryManager entryManager, - NotificationListContainer listContainer) { - mEntryManager = entryManager; + public void setUpWithContainer(NotificationListContainer listContainer) { mListContainer = listContainer; } 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 f4ef0f865a76..24999525ab72 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 @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; @@ -41,15 +42,23 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.annotations.VisibleForTesting; 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.Dumpable; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.statusbar.NotificationLifetimeExtender; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -71,14 +80,20 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx // Dependencies: private final NotificationLockscreenUserManager mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class); + private final StatusBarStateController mStatusBarStateController = + Dependency.get(StatusBarStateController.class); + private final DeviceProvisionedController mDeviceProvisionedController = + Dependency.get(DeviceProvisionedController.class); + private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); // which notification is currently being longpress-examined by the user + private final IStatusBarService mBarService; private NotificationGuts mNotificationGutsExposed; private NotificationMenuRowPlugin.MenuItem mGutsMenuItem; - private NotificationPresenter mPresenter; private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback; + private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; - private NotificationInfo.CheckSaveListener mCheckSaveListener; + private CheckSaveListener mCheckSaveListener; private OnSettingsClickListener mOnSettingsClickListener; @VisibleForTesting protected String mKeyToRemoveOnGutsClosed; @@ -89,16 +104,17 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); + mBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } public void setUpWithPresenter(NotificationPresenter presenter, NotificationListContainer listContainer, - NotificationInfo.CheckSaveListener checkSaveListener, - OnSettingsClickListener onSettingsClickListener) { + CheckSaveListener checkSave, OnSettingsClickListener onSettingsClick) { mPresenter = presenter; mListContainer = listContainer; - mCheckSaveListener = checkSaveListener; - mOnSettingsClickListener = onSettingsClickListener; + mCheckSaveListener = checkSave; + mOnSettingsClickListener = onSettingsClick; } public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) { @@ -264,7 +280,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx onSettingsClick = (View v, NotificationChannel channel, int appUid) -> { mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO); guts.resetFalsingCheck(); - mOnSettingsClickListener.onClick(sbn.getKey()); + mOnSettingsClickListener.onSettingsClick(sbn.getKey()); startAppNotificationSettingsActivity(packageName, appUid, channel, row); }; } @@ -279,7 +295,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mCheckSaveListener, onSettingsClick, onAppSettingsClick, - mPresenter.isDeviceProvisioned(), + mDeviceProvisionedController.isDeviceProvisioned(), row.getIsNonblockable(), isForBlockingHelper, row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE); @@ -317,6 +333,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mNotificationGutsExposed = guts; } + public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() { + return this::openGuts; + } + /** * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for * the normal half-swipe and long-press use cases via a circular reveal. When the blocking @@ -385,7 +405,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx guts.setVisibility(View.VISIBLE); final boolean needsFalsingProtection = - (mPresenter.isPresenterLocked() && + (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !mAccessibilityManager.isTouchExplorationEnabled()); guts.openControls( @@ -442,6 +462,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } public interface OnSettingsClickListener { - void onClick(String key); + public void onSettingsClick(String key); } } 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 659f6c75c703..c9cbb40317da 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 @@ -44,7 +44,6 @@ import android.os.Bundle; import android.os.ServiceManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; - import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -88,33 +87,33 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEv import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; +import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarStateController.StateListener; +import com.android.systemui.statusbar.notification.FakeShadowView; +import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +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.VisibilityLocationProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; -import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.row.NotificationGuts; -import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationSnooze; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.FakeShadowView; -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.VisibilityLocationProvider; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; @@ -125,6 +124,7 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChan import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.NotificationPanelView; import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -435,6 +435,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private float mVerticalPanelTranslation; private final NotificationLockscreenUserManager mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class); + protected final NotificationGutsManager mGutsManager = + Dependency.get(NotificationGutsManager.class); private final Rect mTmpRect = new Rect(); private final NotificationEntryManager mEntryManager = Dependency.get(NotificationEntryManager.class); @@ -454,6 +456,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN; private NotificationPanelView mNotificationPanel; + private final ShadeController mShadeController = Dependency.get(ShadeController.class); private final NotificationGutsManager mNotificationGutsManager = Dependency.get(NotificationGutsManager.class); @@ -4740,7 +4743,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void setStatusBarState(int statusBarState) { + @VisibleForTesting + protected void setStatusBarState(int statusBarState) { mStatusBarState = statusBarState; mAmbientState.setStatusBarState(statusBarState); } @@ -4942,7 +4946,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return; } - mStatusBar.addPostCollapseAction(() -> { + mShadeController.addPostCollapseAction(() -> { setDismissAllInProgress(false); for (ExpandableNotificationRow rowToRemove : viewsToRemove) { if (canChildBeDismissed(rowToRemove)) { @@ -5042,6 +5046,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mNotificationPanel = notificationPanelView; } + public void updateIconAreaViews() { + mIconAreaController.updateNotificationIcons(); + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ @@ -5662,7 +5670,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd if (mNotificationPanel.onDraggedDown() || startingChild != null) { // We have notifications, go to locked shade. - mStatusBar.goToLockedShade(startingChild); + mShadeController.goToLockedShade(startingChild); if (startingChild instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild; row.onExpandedByGesture(true /* drag down is always an open */); 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 c094669e67bf..8325bf8085bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -32,6 +32,7 @@ import com.android.systemui.Dependency; 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 java.io.PrintWriter; @@ -95,6 +96,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { */ private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f; + private final NotificationMediaManager mMediaManager = + Dependency.get(NotificationMediaManager.class); private PowerManager mPowerManager; private Handler mHandler = new Handler(); private PowerManager.WakeLock mWakeLock; @@ -264,7 +267,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { case MODE_WAKE_AND_UNLOCK: if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) { Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING"); - mStatusBar.updateMediaMetaData(false /* metaDataChanged */, + mMediaManager.updateMediaMetaData(false /* metaDataChanged */, true /* allowEnterAnimation */); } else if (mMode == MODE_WAKE_AND_UNLOCK){ Trace.beginSection("MODE_WAKE_AND_UNLOCK"); 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 a781be69c93e..fa63831b5e1b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -64,11 +64,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private StatusBar mStatusBarComponent; private DarkIconManager mDarkIconManager; private View mOperatorNameFrame; + private CommandQueue mCommandQueue; private SignalCallback mSignalCallback = new SignalCallback() { @Override public void setIsAirplaneMode(NetworkController.IconState icon) { - mStatusBarComponent.recomputeDisableFlags(true /* animate */); + mCommandQueue.recomputeDisableFlags(true /* animate */); } }; @@ -78,6 +79,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); mNetworkController = Dependency.get(NetworkController.class); mStatusBarComponent = SysUiServiceProvider.getComponent(getContext(), StatusBar.class); + mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class); } @Override @@ -116,13 +118,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onResume() { super.onResume(); - SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this); + mCommandQueue.addCallbacks(this); } @Override public void onPause() { super.onPause(); - SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this); + mCommandQueue.removeCallbacks(this); } @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 072343a8b101..32c930132977 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -787,6 +787,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mIndicationController = keyguardIndicationController; } + public void showTransientIndication(int id) { + mIndicationController.showTransientIndication(id); + } + public void updateLeftAffordance() { updateLeftAffordanceIcon(); updateLeftPreview(); 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 8ac867727e65..235629bbb509 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.phone; -import static com.android.keyguard.KeyguardHostView.OnDismissAction; +import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; import static com.android.keyguard.KeyguardSecurityModel.SecurityMode; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java index 76ddca47d33e..6111178bbac9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java @@ -16,9 +16,7 @@ package com.android.systemui.statusbar.phone; -import android.annotation.Nullable; - -import com.android.keyguard.KeyguardHostView.OnDismissAction; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; /** Executes actions that require the screen to be unlocked. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java index d67669289915..462201c6dac2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.phone; import android.util.Log; -import com.android.keyguard.KeyguardHostView.OnDismissAction; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; /** * Executes actions that require the screen to be unlocked. Delegates the actual handling to an diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java new file mode 100644 index 000000000000..b3423a84542e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static com.android.systemui.statusbar.phone.StatusBar.DEBUG; +import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG; + +import android.service.notification.StatusBarNotification; +import android.util.Log; + +import com.android.systemui.Dependency; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; + +public class KeyguardEnvironmentImpl implements KeyguardEnvironment { + + private static final String TAG = "KeyguardEnvironmentImpl"; + + private final NotificationLockscreenUserManager mLockscreenUserManager = + Dependency.get(NotificationLockscreenUserManager.class); + private final DeviceProvisionedController mDeviceProvisionedController = + Dependency.get(DeviceProvisionedController.class); + private final NotificationMediaManager mMediaManager = + Dependency.get(NotificationMediaManager.class); + + public KeyguardEnvironmentImpl() { + } + + @Override // NotificationData.KeyguardEnvironment + public boolean isDeviceProvisioned() { + return mDeviceProvisionedController.isDeviceProvisioned(); + } + + @Override // NotificationData.KeyguardEnvironment + public boolean isNotificationForCurrentProfiles(StatusBarNotification n) { + final int notificationUserId = n.getUserId(); + if (DEBUG && MULTIUSER_DEBUG) { + Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", n, + mLockscreenUserManager.getCurrentUserId(), notificationUserId)); + } + return mLockscreenUserManager.isCurrentProfile(notificationUserId); + } +} 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 40ddf5b497ae..673cdb7c78ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -40,6 +40,8 @@ import android.app.WallpaperColors; import android.util.Log; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.Dependency; +import com.android.systemui.statusbar.NotificationMediaManager; import libcore.io.IoUtils; @@ -52,6 +54,9 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen private static final String TAG = "LockscreenWallpaper"; + private final NotificationMediaManager mMediaManager = + Dependency.get(NotificationMediaManager.class); + private final StatusBar mBar; private final WallpaperManager mWallpaperManager; private final Handler mH; @@ -193,7 +198,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen mCached = true; mCache = result.bitmap; mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null); - mBar.updateMediaMetaData( + mMediaManager.updateMediaMetaData( true /* metaDataChanged */, true /* allowEnterAnimation */); } mLoader = null; 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 80f35060b737..f105f6121709 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -95,6 +95,7 @@ import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyButtonDrawable; import com.android.systemui.statusbar.policy.KeyButtonView; import com.android.systemui.statusbar.policy.RotationLockController; @@ -126,6 +127,9 @@ public class NavigationBarFragment extends Fragment implements Callbacks { /** Allow some time inbetween the long press for back and recents. */ private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; + private final DeviceProvisionedController mDeviceProvisionedController = + Dependency.get(DeviceProvisionedController.class); + protected NavigationBarView mNavigationBarView = null; protected AssistManager mAssistManager; @@ -725,7 +729,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } private boolean shouldDisableNavbarGestures() { - return !mStatusBar.isDeviceProvisioned() + return !mDeviceProvisionedController.isDeviceProvisioned() || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0; } 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 e92656ae0c02..a2bd00eae6c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -350,7 +350,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav @Override public boolean onTouchEvent(MotionEvent event) { shouldDeadZoneConsumeTouchEvents(event); - if (mGestureHelper.onTouchEvent(event)) { + if (mGestureHelper != null && mGestureHelper.onTouchEvent(event)) { return true; } return super.onTouchEvent(event); @@ -680,7 +680,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } public void onNavigationButtonLongPress(View v) { - mGestureHelper.onNavigationButtonLongPress(v); + if (mGestureHelper != null) { + mGestureHelper.onNavigationButtonLongPress(v); + } } public void onPanelExpandedChange(boolean expanded) { @@ -807,7 +809,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav @Override protected void onDraw(Canvas canvas) { - mGestureHelper.onDraw(canvas); + if (mGestureHelper != null) { + mGestureHelper.onDraw(canvas); + } mDeadZone.onDraw(canvas); super.onDraw(canvas); } @@ -819,7 +823,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds); updateButtonLocationOnScreen(getRecentsButton(), mRecentsButtonBounds); updateButtonLocationOnScreen(getRotateSuggestionButton(), mRotationButtonBounds); - mGestureHelper.onLayout(changed, left, top, right, bottom); + if (mGestureHelper != null) { + mGestureHelper.onLayout(changed, left, top, right, bottom); + } mRecentsOnboarding.setNavBarHeight(getMeasuredHeight()); } @@ -1117,7 +1123,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav pw.println(" }"); mContextualButtonGroup.dump(pw); - mGestureHelper.dump(pw); + if (mGestureHelper != null) { + mGestureHelper.dump(pw); + } mRecentsOnboarding.dump(pw); } 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 e31bad65dbb2..5ee08237e228 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.SysUiServiceProvider.getComponent; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator .ExpandAnimationParameters; @@ -65,10 +66,12 @@ 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.QSFragment; +import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; @@ -337,6 +340,11 @@ public class NotificationPanelView extends PanelView implements Dependency.get(NotificationEntryManager.class); private final StateListener mListener = this::setBarState; + private final CommandQueue mCommandQueue; + private final NotificationLockscreenUserManager mLockscreenUserManager = + Dependency.get(NotificationLockscreenUserManager.class); + private final ShadeController mShadeController = + Dependency.get(ShadeController.class); public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -347,6 +355,7 @@ public class NotificationPanelView extends PanelView implements setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); setPanelAlpha(255, false /* animate */); + mCommandQueue = getComponent(context, CommandQueue.class); } private void setStatusBar(StatusBar bar) { @@ -630,7 +639,7 @@ public class NotificationPanelView extends PanelView implements if (suppressedSummary) { continue; } - if (!mStatusBar.getNotificationLockscreenUserManager().shouldShowOnKeyguard( + if (!mLockscreenUserManager.shouldShowOnKeyguard( row.getStatusBarNotification())) { continue; } @@ -2414,7 +2423,7 @@ public class NotificationPanelView extends PanelView implements return true; case StatusBarState.SHADE_LOCKED: if (!mQsExpanded) { - mStatusBar.goToKeyguard(); + mShadeController.goToKeyguard(); } return true; case StatusBarState.SHADE: @@ -2617,7 +2626,7 @@ public class NotificationPanelView extends PanelView implements } if (showIconsWhenExpanded != mShowIconsWhenExpanded) { mShowIconsWhenExpanded = showIconsWhenExpanded; - mStatusBar.recomputeDisableFlags(false); + mCommandQueue.recomputeDisableFlags(false); } } @@ -2904,7 +2913,7 @@ public class NotificationPanelView extends PanelView implements if (hideIcons != mHideIconsDuringNotificationLaunch) { mHideIconsDuringNotificationLaunch = hideIcons; if (!hideIcons) { - mStatusBar.recomputeDisableFlags(true /* animate */); + mCommandQueue.recomputeDisableFlags(true /* animate */); } } } @@ -2972,6 +2981,7 @@ public class NotificationPanelView extends PanelView implements mNotificationStackScroller.updateSpeedBumpIndex(); mNotificationStackScroller.updateFooter(); updateShowEmptyShadeView(); + mNotificationStackScroller.updateIconAreaViews(); } public void onUpdateRowStates() { @@ -3019,6 +3029,10 @@ public class NotificationPanelView extends PanelView implements updateShowEmptyShadeView(); } + public void showTransientIndication(int id) { + mKeyguardBottomArea.showTransientIndication(id); + } + /** * Whenever a user drags down on the empty area (pulling down the shade and clock) and lets go. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 59863ecb1191..2129835945d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection; +import static com.android.systemui.SysUiServiceProvider.getComponent; import android.annotation.Nullable; import android.content.Context; @@ -42,6 +43,7 @@ import android.widget.LinearLayout; import com.android.systemui.Dependency; import com.android.systemui.EventLogTags; import com.android.systemui.R; +import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; @@ -52,6 +54,7 @@ public class PhoneStatusBarView extends PanelBar { private static final boolean DEBUG = StatusBar.DEBUG; private static final boolean DEBUG_GESTURES = false; private static final int NO_VALUE = Integer.MIN_VALUE; + private final CommandQueue mCommandQueue; StatusBar mBar; @@ -82,6 +85,7 @@ public class PhoneStatusBarView extends PanelBar { super(context, attrs); mBarTransitions = new PhoneStatusBarTransitions(this); + mCommandQueue = getComponent(context, CommandQueue.class); } public BarTransitions getBarTransitions() { @@ -166,7 +170,7 @@ public class PhoneStatusBarView extends PanelBar { @Override public boolean panelEnabled() { - return mBar.panelsEnabled(); + return mCommandQueue.panelsEnabled(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java new file mode 100644 index 000000000000..e546119968aa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.view.View; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; + +/** + * {@link ShadeController} is an abstraction of the work that used to be hard-coded in + * {@link StatusBar}. The shade itself represents the concept of the status bar window state, and + * can be in multiple states: dozing, locked, showing the bouncer, occluded, etc. All/some of these + * are coordinated with {@link StatusBarKeyguardViewManager} via + * {@link com.android.systemui.keyguard.KeyguardViewMediator} and others. + */ +public interface ShadeController { + + /** + * Shows the keyguard bouncer - the password challenge on the lock screen + * + * @param scrimmed true when the bouncer should show scrimmed, false when the user will be + * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)} + */ + void showBouncer(boolean scrimmed); + + /** + * Make our window larger and the panel expanded + */ + void instantExpandNotificationsPanel(); + + /** + * If the notifications panel is not fully expanded, collapse it animated. + * + * @return Seems to always return false + */ + boolean closeShadeIfOpen(); + + /** + * Add a runnable for NotificationPanelView to post when the panel is expanded. + * + * @param action the action to post + */ + void postOnShadeExpanded(Runnable action); + + /** + * Add a runnable to be executed after the shade collapses. Post-collapse runnables are + * aggregated and run serially. + * + * @param action the action to execute + */ + void addPostCollapseAction(Runnable action); + + /** + * Ask shade controller to set the state to {@link StatusBarState#KEYGUARD}, but only from + * {@link StatusBarState#SHADE_LOCKED} + */ + void goToKeyguard(); + + /** + * When the keyguard is showing and covered by something (bouncer, keyguard activity, etc.) it + * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager} + * + * @return whether the keyguard is currently occluded + */ + boolean isOccluded(); + + /** + * Notify the shade controller that the current user changed + * + * @param newUserId userId of the new user + */ + void setLockscreenUser(int newUserId); + + /** + * Dozing is when the screen is in AOD or asleep + * + * @return true if we are dozing + */ + boolean isDozing(); + + /** + * Ask the display to wake up if currently dozing, else do nothing + * + * @param time when to wake up + * @param view the view requesting the wakeup + */ + void wakeUpIfDozing(long time, View view); + + /** + * If secure with redaction: Show bouncer, go to unlocked shade. + * + * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> + * + * @param startingChild The view to expand after going to the shade. + */ + void goToLockedShade(View startingChild); + + /** + * Adds a {@param runnable} to be executed after Keyguard is gone. + */ + void addAfterKeyguardGoneRunnable(Runnable runnable); + + /** + * Close the shade if it was open + * + * @return true if the shade was open, else false + */ + boolean collapsePanel(); + + /** + * If {@param animate}, does the same as {@link #collapsePanel()}. Otherwise, instantly collapse + * the panel. Post collapse runnables will be executed + * + * @param animate + */ + void collapsePanel(boolean animate); + + /** + * Callback to tell the shade controller that an activity launch animation was canceled + */ + void onLaunchAnimationCancelled(); + + /** + * When notifications update, give the shade controller a chance to do thing in response to + * the new data set + */ + void updateAreThereNotifications(); + + /** + * Callback to notify the shade controller that a {@link ActivatableNotificationView} has become + * inactive + */ + void onActivationReset(); + +} 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 56e5a1e1bd6f..f56e219b073a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -26,10 +26,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; -import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT; import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID; -import static com.android.systemui.statusbar.NotificationLockscreenUserManager - .NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION; +import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT; import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF; import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; @@ -54,7 +52,6 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.StatusBarManager; -import android.app.TaskStackBuilder; import android.app.UiModeManager; import android.app.WallpaperInfo; import android.app.WallpaperManager; @@ -65,31 +62,21 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.IntentSender; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.PointF; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; import android.media.AudioAttributes; -import android.media.MediaMetadata; import android.metrics.LogMaker; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; @@ -105,14 +92,10 @@ import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; -import android.service.vr.IVrManager; -import android.service.vr.IVrStateCallbacks; -import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.Slog; -import android.util.SparseArray; import android.view.Display; import android.view.IWindowManager; import android.view.KeyEvent; @@ -122,7 +105,6 @@ import android.view.RemoteAnimationAdapter; import android.view.ThreadedRenderer; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -130,19 +112,15 @@ import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; import android.widget.DateTimeView; import android.widget.ImageView; -import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; 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.NotificationVisibility; import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.MessagingGroup; import com.android.internal.widget.MessagingMessage; -import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; @@ -152,11 +130,11 @@ import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.EventLogTags; +import com.android.systemui.InitController; import com.android.systemui.Interpolators; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.RecentsComponent; -import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUI; import com.android.systemui.SystemUIFactory; import com.android.systemui.UiOffloadThread; @@ -185,38 +163,34 @@ import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.WindowManagerProxy; -import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.AmbientPulseManager; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; -import com.android.systemui.statusbar.notification.AppOpsListener; 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.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyboardShortcuts; import com.android.systemui.statusbar.KeyguardIndicationController; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationData.Entry; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.row.NotificationInfo; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationViewHierarchyManager; -import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.ScrimView; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; +import com.android.systemui.statusbar.notification.AppOpsListener; +import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.NotificationData.Entry; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.VisualStabilityManager; +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.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; @@ -230,7 +204,6 @@ 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.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; @@ -253,9 +226,9 @@ import java.util.Map; public class StatusBar extends SystemUI implements DemoMode, ActivityStarter, OnUnlockMethodChangedListener, OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback, - ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter, - StatusBarStateController.StateListener, AmbientPulseManager.OnAmbientChangedListener, - ActivityLaunchAnimator.Callback { + ColorExtractor.OnColorsChangedListener, ConfigurationListener, + StatusBarStateController.StateListener, ShadeController, + ActivityLaunchAnimator.Callback, AmbientPulseManager.OnAmbientChangedListener { public static final boolean MULTIUSER_DEBUG = false; public static final boolean ENABLE_CHILD_NOTIFICATIONS @@ -324,10 +297,10 @@ public class StatusBar extends SystemUI implements DemoMode, /** If true, the system is in the half-boot-to-decryption-screen state. * Prudently disable QS and notifications. */ - private static final boolean ONLY_CORE_APPS; + public static final boolean ONLY_CORE_APPS; /** If true, the lockscreen will show a distinct wallpaper */ - private static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true; + public static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true; static { boolean onlyCoreApps; @@ -376,7 +349,6 @@ public class StatusBar extends SystemUI implements DemoMode, // expanded notifications protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window - private TextView mNotificationPanelDebugText; // settings private QSPanel mQSPanel; @@ -385,15 +357,12 @@ public class StatusBar extends SystemUI implements DemoMode, // RemoteInputView to be activated after unlock private View mPendingRemoteInputView; - private View mPendingWorkRemoteInputView; private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class); private View mReportRejectedTouch; - private int mMaxAllowedKeyguardNotifications; - private boolean mExpandedVisible; private final int[] mAbsPos = new int[2]; @@ -460,7 +429,6 @@ public class StatusBar extends SystemUI implements DemoMode, private int mInteractingWindows; private boolean mAutohideSuspended; private int mStatusBarMode; - private int mMaxKeyguardNotifications; private ViewMediatorCallback mKeyguardViewMediatorCallback; protected ScrimController mScrimController; @@ -479,9 +447,6 @@ public class StatusBar extends SystemUI implements DemoMode, protected BackDropView mBackdrop; protected ImageView mBackdropFront, mBackdropBack; - protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); - protected final PorterDuffXfermode mSrcOverXferMode = - new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER); private NotificationMediaManager mMediaManager; protected NotificationLockscreenUserManager mLockscreenUserManager; @@ -524,7 +489,6 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mIsOccluded; private boolean mWereIconsJustHidden; private boolean mBouncerWasShowingWhenHidden; - private boolean mIsCollapsingToShowActivityOverLockscreen; // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over, // this animation is tied to the scrim for historic reasons. @@ -557,13 +521,9 @@ public class StatusBar extends SystemUI implements DemoMode, private BatteryController mBatteryController; protected boolean mPanelExpanded; private UiModeManager mUiModeManager; - private boolean mKeyguardRequested; private boolean mIsKeyguard; private LogMaker mStatusBarStateLog; - private final LockscreenGestureLogger mLockscreenGestureLogger = - Dependency.get(LockscreenGestureLogger.class); protected NotificationIconAreaController mNotificationIconAreaController; - private boolean mReinflateNotificationsOnUserSwitched; @Nullable private View mAmbientIndicationContainer; private SysuiColorExtractor mColorExtractor; private ScreenLifecycle mScreenLifecycle; @@ -598,10 +558,10 @@ public class StatusBar extends SystemUI implements DemoMode, private NavigationBarFragment mNavigationBar; private View mNavigationBarView; - protected ActivityLaunchAnimator mActivityLaunchAnimator; private HeadsUpAppearanceController mHeadsUpAppearanceController; private boolean mVibrateOnOpening; private VibratorHelper mVibratorHelper; + protected NotificationPresenter mPresenter; @Override public void start() { @@ -626,11 +586,11 @@ public class StatusBar extends SystemUI implements DemoMode, mEntryManager = Dependency.get(NotificationEntryManager.class); mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); mAppOpsListener = Dependency.get(AppOpsListener.class); - mAppOpsListener.setUpWithPresenter(this, mEntryManager); mZenController = Dependency.get(ZenModeController.class); mKeyguardViewMediator = getComponent(KeyguardViewMediator.class); - mColorExtractor = Dependency.get(SysuiColorExtractor.class); + mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); + mColorExtractor.addOnColorsChangedListener(this); mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR); @@ -659,17 +619,12 @@ public class StatusBar extends SystemUI implements DemoMode, mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); - mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mRecents = getComponent(Recents.class); mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - mLockPatternUtils = new LockPatternUtils(mContext); - - mMediaManager.setUpWithPresenter(this, mEntryManager); // Connect in to the status bar manager service mCommandQueue = getComponent(CommandQueue.class); @@ -696,7 +651,6 @@ public class StatusBar extends SystemUI implements DemoMode, wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */); mWallpaperChangedReceiver.onReceive(mContext, null); - mLockscreenUserManager.setUpWithPresenter(this, mEntryManager); mCommandQueue.disable(switches[0], switches[6], false /* animate */); setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff, fullscreenStackBounds, dockedStackBounds); @@ -711,7 +665,16 @@ public class StatusBar extends SystemUI implements DemoMode, } // Set up the initial notification state. - mNotificationListener.setUpWithPresenter(this, mEntryManager); + mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel, + mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, + mScrimController, this); + mAppOpsListener.setUpWithPresenter(mPresenter); + mNotificationListener.setUpWithPresenter(mPresenter); + mNotificationShelf.setOnActivatedListener(mPresenter); + mRemoteInputManager.getController().addCallback(mStatusBarWindowController); + + // set the initial view visibility + Dependency.get(InitController.class).addPostInitTask(this::updateAreThereNotifications); if (DEBUG) { Log.d(TAG, String.format( @@ -724,24 +687,12 @@ public class StatusBar extends SystemUI implements DemoMode, )); } - setHeadsUpUser(mLockscreenUserManager.getCurrentUserId()); - IntentFilter internalFilter = new IntentFilter(); internalFilter.addAction(BANNER_ACTION_CANCEL); internalFilter.addAction(BANNER_ACTION_SETUP); mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF, null); - IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService( - Context.VR_SERVICE)); - if (vrManager != null) { - try { - vrManager.registerListener(mVrStateCallbacks); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to register VR mode state listener: " + e); - } - } - IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface( ServiceManager.getService(Context.WALLPAPER_SERVICE)); try { @@ -788,24 +739,9 @@ public class StatusBar extends SystemUI implements DemoMode, // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel); mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller); - NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller; mZenController.addCallback(this); - mActivityLaunchAnimator = new ActivityLaunchAnimator(mStatusBarWindow, - this, - mNotificationPanel, - notifListContainer); - mGutsManager.setUpWithPresenter(this, notifListContainer, mCheckSaveListener, - key -> { - try { - mBarService.onNotificationSettingsViewed(key); - } catch (RemoteException e) { - // if we're here we're dead - } - }); - mNotificationLogger.setUpWithEntryManager(mEntryManager, notifListContainer); - mAboveShelfObserver = new AboveShelfObserver(mStackScroller); - mAboveShelfObserver.setListener(mStatusBarWindow.findViewById( - R.id.notification_container_parent)); + NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller; + mNotificationLogger.setUpWithContainer(notifListContainer); mNotificationIconAreaController = SystemUIFactory.getInstance() .createNotificationIconAreaController(context, this); @@ -850,7 +786,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow); mHeadsUpAppearanceController.readFrom(oldController); mStatusBarWindow.setStatusBarView(mStatusBarView); - setAreThereNotifications(); + updateAreThereNotifications(); checkBarModes(); }).getFragmentManager() .beginTransaction() @@ -872,13 +808,6 @@ public class StatusBar extends SystemUI implements DemoMode, mGroupManager.setHeadsUpManager(mHeadsUpManager); putComponent(HeadsUpManager.class, mHeadsUpManager); - mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager); - mViewHierarchyManager.setUpWithPresenter(this, mEntryManager, notifListContainer); - - if (MULTIUSER_DEBUG) { - mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info); - mNotificationPanelDebugText.setVisibility(View.VISIBLE); - } try { boolean showNav = mWindowManagerService.hasNavigationBar(); @@ -890,14 +819,16 @@ public class StatusBar extends SystemUI implements DemoMode, // no window manager? good luck with that } - mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop); - mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front); - mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back); - if (ENABLE_LOCKSCREEN_WALLPAPER) { mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler); } + mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop); + mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front); + mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back); + mMediaManager.setup(mBackdrop, mBackdropFront, mBackdropBack, mBiometricUnlockController, + mScrimController, mLockscreenWallpaper); + mKeyguardIndicationController = SystemUIFactory.getInstance().createKeyguardIndicationController(mContext, mStatusBarWindow.findViewById(R.id.keyguard_indication_area), @@ -908,9 +839,6 @@ public class StatusBar extends SystemUI implements DemoMode, mAmbientIndicationContainer = mStatusBarWindow.findViewById( R.id.ambient_indication_container); - // set the initial view visibility - setAreThereNotifications(); - // TODO: Find better place for this callback. mBatteryController.addCallback(new BatteryStateChangeCallback() { @Override @@ -1053,6 +981,30 @@ public class StatusBar extends SystemUI implements DemoMode, ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); } + @Override + public void addAfterKeyguardGoneRunnable(Runnable runnable) { + mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); + } + + @Override + public boolean isDozing() { + return mDozing && mNotificationPanel.isFullyDark(); + } + + @Override + public void wakeUpIfDozing(long time, View where) { + if (mDozing) { + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + pm.wakeUp(time, "com.android.systemui:NODOZE"); + mWakeUpComingFromTouch = true; + where.getLocationInWindow(mTmpInt2); + mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, + mTmpInt2[1] + where.getHeight() / 2); + mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested(); + mFalsingManager.onScreenOnFromTouch(); + } + } + protected void createNavigationBar() { mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> { mNavigationBar = (NavigationBarFragment) fragment; @@ -1084,7 +1036,6 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationShelf = (NotificationShelf) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_shelf, mStackScroller, false); - mNotificationShelf.setOnActivatedListener(this); mNotificationShelf.setOnClickListener(mGoToLockedShadeListener); } @@ -1092,13 +1043,6 @@ public class StatusBar extends SystemUI implements DemoMode, public void onDensityOrFontScaleChanged() { MessagingMessage.dropCache(); MessagingGroup.dropCache(); - // start old BaseStatusBar.onDensityOrFontScaleChanged(). - if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) { - mEntryManager.updateNotificationsOnDensityOrFontScaleChanged(); - } else { - mReinflateNotificationsOnUserSwitched = true; - } - // end old BaseStatusBar.onDensityOrFontScaleChanged(). // TODO: Remove this. if (mBrightnessMirrorController != null) { mBrightnessMirrorController.onDensityOrFontScaleChanged(); @@ -1169,8 +1113,6 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController, this, UnlockMethodCache.getInstance(mContext)); mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, getBouncerContainer(), mNotificationPanel, mBiometricUnlockController); - //TODO: Can we put the keyguard view manager in Dependency? - mLockscreenUserManager.setKeyguardViewManager(mStatusBarKeyguardViewManager); mKeyguardIndicationController .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); @@ -1234,73 +1176,13 @@ public class StatusBar extends SystemUI implements DemoMode, return true; } - @Override - public void onPerformRemoveNotification(StatusBarNotification n) { - if (mNotificationPanel.hasPulsingNotifications() && - !mAmbientPulseManager.hasNotifications()) { - // We were showing a pulse for a notification, but no notifications are pulsing anymore. - // Finish the pulse. - mDozeScrimController.pulseOutNow(); - } - } - - @Override - public void updateNotificationViews() { - // The function updateRowStates depends on both of these being non-null, so check them here. - // We may be called before they are set from DeviceProvisionedController's callback. - if (mScrimController == null) return; - - // Do not modify the notifications during collapse. - if (isCollapsing()) { - addPostCollapseAction(this::updateNotificationViews); - return; - } - - mViewHierarchyManager.updateNotificationViews(); - - mNotificationPanel.updateNotificationViews(); - - updateQsExpansionEnabled(); - - // Let's also update the icons - mNotificationIconAreaController.updateNotificationIcons(); - } - - @Override - public void onNotificationAdded(Entry shadeEntry) { - // Recalculate the position of the sliding windows and the titles. - setAreThereNotifications(); - } - - @Override - public void onNotificationUpdated(StatusBarNotification notification) { - setAreThereNotifications(); - } - - @Override - public void onNotificationRemoved(String key, StatusBarNotification old) { - if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); - - if (old != null) { - if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications() - && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) { - if (mState == StatusBarState.SHADE) { - animateCollapsePanels(); - } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) { - goToKeyguard(); - } - } - } - setAreThereNotifications(); - } - /** * Disable QS if device not provisioned. * If the user switcher is simple then disable QS during setup because * the user intends to use the lock screen user switcher, QS in not needed. */ private void updateQsExpansionEnabled() { - mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() + mNotificationPanel.setQsExpansionEnabled(mDeviceProvisionedController.isDeviceProvisioned() && (mUserSetup || mUserSwitcherController == null || !mUserSwitcherController.isSimpleUserSwitcher()) && ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0) @@ -1337,12 +1219,11 @@ public class StatusBar extends SystemUI implements DemoMode, mEntryManager.updateNotifications(); } - protected void setAreThereNotifications() { - + public void updateAreThereNotifications() { if (SPEW) { final boolean clearable = hasActiveNotifications() && mNotificationPanel.hasActiveClearableNotifications(); - Log.d(TAG, "setAreThereNotifications: N=" + + Log.d(TAG, "updateAreThereNotifications: N=" + mEntryManager.getNotificationData().getActiveNotifications().size() + " any=" + hasActiveNotifications() + " clearable=" + clearable); } @@ -1368,192 +1249,9 @@ public class StatusBar extends SystemUI implements DemoMode, .start(); } } - mMediaManager.findAndUpdateMediaNotifications(); } - - /** - * Hide the album artwork that is fading out and release its bitmap. - */ - protected final Runnable mHideBackdropFront = new Runnable() { - @Override - public void run() { - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: removing fade layer"); - } - mBackdropFront.setVisibility(View.INVISIBLE); - mBackdropFront.animate().cancel(); - mBackdropFront.setImageDrawable(null); - } - }; - - // TODO: Move this to NotificationMediaManager. - /** - * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper. - */ - @Override - public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) { - Trace.beginSection("StatusBar#updateMediaMetaData"); - if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) { - Trace.endSection(); - return; - } - - if (mBackdrop == null) { - Trace.endSection(); - return; // called too early - } - - boolean wakeAndUnlock = mBiometricUnlockController != null - && mBiometricUnlockController.isWakeAndUnlock(); - if (mLaunchTransitionFadingAway || wakeAndUnlock) { - mBackdrop.setVisibility(View.INVISIBLE); - Trace.endSection(); - return; - } - - MediaMetadata mediaMetadata = mMediaManager.getMediaMetadata(); - - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " - + mMediaManager.getMediaNotificationKey() - + " metadata=" + mediaMetadata - + " metaDataChanged=" + metaDataChanged - + " state=" + mState); - } - - Drawable artworkDrawable = null; - if (mediaMetadata != null) { - Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); - if (artworkBitmap == null) { - artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); - // might still be null - } - if (artworkBitmap != null) { - artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap); - } - } - boolean allowWhenShade = false; - if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) { - Bitmap lockWallpaper = mLockscreenWallpaper.getBitmap(); - if (lockWallpaper != null) { - artworkDrawable = new LockscreenWallpaper.WallpaperDrawable( - mBackdropBack.getResources(), lockWallpaper); - // We're in the SHADE mode on the SIM screen - yet we still need to show - // the lockscreen wallpaper in that mode. - allowWhenShade = mStatusBarKeyguardViewManager != null - && mStatusBarKeyguardViewManager.isShowing(); - } - } - - boolean hideBecauseOccluded = mStatusBarKeyguardViewManager != null - && mStatusBarKeyguardViewManager.isOccluded(); - - final boolean hasArtwork = artworkDrawable != null; - mColorExtractor.setHasBackdrop(hasArtwork); - if (mScrimController != null) { - mScrimController.setHasBackdrop(hasArtwork); - } - - if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) - && (mState != StatusBarState.SHADE || allowWhenShade) - && mBiometricUnlockController.getMode() - != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING - && !hideBecauseOccluded) { - // time to show some art! - if (mBackdrop.getVisibility() != View.VISIBLE) { - mBackdrop.setVisibility(View.VISIBLE); - if (allowEnterAnimation) { - mBackdrop.setAlpha(0); - mBackdrop.animate().alpha(1f); - } else { - mBackdrop.animate().cancel(); - mBackdrop.setAlpha(1f); - } - mStatusBarWindowController.setBackdropShowing(true); - metaDataChanged = true; - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork"); - } - } - if (metaDataChanged) { - if (mBackdropBack.getDrawable() != null) { - Drawable drawable = - mBackdropBack.getDrawable().getConstantState() - .newDrawable(mBackdropFront.getResources()).mutate(); - mBackdropFront.setImageDrawable(drawable); - mBackdropFront.setAlpha(1f); - mBackdropFront.setVisibility(View.VISIBLE); - } else { - mBackdropFront.setVisibility(View.INVISIBLE); - } - - if (DEBUG_MEDIA_FAKE_ARTWORK) { - final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF); - Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c)); - mBackdropBack.setBackgroundColor(0xFFFFFFFF); - mBackdropBack.setImageDrawable(new ColorDrawable(c)); - } else { - mBackdropBack.setImageDrawable(artworkDrawable); - } - - if (mBackdropFront.getVisibility() == View.VISIBLE) { - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from " - + mBackdropFront.getDrawable() - + " to " - + mBackdropBack.getDrawable()); - } - mBackdropFront.animate() - .setDuration(250) - .alpha(0f).withEndAction(mHideBackdropFront); - } - } - } else { - // need to hide the album art, either because we are unlocked, on AOD - // or because the metadata isn't there to support it - if (mBackdrop.getVisibility() != View.GONE) { - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork"); - } - boolean cannotAnimateDoze = mDozing && !ScrimState.AOD.getAnimateChange(); - if (mBiometricUnlockController.getMode() - == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING - || hideBecauseOccluded || cannotAnimateDoze) { - - // We are unlocking directly - no animation! - mBackdrop.setVisibility(View.GONE); - mBackdropBack.setImageDrawable(null); - mStatusBarWindowController.setBackdropShowing(false); - } else { - mStatusBarWindowController.setBackdropShowing(false); - mBackdrop.animate() - .alpha(0) - .setInterpolator(Interpolators.ACCELERATE_DECELERATE) - .setDuration(300) - .setStartDelay(0) - .withEndAction(() -> { - mBackdrop.setVisibility(View.GONE); - mBackdropFront.animate().cancel(); - mBackdropBack.setImageDrawable(null); - mHandler.post(mHideBackdropFront); - }); - if (mKeyguardMonitor.isKeyguardFadingAway()) { - mBackdrop.animate() - // Make it disappear faster, as the focus should be on the activity - // behind. - .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2) - .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) - .setInterpolator(Interpolators.LINEAR) - .start(); - } - } - } - } - Trace.endSection(); - } - private void updateReportRejectedTouchVisibility() { if (mReportRejectedTouch == null) { return; @@ -1646,15 +1344,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - /** - * Reapplies the disable flags as last requested by StatusBarManager. - * - * This needs to be called if state used by adjustDisableFlags changes. - */ - public void recomputeDisableFlags(boolean animate) { - mCommandQueue.recomputeDisableFlags(animate); - } - protected H createHandler() { return new StatusBar.H(); } @@ -1695,53 +1384,6 @@ public class StatusBar extends SystemUI implements DemoMode, return mStatusBarStateController.getState() == StatusBarState.KEYGUARD; } - @Override - public boolean isDozing() { - return mDozing && mNotificationPanel.isFullyDark(); - } - - @Override - public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) { - if (isDozing()) { - return false; - } - - if (mIsOccluded) { - boolean devicePublic = mLockscreenUserManager. - isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); - boolean userPublic = devicePublic - || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId()); - boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry); - if (userPublic && needsRedaction) { - return false; - } - } - - if (!panelsEnabled()) { - if (DEBUG) { - Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey()); - } - return false; - } - - if (sbn.getNotification().fullScreenIntent != null) { - if (mAccessibilityManager.isTouchExplorationEnabled()) { - if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey()); - return false; - } else { - // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent - return !mStatusBarKeyguardViewManager.isShowing() - || mStatusBarKeyguardViewManager.isOccluded(); - } - } - return true; - } - - @Override // NotificationData.Environment - public String getCurrentMediaNotificationKey() { - return mMediaManager.getMediaNotificationKey(); - } - /** * To be called when there's a state change in StatusBarKeyguardViewManager. */ @@ -1815,12 +1457,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - protected void setHeadsUpUser(int newUserId) { - if (mHeadsUpManager != null) { - mHeadsUpManager.setUser(newUserId); - } - } - public boolean isKeyguardCurrentlySecure() { return !mUnlockMethodCache.canSkipBouncer(); } @@ -1868,6 +1504,11 @@ public class StatusBar extends SystemUI implements DemoMode, return mAmbientIndicationContainer; } + @Override + public boolean isOccluded() { + return mIsOccluded; + } + public void setOccluded(boolean occluded) { mIsOccluded = occluded; mScrimController.setKeyguardOccluded(occluded); @@ -1901,10 +1542,10 @@ public class StatusBar extends SystemUI implements DemoMode, mWereIconsJustHidden = true; mHandler.postDelayed(() -> { mWereIconsJustHidden = false; - recomputeDisableFlags(true); + mCommandQueue.recomputeDisableFlags(true); }, 500); } else { - recomputeDisableFlags(animate); + mCommandQueue.recomputeDisableFlags(animate); } } if (shouldHideIconsForBouncer) { @@ -1912,20 +1553,21 @@ public class StatusBar extends SystemUI implements DemoMode, } } + public boolean isHeadsUpShouldBeVisible() { + return mHeadsUpAppearanceController.shouldBeVisible(); + } + + //TODO: These can / should probably be moved to NotificationPresenter or ShadeController @Override public void onLaunchAnimationCancelled() { - if (!isCollapsing()) { + if (!mPresenter.isCollapsing()) { onClosingFinished(); } } - public boolean isHeadsUpShouldBeVisible() { - return mHeadsUpAppearanceController.shouldBeVisible(); - } - @Override public void onExpandAnimationFinished(boolean launchIsFullScreen) { - if (!isCollapsing()) { + if (!mPresenter.isCollapsing()) { onClosingFinished(); } if (launchIsFullScreen) { @@ -1935,8 +1577,9 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onExpandAnimationTimedOut() { - if (isPresenterFullyCollapsed() && !isCollapsing() - && !mActivityLaunchAnimator.isLaunchForActivity()) { + ActivityLaunchAnimator animator = mPresenter.getActivityLaunchAnimator(); + if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing() + && animator != null && !animator.isLaunchForActivity()) { onClosingFinished(); } else { collapsePanel(true /* animate */); @@ -1948,6 +1591,14 @@ public class StatusBar extends SystemUI implements DemoMode, return mState == StatusBarState.SHADE; } + public boolean isDeviceInVrMode() { + return mPresenter.isDeviceInVrMode(); + } + + public NotificationPresenter getPresenter() { + return mPresenter; + } + /** * All changes to the status bar and notifications funnel through here and are batched. */ @@ -2005,7 +1656,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void handleSystemKey(int key) { if (SPEW) Log.d(TAG, "handleNavigationKey: " + key); - if (!panelsEnabled() || !mKeyguardMonitor.isDeviceInteractive() + if (!mCommandQueue.panelsEnabled() || !mKeyguardMonitor.isDeviceInteractive() || mKeyguardMonitor.isShowing() && !mKeyguardMonitor.isOccluded()) { return; } @@ -2047,15 +1698,9 @@ public class StatusBar extends SystemUI implements DemoMode, } } - boolean panelsEnabled() { - return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 - && (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0 - && !ONLY_CORE_APPS; - } - void makeExpandedVisible(boolean force) { if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); - if (!force && (mExpandedVisible || !panelsEnabled())) { + if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) { return; } @@ -2066,7 +1711,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarWindowController.setPanelVisible(true); visibilityChanged(true); - recomputeDisableFlags(!force /* animate */); + mCommandQueue.recomputeDisableFlags(!force /* animate */); setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); } @@ -2099,12 +1744,12 @@ public class StatusBar extends SystemUI implements DemoMode, } } - @Override public void animateCollapsePanels(int flags) { animateCollapsePanels(flags, false /* force */, false /* delayed */, 1.0f /* speedUpFactor */); } + @Override public void animateCollapsePanels(int flags, boolean force) { animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */); } @@ -2155,7 +1800,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public void dispatchNotificationsPanelTouchEvent(MotionEvent ev) { - if (!panelsEnabled()) { + if (!mCommandQueue.panelsEnabled()) { return; } mNotificationPanel.dispatchTouchEvent(ev); @@ -2173,7 +1818,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void animateExpandNotificationsPanel() { if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); - if (!panelsEnabled()) { + if (!mCommandQueue.panelsEnabled()) { return ; } @@ -2185,7 +1830,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void animateExpandSettingsPanel(@Nullable String subPanel) { if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); - if (!panelsEnabled()) { + if (!mCommandQueue.panelsEnabled()) { return; } @@ -2233,12 +1878,13 @@ public class StatusBar extends SystemUI implements DemoMode, runPostCollapseRunnables(); setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); - if (!mIsCollapsingToShowActivityOverLockscreen) { + if (!mPresenter.isCollapsingToShowActivityOverLockscreen()) { showBouncerIfKeyguard(); } else if (DEBUG) { Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen"); } - recomputeDisableFlags(mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */); + mCommandQueue.recomputeDisableFlags( + mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */); // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in // the bouncer appear animation. @@ -2330,7 +1976,7 @@ public class StatusBar extends SystemUI implements DemoMode, // update low profile if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { - setAreThereNotifications(); + updateAreThereNotifications(); } // ready to unhide @@ -2703,9 +2349,6 @@ public class StatusBar extends SystemUI implements DemoMode, private void addStatusBarWindow() { makeStatusBarView(); mStatusBarWindowController = Dependency.get(StatusBarWindowController.class); - mRemoteInputManager.setUpWithPresenter(this, mEntryManager, this, - mNotificationPanel.createRemoteInputDelegate()); - mRemoteInputManager.getController().addCallback(mStatusBarWindowController); mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight()); } @@ -2750,7 +2393,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching, final Callback callback, int flags) { - if (onlyProvisioned && !isDeviceProvisioned()) return; + if (onlyProvisioned && !mDeviceProvisionedController.isDeviceProvisioned()) return; final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( mContext, intent, mLockscreenUserManager.getCurrentUserId()); @@ -2888,7 +2531,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } else if (ACTION_FAKE_ARTWORK.equals(action)) { if (DEBUG_MEDIA_FAKE_ARTWORK) { - updateMediaMetaData(true, true); + mPresenter.updateMediaMetaData(true, true); } } } @@ -2917,7 +2560,8 @@ public class StatusBar extends SystemUI implements DemoMode, dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone); } - private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, + @Override + public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) { if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP && mUnlockMethodCache.canSkipBouncer() @@ -2951,38 +2595,10 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void onUserSwitched(int newUserId) { - // Begin old BaseStatusBar.userSwitched - setHeadsUpUser(newUserId); - // End old BaseStatusBar.userSwitched - if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); - animateCollapsePanels(); - if (mReinflateNotificationsOnUserSwitched) { - mEntryManager.updateNotificationsOnDensityOrFontScaleChanged(); - mReinflateNotificationsOnUserSwitched = false; - } - updateNotificationViews(); - mMediaManager.clearCurrentMediaNotification(); - setLockscreenUser(newUserId); - mWallpaperChangedReceiver.onReceive(mContext, null); - } - - @Override - public NotificationLockscreenUserManager getNotificationLockscreenUserManager() { - return mLockscreenUserManager; - } - - @Override - public void onBindRow(Entry entry, PackageManager pmUser, - StatusBarNotification sbn, ExpandableNotificationRow row) { - row.setAboveShelfChangedListener(mAboveShelfObserver); - row.setSecureStateProvider(this::isKeyguardCurrentlySecure); - } - - protected void setLockscreenUser(int newUserId) { + public void setLockscreenUser(int newUserId) { mLockscreenWallpaper.setCurrentUser(newUserId); mScrimController.setCurrentUser(newUserId); - updateMediaMetaData(true, false); + mWallpaperChangedReceiver.onReceive(mContext, null); } /** @@ -3020,8 +2636,6 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStatusBarWindowController != null && mNaturalBarHeight != oldBarHeight) { mStatusBarWindowController.setBarHeight(mNaturalBarHeight); } - mMaxAllowedKeyguardNotifications = res.getInteger( - R.integer.keyguard_max_notification_count); if (DEBUG) Log.v(TAG, "defineSlots"); } @@ -3058,12 +2672,12 @@ public class StatusBar extends SystemUI implements DemoMode, if (visibleToUser) { boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp(); boolean clearNotificationEffects = - !isPresenterFullyCollapsed() && + !mPresenter.isPresenterFullyCollapsed() && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED); int notificationLoad = mEntryManager.getNotificationData().getActiveNotifications() .size(); - if (pinnedHeadsUp && isPresenterFullyCollapsed()) { + if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) { notificationLoad = 1; } final int finalNotificationLoad = notificationLoad; @@ -3298,13 +2912,8 @@ public class StatusBar extends SystemUI implements DemoMode, } } - @Override - public boolean isPresenterFullyCollapsed() { - return mNotificationPanel.isFullyCollapsed(); - } - public void showKeyguard() { - mKeyguardRequested = true; + mStatusBarStateController.setKeyguardRequested(true); mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); mPendingRemoteInputView = null; updateIsKeyguard(); @@ -3312,11 +2921,12 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean hideKeyguard() { - mKeyguardRequested = false; + mStatusBarStateController.setKeyguardRequested(false); return updateIsKeyguard(); } /** + * stop(tag) * @return True if StatusBar state is FULLSCREEN_USER_SWITCHER. */ public boolean isFullScreenUserSwitcherState() { @@ -3333,7 +2943,8 @@ public class StatusBar extends SystemUI implements DemoMode, // turned off fully. boolean keyguardForDozing = mDozingRequested && (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard)); - boolean shouldBeKeyguard = (mKeyguardRequested || keyguardForDozing) && !wakeAndUnlocking; + boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested() + || keyguardForDozing) && !wakeAndUnlocking; if (keyguardForDozing) { updatePanelExpansionForKeyguard(); } @@ -3385,13 +2996,7 @@ public class StatusBar extends SystemUI implements DemoMode, releaseGestureWakeLock(); runLaunchTransitionEndRunnable(); mLaunchTransitionFadingAway = false; - updateMediaMetaData(true /* metaDataChanged */, true); - } - - public boolean isCollapsing() { - return mNotificationPanel.isCollapsing() - || mActivityLaunchAnimator.isAnimationPending() - || mActivityLaunchAnimator.isAnimationRunning(); + mPresenter.updateMediaMetaData(true /* metaDataChanged */, true); } public void addPostCollapseAction(Runnable r) { @@ -3415,12 +3020,13 @@ public class StatusBar extends SystemUI implements DemoMode, mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); mLaunchTransitionEndRunnable = endRunnable; Runnable hideRunnable = () -> { + mKeyguardMonitor.setLaunchTransitionFadingAway(true); mLaunchTransitionFadingAway = true; if (beforeFading != null) { beforeFading.run(); } updateScrimController(); - updateMediaMetaData(false, true); + mPresenter.updateMediaMetaData(false, true); mNotificationPanel.setAlpha(1); mNotificationPanel.animate() .alpha(0) @@ -3505,9 +3111,8 @@ public class StatusBar extends SystemUI implements DemoMode, mLockscreenUserManager.updatePublicMode(); mEntryManager.updateNotifications(); } - View viewToClick = null; if (mStatusBarStateController.leaveOpenOnKeyguardHide()) { - if (!mKeyguardRequested) { + if (!mStatusBarStateController.isKeyguardRequested()) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); } long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay(); @@ -3516,10 +3121,6 @@ public class StatusBar extends SystemUI implements DemoMode, mDraggedDownRow.setUserLocked(false); mDraggedDownRow = null; } - if (!mKeyguardRequested) { - viewToClick = mPendingRemoteInputView; - mPendingRemoteInputView = null; - } // Disable layout transitions in navbar for this transition because the load is just // too heavy for the CPU and GPU on any device. @@ -3530,10 +3131,6 @@ public class StatusBar extends SystemUI implements DemoMode, instantCollapseNotificationPanel(); } - if (viewToClick != null && viewToClick.isAttachedToWindow()) { - viewToClick.callOnClick(); - } - // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile // visibilities so next time we open the panel we know the correct height already. if (mQSPanel != null) { @@ -3575,7 +3172,7 @@ public class StatusBar extends SystemUI implements DemoMode, mCommandQueue.appTransitionStarting(startTime + fadeoutDuration - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); - recomputeDisableFlags(fadeoutDuration > 0 /* animate */); + mCommandQueue.recomputeDisableFlags(fadeoutDuration > 0 /* animate */); mCommandQueue.appTransitionStarting( startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); @@ -3697,42 +3294,52 @@ public class StatusBar extends SystemUI implements DemoMode, } } - protected void showBouncer(boolean scrimmed) { + @Override + public void showBouncer(boolean scrimmed) { mStatusBarKeyguardViewManager.showBouncer(scrimmed); } - private void instantExpandNotificationsPanel() { + @Override + public void instantExpandNotificationsPanel() { // Make our window larger and the panel expanded. makeExpandedVisible(true); mNotificationPanel.expand(false /* animate */); - recomputeDisableFlags(false /* animate */); + mCommandQueue.recomputeDisableFlags(false /* animate */); } - private void instantCollapseNotificationPanel() { - mNotificationPanel.instantCollapse(); - runPostCollapseRunnables(); + @Override + public boolean closeShadeIfOpen() { + if (!mNotificationPanel.isFullyCollapsed()) { + mCommandQueue.animateCollapsePanels( + CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); + visibilityChanged(false); + mAssistManager.hideAssist(); + } + return false; } @Override - public void onActivated(ActivatableNotificationView view) { - onActivated((View) view); - mNotificationPanel.setActivatedChild(view); + public void postOnShadeExpanded(Runnable executable) { + mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (getStatusBarWindow().getHeight() != getStatusBarHeight()) { + mNotificationPanel.getViewTreeObserver() + .removeOnGlobalLayoutListener(this); + mNotificationPanel.post(executable); + } + } + }); } - public void onActivated(View view) { - mLockscreenGestureLogger.write( - MetricsEvent.ACTION_LS_NOTE, - 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); - mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again); - ActivatableNotificationView previousView = mNotificationPanel.getActivatedChild(); - if (previousView != null) { - previousView.makeInactive(true /* animate */); - } + private void instantCollapseNotificationPanel() { + mNotificationPanel.instantCollapse(); + runPostCollapseRunnables(); } @Override public void onStatePreChange(int oldState, int newState) { - // If we're visible and switched to SHADE_LOCKED (the user dragged // down on the lockscreen), clear notification LED, vibration, // ringing. @@ -3780,7 +3387,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateDozingState(); checkBarModes(); updateScrimController(); - updateMediaMetaData(false, mState != StatusBarState.KEYGUARD); + mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD); mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), mUnlockMethodCache.isMethodSecure(), mStatusBarKeyguardViewManager.isOccluded()); @@ -3797,6 +3404,7 @@ public class StatusBar extends SystemUI implements DemoMode, && DozeParameters.getInstance(mContext).shouldControlScreenOff(); mNotificationPanel.resetViews(dozingAnimated); + updateQsExpansionEnabled(); mKeyguardViewMediator.setAodShowing(mDozing); //TODO: make these folks listeners of StatusBarStateController.onDozingChanged @@ -3828,15 +3436,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarStateController.setIsDozing(dozing); } - @Override - public void onActivationReset(ActivatableNotificationView view) { - if (view == mNotificationPanel.getActivatedChild()) { - mNotificationPanel.setActivatedChild(null); - onActivationReset((View)view); - } - } - - public void onActivationReset(View view) { + public void onActivationReset() { mKeyguardIndicationController.hideTransientIndication(); } @@ -3846,7 +3446,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onClosingFinished() { runPostCollapseRunnables(); - if (!isPresenterFullyCollapsed()) { + if (!mPresenter.isPresenterFullyCollapsed()) { // if we set it not to be focusable when collapsing, we have to undo it when we aborted // the closing mStatusBarWindowController.setStatusBarFocusable(true); @@ -3886,22 +3486,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - @Override - public int getMaxNotificationsWhileLocked(boolean recompute) { - if (recompute) { - mMaxKeyguardNotifications = Math.max(1, - mNotificationPanel.computeMaxKeyguardNotifications( - mMaxAllowedKeyguardNotifications)); - return mMaxKeyguardNotifications; - } - return mMaxKeyguardNotifications; - } - - @Override - public void onUpdateRowStates() { - mNotificationPanel.onUpdateRowStates(); - } - // TODO: Figure out way to remove these. public NavigationBarView getNavigationBarView() { return (mNavigationBar != null ? (NavigationBarView) mNavigationBar.getView() : null); @@ -3958,184 +3542,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - public void onLockedNotificationImportanceChange(OnDismissAction dismissAction) { - mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); - dismissKeyguardThenExecute(dismissAction, true /* afterKeyguardGone */); - } - - @Override - public void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) { - mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); - showBouncer(true /* scrimmed */); - mPendingRemoteInputView = clicked; - } - - @Override - public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, - View clickedView) { - if (isKeyguardShowing()) { - onLockedRemoteInput(row, clickedView); - } else { - row.setUserExpanded(true); - row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick); - } - } - - @Override - public boolean shouldHandleRemoteInput(View view, PendingIntent pendingIntent) { - // Skip remote input as doing so will expand the notification shade. - return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0; - } - - @Override - public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, - Intent fillInIntent, NotificationRemoteInputManager.ClickHandler defaultHandler) { - final boolean isActivity = pendingIntent.isActivity(); - if (isActivity) { - final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( - mContext, pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId()); - dismissKeyguardThenExecute(() -> { - try { - ActivityManager.getService().resumeAppSwitches(); - } catch (RemoteException e) { - } - - boolean handled = defaultHandler.handleClick(); - - // close the shade if it was open - if (handled && !mNotificationPanel.isFullyCollapsed()) { - animateCollapsePanels( - CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); - visibilityChanged(false); - mAssistManager.hideAssist(); - - // Wait for activity start. - return true; - } else { - return false; - } - - }, afterKeyguardGone); - return true; - } else { - return defaultHandler.handleClick(); - } - } - - protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender, - String notificationKey) { - // Clear pending remote view, as we do not want to trigger pending remote input view when - // it's called by other code - mPendingWorkRemoteInputView = null; - // Begin old BaseStatusBar.startWorkChallengeIfNecessary. - final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, - null, userId); - if (newIntent == null) { - return false; - } - final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION); - callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender); - callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey); - callBackIntent.setPackage(mContext.getPackageName()); - - PendingIntent callBackPendingIntent = PendingIntent.getBroadcast( - mContext, - 0, - callBackIntent, - PendingIntent.FLAG_CANCEL_CURRENT | - PendingIntent.FLAG_ONE_SHOT | - PendingIntent.FLAG_IMMUTABLE); - newIntent.putExtra( - Intent.EXTRA_INTENT, - callBackPendingIntent.getIntentSender()); - try { - ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent, - null /*options*/); - } catch (RemoteException ex) { - // ignore - } - return true; - // End old BaseStatusBar.startWorkChallengeIfNecessary. - } - - @Override - public void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row, - View clicked) { - // Collapse notification and show work challenge - animateCollapsePanels(); - startWorkChallengeIfNecessary(userId, null, null); - // Add pending remote input view after starting work challenge, as starting work challenge - // will clear all previous pending review view - mPendingWorkRemoteInputView = clicked; - } - - @Override - public void onWorkChallengeChanged() { - if (mPendingWorkRemoteInputView != null - && !mLockscreenUserManager.isAnyProfilePublicMode()) { - // Expand notification panel and the notification row, then click on remote input view - final Runnable clickPendingViewRunnable = () -> { - final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView; - if (pendingWorkRemoteInputView == null) { - return; - } - - // Climb up the hierarchy until we get to the container for this row. - ViewParent p = pendingWorkRemoteInputView.getParent(); - while (!(p instanceof ExpandableNotificationRow)) { - if (p == null) { - return; - } - p = p.getParent(); - } - - final ExpandableNotificationRow row = (ExpandableNotificationRow) p; - ViewParent viewParent = row.getParent(); - if (viewParent instanceof NotificationStackScrollLayout) { - final NotificationStackScrollLayout scrollLayout = - (NotificationStackScrollLayout) viewParent; - row.makeActionsVisibile(); - row.post(() -> { - final Runnable finishScrollingCallback = () -> { - mPendingWorkRemoteInputView.callOnClick(); - mPendingWorkRemoteInputView = null; - scrollLayout.setFinishScrollingCallback(null); - }; - if (scrollLayout.scrollTo(row)) { - // It scrolls! So call it when it's finished. - scrollLayout.setFinishScrollingCallback(finishScrollingCallback); - } else { - // It does not scroll, so call it now! - finishScrollingCallback.run(); - } - }); - } - }; - mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (mNotificationPanel.mStatusBar.getStatusBarWindow() - .getHeight() != mNotificationPanel.mStatusBar - .getStatusBarHeight()) { - mNotificationPanel.getViewTreeObserver() - .removeOnGlobalLayoutListener(this); - mNotificationPanel.post(clickPendingViewRunnable); - } - } - }); - instantExpandNotificationsPanel(); - } - } - - @Override - public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) { - mHeadsUpManager.setExpanded(clickedEntry, nowExpanded); - if (mState == StatusBarState.KEYGUARD && nowExpanded) { - goToLockedShade(clickedEntry.row); - } - } - /** * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}. */ @@ -4149,7 +3555,7 @@ public class StatusBar extends SystemUI implements DemoMode, mBouncerShowing = bouncerShowing; if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing); updateHideIconsForBouncer(true /* animate */); - recomputeDisableFlags(true /* animate */); + mCommandQueue.recomputeDisableFlags(true /* animate */); updateScrimController(); } @@ -4273,25 +3679,6 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void wakeUpIfDozing(long time, View where) { - if (mDozing) { - PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - pm.wakeUp(time, "com.android.systemui:NODOZE"); - mWakeUpComingFromTouch = true; - where.getLocationInWindow(mTmpInt2); - mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, - mTmpInt2[1] + where.getHeight() / 2); - mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested(); - mFalsingManager.onScreenOnFromTouch(); - } - } - - @Override - public boolean isDeviceLocked(int userId) { - return mKeyguardManager.isDeviceLocked(userId); - } - - @Override public void appTransitionCancelled() { getComponent(Divider.class).onAppTransitionFinished(); } @@ -4669,8 +4056,6 @@ public class StatusBar extends SystemUI implements DemoMode, protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); - private AboveShelfObserver mAboveShelfObserver; - // handling reordering protected VisualStabilityManager mVisualStabilityManager; @@ -4688,7 +4073,6 @@ public class StatusBar extends SystemUI implements DemoMode, protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; protected KeyguardManager mKeyguardManager; - private LockPatternUtils mLockPatternUtils; private DeviceProvisionedController mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); @@ -4707,28 +4091,10 @@ public class StatusBar extends SystemUI implements DemoMode, protected AssistManager mAssistManager; - protected boolean mVrMode; - public boolean isDeviceInteractive() { return mDeviceInteractive; } - @Override // NotificationData.Environment - public boolean isDeviceProvisioned() { - return mDeviceProvisionedController.isDeviceProvisioned(); - } - - private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { - @Override - public void onVrStateChanged(boolean enabled) { - mVrMode = enabled; - } - }; - - public boolean isDeviceInVrMode() { - return mVrMode; - } - private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -4754,182 +4120,13 @@ public class StatusBar extends SystemUI implements DemoMode, }; @Override - public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) { - RemoteInputController controller = mRemoteInputManager.getController(); - if (controller.isRemoteInputActive(row.getEntry()) - && !TextUtils.isEmpty(row.getActiveRemoteInputText())) { - // We have an active remote input typed and the user clicked on the notification. - // this was probably unintentional, so we're closing the edit text instead. - controller.closeRemoteInputs(); - return; - } - Notification notification = sbn.getNotification(); - final PendingIntent intent = notification.contentIntent != null - ? notification.contentIntent - : notification.fullScreenIntent; - final String notificationKey = sbn.getKey(); - - boolean isActivityIntent = intent.isActivity(); - final boolean afterKeyguardGone = isActivityIntent - && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), - mLockscreenUserManager.getCurrentUserId()); - final boolean wasOccluded = mIsOccluded; - boolean showOverLockscreen = mStatusBarKeyguardViewManager.isShowing() - && PreviewInflater.wouldShowOverLockscreen(mContext, - intent.getIntent(), - mLockscreenUserManager.getCurrentUserId()); - OnDismissAction postKeyguardAction = () -> { - // TODO: Some of this code may be able to move to NotificationEntryManager. - if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) { - // Release the HUN notification to the shade. - - if (isPresenterFullyCollapsed()) { - HeadsUpUtil.setIsClickedHeadsUpNotification(row, true); - } - // - // In most cases, when FLAG_AUTO_CANCEL is set, the notification will - // become canceled shortly by NoMan, but we can't assume that. - mHeadsUpManager.removeNotification(sbn.getKey(), - true /* releaseImmediately */); - } - StatusBarNotification parentToCancel = null; - if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) { - StatusBarNotification summarySbn = - mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification(); - if (shouldAutoCancel(summarySbn)) { - parentToCancel = summarySbn; - } - } - final StatusBarNotification parentToCancelFinal = parentToCancel; - final Runnable runnable = () -> { - try { - // The intent we are sending is for the application, which - // won't have permission to immediately start an activity after - // the user switches to home. We know it is safe to do at this - // point, so make sure new activity switches are now allowed. - ActivityManager.getService().resumeAppSwitches(); - } catch (RemoteException e) { - } - int launchResult = ActivityManager.START_CANCELED; - if (intent != null) { - // If we are launching a work activity and require to launch - // separate work challenge, we defer the activity action and cancel - // notification until work challenge is unlocked. - if (isActivityIntent) { - final int userId = intent.getCreatorUserHandle().getIdentifier(); - if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) - && mKeyguardManager.isDeviceLocked(userId)) { - // TODO(b/28935539): should allow certain activities to - // bypass work challenge - if (startWorkChallengeIfNecessary(userId, intent.getIntentSender(), - notificationKey)) { - // Show work challenge, do not run PendingIntent and - // remove notification - collapseOnMainThread(); - return; - } - } - } - Intent fillInIntent = null; - Entry entry = row.getEntry(); - CharSequence remoteInputText = null; - if (!TextUtils.isEmpty(entry.remoteInputText)) { - remoteInputText = entry.remoteInputText; - } - if (!TextUtils.isEmpty(remoteInputText) - && !controller.isSpinning(entry.key)) { - fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT, - remoteInputText.toString()); - } - RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation( - row, wasOccluded); - try { - if (adapter != null) { - ActivityTaskManager.getService() - .registerRemoteAnimationForNextActivityStart( - intent.getCreatorPackage(), adapter); - } - launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, - null, null, getActivityOptions(adapter)); - mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent); - } catch (RemoteException | PendingIntent.CanceledException e) { - // the stack trace isn't very helpful here. - // Just log the exception message. - Log.w(TAG, "Sending contentIntent failed: " + e); - - // TODO: Dismiss Keyguard. - } - if (isActivityIntent) { - mAssistManager.hideAssist(); - } - } - if (shouldCollapse()) { - collapseOnMainThread(); - } - - final int count = - mEntryManager.getNotificationData().getActiveNotifications().size(); - final int rank = mEntryManager.getNotificationData().getRank(notificationKey); - final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey, - rank, count, true); - try { - mBarService.onNotificationClick(notificationKey, nv); - } catch (RemoteException ex) { - // system process is dead if we're here. - } - if (parentToCancelFinal != null) { - removeNotification(parentToCancelFinal); - } - if (shouldAutoCancel(sbn) - || mRemoteInputManager.isNotificationKeptForRemoteInputHistory( - notificationKey)) { - // Automatically remove all notifications that we may have kept around longer - removeNotification(sbn); - } - - mIsCollapsingToShowActivityOverLockscreen = false; - }; - - if (showOverLockscreen) { - addPostCollapseAction(runnable); - collapsePanel(true /* animate */); - } else if (mStatusBarKeyguardViewManager.isShowing() - && mStatusBarKeyguardViewManager.isOccluded()) { - mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); - collapsePanel(true /* animate */); - } else { - new Thread(runnable).start(); - } - - return !mNotificationPanel.isFullyCollapsed(); - }; - if (showOverLockscreen) { - mIsCollapsingToShowActivityOverLockscreen = true; - postKeyguardAction.onDismiss(); - } else { - dismissKeyguardThenExecute(postKeyguardAction, afterKeyguardGone); - } - } - - private void collapseOnMainThread() { - if (Looper.getMainLooper().isCurrentThread()) { - collapsePanel(); - } else { - Dependency.get(Dependency.MAIN_HANDLER).post(this::collapsePanel); - } - } - - private boolean shouldCollapse() { - return mState != StatusBarState.SHADE || !mActivityLaunchAnimator.isAnimationPending(); - } - public void collapsePanel(boolean animate) { if (animate) { boolean willCollapse = collapsePanel(); if (!willCollapse) { runPostCollapseRunnables(); } - } else if (!isPresenterFullyCollapsed()) { + } else if (!mPresenter.isPresenterFullyCollapsed()) { instantCollapseNotificationPanel(); visibilityChanged(false); } else { @@ -4937,7 +4134,8 @@ public class StatusBar extends SystemUI implements DemoMode, } } - private boolean collapsePanel() { + @Override + public boolean collapsePanel() { if (!mNotificationPanel.isFullyCollapsed()) { // close the shade if it was open animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, @@ -4950,59 +4148,8 @@ public class StatusBar extends SystemUI implements DemoMode, } } - private void removeNotification(StatusBarNotification notification) { - // We have to post it to the UI thread for synchronization - mHandler.post(() -> { - Runnable removeRunnable = - () -> mEntryManager.performRemoveNotification(notification); - if (isCollapsing()) { - // To avoid lags we're only performing the remove - // after the shade was collapsed - addPostCollapseAction(removeRunnable); - } else { - removeRunnable.run(); - } - }); - } - protected NotificationListener mNotificationListener; - @Override // NotificationData.Environment - public boolean isNotificationForCurrentProfiles(StatusBarNotification n) { - final int notificationUserId = n.getUserId(); - if (DEBUG && MULTIUSER_DEBUG) { - Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", n, - mLockscreenUserManager.getCurrentUserId(), notificationUserId)); - } - return mLockscreenUserManager.isCurrentProfile(notificationUserId); - } - - @Override - public NotificationGroupManager getGroupManager() { - return mGroupManager; - } - - @Override - public void startNotificationGutsIntent(final Intent intent, final int appUid, - ExpandableNotificationRow row) { - dismissKeyguardThenExecute(() -> { - AsyncTask.execute(() -> { - int launchResult = TaskStackBuilder.create(mContext) - .addNextIntentWithParentStack(intent) - .startActivities(getActivityOptions( - mActivityLaunchAnimator.getLaunchAnimation(row, mIsOccluded)), - new UserHandle(UserHandle.getUserId(appUid))); - mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */); - if (shouldCollapse()) { - // Putting it back on the main thread, since we're touching views - mStatusBarWindow.post(() -> animateCollapsePanels( - CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */)); - } - }); - return true; - }, false /* afterKeyguardGone */); - } - public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) { if (snoozeOption.getSnoozeCriterion() != null) { mNotificationListener.snoozeNotification(sbn.getKey(), @@ -5063,7 +4210,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Immediately update the icon hidden state, since that should only apply if we're // staying fullscreen. mWereIconsJustHidden = false; - recomputeDisableFlags(true); + mCommandQueue.recomputeDisableFlags(true); } updateHideIconsForBouncer(true /* animate */); } @@ -5076,24 +4223,6 @@ public class StatusBar extends SystemUI implements DemoMode, KeyboardShortcuts.dismiss(); } - @Override // NotificationData.Environment - public boolean shouldHideNotifications(int userId) { - return mLockscreenUserManager.shouldHideNotifications(userId); - } - - @Override // NotificationDate.Environment - public boolean shouldHideNotifications(String key) { - return mLockscreenUserManager.shouldHideNotifications(key); - } - - /** - * Returns true if we're on a secure lockscreen. - */ - @Override // NotificationData.Environment - public boolean isSecurelyLocked(int userId) { - return mLockscreenUserManager.isLockscreenPublicMode(userId); - } - /** * Called when the notification panel layouts */ @@ -5105,8 +4234,8 @@ public class StatusBar extends SystemUI implements DemoMode, if (mState == StatusBarState.KEYGUARD) { // Since the number of notifications is determined based on the height of the view, we // need to update them. - int maxBefore = getMaxNotificationsWhileLocked(false /* recompute */); - int maxNotifications = getMaxNotificationsWhileLocked(true /* recompute */); + int maxBefore = mPresenter.getMaxNotificationsWhileLocked(false /* recompute */); + int maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */); if (maxBefore != maxNotifications) { mViewHierarchyManager.updateRowStates(); } @@ -5114,7 +4243,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public void startPendingIntentDismissingKeyguard(final PendingIntent intent) { - if (!isDeviceProvisioned()) return; + if (!mDeviceProvisionedController.isDeviceProvisioned()) return; final boolean afterKeyguardGone = intent.isActivity() && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), @@ -5148,18 +4277,7 @@ public class StatusBar extends SystemUI implements DemoMode, }, afterKeyguardGone); } - private boolean shouldAutoCancel(StatusBarNotification sbn) { - int flags = sbn.getNotification().flags; - if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) { - return false; - } - if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { - return false; - } - return true; - } - - protected Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) { + public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) { ActivityOptions options; if (animationAdapter != null) { options = ActivityOptions.makeRemoteAnimation(animationAdapter); @@ -5276,28 +4394,4 @@ public class StatusBar extends SystemUI implements DemoMode, public NotificationGutsManager getGutsManager() { return mGutsManager; } - - @Override - public boolean isPresenterLocked() { - return mState == StatusBarState.KEYGUARD; - } - - @Override - public Handler getHandler() { - return mHandler; - } - - private final NotificationInfo.CheckSaveListener mCheckSaveListener = - (Runnable saveImportance, StatusBarNotification sbn) -> { - // If the user has security enabled, show challenge if the setting is changed. - if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier()) - && mKeyguardManager.isKeyguardLocked()) { - onLockedNotificationImportanceChange(() -> { - saveImportance.run(); - return true; - }); - } else { - saveImportance.run(); - } - }; } 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 ac3608bc622a..c5603016fc56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.phone; -import static com.android.keyguard.KeyguardHostView.OnDismissAction; +import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; 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; @@ -43,6 +43,7 @@ import com.android.systemui.Dependency; import com.android.systemui.SystemUIFactory; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback; @@ -124,6 +125,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // Dismiss action to be launched when we stop dozing or the keyguard is gone. private DismissWithActionRequest mPendingWakeupAction; private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + private final NotificationMediaManager mMediaManager = + Dependency.get(NotificationMediaManager.class); private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @@ -381,7 +384,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean isOccluding = !mOccluded && occluded; mOccluded = occluded; if (mShowing) { - mStatusBar.updateMediaMetaData(false, animate && !occluded); + mMediaManager.updateMediaMetaData(false, animate && !occluded); } mStatusBarWindowController.setKeyguardOccluded(occluded); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java new file mode 100644 index 000000000000..635ffc75182f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static com.android.systemui.Dependency.MAIN_HANDLER; +import static com.android.systemui.SysUiServiceProvider.getComponent; +import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED; +import static com.android.systemui.statusbar.phone.StatusBar.DEBUG; +import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG; +import static com.android.systemui.statusbar.phone.StatusBar.SPEW; +import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions; + +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.KeyguardManager; +import android.app.Notification; +import android.app.PendingIntent; +import android.app.TaskStackBuilder; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.AsyncTask; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.service.vr.IVrManager; +import android.service.vr.IVrStateCallbacks; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; +import android.view.RemoteAnimationAdapter; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityManager; +import android.widget.TextView; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.Dependency; +import com.android.systemui.InitController; +import com.android.systemui.R; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.statusbar.AmbientPulseManager; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback; +import com.android.systemui.statusbar.NotificationViewHierarchyManager; +import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.AboveShelfObserver; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; +import com.android.systemui.statusbar.notification.NotificationData.Entry; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; +import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.policy.HeadsUpUtil; +import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.PreviewInflater; + +public class StatusBarNotificationPresenter implements NotificationPresenter { + + private final LockscreenGestureLogger mLockscreenGestureLogger = + Dependency.get(LockscreenGestureLogger.class); + + private static final String TAG = "StatusBarNotificationPresenter"; + + private final ShadeController mShadeController = Dependency.get(ShadeController.class); + private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); + private final AssistManager mAssistManager = Dependency.get(AssistManager.class); + private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + private final NotificationViewHierarchyManager mViewHierarchyManager = + Dependency.get(NotificationViewHierarchyManager.class); + private final NotificationLockscreenUserManager mLockscreenUserManager = + Dependency.get(NotificationLockscreenUserManager.class); + private final StatusBarStateController mStatusBarStateController = + Dependency.get(StatusBarStateController.class); + private final NotificationEntryManager mEntryManager = + Dependency.get(NotificationEntryManager.class); + private final NotificationMediaManager mMediaManager = + Dependency.get(NotificationMediaManager.class); + private final NotificationRemoteInputManager mRemoteInputManager = + Dependency.get(NotificationRemoteInputManager.class); + private final NotificationGroupManager mGroupManager = + Dependency.get(NotificationGroupManager.class); + private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback = + (StatusBarRemoteInputCallback) Dependency.get(Callback.class); + protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); + + private final NotificationPanelView mNotificationPanel; + private final HeadsUpManagerPhone mHeadsUpManager; + private final AboveShelfObserver mAboveShelfObserver; + private final DozeScrimController mDozeScrimController; + private final ScrimController mScrimController; + private final Context mContext; + private final CommandQueue mCommandQueue; + + private final AccessibilityManager mAccessibilityManager; + private final LockPatternUtils mLockPatternUtils; + private final KeyguardManager mKeyguardManager; + private final ActivityLaunchAnimator mActivityLaunchAnimator; + private final int mMaxAllowedKeyguardNotifications; + private final IStatusBarService mBarService; + private boolean mReinflateNotificationsOnUserSwitched; + private final UnlockMethodCache mUnlockMethodCache; + private TextView mNotificationPanelDebugText; + + protected boolean mVrMode; + private int mMaxKeyguardNotifications; + private boolean mIsCollapsingToShowActivityOverLockscreen; + + public StatusBarNotificationPresenter(Context context, NotificationPanelView panel, + HeadsUpManagerPhone headsUp, StatusBarWindowView statusBarWindow, + ViewGroup stackScroller, DozeScrimController dozeScrimController, + ScrimController scrimController, + ActivityLaunchAnimator.Callback launchAnimatorCallback) { + mContext = context; + mNotificationPanel = panel; + mHeadsUpManager = headsUp; + mCommandQueue = getComponent(context, CommandQueue.class); + mAboveShelfObserver = new AboveShelfObserver(stackScroller); + mAboveShelfObserver.setListener(statusBarWindow.findViewById( + R.id.notification_container_parent)); + mAccessibilityManager = context.getSystemService(AccessibilityManager.class); + mDozeScrimController = dozeScrimController; + mScrimController = scrimController; + mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); + mLockPatternUtils = new LockPatternUtils(context); + mKeyguardManager = context.getSystemService(KeyguardManager.class); + mMaxAllowedKeyguardNotifications = context.getResources().getInteger( + R.integer.keyguard_max_notification_count); + mBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + mActivityLaunchAnimator = new ActivityLaunchAnimator(statusBarWindow, + launchAnimatorCallback, + mNotificationPanel, + (NotificationListContainer) stackScroller); + + if (MULTIUSER_DEBUG) { + mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info); + mNotificationPanelDebugText.setVisibility(View.VISIBLE); + } + + IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService( + Context.VR_SERVICE)); + if (vrManager != null) { + try { + vrManager.registerListener(mVrStateCallbacks); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to register VR mode state listener: " + e); + } + } + mRemoteInputManager.setUpWithPresenter(this, + Dependency.get(NotificationRemoteInputManager.Callback.class), + mNotificationPanel.createRemoteInputDelegate()); + mRemoteInputManager.getController().addCallback( + Dependency.get(StatusBarWindowController.class)); + + NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller; + Dependency.get(InitController.class).addPostInitTask(() -> { + mViewHierarchyManager.setUpWithPresenter(this, notifListContainer); + mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager); + mLockscreenUserManager.setUpWithPresenter(this); + mMediaManager.setUpWithPresenter(this); + Dependency.get(NotificationGutsManager.class).setUpWithPresenter(this, + notifListContainer, mCheckSaveListener, mOnSettingsClickListener); + + onUserSwitched(mLockscreenUserManager.getCurrentUserId()); + }); + } + + public void onDensityOrFontScaleChanged() { + if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) { + mEntryManager.updateNotificationsOnDensityOrFontScaleChanged(); + } else { + mReinflateNotificationsOnUserSwitched = true; + } + } + + @Override + public ActivityLaunchAnimator getActivityLaunchAnimator() { + return mActivityLaunchAnimator; + } + + @Override + public boolean isCollapsing() { + return mNotificationPanel.isCollapsing() + || mActivityLaunchAnimator.isAnimationPending() + || mActivityLaunchAnimator.isAnimationRunning(); + } + + @Override + public boolean isCollapsingToShowActivityOverLockscreen() { + return mIsCollapsingToShowActivityOverLockscreen; + } + + @Override + public void onPerformRemoveNotification(StatusBarNotification n) { + if (mNotificationPanel.hasPulsingNotifications() && + !mAmbientPulseManager.hasNotifications()) { + // We were showing a pulse for a notification, but no notifications are pulsing anymore. + // Finish the pulse. + mDozeScrimController.pulseOutNow(); + } + } + + @Override + public void updateNotificationViews() { + // The function updateRowStates depends on both of these being non-null, so check them here. + // We may be called before they are set from DeviceProvisionedController's callback. + if (mScrimController == null) return; + + // Do not modify the notifications during collapse. + if (isCollapsing()) { + mShadeController.addPostCollapseAction(this::updateNotificationViews); + return; + } + + mViewHierarchyManager.updateNotificationViews(); + + mNotificationPanel.updateNotificationViews(); + } + + @Override + public void onNotificationAdded(Entry shadeEntry) { + // Recalculate the position of the sliding windows and the titles. + mShadeController.updateAreThereNotifications(); + } + + @Override + public void onNotificationUpdated(StatusBarNotification notification) { + mShadeController.updateAreThereNotifications(); + } + + @Override + public void onNotificationRemoved(String key, StatusBarNotification old) { + if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); + + if (old != null) { + if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications() + && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) { + if (mStatusBarStateController.getState() == StatusBarState.SHADE) { + mCommandQueue.animateCollapsePanels(); + } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED + && !isCollapsing()) { + mShadeController.goToKeyguard(); + } + } + } + mShadeController.updateAreThereNotifications(); + } + + public boolean hasActiveNotifications() { + return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty(); + } + + @Override + public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) { + if (!mShadeController.isDozing()) { + return false; + } + + if (mShadeController.isOccluded()) { + boolean devicePublic = mLockscreenUserManager. + isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); + boolean userPublic = devicePublic + || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId()); + boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry); + if (userPublic && needsRedaction) { + return false; + } + } + + if (!mCommandQueue.panelsEnabled()) { + if (DEBUG) { + Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey()); + } + return false; + } + + if (sbn.getNotification().fullScreenIntent != null) { + if (mAccessibilityManager.isTouchExplorationEnabled()) { + if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey()); + return false; + } else { + // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent + return !mKeyguardMonitor.isShowing() + || mShadeController.isOccluded(); + } + } + return true; + } + + @Override + public void onUserSwitched(int newUserId) { + // Begin old BaseStatusBar.userSwitched + mHeadsUpManager.setUser(newUserId); + // End old BaseStatusBar.userSwitched + if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); + mCommandQueue.animateCollapsePanels(); + if (mReinflateNotificationsOnUserSwitched) { + mEntryManager.updateNotificationsOnDensityOrFontScaleChanged(); + mReinflateNotificationsOnUserSwitched = false; + } + updateNotificationViews(); + mMediaManager.clearCurrentMediaNotification(); + mShadeController.setLockscreenUser(newUserId); + updateMediaMetaData(true, false); + } + + @Override + public void onBindRow(Entry entry, PackageManager pmUser, + StatusBarNotification sbn, ExpandableNotificationRow row) { + row.setAboveShelfChangedListener(mAboveShelfObserver); + row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer); + } + + @Override + public boolean isPresenterFullyCollapsed() { + return mNotificationPanel.isFullyCollapsed(); + } + + @Override + public void onActivated(ActivatableNotificationView view) { + onActivated(); + if (view != null) mNotificationPanel.setActivatedChild(view); + } + + public void onActivated() { + mLockscreenGestureLogger.write( + MetricsEvent.ACTION_LS_NOTE, + 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); + mNotificationPanel.showTransientIndication(R.string.notification_tap_again); + ActivatableNotificationView previousView = mNotificationPanel.getActivatedChild(); + if (previousView != null) { + previousView.makeInactive(true /* animate */); + } + } + + @Override + public void onActivationReset(ActivatableNotificationView view) { + if (view == mNotificationPanel.getActivatedChild()) { + mNotificationPanel.setActivatedChild(null); + mShadeController.onActivationReset(); + } + } + + @Override + public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) { + mMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation); + } + + @Override + public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) { + RemoteInputController controller = mRemoteInputManager.getController(); + if (controller.isRemoteInputActive(row.getEntry()) + && !TextUtils.isEmpty(row.getActiveRemoteInputText())) { + // We have an active remote input typed and the user clicked on the notification. + // this was probably unintentional, so we're closing the edit text instead. + controller.closeRemoteInputs(); + return; + } + Notification notification = sbn.getNotification(); + final PendingIntent intent = notification.contentIntent != null + ? notification.contentIntent + : notification.fullScreenIntent; + final String notificationKey = sbn.getKey(); + + boolean isActivityIntent = intent.isActivity(); + final boolean afterKeyguardGone = isActivityIntent + && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), + mLockscreenUserManager.getCurrentUserId()); + final boolean wasOccluded = mShadeController.isOccluded(); + boolean showOverLockscreen = mKeyguardMonitor.isShowing() + && PreviewInflater.wouldShowOverLockscreen(mContext, + intent.getIntent(), + mLockscreenUserManager.getCurrentUserId()); + OnDismissAction postKeyguardAction = () -> { + // TODO: Some of this code may be able to move to NotificationEntryManager. + if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) { + // Release the HUN notification to the shade. + + if (isPresenterFullyCollapsed()) { + HeadsUpUtil.setIsClickedHeadsUpNotification(row, true); + } + // + // In most cases, when FLAG_AUTO_CANCEL is set, the notification will + // become canceled shortly by NoMan, but we can't assume that. + mHeadsUpManager.removeNotification(sbn.getKey(), + true /* releaseImmediately */); + } + StatusBarNotification parentToCancel = null; + if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) { + StatusBarNotification summarySbn = + mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification(); + if (shouldAutoCancel(summarySbn)) { + parentToCancel = summarySbn; + } + } + final StatusBarNotification parentToCancelFinal = parentToCancel; + final Runnable runnable = () -> { + try { + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManager.getService().resumeAppSwitches(); + } catch (RemoteException e) { + } + int launchResult = ActivityManager.START_CANCELED; + if (intent != null) { + // If we are launching a work activity and require to launch + // separate work challenge, we defer the activity action and cancel + // notification until work challenge is unlocked. + if (isActivityIntent) { + final int userId = intent.getCreatorUserHandle().getIdentifier(); + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) + && mKeyguardManager.isDeviceLocked(userId)) { + // TODO(b/28935539): should allow certain activities to + // bypass work challenge + if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId, + intent.getIntentSender(), notificationKey)) { + // Show work challenge, do not run PendingIntent and + // remove notification + collapseOnMainThread(); + return; + } + } + } + Intent fillInIntent = null; + Entry entry = row.getEntry(); + CharSequence remoteInputText = null; + if (!TextUtils.isEmpty(entry.remoteInputText)) { + remoteInputText = entry.remoteInputText; + } + if (!TextUtils.isEmpty(remoteInputText) + && !controller.isSpinning(entry.key)) { + fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT, + remoteInputText.toString()); + } + RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation( + row, wasOccluded); + try { + if (adapter != null) { + ActivityTaskManager.getService() + .registerRemoteAnimationForNextActivityStart( + intent.getCreatorPackage(), adapter); + } + launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, + null, null, getActivityOptions(adapter)); + mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent); + } catch (RemoteException | PendingIntent.CanceledException e) { + // the stack trace isn't very helpful here. + // Just log the exception message. + Log.w(TAG, "Sending contentIntent failed: " + e); + + // TODO: Dismiss Keyguard. + } + if (isActivityIntent) { + mAssistManager.hideAssist(); + } + } + if (shouldCollapse()) { + collapseOnMainThread(); + } + + final int count = + mEntryManager.getNotificationData().getActiveNotifications().size(); + final int rank = mEntryManager.getNotificationData().getRank(notificationKey); + final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey, + rank, count, true); + try { + mBarService.onNotificationClick(notificationKey, nv); + } catch (RemoteException ex) { + // system process is dead if we're here. + } + if (parentToCancelFinal != null) { + removeNotification(parentToCancelFinal); + } + if (shouldAutoCancel(sbn) + || mRemoteInputManager.isNotificationKeptForRemoteInputHistory( + notificationKey)) { + // Automatically remove all notifications that we may have kept around longer + removeNotification(sbn); + } + mIsCollapsingToShowActivityOverLockscreen = false; + }; + + if (showOverLockscreen) { + mShadeController.addPostCollapseAction(runnable); + mShadeController.collapsePanel(true /* animate */); + } else if (mKeyguardMonitor.isShowing() + && mShadeController.isOccluded()) { + mShadeController.addAfterKeyguardGoneRunnable(runnable); + mShadeController.collapsePanel(); + } else { + new Thread(runnable).start(); + } + + return !mNotificationPanel.isFullyCollapsed(); + }; + if (showOverLockscreen) { + mIsCollapsingToShowActivityOverLockscreen = true; + postKeyguardAction.onDismiss(); + } else { + mActivityStarter.dismissKeyguardThenExecute( + postKeyguardAction, null /* cancel */, afterKeyguardGone); + } + } + + private void removeNotification(StatusBarNotification notification) { + // We have to post it to the UI thread for synchronization + Dependency.get(MAIN_HANDLER).post(() -> { + Runnable removeRunnable = + () -> mEntryManager.performRemoveNotification(notification); + if (isCollapsing()) { + // To avoid lags we're only performing the remove + // after the shade was collapsed + mShadeController.addPostCollapseAction(removeRunnable); + } else { + removeRunnable.run(); + } + }); + } + + @Override + public void startNotificationGutsIntent(final Intent intent, final int appUid, + ExpandableNotificationRow row) { + mActivityStarter.dismissKeyguardThenExecute(() -> { + AsyncTask.execute(() -> { + int launchResult = TaskStackBuilder.create(mContext) + .addNextIntentWithParentStack(intent) + .startActivities(getActivityOptions( + mActivityLaunchAnimator.getLaunchAnimation( + row, mShadeController.isOccluded())), + new UserHandle(UserHandle.getUserId(appUid))); + mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */); + if (shouldCollapse()) { + // Putting it back on the main thread, since we're touching views + Dependency.get(MAIN_HANDLER).post(() -> mCommandQueue.animateCollapsePanels( + CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */)); + } + }); + return true; + }, null, false /* afterKeyguardGone */); + } + + @Override + public int getMaxNotificationsWhileLocked(boolean recompute) { + if (recompute) { + mMaxKeyguardNotifications = Math.max(1, + mNotificationPanel.computeMaxKeyguardNotifications( + mMaxAllowedKeyguardNotifications)); + return mMaxKeyguardNotifications; + } + return mMaxKeyguardNotifications; + } + + @Override + public void onUpdateRowStates() { + mNotificationPanel.onUpdateRowStates(); + } + + @Override + public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) { + mHeadsUpManager.setExpanded(clickedEntry, nowExpanded); + if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) { + mShadeController.goToLockedShade(clickedEntry.row); + } + } + + @Override + public boolean isDeviceInVrMode() { + return mVrMode; + } + + @Override + public boolean isPresenterLocked() { + return mStatusBarStateController.getState() == StatusBarState.KEYGUARD; + } + + private void collapseOnMainThread() { + if (Looper.getMainLooper().isCurrentThread()) { + mShadeController.collapsePanel(); + } else { + Dependency.get(MAIN_HANDLER).post(mShadeController::collapsePanel); + } + } + + private boolean shouldCollapse() { + return mStatusBarStateController.getState() != StatusBarState.SHADE + || !mActivityLaunchAnimator.isAnimationPending(); + } + + private void onLockedNotificationImportanceChange(OnDismissAction dismissAction) { + mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); + mActivityStarter.dismissKeyguardThenExecute(dismissAction, null, + true /* afterKeyguardGone */); + } + + private static boolean shouldAutoCancel(StatusBarNotification sbn) { + int flags = sbn.getNotification().flags; + if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) { + return false; + } + if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { + return false; + } + return true; + } + + private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { + @Override + public void onVrStateChanged(boolean enabled) { + mVrMode = enabled; + } + }; + + private final CheckSaveListener mCheckSaveListener = new CheckSaveListener() { + @Override + public void checkSave(Runnable saveImportance, StatusBarNotification sbn) { + int state = mStatusBarStateController.getState(); + // If the user has security enabled, show challenge if the setting is changed. + if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier()) + && mKeyguardManager.isKeyguardLocked()) { + onLockedNotificationImportanceChange(() -> { + saveImportance.run(); + return true; + }); + } else { + saveImportance.run(); + } + } + }; + + private final OnSettingsClickListener mOnSettingsClickListener = new OnSettingsClickListener() { + @Override + public void onSettingsClick(String key) { + try { + mBarService.onNotificationSettingsViewed(key); + } catch (RemoteException e) { + // if we're here we're dead + } + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java new file mode 100644 index 000000000000..06f9658a0902 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED; + +import static com.android.systemui.SysUiServiceProvider.getComponent; +import static com.android.systemui.statusbar.NotificationLockscreenUserManager + .NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION; + +import android.app.ActivityManager; +import android.app.KeyguardManager; +import android.app.PendingIntent; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.os.RemoteException; +import android.os.UserHandle; +import android.view.View; +import android.view.ViewParent; +import android.view.ViewTreeObserver; + +import com.android.systemui.Dependency; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.CommandQueue.Callbacks; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +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.PreviewInflater; + +public class StatusBarRemoteInputCallback implements Callback, Callbacks { + + private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + private final StatusBarStateController mStatusBarStateController + = Dependency.get(StatusBarStateController.class); + private final NotificationLockscreenUserManager mLockscreenUserManager + = Dependency.get(NotificationLockscreenUserManager.class); + private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); + private final Context mContext; + private View mPendingWorkRemoteInputView; + private final StatusBarStateController.StateListener mStateListener = this::setStatusBarState; + private View mPendingRemoteInputView; + private final ShadeController mShadeController = Dependency.get(ShadeController.class); + private KeyguardManager mKeyguardManager; + private final CommandQueue mCommandQueue; + private int mDisabled2; + protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver(); + + public StatusBarRemoteInputCallback(Context context) { + mContext = context; + mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL, + new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null); + mStatusBarStateController.addListener(mStateListener); + mKeyguardManager = context.getSystemService(KeyguardManager.class); + mCommandQueue = getComponent(context, CommandQueue.class); + mCommandQueue.addCallbacks(this); + } + + private void setStatusBarState(int state) { + if (state == StatusBarState.SHADE && mStatusBarStateController.leaveOpenOnKeyguardHide()) { + if (!mStatusBarStateController.isKeyguardRequested()) { + if (mPendingRemoteInputView != null + && mPendingRemoteInputView.isAttachedToWindow()) { + mPendingRemoteInputView.post(mPendingRemoteInputView::callOnClick); + } + mPendingRemoteInputView = null; + } + } + } + + @Override + public void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) { + mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); + mShadeController.showBouncer(true /* scrimmed */); + mPendingRemoteInputView = clicked; + } + + protected void onWorkChallengeChanged() { + if (mPendingWorkRemoteInputView != null + && !mLockscreenUserManager.isAnyProfilePublicMode()) { + // Expand notification panel and the notification row, then click on remote input view + final Runnable clickPendingViewRunnable = () -> { + final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView; + if (pendingWorkRemoteInputView == null) { + return; + } + + // Climb up the hierarchy until we get to the container for this row. + ViewParent p = pendingWorkRemoteInputView.getParent(); + while (!(p instanceof ExpandableNotificationRow)) { + if (p == null) { + return; + } + p = p.getParent(); + } + + final ExpandableNotificationRow row = (ExpandableNotificationRow) p; + ViewParent viewParent = row.getParent(); + if (viewParent instanceof NotificationStackScrollLayout) { + final NotificationStackScrollLayout scrollLayout = + (NotificationStackScrollLayout) viewParent; + row.makeActionsVisibile(); + row.post(() -> { + final Runnable finishScrollingCallback = () -> { + mPendingWorkRemoteInputView.callOnClick(); + mPendingWorkRemoteInputView = null; + scrollLayout.setFinishScrollingCallback(null); + }; + if (scrollLayout.scrollTo(row)) { + // It scrolls! So call it when it's finished. + scrollLayout.setFinishScrollingCallback(finishScrollingCallback); + } else { + // It does not scroll, so call it now! + finishScrollingCallback.run(); + } + }); + } + }; + mShadeController.postOnShadeExpanded(clickPendingViewRunnable); + mShadeController.instantExpandNotificationsPanel(); + } + } + + @Override + public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, + View clickedView) { + if (mKeyguardMonitor.isShowing()) { + onLockedRemoteInput(row, clickedView); + } else { + row.setUserExpanded(true); + row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick); + } + } + + @Override + public void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row, + View clicked) { + // Collapse notification and show work challenge + mCommandQueue.animateCollapsePanels(); + startWorkChallengeIfNecessary(userId, null, null); + // Add pending remote input view after starting work challenge, as starting work challenge + // will clear all previous pending review view + mPendingWorkRemoteInputView = clicked; + } + + protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender, + String notificationKey) { + // Clear pending remote view, as we do not want to trigger pending remote input view when + // it's called by other code + mPendingWorkRemoteInputView = null; + // Begin old BaseStatusBar.startWorkChallengeIfNecessary. + final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, + null, userId); + if (newIntent == null) { + return false; + } + final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION); + callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender); + callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey); + callBackIntent.setPackage(mContext.getPackageName()); + + PendingIntent callBackPendingIntent = PendingIntent.getBroadcast( + mContext, + 0, + callBackIntent, + PendingIntent.FLAG_CANCEL_CURRENT | + PendingIntent.FLAG_ONE_SHOT | + PendingIntent.FLAG_IMMUTABLE); + newIntent.putExtra( + Intent.EXTRA_INTENT, + callBackPendingIntent.getIntentSender()); + try { + ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent, + null /*options*/); + } catch (RemoteException ex) { + // ignore + } + return true; + // End old BaseStatusBar.startWorkChallengeIfNecessary. + } + + @Override + public boolean shouldHandleRemoteInput(View view, PendingIntent pendingIntent) { + // Skip remote input as doing so will expand the notification shade. + return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0; + } + + @Override + public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, + Intent fillInIntent, NotificationRemoteInputManager.ClickHandler defaultHandler) { + final boolean isActivity = pendingIntent.isActivity(); + if (isActivity) { + final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( + mContext, pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId()); + mActivityStarter.dismissKeyguardThenExecute(() -> { + try { + ActivityManager.getService().resumeAppSwitches(); + } catch (RemoteException e) { + } + + boolean handled = defaultHandler.handleClick(); + + // close the shade if it was open and maybe wait for activity start. + return handled && mShadeController.closeShadeIfOpen(); + }, null, afterKeyguardGone); + return true; + } else { + return defaultHandler.handleClick(); + } + } + + @Override + public void disable(int state1, int state2, boolean animate) { + mDisabled2 = state2; + } + + protected class ChallengeReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) { + if (userId != mLockscreenUserManager.getCurrentUserId() + && mLockscreenUserManager.isCurrentProfile(userId)) { + onWorkChallengeChanged(); + } + } + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java index 7b42dd4c9817..aba23778f08c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java @@ -24,6 +24,7 @@ public interface KeyguardMonitor extends CallbackController<Callback> { boolean isOccluded(); boolean isKeyguardFadingAway(); boolean isKeyguardGoingAway(); + boolean isLaunchTransitionFadingAway(); long getKeyguardFadingAwayDuration(); long getKeyguardFadingAwayDelay(); long calculateGoingToFullShadeDelay(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java index 10cb09b75c16..5eb0fb76794d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java @@ -28,7 +28,7 @@ import java.util.ArrayList; public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback implements KeyguardMonitor { - private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); + private final ArrayList<Callback> mCallbacks = new ArrayList<>(); private final Context mContext; private final CurrentUserTracker mUserTracker; @@ -45,6 +45,7 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback private long mKeyguardFadingAwayDelay; private long mKeyguardFadingAwayDuration; private boolean mKeyguardGoingAway; + private boolean mLaunchTransitionFadingAway; public KeyguardMonitorImpl(Context context) { mContext = context; @@ -123,7 +124,7 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback private void notifyKeyguardChanged() { // Copy the list to allow removal during callback. - new ArrayList<Callback>(mCallbacks).forEach(Callback::onKeyguardShowingChanged); + new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged); } public void notifyKeyguardFadingAway(long delay, long fadeoutDuration) { @@ -165,4 +166,13 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback 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/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index dd031623f356..aa4782fd864a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -28,7 +28,7 @@ import android.widget.Button; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ContrastColorUtil; -import com.android.keyguard.KeyguardHostView.OnDismissAction; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.notification.NotificationData; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java index 3f85c9d169c7..a69fd5619e99 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java @@ -23,6 +23,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import org.junit.Assert; import org.junit.Before; @@ -44,7 +45,7 @@ public class KeyguardHostViewTest extends SysuiTestCase { @Test public void testHasDismissActions() { Assert.assertFalse("Action not set yet", mKeyguardHostView.hasDismissActions()); - mKeyguardHostView.setOnDismissAction(mock(KeyguardHostView.OnDismissAction.class), + mKeyguardHostView.setOnDismissAction(mock(OnDismissAction.class), null /* cancelAction */); Assert.assertTrue("Action should exist", mKeyguardHostView.hasDismissActions()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index a35ca46833e7..a58bc8548bd4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -58,7 +58,6 @@ public abstract class SysuiTestCase { @Before public void SysuiSetup() throws Exception { - mContext.setTheme(R.style.Theme_SystemUI); SystemUIFactory.createFromConfig(mContext); mRealInstrumentation = InstrumentationRegistry.getInstrumentation(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java index 9d3124e9ecc6..e811270a4450 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java @@ -25,10 +25,12 @@ public class SysuiTestableContext extends TestableContext implements SysUiServic public SysuiTestableContext(Context base) { super(base); + setTheme(R.style.Theme_SystemUI); } public SysuiTestableContext(Context base, LeakCheck check) { super(base, check); + setTheme(R.style.Theme_SystemUI); } public ArrayMap<Class<?>, Object> getComponents() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java index 5c83d99b646a..0c8d137569f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java +++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java @@ -25,6 +25,9 @@ public class TestableDependency extends Dependency { private final ArraySet<Object> mInstantiatedObjects = new ArraySet<>(); public TestableDependency(Context context) { + if (context instanceof SysuiTestableContext) { + mComponents = ((SysuiTestableContext) context).getComponents(); + } mContext = context; if (SystemUIFactory.getInstance() == null) { SystemUIFactory.createFromConfig(context); @@ -43,6 +46,9 @@ public class TestableDependency extends Dependency { } public <T> void injectTestDependency(Class<T> key, T obj) { + if (mInstantiatedObjects.contains(key)) { + throw new IllegalStateException(key + " was already initialized"); + } mObjs.put(key, obj); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index a02ef98f834d..8ae3cd8d6acd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -84,7 +84,7 @@ public class CommandQueueTest extends SysuiTestCase { public void testCollapsePanels() { mCommandQueue.animateCollapsePanels(); waitForIdleSync(); - verify(mCallbacks).animateCollapsePanels(eq(0)); + verify(mCallbacks).animateCollapsePanels(eq(0), eq(false)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java index da59450af4df..fe0a7c78def1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar; import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.when; import android.os.Handler; import android.os.Looper; @@ -26,13 +25,15 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.systemui.Dependency; +import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.row.NotificationInfo; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; +import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -49,25 +50,27 @@ import org.mockito.MockitoAnnotations; */ @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class NonPhoneDependencyTest extends SysuiTestCase { @Mock private NotificationPresenter mPresenter; @Mock private NotificationListContainer mListContainer; @Mock private NotificationEntryManager.Callback mEntryManagerCallback; @Mock private HeadsUpManager mHeadsUpManager; @Mock private RemoteInputController.Delegate mDelegate; - @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener; - @Mock private NotificationGutsManager.OnSettingsClickListener mOnClickListener; @Mock private NotificationRemoteInputManager.Callback mRemoteInputManagerCallback; + @Mock private CheckSaveListener mCheckSaveListener; + @Mock private OnSettingsClickListener mOnSettingsClickListener; @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper())); + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, + new Handler(TestableLooper.get(this).getLooper())); } @Test public void testNotificationManagementCodeHasNoDependencyOnStatusBarWindowManager() { + mDependency.injectMockDependency(ShadeController.class); NotificationEntryManager entryManager = Dependency.get(NotificationEntryManager.class); NotificationGutsManager gutsManager = Dependency.get(NotificationGutsManager.class); NotificationListener notificationListener = Dependency.get(NotificationListener.class); @@ -79,24 +82,20 @@ public class NonPhoneDependencyTest extends SysuiTestCase { Dependency.get(NotificationLockscreenUserManager.class); NotificationViewHierarchyManager viewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); - NotificationGroupManager groupManager = Dependency.get(NotificationGroupManager.class); - - when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(lockscreenUserManager); - when(mPresenter.getGroupManager()).thenReturn(groupManager); - + Dependency.get(InitController.class).executePostInitTasks(); entryManager.setUpWithPresenter(mPresenter, mListContainer, mEntryManagerCallback, mHeadsUpManager); - groupManager.setHeadsUpManager(mHeadsUpManager); - gutsManager.setUpWithPresenter(mPresenter, mListContainer, mCheckSaveListener, - mOnClickListener); - notificationLogger.setUpWithEntryManager(entryManager, mListContainer); - mediaManager.setUpWithPresenter(mPresenter, entryManager); - remoteInputManager.setUpWithPresenter(mPresenter, entryManager, mRemoteInputManagerCallback, + gutsManager.setUpWithPresenter(mPresenter, mListContainer, + mCheckSaveListener, mOnSettingsClickListener); + notificationLogger.setUpWithContainer(mListContainer); + mediaManager.setUpWithPresenter(mPresenter); + remoteInputManager.setUpWithPresenter(mPresenter, mRemoteInputManagerCallback, mDelegate); - lockscreenUserManager.setUpWithPresenter(mPresenter, entryManager); - viewHierarchyManager.setUpWithPresenter(mPresenter, entryManager, mListContainer); - notificationListener.setUpWithPresenter(mPresenter, entryManager); + lockscreenUserManager.setUpWithPresenter(mPresenter); + viewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer); + notificationListener.setUpWithPresenter(mPresenter); + TestableLooper.get(this).processAllMessages(); assertFalse(mDependency.hasInstantiatedDependency(StatusBarWindowController.class)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java index 7b0c0a077332..63ececbe2994 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java @@ -32,6 +32,7 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -44,7 +45,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class NotificationListenerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; private static final int TEST_UID = 0; @@ -66,15 +67,16 @@ public class NotificationListenerTest extends SysuiTestCase { mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); mDependency.injectTestDependency(NotificationRemoteInputManager.class, mRemoteInputManager); + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, + new Handler(TestableLooper.get(this).getLooper())); - when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper())); when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); mListener = new NotificationListener(mContext); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.CURRENT, null, 0); - mListener.setUpWithPresenter(mPresenter, mEntryManager); + mListener.setUpWithPresenter(mPresenter); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 515c10980ff6..f168a490acf4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -40,7 +40,7 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.Log; +import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -83,12 +83,12 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { when(mUserManager.getProfiles(mCurrentUserId)).thenReturn(Lists.newArrayList( new UserInfo(mCurrentUserId, "", 0), new UserInfo(mCurrentUserId + 1, "", 0))); - when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper())); when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, + Handler.createAsync(Looper.myLooper())); mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext); - mLockscreenUserManager.setUpWithPresenter(mPresenter, mEntryManager); - mLockscreenUserManager.setKeyguardViewManager(mKeyguardViewManager); + mLockscreenUserManager.setUpWithPresenter(mPresenter); } @Test @@ -137,15 +137,6 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { } @Test - public void testActionDeviceLockedChangedWithDifferentUserIdCallsOnWorkChallengeChanged() { - Intent intent = new Intent() - .setAction(ACTION_DEVICE_LOCKED_CHANGED) - .putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId + 1); - mLockscreenUserManager.getAllUsersReceiverForTest().onReceive(mContext, intent); - verify(mPresenter, times(1)).onWorkChallengeChanged(); - } - - @Test public void testActionUserSwitchedCallsOnUserSwitched() { Intent intent = new Intent() .setAction(ACTION_USER_SWITCHED) @@ -161,15 +152,11 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUserId)); } - private class TestNotificationLockscreenUserManager extends NotificationLockscreenUserManager { + private class TestNotificationLockscreenUserManager extends NotificationLockscreenUserManagerImpl { public TestNotificationLockscreenUserManager(Context context) { super(context); } - public BroadcastReceiver getAllUsersReceiverForTest() { - return mAllUsersReceiver; - } - public BroadcastReceiver getBaseBroadcastReceiverForTest() { return mBaseBroadcastReceiver; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java index 8a59e968d16e..d409e2b254df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java @@ -20,6 +20,7 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.NotificationData; @@ -70,8 +71,8 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { mDependency.injectTestDependency(NotificationLockscreenUserManager.class, mLockscreenUserManager); mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController); - - when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper())); + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, + Handler.createAsync(Looper.myLooper())); mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, @@ -79,7 +80,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { mEntry = new NotificationData.Entry(mSbn); mEntry.row = mRow; - mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mEntryManager, mCallback, + mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback, mDelegate, mController); for (NotificationLifetimeExtender extender : mRemoteInputManager.getLifetimeExtenders()) { extender.setCallback( @@ -201,11 +202,10 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { } public void setUpWithPresenterForTest(NotificationPresenter presenter, - NotificationEntryManager entryManager, Callback callback, RemoteInputController.Delegate delegate, RemoteInputController controller) { - super.setUpWithPresenter(presenter, entryManager, callback, delegate); + super.setUpWithPresenter(presenter, callback, delegate); mRemoteInputController = controller; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 6b4ccc4a80b5..602e613388fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -32,6 +32,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; +import com.android.systemui.Dependency; +import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.statusbar.notification.NotificationData; @@ -42,6 +44,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.ShadeController; import com.google.android.collect.Lists; @@ -67,6 +70,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationGroupManager mGroupManager; @Mock private VisualStabilityManager mVisualStabilityManager; + @Mock private ShadeController mShadeController; private NotificationViewHierarchyManager mViewHierarchyManager; private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);; @@ -79,11 +83,13 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mLockscreenUserManager); mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager); mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); + mDependency.injectTestDependency(ShadeController.class, mShadeController); when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); mViewHierarchyManager = new NotificationViewHierarchyManager(mContext); - mViewHierarchyManager.setUpWithPresenter(mPresenter, mEntryManager, mListContainer); + Dependency.get(InitController.class).executePostInitTasks(); + mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer); } private NotificationData.Entry createEntry() throws Exception { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java index 17daaacded87..1d977d8c5dad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java @@ -80,8 +80,7 @@ public class SmartReplyControllerTest extends SysuiTestCase { mSmartReplyController); mRemoteInputManager = new NotificationRemoteInputManager(mContext); - mRemoteInputManager.setUpWithPresenter(mPresenter, mNotificationEntryManager, mCallback, - mDelegate); + mRemoteInputManager.setUpWithPresenter(mPresenter, mCallback, mDelegate); mNotification = new Notification.Builder(mContext, "") .setSmallIcon(R.drawable.ic_person) .setContentTitle("Title") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java index 78be783da17b..b405a5cd91bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.os.Handler; @@ -27,6 +26,7 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationPresenter; @@ -59,14 +59,15 @@ public class AppOpsListenerTest extends SysuiTestCase { mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); mDependency.injectTestDependency(ForegroundServiceController.class, mFsc); getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager); - when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper())); + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, + Handler.createAsync(Looper.myLooper())); mListener = new AppOpsListener(mContext); } @Test public void testOnlyListenForFewOps() { - mListener.setUpWithPresenter(mPresenter, mEntryManager); + mListener.setUpWithPresenter(mPresenter); verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsListener.OPS, mListener); } @@ -79,7 +80,7 @@ public class AppOpsListenerTest extends SysuiTestCase { @Test public void testInformEntryMgrOnAppOpsChange() { - mListener.setUpWithPresenter(mPresenter, mEntryManager); + mListener.setUpWithPresenter(mPresenter); mListener.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); TestableLooper.get(this).processAllMessages(); @@ -89,7 +90,7 @@ public class AppOpsListenerTest extends SysuiTestCase { @Test public void testInformFscOnAppOpsChange() { - mListener.setUpWithPresenter(mPresenter, mEntryManager); + mListener.setUpWithPresenter(mPresenter); mListener.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); TestableLooper.get(this).processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java index de5a8a0a6d2d..459dc5d43c2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java @@ -55,13 +55,16 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.util.ArraySet; +import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; +import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.ShadeController; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -87,7 +90,7 @@ public class NotificationDataTest extends SysuiTestCase { @Mock ForegroundServiceController mFsc; @Mock - NotificationData.Environment mEnvironment; + NotificationData.KeyguardEnvironment mEnvironment; private final IPackageManager mMockPackageManager = mock(IPackageManager.class); private NotificationData mNotificationData; @@ -108,10 +111,14 @@ public class NotificationDataTest extends SysuiTestCase { .thenReturn(PackageManager.PERMISSION_GRANTED); mDependency.injectTestDependency(ForegroundServiceController.class, mFsc); - when(mEnvironment.getGroupManager()).thenReturn(new NotificationGroupManager()); + mDependency.injectTestDependency(NotificationGroupManager.class, + new NotificationGroupManager()); + mDependency.injectMockDependency(ShadeController.class); + mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment); when(mEnvironment.isDeviceProvisioned()).thenReturn(true); when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true); - mNotificationData = new TestableNotificationData(mEnvironment); + mNotificationData = new TestableNotificationData(); + Dependency.get(InitController.class).executePostInitTasks(); mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class)); mRow = new NotificationTestHelper(getContext()).createRow(); } @@ -298,11 +305,11 @@ public class NotificationDataTest extends SysuiTestCase { mRow.getEntry().notification)).thenReturn(false); when(mEnvironment.isNotificationForCurrentProfiles( row2.getEntry().notification)).thenReturn(true); - ArrayList<NotificationData.Entry> reuslt = + ArrayList<NotificationData.Entry> result = mNotificationData.getNotificationsForCurrentUser(); - assertEquals(reuslt.size(), 1); - junit.framework.Assert.assertEquals(reuslt.get(0), row2.getEntry()); + assertEquals(result.size(), 1); + junit.framework.Assert.assertEquals(result.get(0), row2.getEntry()); } @Test @@ -416,8 +423,8 @@ public class NotificationDataTest extends SysuiTestCase { } private class TestableNotificationData extends NotificationData { - public TestableNotificationData(Environment environment) { - super(environment); + public TestableNotificationData() { + super(); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index f01ae7a23533..fb4ecd1383c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -54,7 +54,9 @@ import android.widget.FrameLayout; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; +import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationLifetimeExtender; @@ -66,12 +68,14 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -97,6 +101,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { private static final int TEST_UID = 0; @Mock private NotificationPresenter mPresenter; + @Mock private KeyguardEnvironment mEnvironment; @Mock private ExpandableNotificationRow mRow; @Mock private NotificationListContainer mListContainer; @Mock private NotificationEntryManager.Callback mCallback; @@ -190,6 +195,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + mDependency.injectMockDependency(ShadeController.class); mDependency.injectTestDependency(ForegroundServiceController.class, mForegroundServiceController); mDependency.injectTestDependency(NotificationLockscreenUserManager.class, @@ -204,12 +210,12 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController); + mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment); mCountDownLatch = new CountDownLatch(1); - when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper())); - when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(mLockscreenUserManager); - when(mPresenter.getGroupManager()).thenReturn(mGroupManager); + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, + Handler.createAsync(Looper.myLooper())); when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); when(mListContainer.getViewParentForNotification(any())).thenReturn( new FrameLayout(mContext)); @@ -224,6 +230,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntry.expandedIcon = mock(StatusBarIconView.class); mEntryManager = new TestableNotificationEntryManager(mContext, mBarService); + Dependency.get(InitController.class).executePostInitTasks(); mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager); setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL); @@ -421,8 +428,9 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationRanking() { - when(mPresenter.isDeviceProvisioned()).thenReturn(true); - when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true); + when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); + when(mEnvironment.isDeviceProvisioned()).thenReturn(true); + when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true); mEntry.row = mRow; mEntry.setInflationTask(mAsyncInflationTask); @@ -437,8 +445,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationRanking_noChange() { - when(mPresenter.isDeviceProvisioned()).thenReturn(true); - when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true); + when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); + when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true); mEntry.row = mRow; mEntryManager.getNotificationData().add(mEntry); @@ -451,8 +459,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationRanking_rowNotInflatedYet() { - when(mPresenter.isDeviceProvisioned()).thenReturn(true); - when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true); + when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); + when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true); mEntry.row = null; mEntryManager.getNotificationData().add(mEntry); @@ -466,8 +474,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationRanking_pendingNotification() { - when(mPresenter.isDeviceProvisioned()).thenReturn(true); - when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true); + when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); + when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true); mEntry.row = null; mEntryManager.mPendingNotifications.put(mEntry.key, mEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index ca62c3b5ee6f..1c7a8e8185a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -92,7 +92,7 @@ public class NotificationLoggerTest extends SysuiTestCase { mEntry.row = mRow; mLogger = new TestableNotificationLogger(mBarService); - mLogger.setUpWithEntryManager(mEntryManager, mListContainer); + mLogger.setUpWithContainer(mListContainer); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 8edbf176215a..ee35449e05da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -61,7 +61,9 @@ import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import org.junit.Before; import org.junit.Rule; @@ -93,11 +95,14 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Mock private NotificationEntryManager mEntryManager; @Mock private NotificationStackScrollLayout mStackScroller; @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener; - @Mock private NotificationGutsManager.OnSettingsClickListener mOnSettingsClickListener; + @Mock private OnSettingsClickListener mOnSettingsClickListener; + @Mock private DeviceProvisionedController mDeviceProvisionedController; @Before public void setUp() { mTestableLooper = TestableLooper.get(this); + mDependency.injectTestDependency(DeviceProvisionedController.class, + mDeviceProvisionedController); mHandler = Handler.createAsync(mTestableLooper.getLooper()); mHelper = new NotificationTestHelper(mContext); @@ -330,7 +335,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - when(mPresenter.isDeviceProvisioned()).thenReturn(true); + when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); mGutsManager.initializeNotificationInfo(row, notificationInfoView); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index f8b24363da8f..d2fe82fd93c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -17,6 +17,8 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -40,30 +42,31 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.View; +import com.android.systemui.Dependency; import com.android.systemui.ExpandHelper; +import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.EmptyShadeView; -import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShelf; +import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; -import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.NotificationShelf; -import com.android.systemui.statusbar.RemoteInputController; -import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager; -import java.util.ArrayList; - import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -74,6 +77,8 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.ArrayList; + /** * Tests for {@link NotificationStackScrollLayout}. */ @@ -108,6 +113,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mDependency.injectTestDependency(StatusBarStateController.class, mBarState); mDependency.injectTestDependency(NotificationRemoteInputManager.class, mRemoteInputManager); + mDependency.injectMockDependency(ShadeController.class); when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); IPowerManager powerManagerService = mock(IPowerManager.class); @@ -116,11 +122,13 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager, mContext); + mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); + Dependency.get(InitController.class).executePostInitTasks(); mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, null, mHeadsUpManager, mNotificationData); - mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); - NotificationShelf notificationShelf = spy(new NotificationShelf(getContext(), null)); + + NotificationShelf notificationShelf = mock(NotificationShelf.class); mStackScroller = spy(new NotificationStackScrollLayout(getContext())); mStackScroller.setShelf(notificationShelf); mStackScroller.setStatusBar(mBar); @@ -147,7 +155,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { when(mBarState.getState()).thenReturn(StatusBarState.SHADE); mStackScroller.setDimmed(true /* dimmed */, false /* animate */); mStackScroller.setDimmed(true /* dimmed */, true /* animate */); - Assert.assertFalse(mStackScroller.isDimmed()); + assertFalse(mStackScroller.isDimmed()); } @Test @@ -246,7 +254,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { assertEquals(0, mNotificationData.getActiveNotifications().size()); mStackScroller.updateFooter(); - verify(mStackScroller).updateFooterView(false, false); + verify(mStackScroller, atLeastOnce()).updateFooterView(false, false); } @Test @@ -287,7 +295,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { setBarStateForTest(StatusBarState.SHADE); ArrayList<Entry> entries = new ArrayList<>(); entries.add(mock(Entry.class)); - when(mNotificationData.getActiveNotifications()).thenReturn(entries); + when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries); + assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0); mStackScroller.updateFooter(); verify(mStackScroller).updateFooterView(true, false); @@ -348,9 +357,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } private void setBarStateForTest(int state) { - ArgumentCaptor<StatusBarStateController.StateListener> captor = - ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); - verify(mBarState, atLeastOnce()).addListener(captor.capture()); - captor.getValue().onStateChanged(state); + // Can't inject this through the listener or we end up on the actual implementation + // rather than the mock because the spy just coppied the anonymous inner /shruggie. + mStackScroller.setStatusBarState(state); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index 020682b6b4e8..7f8668fa064c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -45,6 +45,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.keyguard.DismissCallbackRegistry; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import org.junit.Assert; import org.junit.Before; @@ -238,7 +239,7 @@ public class KeyguardBouncerTest extends SysuiTestCase { @Test public void testShowOnDismissAction_showsBouncer() { - final KeyguardHostView.OnDismissAction dismissAction = () -> false; + final OnDismissAction dismissAction = () -> false; final Runnable cancelAction = () -> {}; mBouncer.showWithDismissAction(dismissAction, cancelAction); verify(mKeyguardHostView).setOnDismissAction(dismissAction, cancelAction); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index be4560b5b845..93dedfbe9656 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -31,10 +31,10 @@ import android.testing.TestableLooper; import android.view.ViewGroup; import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardHostView; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.DismissCallbackRegistry; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import org.junit.Before; import org.junit.Test; @@ -78,7 +78,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test public void dismissWithAction_AfterKeyguardGoneSetToFalse() { - KeyguardHostView.OnDismissAction action = () -> false; + OnDismissAction action = () -> false; Runnable cancelAction = () -> {}; mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, false /* afterKeyguardGone */); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java new file mode 100644 index 000000000000..24baa7d770f1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.mock; + +import android.app.Notification; +import android.app.StatusBarManager; +import android.content.Context; +import android.metrics.LogMaker; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.filters.SmallTest; +import android.support.test.metricshelper.MetricsAsserts; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; +import android.view.ViewGroup; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.logging.testing.FakeMetricsLogger; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; +import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper(setAsMainLooper = true) +public class StatusBarNotificationPresenterTest extends SysuiTestCase { + + + private StatusBarNotificationPresenter mStatusBar; + private CommandQueue mCommandQueue; + private FakeMetricsLogger mMetricsLogger; + private ShadeController mShadeController = mock(ShadeController.class); + + @Before + public void setup() { + mMetricsLogger = new FakeMetricsLogger(); + mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); + mCommandQueue = new CommandQueue(); + mContext.putComponent(CommandQueue.class, mCommandQueue); + mDependency.injectTestDependency(ShadeController.class, mShadeController); + + mStatusBar = new StatusBarNotificationPresenter(mContext, + mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class), + mock(StatusBarWindowView.class), mock(NotificationListContainerViewGroup.class), + mock(DozeScrimController.class), mock(ScrimController.class), + mock(ActivityLaunchAnimator.Callback.class)); + } + + @Test + public void testHeadsUp_disabledStatusBar() { + Notification n = new Notification.Builder(getContext(), "a").build(); + StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, + UserHandle.of(0), null, 0); + NotificationData.Entry entry = new NotificationData.Entry(sbn); + mCommandQueue.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); + TestableLooper.get(this).processAllMessages(); + + assertFalse("The panel shouldn't allow heads up while disabled", + mStatusBar.canHeadsUp(entry, sbn)); + } + + @Test + public void testHeadsUp_disabledNotificationShade() { + Notification n = new Notification.Builder(getContext(), "a").build(); + StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, + UserHandle.of(0), null, 0); + NotificationData.Entry entry = new NotificationData.Entry(sbn); + mCommandQueue.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); + TestableLooper.get(this).processAllMessages(); + + assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled", + mStatusBar.canHeadsUp(entry, sbn)); + } + + @Test + public void onActivatedMetrics() { + ActivatableNotificationView view = mock(ActivatableNotificationView.class); + mStatusBar.onActivated(view); + + MetricsAsserts.assertHasLog("missing lockscreen note tap log", + mMetricsLogger.getLogs(), + new LogMaker(MetricsEvent.ACTION_LS_NOTE) + .setType(MetricsEvent.TYPE_ACTION)); + } + + // We need this because mockito doesn't know how to construct a mock that extends ViewGroup + // and implements NotificationListContainer without it because of classloader issues. + private abstract static class NotificationListContainerViewGroup extends ViewGroup + implements NotificationListContainer { + + public NotificationListContainerViewGroup(Context context) { + super(context); + } + } +} + diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java new file mode 100644 index 000000000000..f28757f516da --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.UserManager; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class StatusBarRemoteInputCallbackTest extends SysuiTestCase { + @Mock private NotificationPresenter mPresenter; + @Mock private UserManager mUserManager; + + // Dependency mocks: + @Mock private NotificationEntryManager mEntryManager; + @Mock private DeviceProvisionedController mDeviceProvisionedController; + @Mock private ShadeController mShadeController; + @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager; + + private int mCurrentUserId = 0; + private StatusBarRemoteInputCallback mRemoteInputCallback; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); + mDependency.injectTestDependency(DeviceProvisionedController.class, + mDeviceProvisionedController); + mDependency.injectTestDependency(ShadeController.class, mShadeController); + mDependency.injectTestDependency(NotificationLockscreenUserManager.class, + mNotificationLockscreenUserManager); + mDependency.putComponent(CommandQueue.class, mock(CommandQueue.class)); + + mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext)); + mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver(); + } + + @Test + public void testActionDeviceLockedChangedWithDifferentUserIdCallsOnWorkChallengeChanged() { + when(mNotificationLockscreenUserManager.getCurrentUserId()).thenReturn(mCurrentUserId); + when(mNotificationLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(true); + Intent intent = new Intent() + .setAction(ACTION_DEVICE_LOCKED_CHANGED) + .putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId + 1); + mRemoteInputCallback.mChallengeReceiver.onReceive(mContext, intent); + verify(mRemoteInputCallback, times(1)).onWorkChallengeChanged(); + } + +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index da93327061a8..939245f7cc7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -18,9 +18,7 @@ package com.android.systemui.statusbar.phone; import static android.app.NotificationManager.IMPORTANCE_HIGH; -import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.fail; @@ -64,42 +62,41 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.internal.statusbar.IStatusBarService; -import com.android.keyguard.KeyguardHostView.OnDismissAction; +import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; +import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; -import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; -import com.android.systemui.statusbar.notification.AppOpsListener; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationData.Entry; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.RemoteInputController; -import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.AppOpsListener; +import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.NotificationData.Entry; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import org.junit.Before; import org.junit.Test; @@ -137,18 +134,21 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private RemoteInputController mRemoteInputController; @Mock private StatusBarStateController mStatusBarStateController; + @Mock private DeviceProvisionedController mDeviceProvisionedController; + @Mock private NotificationPresenter mNotificationPresenter; + @Mock private NotificationEntryManager.Callback mCallback; private TestableStatusBar mStatusBar; private FakeMetricsLogger mMetricsLogger; private PowerManager mPowerManager; private TestableNotificationEntryManager mEntryManager; private NotificationLogger mNotificationLogger; + private CommandQueue mCommandQueue; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); mDependency.injectMockDependency(AssistManager.class); - mDependency.injectMockDependency(DeviceProvisionedController.class); mDependency.injectMockDependency(NotificationGutsManager.class); mDependency.injectMockDependency(NotificationMediaManager.class); mDependency.injectMockDependency(ForegroundServiceController.class); @@ -159,6 +159,8 @@ public class StatusBarTest extends SysuiTestCase { mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class)); mDependency.injectTestDependency(AppOpsListener.class, mock(AppOpsListener.class)); mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController); + mDependency.injectTestDependency(DeviceProvisionedController.class, + mDeviceProvisionedController); mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class)); mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class)); @@ -172,9 +174,9 @@ public class StatusBarTest extends SysuiTestCase { mPowerManager = new PowerManager(mContext, powerManagerService, Handler.createAsync(Looper.myLooper())); - CommandQueue commandQueue = mock(CommandQueue.class); - when(commandQueue.asBinder()).thenReturn(new Binder()); - mContext.putComponent(CommandQueue.class, commandQueue); + mCommandQueue = mock(CommandQueue.class); + when(mCommandQueue.asBinder()).thenReturn(new Binder()); + mContext.putComponent(CommandQueue.class, mCommandQueue); mContext.setTheme(R.style.Theme_SystemUI_Light); @@ -202,17 +204,19 @@ public class StatusBarTest extends SysuiTestCase { mPowerManager, mNotificationPanelView, mBarService, mNotificationListener, mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager, mEntryManager, mScrimController, mBiometricUnlockController, - mock(ActivityLaunchAnimator.class), mKeyguardViewMediator, - mRemoteInputManager, mock(NotificationGroupManager.class), + mKeyguardViewMediator, mRemoteInputManager, mock(NotificationGroupManager.class), mock(FalsingManager.class), mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class), mock(DozeScrimController.class), mock(NotificationShelf.class), mLockscreenUserManager, - mock(CommandQueue.class)); + mCommandQueue, + mNotificationPresenter); mStatusBar.mContext = mContext; mStatusBar.mComponents = mContext.getComponents(); - mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar, mHeadsUpManager, - mNotificationData); - mNotificationLogger.setUpWithEntryManager(mEntryManager, mStackScroller); + mStatusBar.putComponent(StatusBar.class, mStatusBar); + Dependency.get(InitController.class).executePostInitTasks(); + mEntryManager.setUpForTest(mock(NotificationPresenter.class), mStackScroller, + mCallback, mHeadsUpManager, mNotificationData); + mNotificationLogger.setUpWithContainer(mStackScroller); TestableLooper.get(this).setMessageHandler(m -> { if (m.getCallback() == mStatusBar.mNotificationLogger.getVisibilityReporter()) { @@ -347,17 +351,6 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void onActivatedMetrics() { - ActivatableNotificationView view = mock(ActivatableNotificationView.class); - mStatusBar.onActivated(view); - - MetricsAsserts.assertHasLog("missing lockscreen note tap log", - mMetricsLogger.getLogs(), - new LogMaker(MetricsEvent.ACTION_LS_NOTE) - .setType(MetricsEvent.TYPE_ACTION)); - } - - @Test public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception { when(mPowerManager.isScreenOn()).thenReturn(true); when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); @@ -365,6 +358,7 @@ public class StatusBarTest extends SysuiTestCase { when(mNotificationData.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH); + when(mCallback.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a") .setGroup("a") @@ -386,6 +380,7 @@ public class StatusBarTest extends SysuiTestCase { when(mNotificationData.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH); + when(mCallback.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a") .setGroup("a") @@ -406,6 +401,7 @@ public class StatusBarTest extends SysuiTestCase { when(mNotificationData.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH); + when(mCallback.canHeadsUp(any(), any())).thenReturn(true); when(mNotificationData.shouldSuppressPeek(any())).thenReturn(true); @@ -424,6 +420,7 @@ public class StatusBarTest extends SysuiTestCase { when(mNotificationData.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH); + when(mCallback.canHeadsUp(any(), any())).thenReturn(true); when(mNotificationData.shouldSuppressPeek(any())).thenReturn(false); @@ -436,30 +433,6 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void testHeadsUp_disabledStatusBar() { - Notification n = new Notification.Builder(getContext(), "a").build(); - StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, - UserHandle.of(0), null, 0); - NotificationData.Entry entry = new NotificationData.Entry(sbn); - mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); - - assertFalse("The panel shouldn't allow heads up while disabled", - mStatusBar.canHeadsUp(entry, sbn)); - } - - @Test - public void testHeadsUp_disabledNotificationShade() { - Notification n = new Notification.Builder(getContext(), "a").build(); - StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, - UserHandle.of(0), null, 0); - NotificationData.Entry entry = new NotificationData.Entry(sbn); - mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); - - assertFalse("The panel shouldn't allow heads up while notification shade disabled", - mStatusBar.canHeadsUp(entry, sbn)); - } - - @Test public void testLogHidden() { try { mStatusBar.handleVisibleToUserChanged(false); @@ -473,10 +446,11 @@ public class StatusBarTest extends SysuiTestCase { @Test public void testPanelOpenForHeadsUp() { + when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList); when(mNotificationList.size()).thenReturn(5); - when(mNotificationPanelView.isFullyCollapsed()).thenReturn(true); + when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true); mStatusBar.setBarStateForTest(StatusBarState.SHADE); try { @@ -495,7 +469,7 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList); when(mNotificationList.size()).thenReturn(5); - when(mNotificationPanelView.isFullyCollapsed()).thenReturn(false); + when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false); mStatusBar.setBarStateForTest(StatusBarState.SHADE); try { @@ -514,7 +488,7 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList); when(mNotificationList.size()).thenReturn(5); - when(mNotificationPanelView.isFullyCollapsed()).thenReturn(false); + when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false); mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); try { @@ -532,8 +506,9 @@ public class StatusBarTest extends SysuiTestCase { public void testDisableExpandStatusBar() { mStatusBar.setBarStateForTest(StatusBarState.SHADE); mStatusBar.setUserSetupForTest(true); - when(mStatusBar.isDeviceProvisioned()).thenReturn(true); + when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); + when(mCommandQueue.panelsEnabled()).thenReturn(false); mStatusBar.disable(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false); verify(mNotificationPanelView).setQsExpansionEnabled(false); @@ -542,6 +517,7 @@ public class StatusBarTest extends SysuiTestCase { mStatusBar.animateExpandSettingsPanel(null); verify(mNotificationPanelView, never()).expand(anyBoolean()); + when(mCommandQueue.panelsEnabled()).thenReturn(true); mStatusBar.disable(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE, false); verify(mNotificationPanelView).setQsExpansionEnabled(true); mStatusBar.animateExpandNotificationsPanel(); @@ -627,7 +603,7 @@ public class StatusBarTest extends SysuiTestCase { NotificationViewHierarchyManager viewHierarchyManager, TestableNotificationEntryManager entryManager, ScrimController scrimController, BiometricUnlockController biometricUnlockController, - ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator, + KeyguardViewMediator keyguardViewMediator, NotificationRemoteInputManager notificationRemoteInputManager, NotificationGroupManager notificationGroupManager, FalsingManager falsingManager, @@ -636,7 +612,8 @@ public class StatusBarTest extends SysuiTestCase { DozeScrimController dozeScrimController, NotificationShelf notificationShelf, NotificationLockscreenUserManager notificationLockscreenUserManager, - CommandQueue commandQueue) { + CommandQueue commandQueue, + NotificationPresenter notificationPresenter) { mStatusBarKeyguardViewManager = man; mUnlockMethodCache = unlock; mKeyguardIndicationController = key; @@ -653,7 +630,6 @@ public class StatusBarTest extends SysuiTestCase { mEntryManager = entryManager; mScrimController = scrimController; mBiometricUnlockController = biometricUnlockController; - mActivityLaunchAnimator = launchAnimator; mKeyguardViewMediator = keyguardViewMediator; mRemoteInputManager = notificationRemoteInputManager; mGroupManager = notificationGroupManager; @@ -664,6 +640,7 @@ public class StatusBarTest extends SysuiTestCase { mNotificationShelf = notificationShelf; mLockscreenUserManager = notificationLockscreenUserManager; mCommandQueue = commandQueue; + mPresenter = notificationPresenter; } private WakefulnessLifecycle createAwakeWakefulnessLifecycle() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index 01e63070e07d..4534ebee52be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -41,7 +41,7 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; -import com.android.keyguard.KeyguardHostView.OnDismissAction; +import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.NotificationData; diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java index 51b86c26dff2..95c7a4d09f92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java @@ -62,6 +62,11 @@ public class FakeKeyguardMonitor implements KeyguardMonitor { } @Override + public boolean isLaunchTransitionFadingAway() { + return false; + } + + @Override public long getKeyguardFadingAwayDuration() { return 0; } |