summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src
diff options
context:
space:
mode:
authorSteven Laver <lavers@google.com>2020-02-13 20:29:13 -0800
committerSteven Laver <lavers@google.com>2020-02-13 20:29:13 -0800
commitd28a4f6b38dbab44128b4319f665dd65c3e4ec2c (patch)
tree680912fe833379242ee026450323ed4f34a6c64b /packages/SystemUI/src
parent029ad4fa703b5dcb74e8c4c272617464a9ba5fc8 (diff)
parent852c9950280d93875c529e4cae8396d94176f66e (diff)
Merge RP1A.200204.001
Change-Id: I1e6c199dbee77379f84675965391c839eae04961
Diffstat (limited to 'packages/SystemUI/src')
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/CarrierTextController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java46
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/DejankUtils.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/DumpController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java393
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java571
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java174
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt150
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt143
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt134
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt (renamed from packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapper.kt)39
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt126
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt202
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt183
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java142
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java185
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt209
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSuppressedHandler.java124
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt213
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogLevel.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogMessage.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt133
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/SysuiLog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipUI.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java203
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java115
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java169
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt217
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt157
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java144
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java4
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java120
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java10
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastUI.java212
-rw-r--r--packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java145
-rw-r--r--packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto57
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Utils.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java109
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayController.java (renamed from packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java)82
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java12
174 files changed, 6244 insertions, 2218 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 2f8ef2dc8828..b2423b9bf252 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -172,7 +172,7 @@ public class AdminSecondaryLockScreenController {
private void onSurfaceReady() {
try {
- mClient.onSurfaceReady(mView.getInputToken(), mCallback);
+ mClient.onSurfaceReady(mView.getHostToken(), mCallback);
} catch (RemoteException e) {
Log.e(TAG, "Error in onSurfaceReady", e);
dismiss(KeyguardUpdateMonitor.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index ec4411cfcabd..e0594a999a23 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -39,7 +39,6 @@ import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.settingslib.WirelessUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -347,15 +346,15 @@ public class CarrierTextController {
CharSequence text =
getContext().getText(com.android.internal.R.string.emergency_calls_only);
Intent i = getContext().registerReceiver(null,
- new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION));
+ new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED));
if (i != null) {
String spn = "";
String plmn = "";
- if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
- spn = i.getStringExtra(TelephonyIntents.EXTRA_SPN);
+ if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)) {
+ spn = i.getStringExtra(TelephonyManager.EXTRA_SPN);
}
- if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
- plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
+ if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)) {
+ plmn = i.getStringExtra(TelephonyManager.EXTRA_PLMN);
}
if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
if (Objects.equals(plmn, spn)) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 4e7956db4a2b..571c4ae0e386 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -290,7 +290,7 @@ public class KeyguardDisplayManager {
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- getWindow().setFitWindowInsetsTypes(0 /* types */);
+ getWindow().getAttributes().setFitInsetsTypes(0 /* types */);
getWindow().setNavigationBarContrastEnforced(false);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index ed1cd8191092..d5a08dda9853 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -409,15 +409,6 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback {
mAudioManager.dispatchMediaKeyEvent(keyEvent);
}
- @Override
- public void dispatchSystemUiVisibilityChanged(int visibility) {
- super.dispatchSystemUiVisibilityChanged(visibility);
-
- if (!(mContext instanceof Activity)) {
- setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
- }
- }
-
/**
* In general, we enable unlocking the insecure keyguard with the menu key. However, there are
* some cases where we wish to disable it, notably when the menu button placement or technology
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ae787260adca..29c67ae1b4a6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -15,7 +15,12 @@
*/
package com.android.keyguard;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.ViewRootImpl.sNewInsetsMode;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.systemBars;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static java.lang.Integer.max;
import android.app.Activity;
import android.app.AlertDialog;
@@ -38,6 +43,7 @@ import android.view.SurfaceControl;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -339,13 +345,22 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
}
@Override
- protected boolean fitSystemWindows(Rect insets) {
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+
// Consume bottom insets because we're setting the padding locally (for IME and navbar.)
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), insets.bottom);
- insets.bottom = 0;
- return false;
+ int inset;
+ if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+ int bottomInset = insets.getInsetsIgnoringVisibility(systemBars()).bottom;
+ int imeInset = insets.getInsets(ime()).bottom;
+ inset = max(bottomInset, imeInset);
+ } else {
+ inset = insets.getSystemWindowInsetBottom();
+ }
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), inset);
+ return insets.inset(0, 0, 0, inset);
}
+
private void showDialog(String title, String message) {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 5d35169cf926..f61f585cfe7c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -140,6 +140,13 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
mLayoutTransition.setAnimateParentHierarchy(false);
}
+ // Temporary workaround to allow KeyguardStatusView to inflate a copy for Universal Smartspace.
+ // Eventually the existing copy will be reparented instead, and we won't need this.
+ public KeyguardSliceView(Context context, AttributeSet attributeSet) {
+ this(context, attributeSet, Dependency.get(ActivityStarter.class),
+ Dependency.get(ConfigurationController.class));
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 5a1c9976f021..61caf3bc5d8f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -18,10 +18,15 @@ package com.android.keyguard;
import android.app.ActivityManager;
import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.PixelFormat;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -30,7 +35,11 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
import android.widget.GridLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -40,6 +49,7 @@ import androidx.core.graphics.ColorUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.shared.system.UniversalSmartspaceUtils;
import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.FileDescriptor;
@@ -76,6 +86,7 @@ public class KeyguardStatusView extends GridLayout implements
private int mIconTopMargin;
private int mIconTopMarginWithHeader;
private boolean mShowingHeader;
+ private SurfaceControlViewHost mUniversalSmartspaceViewHost;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@@ -122,6 +133,38 @@ public class KeyguardStatusView extends GridLayout implements
}
};
+ private BroadcastReceiver mUniversalSmartspaceBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent i) {
+ // TODO(b/148159743): Restrict to Pixel Launcher.
+ if (UniversalSmartspaceUtils.ACTION_REQUEST_SMARTSPACE_VIEW.equals(i.getAction())) {
+ if (mUniversalSmartspaceViewHost != null) {
+ mUniversalSmartspaceViewHost.die();
+ }
+ SurfaceControl surfaceControl = UniversalSmartspaceUtils.getSurfaceControl(i);
+ if (surfaceControl != null) {
+ IBinder input = UniversalSmartspaceUtils.getInputToken(i);
+
+ WindowlessWindowManager windowlessWindowManager =
+ new WindowlessWindowManager(context.getResources().getConfiguration(),
+ surfaceControl, input);
+ mUniversalSmartspaceViewHost = new SurfaceControlViewHost(context,
+ context.getDisplay(), windowlessWindowManager);
+ WindowManager.LayoutParams layoutParams =
+ new WindowManager.LayoutParams(
+ surfaceControl.getWidth(),
+ surfaceControl.getHeight(),
+ WindowManager.LayoutParams.TYPE_APPLICATION,
+ 0,
+ PixelFormat.TRANSPARENT);
+
+ mUniversalSmartspaceViewHost.addView(
+ inflate(context, R.layout.keyguard_status_area, null), layoutParams);
+ }
+ }
+ }
+ };;
+
public KeyguardStatusView(Context context) {
this(context, null, 0);
}
@@ -316,6 +359,8 @@ public class KeyguardStatusView extends GridLayout implements
super.onAttachedToWindow();
Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback);
Dependency.get(ConfigurationController.class).addCallback(this);
+ getContext().registerReceiver(mUniversalSmartspaceBroadcastReceiver,
+ new IntentFilter(UniversalSmartspaceUtils.ACTION_REQUEST_SMARTSPACE_VIEW));
}
@Override
@@ -323,6 +368,7 @@ public class KeyguardStatusView extends GridLayout implements
super.onDetachedFromWindow();
Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback);
Dependency.get(ConfigurationController.class).removeCallback(this);
+ getContext().unregisterReceiver(mUniversalSmartspaceBroadcastReceiver);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d71912a89504..4979329ab9d6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -92,7 +92,6 @@ import android.util.Log;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
import com.android.systemui.DejankUtils;
@@ -372,7 +371,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
checkIsHandlerThread();
if (DEBUG_SIM_STATES) {
Log.v(TAG, "onSubscriptionInfoChanged()");
- List<SubscriptionInfo> sil = mSubscriptionManager.getActiveSubscriptionInfoList(false);
+ List<SubscriptionInfo> sil = mSubscriptionManager
+ .getActiveAndHiddenSubscriptionInfoList();
if (sil != null) {
for (SubscriptionInfo subInfo : sil) {
Log.v(TAG, "SubInfo:" + subInfo);
@@ -426,10 +426,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
List<SubscriptionInfo> sil = mSubscriptionInfo;
if (sil == null || forceReload) {
- sil = mSubscriptionManager.getActiveSubscriptionInfoList(false);
+ sil = mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList();
}
if (sil == null) {
- // getActiveSubscriptionInfoList was null callers expect an empty list.
+ // getActiveAndHiddenSubscriptionInfoList was null callers expect an empty list.
mSubscriptionInfo = new ArrayList<SubscriptionInfo>();
} else {
mSubscriptionInfo = sil;
@@ -1083,7 +1083,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health,
maxChargingMicroWatt));
mHandler.sendMessage(msg);
- } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
+ } else if (Intent.ACTION_SIM_STATE_CHANGED.equals(action)) {
SimData args = SimData.fromIntent(intent);
// ACTION_SIM_STATE_CHANGED is rebroadcast after unlocking the device to
// keep compatibility with apps that aren't direct boot aware.
@@ -1122,7 +1122,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
mHandler.sendMessage(
mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
- } else if (TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
+ } else if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED);
} else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
action)) {
@@ -1273,7 +1273,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
static SimData fromIntent(Intent intent) {
int state;
- if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
+ if (!Intent.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
}
String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
@@ -1673,7 +1673,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(Intent.ACTION_SERVICE_STATE);
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
@@ -1883,9 +1883,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep;
final int user = getCurrentUser();
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
- final boolean isLockOutOrLockDown =
- containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_LOCKOUT)
- || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+ final boolean isLockDown =
+ containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
final boolean isEncryptedOrTimedOut =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
@@ -1899,9 +1898,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
boolean becauseCannotSkipBouncer = !getUserCanSkipBouncer(user) || canBypass;
// Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
- // Lockout/lockdown modes shouldn't scan, since they are more explicit.
+ // Lock-down mode shouldn't scan, since it is more explicit.
boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass && !mBouncer)
- && !isLockOutOrLockDown;
+ && !isLockDown;
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
diff --git a/packages/SystemUI/src/com/android/systemui/DejankUtils.java b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
index 97578e19ccd1..3fce55f6e515 100644
--- a/packages/SystemUI/src/com/android/systemui/DejankUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
@@ -61,19 +61,21 @@ public class DejankUtils {
|| !isMainThread() || sTemporarilyIgnoreStrictMode) {
return null;
}
+ }
- try {
- String description = binder.getInterfaceDescriptor();
+ try {
+ String description = binder.getInterfaceDescriptor();
+ synchronized (sLock) {
if (sWhitelistedFrameworkClasses.contains(description)) {
return null;
}
- } catch (RemoteException e) {
- e.printStackTrace();
}
-
- StrictMode.noteSlowCall("IPC detected on critical path: " + sBlockingIpcs.peek());
- return null;
+ } catch (RemoteException e) {
+ e.printStackTrace();
}
+
+ StrictMode.noteSlowCall("IPC detected on critical path: " + sBlockingIpcs.peek());
+ return null;
}
@Override
@@ -126,9 +128,11 @@ public class DejankUtils {
if (STRICT_MODE_ENABLED && sBlockingIpcs.empty()) {
synchronized (sLock) {
sBlockingIpcs.push("detectBlockingIpcs");
- try {
- runnable.run();
- } finally {
+ }
+ try {
+ runnable.run();
+ } finally {
+ synchronized (sLock) {
sBlockingIpcs.pop();
}
}
@@ -177,9 +181,11 @@ public class DejankUtils {
if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
synchronized (sLock) {
sTemporarilyIgnoreStrictMode = true;
- try {
- runnable.run();
- } finally {
+ }
+ try {
+ runnable.run();
+ } finally {
+ synchronized (sLock) {
sTemporarilyIgnoreStrictMode = false;
}
}
@@ -196,14 +202,16 @@ public class DejankUtils {
if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
synchronized (sLock) {
sTemporarilyIgnoreStrictMode = true;
- final T val;
- try {
- val = supplier.get();
- } finally {
+ }
+ final T val;
+ try {
+ val = supplier.get();
+ } finally {
+ synchronized (sLock) {
sTemporarilyIgnoreStrictMode = false;
}
- return val;
}
+ return val;
} else {
return supplier.get();
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index aacc2c4f614b..27fe37ef4e69 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -117,14 +117,15 @@ import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.wm.DisplayController;
import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.DisplayWindowController;
import com.android.systemui.wm.SystemWindows;
import java.io.FileDescriptor;
@@ -323,10 +324,11 @@ public class Dependency {
@Inject Lazy<CommandQueue> mCommandQueue;
@Inject Lazy<Recents> mRecents;
@Inject Lazy<StatusBar> mStatusBar;
- @Inject Lazy<DisplayWindowController> mDisplayWindowController;
+ @Inject Lazy<DisplayController> mDisplayController;
@Inject Lazy<SystemWindows> mSystemWindows;
@Inject Lazy<DisplayImeController> mDisplayImeController;
@Inject Lazy<RecordingController> mRecordingController;
+ @Inject Lazy<ProtoTracer> mProtoTracer;
@Inject
public Dependency() {
@@ -516,9 +518,10 @@ public class Dependency {
mProviders.put(CommandQueue.class, mCommandQueue::get);
mProviders.put(Recents.class, mRecents::get);
mProviders.put(StatusBar.class, mStatusBar::get);
- mProviders.put(DisplayWindowController.class, mDisplayWindowController::get);
+ mProviders.put(DisplayController.class, mDisplayController::get);
mProviders.put(SystemWindows.class, mSystemWindows::get);
mProviders.put(DisplayImeController.class, mDisplayImeController::get);
+ mProviders.put(ProtoTracer.class, mProtoTracer::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/DumpController.kt b/packages/SystemUI/src/com/android/systemui/DumpController.kt
index f14c4cd8e6c6..7a83a8948bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/DumpController.kt
+++ b/packages/SystemUI/src/com/android/systemui/DumpController.kt
@@ -19,10 +19,10 @@ package com.android.systemui
import android.util.ArraySet
import android.util.Log
import androidx.annotation.GuardedBy
-import com.android.internal.util.Preconditions
import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.ref.WeakReference
+import java.util.Objects.requireNonNull
import javax.inject.Inject
import javax.inject.Singleton
@@ -58,7 +58,7 @@ class DumpController @Inject constructor() : Dumpable {
* @param dumpable the [Dumpable] to be added
*/
fun registerDumpable(dumpable: Dumpable) {
- Preconditions.checkNotNull(dumpable, "The dumpable to be added cannot be null")
+ requireNonNull(dumpable, "The dumpable to be added cannot be null")
registerDumpable(dumpable.javaClass.simpleName, dumpable)
}
@@ -71,7 +71,7 @@ class DumpController @Inject constructor() : Dumpable {
* @param dumpable the [Dumpable] to be added
*/
fun registerDumpable(tag: String, dumpable: Dumpable) {
- Preconditions.checkNotNull(dumpable, "The dumpable to be added cannot be null")
+ requireNonNull(dumpable, "The dumpable to be added cannot be null")
if (DEBUG) Log.v(TAG, "*** register callback for $dumpable")
synchronized<Unit>(listeners) {
if (listeners.any { it.tag == tag }) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index db8b5831faf1..e66b9f21bd8c 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -503,7 +503,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
lp.gravity = Gravity.TOP | Gravity.LEFT;
}
lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- lp.setFitWindowInsetsTypes(0 /* types */);
+ lp.setFitInsetsTypes(0 /* types */);
if (isLandscape(mRotation)) {
lp.width = WRAP_CONTENT;
lp.height = MATCH_PARENT;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 75063e482f08..7b0bd9cadb9b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -35,6 +35,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -153,6 +154,7 @@ public class SystemUIFactory {
return new NotificationIconAreaController(context, statusBar, statusBarStateController,
wakeUpCoordinator, keyguardBypassController,
Dependency.get(NotificationMediaManager.class),
+ Dependency.get(NotificationListener.class),
Dependency.get(DozeParameters.class));
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
new file mode 100644
index 000000000000..7262f8caac89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2020 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.accessibility;
+
+import android.accessibilityservice.AccessibilityService;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.R;
+import com.android.internal.util.ScreenshotHelper;
+import com.android.systemui.Dependency;
+import com.android.systemui.SystemUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Class to register system actions with accessibility framework.
+ */
+@Singleton
+public class SystemActions extends SystemUI {
+ private static final String TAG = "SystemActions";
+ // TODO(b/147916452): add implementation on launcher side to register this action.
+
+ /**
+ * Action ID to go back.
+ */
+ private static final int SYSTEM_ACTION_ID_BACK = AccessibilityService.GLOBAL_ACTION_BACK; // = 1
+
+ /**
+ * Action ID to go home.
+ */
+ private static final int SYSTEM_ACTION_ID_HOME = AccessibilityService.GLOBAL_ACTION_HOME; // = 2
+
+ /**
+ * Action ID to toggle showing the overview of recent apps. Will fail on platforms that don't
+ * show recent apps.
+ */
+ private static final int SYSTEM_ACTION_ID_RECENTS =
+ AccessibilityService.GLOBAL_ACTION_RECENTS; // = 3
+
+ /**
+ * Action ID to open the notifications.
+ */
+ private static final int SYSTEM_ACTION_ID_NOTIFICATIONS =
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS; // = 4
+
+ /**
+ * Action ID to open the quick settings.
+ */
+ private static final int SYSTEM_ACTION_ID_QUICK_SETTINGS =
+ AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS; // = 5
+
+ /**
+ * Action ID to open the power long-press dialog.
+ */
+ private static final int SYSTEM_ACTION_ID_POWER_DIALOG =
+ AccessibilityService.GLOBAL_ACTION_POWER_DIALOG; // = 6
+
+ /**
+ * Action ID to toggle docking the current app's window
+ */
+ private static final int SYSTEM_ACTION_ID_TOGGLE_SPLIT_SCREEN =
+ AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN; // = 7
+
+ /**
+ * Action ID to lock the screen
+ */
+ private static final int SYSTEM_ACTION_ID_LOCK_SCREEN =
+ AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN; // = 8
+
+ /**
+ * Action ID to take a screenshot
+ */
+ private static final int SYSTEM_ACTION_ID_TAKE_SCREENSHOT =
+ AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; // = 9
+
+ /**
+ * Action ID to show accessibility menu
+ */
+ private static final int SYSTEM_ACTION_ID_ACCESSIBILITY_MENU = 10;
+
+ private Recents mRecents;
+ private StatusBar mStatusBar;
+ private SystemActionsBroadcastReceiver mReceiver;
+
+ @Inject
+ public SystemActions(Context context) {
+ super(context);
+ mRecents = Dependency.get(Recents.class);
+ mStatusBar = Dependency.get(StatusBar.class);
+ mReceiver = new SystemActionsBroadcastReceiver();
+ }
+
+ @Override
+ public void start() {
+ mContext.registerReceiverForAllUsers(mReceiver, mReceiver.createIntentFilter(), null, null);
+
+ // TODO(b/148087487): update the icon used below to a valid one
+ RemoteAction actionBack = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_back_label),
+ mContext.getString(R.string.accessibility_system_action_back_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_BACK));
+ RemoteAction actionHome = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_home_label),
+ mContext.getString(R.string.accessibility_system_action_home_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_HOME));
+
+ RemoteAction actionRecents = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_recents_label),
+ mContext.getString(R.string.accessibility_system_action_recents_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_RECENTS));
+
+ RemoteAction actionNotifications = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_notifications_label),
+ mContext.getString(R.string.accessibility_system_action_notifications_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_NOTIFICATIONS));
+
+ RemoteAction actionQuickSettings = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_quick_settings_label),
+ mContext.getString(R.string.accessibility_system_action_quick_settings_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_QUICK_SETTINGS));
+
+ RemoteAction actionPowerDialog = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_power_dialog_label),
+ mContext.getString(R.string.accessibility_system_action_power_dialog_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_POWER_DIALOG));
+
+ RemoteAction actionToggleSplitScreen = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_toggle_split_screen_label),
+ mContext.getString(R.string.accessibility_system_action_toggle_split_screen_label),
+ mReceiver.createPendingIntent(
+ mContext,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_TOGGLE_SPLIT_SCREEN));
+
+ RemoteAction actionLockScreen = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_lock_screen_label),
+ mContext.getString(R.string.accessibility_system_action_lock_screen_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_LOCK_SCREEN));
+
+ RemoteAction actionTakeScreenshot = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_screenshot_label),
+ mContext.getString(R.string.accessibility_system_action_screenshot_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT));
+
+ RemoteAction actionAccessibilityMenu = new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_info),
+ mContext.getString(R.string.accessibility_system_action_accessibility_menu_label),
+ mContext.getString(R.string.accessibility_system_action_accessibility_menu_label),
+ mReceiver.createPendingIntent(
+ mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_MENU));
+
+ AccessibilityManager am = (AccessibilityManager) mContext.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+
+ am.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK);
+ am.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME);
+ am.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS);
+ am.registerSystemAction(actionNotifications, SYSTEM_ACTION_ID_NOTIFICATIONS);
+ am.registerSystemAction(actionQuickSettings, SYSTEM_ACTION_ID_QUICK_SETTINGS);
+ am.registerSystemAction(actionPowerDialog, SYSTEM_ACTION_ID_POWER_DIALOG);
+ am.registerSystemAction(actionToggleSplitScreen, SYSTEM_ACTION_ID_TOGGLE_SPLIT_SCREEN);
+ am.registerSystemAction(actionLockScreen, SYSTEM_ACTION_ID_LOCK_SCREEN);
+ am.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
+ am.registerSystemAction(actionAccessibilityMenu, SYSTEM_ACTION_ID_ACCESSIBILITY_MENU);
+ }
+
+ private void handleBack() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
+ }
+
+ private void handleHome() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
+ }
+
+ private void sendDownAndUpKeyEvents(int keyCode) {
+ final long downTime = SystemClock.uptimeMillis();
+ sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime);
+ sendKeyEventIdentityCleared(
+ keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis());
+ }
+
+ private void sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time) {
+ KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
+ InputDevice.SOURCE_KEYBOARD, null);
+ InputManager.getInstance()
+ .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ event.recycle();
+ }
+
+ private void handleRecents() {
+ mRecents.toggleRecentApps();
+ }
+
+ private void handleNotifications() {
+ mStatusBar.animateExpandNotificationsPanel();
+ }
+
+ private void handleQuickSettings() {
+ mStatusBar.animateExpandSettingsPanel(null);
+ }
+
+ private void handlePowerDialog() {
+ IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
+
+ try {
+ windowManager.showGlobalActions();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to display power dialog.");
+ }
+ }
+
+ private void handleToggleSplitScreen() {
+ mStatusBar.toggleSplitScreen();
+ }
+
+ private void handleLockScreen() {
+ IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
+
+ mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0);
+ try {
+ windowManager.lockNow(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to lock screen.");
+ }
+ }
+
+ private void handleTakeScreenshot() {
+ ScreenshotHelper screenshotHelper = new ScreenshotHelper(mContext);
+ screenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+ true, true, new Handler(Looper.getMainLooper()), null);
+ }
+
+ private void handleAccessibilityMenu() {
+ AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
+ Display.DEFAULT_DISPLAY);
+ }
+
+ private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
+ private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
+ private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
+ private static final String INTENT_ACTION_RECENTS = "SYSTEM_ACTION_RECENTS";
+ private static final String INTENT_ACTION_NOTIFICATIONS = "SYSTEM_ACTION_NOTIFICATIONS";
+ private static final String INTENT_ACTION_QUICK_SETTINGS = "SYSTEM_ACTION_QUICK_SETTINGS";
+ private static final String INTENT_ACTION_POWER_DIALOG = "SYSTEM_ACTION_POWER_DIALOG";
+ private static final String INTENT_ACTION_TOGGLE_SPLIT_SCREEN =
+ "SYSTEM_ACTION_TOGGLE_SPLIT_SCREEN";
+ private static final String INTENT_ACTION_LOCK_SCREEN = "SYSTEM_ACTION_LOCK_SCREEN";
+ private static final String INTENT_ACTION_TAKE_SCREENSHOT = "SYSTEM_ACTION_TAKE_SCREENSHOT";
+ private static final String INTENT_ACTION_ACCESSIBILITY_MENU =
+ "SYSTEM_ACTION_ACCESSIBILITY_MENU";
+
+ private PendingIntent createPendingIntent(Context context, String intentAction) {
+ switch (intentAction) {
+ case INTENT_ACTION_BACK:
+ case INTENT_ACTION_HOME:
+ case INTENT_ACTION_RECENTS:
+ case INTENT_ACTION_NOTIFICATIONS:
+ case INTENT_ACTION_QUICK_SETTINGS:
+ case INTENT_ACTION_POWER_DIALOG:
+ case INTENT_ACTION_TOGGLE_SPLIT_SCREEN:
+ case INTENT_ACTION_LOCK_SCREEN:
+ case INTENT_ACTION_TAKE_SCREENSHOT:
+ case INTENT_ACTION_ACCESSIBILITY_MENU: {
+ Intent intent = new Intent(intentAction);
+ return PendingIntent.getBroadcast(context, 0, intent, 0);
+ }
+ default:
+ break;
+ }
+ return null;
+ }
+
+ private IntentFilter createIntentFilter() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(INTENT_ACTION_BACK);
+ intentFilter.addAction(INTENT_ACTION_HOME);
+ intentFilter.addAction(INTENT_ACTION_RECENTS);
+ intentFilter.addAction(INTENT_ACTION_NOTIFICATIONS);
+ intentFilter.addAction(INTENT_ACTION_QUICK_SETTINGS);
+ intentFilter.addAction(INTENT_ACTION_POWER_DIALOG);
+ intentFilter.addAction(INTENT_ACTION_TOGGLE_SPLIT_SCREEN);
+ intentFilter.addAction(INTENT_ACTION_LOCK_SCREEN);
+ intentFilter.addAction(INTENT_ACTION_TAKE_SCREENSHOT);
+ intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_MENU);
+ return intentFilter;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String intentAction = intent.getAction();
+ switch (intentAction) {
+ case INTENT_ACTION_BACK: {
+ handleBack();
+ break;
+ }
+ case INTENT_ACTION_HOME: {
+ handleHome();
+ break;
+ }
+ case INTENT_ACTION_RECENTS: {
+ handleRecents();
+ break;
+ }
+ case INTENT_ACTION_NOTIFICATIONS: {
+ handleNotifications();
+ break;
+ }
+ case INTENT_ACTION_QUICK_SETTINGS: {
+ handleQuickSettings();
+ break;
+ }
+ case INTENT_ACTION_POWER_DIALOG: {
+ handlePowerDialog();
+ break;
+ }
+ case INTENT_ACTION_TOGGLE_SPLIT_SCREEN: {
+ handleToggleSplitScreen();
+ break;
+ }
+ case INTENT_ACTION_LOCK_SCREEN: {
+ handleLockScreen();
+ break;
+ }
+ case INTENT_ACTION_TAKE_SCREENSHOT: {
+ handleTakeScreenshot();
+ break;
+ }
+ case INTENT_ACTION_ACCESSIBILITY_MENU: {
+ handleAccessibilityMenu();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index c243309d960a..581cf7a2fbef 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -86,6 +86,8 @@ public class WindowMagnificationController implements View.OnClickListener,
private SurfaceView mMirrorSurfaceView;
private View mControlsView;
private View mOverlayView;
+ // The boundary of magnification frame.
+ private final Rect mMagnificationFrameBoundary = new Rect();
private MoveMirrorRunnable mMoveMirrorRunnable = new MoveMirrorRunnable();
@@ -93,7 +95,7 @@ public class WindowMagnificationController implements View.OnClickListener,
mContext = context;
mHandler = handler;
Display display = mContext.getDisplay();
- display.getSize(mDisplaySize);
+ display.getRealSize(mDisplaySize);
mDisplayId = mContext.getDisplayId();
mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -114,6 +116,7 @@ public class WindowMagnificationController implements View.OnClickListener,
return;
}
setInitialStartBounds();
+ setMagnificationFrameBoundary();
createOverlayWindow();
}
@@ -330,7 +333,7 @@ public class WindowMagnificationController implements View.OnClickListener,
@Override
public void onClick(View v) {
setMoveOffset(v, mMoveFrameAmountShort);
- moveMirrorFromControls();
+ moveMirrorWindow(mMoveWindowOffset.x, mMoveWindowOffset.y);
}
@Override
@@ -370,10 +373,8 @@ public class WindowMagnificationController implements View.OnClickListener,
case MotionEvent.ACTION_MOVE:
int xDiff = (int) (event.getRawX() - mLastDrag.x);
int yDiff = (int) (event.getRawY() - mLastDrag.y);
- mMagnificationFrame.offset(xDiff, yDiff);
+ moveMirrorWindow(xDiff, yDiff);
mLastDrag.set(event.getRawX(), event.getRawY());
- modifyWindowMagnification(mTransaction);
- mTransaction.apply();
return true;
}
return false;
@@ -393,11 +394,11 @@ public class WindowMagnificationController implements View.OnClickListener,
}
}
- private void moveMirrorFromControls() {
- mMagnificationFrame.offset(mMoveWindowOffset.x, mMoveWindowOffset.y);
-
- modifyWindowMagnification(mTransaction);
- mTransaction.apply();
+ private void moveMirrorWindow(int xOffset, int yOffset) {
+ if (updateMagnificationFramePosition(xOffset, yOffset)) {
+ modifyWindowMagnification(mTransaction);
+ mTransaction.apply();
+ }
}
/**
@@ -414,6 +415,52 @@ public class WindowMagnificationController implements View.OnClickListener,
return new Rect(left, top, right, bottom);
}
+ private void setMagnificationFrameBoundary() {
+ // Calculates width and height for magnification frame could exceed out the screen.
+ // TODO : re-calculating again when scale is changed.
+ // The half width of magnification frame.
+ final int halfWidth = mMagnificationFrame.width() / 2;
+ // The half height of magnification frame.
+ final int halfHeight = mMagnificationFrame.height() / 2;
+ // The scaled half width of magnified region.
+ final int scaledWidth = (int) (halfWidth / mScale);
+ // The scaled half height of magnified region.
+ final int scaledHeight = (int) (halfHeight / mScale);
+ final int exceededWidth = halfWidth - scaledWidth;
+ final int exceededHeight = halfHeight - scaledHeight;
+
+ mMagnificationFrameBoundary.set(-exceededWidth, -exceededHeight,
+ mDisplaySize.x + exceededWidth, mDisplaySize.y + exceededHeight);
+ }
+
+ /**
+ * Calculates and sets the real position of magnification frame based on the magnified region
+ * should be limited by the region of the display.
+ */
+ private boolean updateMagnificationFramePosition(int xOffset, int yOffset) {
+ mTmpRect.set(mMagnificationFrame);
+ mTmpRect.offset(xOffset, yOffset);
+
+ if (mTmpRect.left < mMagnificationFrameBoundary.left) {
+ mTmpRect.offsetTo(mMagnificationFrameBoundary.left, mTmpRect.top);
+ } else if (mTmpRect.right > mMagnificationFrameBoundary.right) {
+ final int leftOffset = mMagnificationFrameBoundary.right - mMagnificationFrame.width();
+ mTmpRect.offsetTo(leftOffset, mTmpRect.top);
+ }
+
+ if (mTmpRect.top < mMagnificationFrameBoundary.top) {
+ mTmpRect.offsetTo(mTmpRect.left, mMagnificationFrameBoundary.top);
+ } else if (mTmpRect.bottom > mMagnificationFrameBoundary.bottom) {
+ final int topOffset = mMagnificationFrameBoundary.bottom - mMagnificationFrame.height();
+ mTmpRect.offsetTo(mTmpRect.left, topOffset);
+ }
+
+ if (!mTmpRect.equals(mMagnificationFrame)) {
+ mMagnificationFrame.set(mTmpRect);
+ return true;
+ }
+ return false;
+ }
@Override
public void surfaceCreated(SurfaceHolder holder) {
createMirror();
@@ -431,7 +478,7 @@ public class WindowMagnificationController implements View.OnClickListener,
@Override
public void run() {
if (mIsPressedDown) {
- moveMirrorFromControls();
+ moveMirrorWindow(mMoveWindowOffset.x, mMoveWindowOffset.y);
mHandler.postDelayed(mMoveMirrorRunnable, 100);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index 659629b59b45..5532a0427e79 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -70,7 +70,7 @@ public class AssistDisclosure {
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
PixelFormat.TRANSLUCENT);
lp.setTitle("AssistDisclosure");
- lp.setFitWindowInsetsTypes(0 /* types */);
+ lp.setFitInsetsTypes(0 /* types */);
mWm.addView(mView, lp);
mViewAdded = true;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index eb615a0392fa..f201a6fb1137 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -85,7 +85,7 @@ public class DefaultUiController implements AssistManager.UiController {
PixelFormat.TRANSLUCENT);
mLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
mLayoutParams.gravity = Gravity.BOTTOM;
- mLayoutParams.setFitWindowInsetsTypes(0 /* types */);
+ mLayoutParams.setFitInsetsTypes(0 /* types */);
mLayoutParams.setTitle("Assist");
mInvocationLightsView = (InvocationLightsView)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 89446adb0fd7..b8d32aec30e3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -592,7 +592,7 @@ public class AuthContainerView extends LinearLayout
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("BiometricPrompt");
lp.token = windowToken;
- lp.setFitWindowInsetsTypes(lp.getFitWindowInsetsTypes() & ~Type.statusBars());
+ lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
return lp;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index ccfd3a57811c..82c8a469a057 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -72,10 +72,10 @@ public class AuthCredentialPasswordView extends AuthCredentialView
}
// Wait a bit to focus the field so the focusable flag on the window is already set then.
- post(() -> {
+ postDelayed(() -> {
mPasswordField.requestFocus();
mImm.showSoftInput(mPasswordField, InputMethodManager.SHOW_IMPLICIT);
- });
+ }, 100);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index dc2499602125..601bae286451 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -89,12 +89,12 @@ public class BadgedImageView extends ImageView {
/**
* Updates the view with provided info.
*/
- public void update(Bubble bubble, Bitmap bubbleImage, int dotColor, Path dotPath) {
+ public void update(Bubble bubble) {
mBubble = bubble;
- setImageBitmap(bubbleImage);
+ setImageBitmap(bubble.getBadgedImage());
setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT);
- mDotColor = dotColor;
- drawDot(dotPath);
+ mDotColor = bubble.getDotColor();
+ drawDot(bubble.getDotPath());
animateDot();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 2d9775deb4b5..45705b76f09c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -31,6 +31,8 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -58,6 +60,14 @@ class Bubble {
private long mLastUpdated;
private long mLastAccessed;
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+
+ /** Whether the bubble should show a dot for the notification indicating updated content. */
+ private boolean mShowBubbleUpdateDot = true;
+
+ /** Whether flyout text should be suppressed, regardless of any other flags or state. */
+ private boolean mSuppressFlyout;
+
// Items that are typically loaded later
private String mAppName;
private ShortcutInfo mShortcutInfo;
@@ -69,20 +79,6 @@ class Bubble {
private boolean mInflateSynchronously;
/**
- * Whether this notification should be shown in the shade when it is also displayed as a bubble.
- *
- * <p>When a notification is a bubble we don't show it in the shade once the bubble has been
- * expanded</p>
- */
- private boolean mShowInShadeWhenBubble = true;
-
- /** Whether the bubble should show a dot for the notification indicating updated content. */
- private boolean mShowBubbleUpdateDot = true;
-
- /** Whether flyout text should be suppressed, regardless of any other flags or state. */
- private boolean mSuppressFlyout;
-
- /**
* Presentational info about the flyout.
*/
public static class FlyoutMessage {
@@ -93,6 +89,9 @@ class Bubble {
}
private FlyoutMessage mFlyoutMessage;
+ private Bitmap mBadgedImage;
+ private int mDotColor;
+ private Path mDotPath;
public static String groupId(NotificationEntry entry) {
UserHandle user = entry.getSbn().getUser();
@@ -101,11 +100,13 @@ class Bubble {
/** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
- Bubble(NotificationEntry e) {
+ Bubble(NotificationEntry e,
+ BubbleController.NotificationSuppressionChangedListener listener) {
mEntry = e;
mKey = e.getKey();
mLastUpdated = e.getSbn().getPostTime();
mGroupId = groupId(e);
+ mSuppressionListener = listener;
}
public String getKey() {
@@ -124,6 +125,18 @@ class Bubble {
return mEntry.getSbn().getPackageName();
}
+ public Bitmap getBadgedImage() {
+ return mBadgedImage;
+ }
+
+ public int getDotColor() {
+ return mDotColor;
+ }
+
+ public Path getDotPath() {
+ return mDotPath;
+ }
+
@Nullable
public String getAppName() {
return mAppName;
@@ -205,8 +218,12 @@ class Bubble {
mAppName = info.appName;
mFlyoutMessage = info.flyoutMessage;
+ mBadgedImage = info.badgedBubbleImage;
+ mDotColor = info.dotColor;
+ mDotPath = info.dotPath;
+
mExpandedView.update(this);
- mIconView.update(this, info.badgedBubbleImage, info.dotColor, info.dotPath);
+ mIconView.update(this);
}
/**
@@ -257,25 +274,42 @@ class Bubble {
*/
void markAsAccessedAt(long lastAccessedMillis) {
mLastAccessed = lastAccessedMillis;
- setShowInShade(false);
+ setSuppressNotification(true);
setShowDot(false /* show */, true /* animate */);
}
/**
- * Whether this notification should be shown in the shade when it is also displayed as a
- * bubble.
+ * Should be invoked whenever a Bubble is promoted from overflow.
+ */
+ void markUpdatedAt(long lastAccessedMillis) {
+ mLastUpdated = lastAccessedMillis;
+ }
+
+ /**
+ * Whether this notification should be shown in the shade.
*/
boolean showInShade() {
- return !mEntry.isRowDismissed() && !shouldSuppressNotification()
- && (!mEntry.isClearable() || mShowInShadeWhenBubble);
+ return !shouldSuppressNotification() || !mEntry.isClearable();
}
/**
- * Sets whether this notification should be shown in the shade when it is also displayed as a
- * bubble.
+ * Sets whether this notification should be suppressed in the shade.
*/
- void setShowInShade(boolean showInShade) {
- mShowInShadeWhenBubble = showInShade;
+ void setSuppressNotification(boolean suppressNotification) {
+ boolean prevShowInShade = showInShade();
+
+ Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+ int flags = data.getFlags();
+ if (suppressNotification) {
+ flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ } else {
+ flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ }
+ data.setFlags(flags);
+
+ if (showInShade() != prevShowInShade && mSuppressionListener != null) {
+ mSuppressionListener.onBubbleNotificationSuppressionChange(this);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e642d4e16802..a26cce0bfc0c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -103,6 +103,7 @@ import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -151,6 +152,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
+ private int mMaxBubbles;
// Tracks the id of the current (foreground) user.
private int mCurrentUserId;
@@ -171,6 +173,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private StatusBarStateListener mStatusBarStateListener;
private final ScreenshotHelper mScreenshotHelper;
+ // Callback that updates BubbleOverflowActivity on data change.
+ @Nullable private Runnable mOverflowCallback = null;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private IStatusBarService mBarService;
@@ -186,6 +190,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private boolean mInflateSynchronously;
+ // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
+ private final List<NotifCallback> mCallbacks = new ArrayList<>();
+
/**
* Listener to be notified when some states of the bubbles change.
*/
@@ -220,6 +227,45 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
+ * Listener to be notified when a bubbles' notification suppression state changes.
+ */
+ public interface NotificationSuppressionChangedListener {
+ /**
+ * Called when the notification suppression state of a bubble changes.
+ */
+ void onBubbleNotificationSuppressionChange(Bubble bubble);
+ }
+
+ /**
+ * Callback for when the BubbleController wants to interact with the notification pipeline to:
+ * - Remove a previously bubbled notification
+ * - Update the notification shade since bubbled notification should/shouldn't be showing
+ */
+ public interface NotifCallback {
+ /**
+ * Called when the BubbleController wants to remove an entry that it was previously hiding
+ * from the shade. See {@link BubbleController#isBubbleNotificationSuppressedFromShade}.
+ */
+ void removeNotification(NotificationEntry entry);
+
+ /**
+ * Called when a bubbled notification has changed whether it should be
+ * filtered from the shade.
+ */
+ void invalidateNotificationFilter(String reason);
+
+ /**
+ * Called on a bubbled entry that has been removed when there are no longer
+ * bubbled entries in its group.
+ *
+ * Checks whether its group has any other (non-bubbled) children. If it doesn't,
+ * removes all remnants of the group's summary from the notification pipeline.
+ * TODO: (b/145659174) Only old pipeline needs this - delete post-migration.
+ */
+ void maybeCancelSummary(NotificationEntry entry);
+ }
+
+ /**
* Listens for the current state of the status bar and updates the visibility state
* of bubbles as needed.
*/
@@ -299,30 +345,26 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
configurationController.addCallback(this /* configurationListener */);
+ mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
+ mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() {
+ @Override
+ public void onBubbleNotificationSuppressionChange(Bubble bubble) {
+ // Make sure NoMan knows it's not showing in the shade anymore so anyone querying it
+ // can tell.
+ try {
+ mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(),
+ !bubble.showInShade());
+ } catch (RemoteException e) {
+ // Bad things have happened
+ }
+ }
+ });
mNotificationEntryManager = entryManager;
- mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
- mNotificationEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
mNotificationGroupManager = groupManager;
- mNotificationGroupManager.addOnGroupChangeListener(
- new NotificationGroupManager.OnGroupChangeListener() {
- @Override
- public void onGroupSuppressionChanged(
- NotificationGroupManager.NotificationGroup group,
- boolean suppressed) {
- // More notifications could be added causing summary to no longer
- // be suppressed -- in this case need to remove the key.
- final String groupKey = group.summary != null
- ? group.summary.getSbn().getGroupKey()
- : null;
- if (!suppressed && groupKey != null
- && mBubbleData.isSummarySuppressed(groupKey)) {
- mBubbleData.removeSuppressedSummary(groupKey);
- }
- }
- });
+ setupNEM();
mNotificationShadeWindowController = notificationShadeWindowController;
mStatusBarStateListener = new StatusBarStateListener();
@@ -362,6 +404,104 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
+ * See {@link NotifCallback}.
+ */
+ public void addNotifCallback(NotifCallback callback) {
+ mCallbacks.add(callback);
+ }
+
+ private void setupNEM() {
+ mNotificationEntryManager.addNotificationEntryListener(
+ new NotificationEntryListener() {
+ @Override
+ public void onPendingEntryAdded(NotificationEntry entry) {
+ onEntryAdded(entry);
+ }
+
+ @Override
+ public void onPreEntryUpdated(NotificationEntry entry) {
+ onEntryUpdated(entry);
+ }
+
+ @Override
+ public void onNotificationRankingUpdated(RankingMap rankingMap) {
+ onRankingUpdated(rankingMap);
+ }
+ });
+
+ mNotificationEntryManager.setNotificationRemoveInterceptor(
+ new NotificationRemoveInterceptor() {
+ @Override
+ public boolean onNotificationRemoveRequested(String key, int reason) {
+ NotificationEntry entry =
+ mNotificationEntryManager.getActiveNotificationUnfiltered(key);
+ return shouldInterceptDismissal(entry, reason);
+ }
+ });
+
+ mNotificationGroupManager.addOnGroupChangeListener(
+ new NotificationGroupManager.OnGroupChangeListener() {
+ @Override
+ public void onGroupSuppressionChanged(
+ NotificationGroupManager.NotificationGroup group,
+ boolean suppressed) {
+ // More notifications could be added causing summary to no longer
+ // be suppressed -- in this case need to remove the key.
+ final String groupKey = group.summary != null
+ ? group.summary.getSbn().getGroupKey()
+ : null;
+ if (!suppressed && groupKey != null
+ && mBubbleData.isSummarySuppressed(groupKey)) {
+ mBubbleData.removeSuppressedSummary(groupKey);
+ }
+ }
+ });
+
+ addNotifCallback(new NotifCallback() {
+ @Override
+ public void removeNotification(NotificationEntry entry) {
+ mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
+ UNDEFINED_DISMISS_REASON);
+ }
+
+ @Override
+ public void invalidateNotificationFilter(String reason) {
+ mNotificationEntryManager.updateNotifications(reason);
+ }
+
+ @Override
+ public void maybeCancelSummary(NotificationEntry entry) {
+ // Check if removed bubble has an associated suppressed group summary that needs
+ // to be removed now.
+ final String groupKey = entry.getSbn().getGroup();
+ if (mBubbleData.isSummarySuppressed(groupKey)) {
+ mBubbleData.removeSuppressedSummary(entry.getSbn().getGroupKey());
+
+ final NotificationEntry summary =
+ mNotificationEntryManager.getActiveNotificationUnfiltered(
+ mBubbleData.getSummaryKey(groupKey));
+ mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
+ UNDEFINED_DISMISS_REASON);
+ }
+
+ // Check if summary should be removed from NoManGroup
+ NotificationEntry summary =
+ mNotificationGroupManager.getLogicalGroupSummary(entry.getSbn());
+ if (summary != null) {
+ ArrayList<NotificationEntry> summaryChildren =
+ mNotificationGroupManager.getLogicalChildren(summary.getSbn());
+ boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
+ if (!isSummaryThisNotif && (summaryChildren == null
+ || summaryChildren.isEmpty())) {
+ mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
+ UNDEFINED_DISMISS_REASON);
+ }
+ }
+ }
+ });
+ }
+
+ /**
* Sets whether to perform inflation on the same thread as the caller. This method should only
* be used in tests, not in production.
*/
@@ -370,6 +510,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mInflateSynchronously = inflateSynchronously;
}
+ void setOverflowCallback(Runnable updateOverflow) {
+ mOverflowCallback = updateOverflow;
+ }
+
+ /**
+ * @return Bubbles for updating overflow.
+ */
+ List<Bubble> getOverflowBubbles() {
+ return mBubbleData.getOverflowBubbles();
+ }
+
+
/**
* BubbleStackView is lazily created by this method the first time a Bubble is added. This
* method initializes the stack view and adds it to the StatusBar just above the scrim.
@@ -521,13 +673,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
*
* False otherwise.
*/
- public boolean isBubbleNotificationSuppressedFromShade(String key) {
+ public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) {
+ String key = entry.getKey();
boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key)
&& !mBubbleData.getBubbleWithKey(key).showInShade();
- NotificationEntry entry = mNotificationEntryManager.getActiveNotificationUnfiltered(key);
- String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
+
+ String groupKey = entry.getSbn().getGroupKey();
boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey);
boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey));
+
return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed;
}
@@ -537,6 +691,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mBubbleData.setSelectedBubble(bubble);
}
+ void promoteBubbleFromOverflow(Bubble bubble) {
+ mBubbleData.promoteBubbleFromOverflow(bubble);
+ }
+
/**
* Request the stack expand if needed, then select the specified Bubble as current.
*
@@ -657,168 +815,55 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
- @SuppressWarnings("FieldCanBeLocal")
- private final NotificationRemoveInterceptor mRemoveInterceptor =
- new NotificationRemoveInterceptor() {
- @Override
- public boolean onNotificationRemoveRequested(String key, int reason) {
- NotificationEntry entry =
- mNotificationEntryManager.getActiveNotificationUnfiltered(key);
- String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
- ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
-
- boolean inBubbleData = mBubbleData.hasBubbleWithKey(key);
- boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
- && mBubbleData.getSummaryKey(groupKey).equals(key));
- boolean isSummary = entry != null
- && entry.getSbn().getNotification().isGroupSummary();
- boolean isSummaryOfBubbles = (isSuppressedSummary || isSummary)
- && bubbleChildren != null && !bubbleChildren.isEmpty();
-
- if (!inBubbleData && !isSummaryOfBubbles) {
- return false;
- }
-
- final boolean isClearAll = reason == REASON_CANCEL_ALL;
- final boolean isUserDimiss = reason == REASON_CANCEL || reason == REASON_CLICK;
- final boolean isAppCancel = reason == REASON_APP_CANCEL
- || reason == REASON_APP_CANCEL_ALL;
- final boolean isSummaryCancel = reason == REASON_GROUP_SUMMARY_CANCELED;
-
- // Need to check for !appCancel here because the notification may have
- // previously been dismissed & entry.isRowDismissed would still be true
- boolean userRemovedNotif = (entry != null && entry.isRowDismissed() && !isAppCancel)
- || isClearAll || isUserDimiss || isSummaryCancel;
-
- if (isSummaryOfBubbles) {
- return handleSummaryRemovalInterception(entry, userRemovedNotif);
- }
-
- // The bubble notification sticks around in the data as long as the bubble is
- // not dismissed and the app hasn't cancelled the notification.
- Bubble bubble = mBubbleData.getBubbleWithKey(key);
- boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif;
- if (bubbleExtended) {
- bubble.setShowInShade(false);
- bubble.setShowDot(false /* show */, true /* animate */);
- mNotificationEntryManager.updateNotifications(
- "BubbleController.onNotificationRemoveRequested");
- return true;
- } else if (!userRemovedNotif && entry != null
- && !isUserCreatedBubble(bubble.getKey())) {
- // This wasn't a user removal so we should remove the bubble as well
- mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
- return false;
- }
- return false;
- }
- };
-
- private boolean handleSummaryRemovalInterception(NotificationEntry summary,
- boolean userRemovedNotif) {
- String groupKey = summary.getSbn().getGroupKey();
- ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
-
- if (userRemovedNotif) {
- // If it's a user dismiss we mark the children to be hidden from the shade.
- for (int i = 0; i < bubbleChildren.size(); i++) {
- Bubble bubbleChild = bubbleChildren.get(i);
- // As far as group manager is concerned, once a child is no longer shown
- // in the shade, it is essentially removed.
- mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
- bubbleChild.setShowInShade(false);
- bubbleChild.setShowDot(false /* show */, true /* animate */);
+ private void onEntryAdded(NotificationEntry entry) {
+ boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
+ boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
+ boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
+ mContext, entry, previouslyUserCreated, userBlocked);
+
+ if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+ && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
+ if (wasAdjusted && !previouslyUserCreated) {
+ // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
+ mUserCreatedBubbles.add(entry.getKey());
}
- // And since all children are removed, remove the summary.
- mNotificationGroupManager.onEntryRemoved(summary);
-
- // If the summary was auto-generated we don't need to keep that notification around
- // because apps can't cancel it; so we only intercept & suppress real summaries.
- boolean isAutogroupSummary = (summary.getSbn().getNotification().flags
- & FLAG_AUTOGROUP_SUMMARY) != 0;
- if (!isAutogroupSummary) {
- mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
- summary.getKey());
- // Tell shade to update for the suppression
- mNotificationEntryManager.updateNotifications(
- "BubbleController.handleSummaryRemovalInterception");
- }
- return !isAutogroupSummary;
- } else {
- // If it's not a user dismiss it's a cancel.
- for (int i = 0; i < bubbleChildren.size(); i++) {
- // First check if any of these are user-created (i.e. experimental bubbles)
- if (mUserCreatedBubbles.contains(bubbleChildren.get(i).getKey())) {
- // Experimental bubble! Intercept the removal.
- return true;
- }
- }
- // Not an experimental bubble, safe to remove.
- mBubbleData.removeSuppressedSummary(groupKey);
- // Remove any associated bubble children with the summary.
- for (int i = 0; i < bubbleChildren.size(); i++) {
- Bubble bubbleChild = bubbleChildren.get(i);
- mBubbleData.notificationEntryRemoved(bubbleChild.getEntry(),
- DISMISS_GROUP_CANCELLED);
- }
- return false;
+ updateBubble(entry);
}
}
- @SuppressWarnings("FieldCanBeLocal")
- private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
- @Override
- public void onNotificationAdded(NotificationEntry entry) {
- boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
- boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
- boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
- mContext, entry, previouslyUserCreated, userBlocked);
-
- if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
- && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
- if (wasAdjusted && !previouslyUserCreated) {
- // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
- mUserCreatedBubbles.add(entry.getKey());
- }
- updateBubble(entry);
- }
- }
-
- @Override
- public void onPreEntryUpdated(NotificationEntry entry) {
- boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
- boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
- boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
- mContext, entry, previouslyUserCreated, userBlocked);
-
- boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
- && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
- if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
- // It was previously a bubble but no longer a bubble -- lets remove it
- removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
- } else if (shouldBubble) {
- if (wasAdjusted && !previouslyUserCreated) {
- // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
- mUserCreatedBubbles.add(entry.getKey());
- }
- updateBubble(entry);
+ private void onEntryUpdated(NotificationEntry entry) {
+ boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
+ boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
+ boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
+ mContext, entry, previouslyUserCreated, userBlocked);
+
+ boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+ && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
+ if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
+ // It was previously a bubble but no longer a bubble -- lets remove it
+ removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
+ } else if (shouldBubble) {
+ if (wasAdjusted && !previouslyUserCreated) {
+ // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
+ mUserCreatedBubbles.add(entry.getKey());
}
+ updateBubble(entry);
}
+ }
- @Override
- public void onNotificationRankingUpdated(RankingMap rankingMap) {
- // Forward to BubbleData to block any bubbles which should no longer be shown
- mBubbleData.notificationRankingUpdated(rankingMap);
- }
- };
+ private void onRankingUpdated(RankingMap rankingMap) {
+ // Forward to BubbleData to block any bubbles which should no longer be shown
+ mBubbleData.notificationRankingUpdated(rankingMap);
+ }
@SuppressWarnings("FieldCanBeLocal")
private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
@Override
public void applyUpdate(BubbleData.Update update) {
- if (update.addedBubble != null) {
- mStackView.addBubble(update.addedBubble);
+ // Update bubbles in overflow.
+ if (mOverflowCallback != null) {
+ mOverflowCallback.run();
}
// Collapsing? Do this first before remaining steps.
@@ -838,9 +883,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (reason != DISMISS_USER_CHANGED) {
if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
&& !bubble.showInShade()) {
- // The bubble is gone & the notification is gone, time to actually remove it
- mNotificationEntryManager.performRemoveNotification(
- bubble.getEntry().getSbn(), UNDEFINED_DISMISS_REASON);
+ // The bubble is now gone & the notification is hidden from the shade, so
+ // time to actually remove it
+ for (NotifCallback cb : mCallbacks) {
+ cb.removeNotification(bubble.getEntry());
+ }
} else {
// Update the flag for SysUI
bubble.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
@@ -855,41 +902,26 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
- // Check if removed bubble has an associated suppressed group summary that needs
- // to be removed now.
final String groupKey = bubble.getEntry().getSbn().getGroupKey();
- if (mBubbleData.isSummarySuppressed(groupKey)
- && mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
- // Time to actually remove the summary.
- String notifKey = mBubbleData.getSummaryKey(groupKey);
- mBubbleData.removeSuppressedSummary(groupKey);
- NotificationEntry entry =
- mNotificationEntryManager.getActiveNotificationUnfiltered(notifKey);
- mNotificationEntryManager.performRemoveNotification(
- entry.getSbn(), UNDEFINED_DISMISS_REASON);
- }
-
- // Check if summary should be removed from NoManGroup
- NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(
- bubble.getEntry().getSbn());
- if (summary != null) {
- ArrayList<NotificationEntry> summaryChildren =
- mNotificationGroupManager.getLogicalChildren(summary.getSbn());
- boolean isSummaryThisNotif = summary.getKey().equals(
- bubble.getEntry().getKey());
- if (!isSummaryThisNotif
- && (summaryChildren == null || summaryChildren.isEmpty())) {
- mNotificationEntryManager.performRemoveNotification(
- summary.getSbn(), UNDEFINED_DISMISS_REASON);
+ if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
+ // Time to potentially remove the summary
+ for (NotifCallback cb : mCallbacks) {
+ cb.maybeCancelSummary(bubble.getEntry());
}
}
}
}
+ if (update.addedBubble != null) {
+ mStackView.addBubble(update.addedBubble);
+ }
+
if (update.updatedBubble != null) {
mStackView.updateBubble(update.updatedBubble);
}
+ // At this point, the correct bubbles are inflated in the stack.
+ // Make sure the order in bubble data is reflected in bubble row.
if (update.orderChanged) {
mStackView.updateBubbleOrder(update.bubbles);
}
@@ -907,25 +939,150 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mStackView.setExpanded(true);
}
- mNotificationEntryManager.updateNotifications(
- "BubbleData.Listener.applyUpdate");
+ for (NotifCallback cb : mCallbacks) {
+ cb.invalidateNotificationFilter("BubbleData.Listener.applyUpdate");
+ }
updateStack();
if (DEBUG_BUBBLE_CONTROLLER) {
- Log.d(TAG, "[BubbleData]");
+ Log.d(TAG, "\n[BubbleData] bubbles:");
Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getBubbles(),
mBubbleData.getSelectedBubble()));
if (mStackView != null) {
- Log.d(TAG, "[BubbleStackView]");
+ Log.d(TAG, "\n[BubbleStackView]");
Log.d(TAG, BubbleDebugConfig.formatBubblesString(mStackView.getBubblesOnScreen(),
mStackView.getExpandedBubble()));
}
+ Log.d(TAG, "\n[BubbleData] overflow:");
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getOverflowBubbles(),
+ null));
}
}
};
/**
+ * We intercept notification entries cancelled by the user (i.e. dismissed) when there is an
+ * active bubble associated with it. We do this so that developers can still cancel it
+ * (and hence the bubbles associated with it). However, these intercepted notifications
+ * should then be hidden from the shade since the user has cancelled them, so we update
+ * {@link Bubble#showInShade}.
+ *
+ * The cancellation of summaries with children associated with bubbles are also handled in this
+ * method. User-cancelled summaries are tracked by {@link BubbleData#addSummaryToSuppress}.
+ *
+ * @return true if we want to intercept the dismissal of the entry, else false
+ */
+ public boolean shouldInterceptDismissal(NotificationEntry entry, int dismissReason) {
+ if (entry == null) {
+ return false;
+ }
+ String key = entry.getKey();
+ String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
+ ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
+
+ boolean inBubbleData = mBubbleData.hasBubbleWithKey(key);
+ boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
+ && mBubbleData.getSummaryKey(groupKey).equals(key));
+ boolean isSummary = entry != null
+ && entry.getSbn().getNotification().isGroupSummary();
+ boolean isSummaryOfBubbles = (isSuppressedSummary || isSummary)
+ && bubbleChildren != null && !bubbleChildren.isEmpty();
+
+ if (!inBubbleData && !isSummaryOfBubbles) {
+ return false;
+ }
+
+ final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
+ final boolean isUserDimiss = dismissReason == REASON_CANCEL
+ || dismissReason == REASON_CLICK;
+ final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
+ || dismissReason == REASON_APP_CANCEL_ALL;
+ final boolean isSummaryCancel = dismissReason == REASON_GROUP_SUMMARY_CANCELED;
+
+ // Need to check for !appCancel here because the notification may have
+ // previously been dismissed & entry.isRowDismissed would still be true
+ boolean userRemovedNotif = (entry != null && entry.isRowDismissed() && !isAppCancel)
+ || isClearAll || isUserDimiss || isSummaryCancel;
+ if (isSummaryOfBubbles) {
+ return handleSummaryRemovalInterception(entry, userRemovedNotif);
+ }
+
+ // The bubble notification sticks around in the data as long as the bubble is
+ // not dismissed and the app hasn't cancelled the notification.
+ Bubble bubble = mBubbleData.getBubbleWithKey(key);
+ boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif;
+ if (bubbleExtended) {
+ bubble.setSuppressNotification(true);
+ bubble.setShowDot(false /* show */, true /* animate */);
+ for (NotifCallback cb : mCallbacks) {
+ cb.invalidateNotificationFilter("BubbleController"
+ + ".shouldInterceptDismissal");
+ }
+ return true;
+ } else if (!userRemovedNotif && entry != null
+ && !isUserCreatedBubble(bubble.getKey())) {
+ // This wasn't a user removal so we should remove the bubble as well
+ mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
+ return false;
+ }
+ return false;
+ }
+
+ private boolean handleSummaryRemovalInterception(NotificationEntry summary,
+ boolean userRemovedNotif) {
+ String groupKey = summary.getSbn().getGroupKey();
+ ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
+
+ if (userRemovedNotif) {
+ // If it's a user dismiss we mark the children to be hidden from the shade.
+ for (int i = 0; i < bubbleChildren.size(); i++) {
+ Bubble bubbleChild = bubbleChildren.get(i);
+ // As far as group manager is concerned, once a child is no longer shown
+ // in the shade, it is essentially removed.
+ mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
+ bubbleChild.setSuppressNotification(true);
+ bubbleChild.setShowDot(false /* show */, true /* animate */);
+ }
+ // And since all children are removed, remove the summary.
+ mNotificationGroupManager.onEntryRemoved(summary);
+
+ // If the summary was auto-generated we don't need to keep that notification around
+ // because apps can't cancel it; so we only intercept & suppress real summaries.
+ boolean isAutogroupSummary = (summary.getSbn().getNotification().flags
+ & FLAG_AUTOGROUP_SUMMARY) != 0;
+ if (!isAutogroupSummary) {
+ // TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated
+ mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
+ summary.getKey());
+ // Tell shade to update for the suppression
+ mNotificationEntryManager.updateNotifications("BubbleController"
+ + ".handleSummaryRemovalInterception");
+ }
+ return !isAutogroupSummary;
+ } else {
+ // If it's not a user dismiss it's a cancel.
+ for (int i = 0; i < bubbleChildren.size(); i++) {
+ // First check if any of these are user-created (i.e. experimental bubbles)
+ if (mUserCreatedBubbles.contains(bubbleChildren.get(i).getKey())) {
+ // Experimental bubble! Intercept the removal.
+ return true;
+ }
+ }
+
+ // Not an experimental bubble, safe to remove.
+ mBubbleData.removeSuppressedSummary(groupKey);
+ // Remove any associated bubble children with the summary.
+ for (int i = 0; i < bubbleChildren.size(); i++) {
+ Bubble bubbleChild = bubbleChildren.get(i);
+ mBubbleData.notificationEntryRemoved(bubbleChild.getEntry(),
+ DISMISS_GROUP_CANCELLED);
+ }
+ return false;
+ }
+ }
+
+ /**
* Lets any listeners know if bubble state has changed.
* Updates the visibility of the bubbles based on current state.
* Does not un-bubble, just hides or un-hides. Notifies any
@@ -1043,12 +1200,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void onSingleTaskDisplayDrawn(int displayId) {
- final Bubble expandedBubble = mStackView != null
- ? mStackView.getExpandedBubble()
- : null;
- if (expandedBubble != null && expandedBubble.getDisplayId() == displayId) {
- expandedBubble.setContentVisibility(true);
+ if (mStackView == null) {
+ return;
}
+ mStackView.showExpandedViewContents(displayId);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index cc0824ecc45c..673121f92716 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -78,9 +78,11 @@ public class BubbleData {
// A read-only view of the bubbles list, changes there will be reflected here.
final List<Bubble> bubbles;
+ final List<Bubble> overflowBubbles;
- private Update(List<Bubble> bubbleOrder) {
- bubbles = Collections.unmodifiableList(bubbleOrder);
+ private Update(List<Bubble> row, List<Bubble> overflow) {
+ bubbles = Collections.unmodifiableList(row);
+ overflowBubbles = Collections.unmodifiableList(overflow);
}
boolean anythingChanged() {
@@ -113,11 +115,14 @@ public class BubbleData {
private final Context mContext;
/** Bubbles that are actively in the stack. */
private final List<Bubble> mBubbles;
+ /** Bubbles that aged out to overflow. */
+ private final List<Bubble> mOverflowBubbles;
/** Bubbles that are being loaded but haven't been added to the stack just yet. */
private final List<Bubble> mPendingBubbles;
private Bubble mSelectedBubble;
private boolean mExpanded;
private final int mMaxBubbles;
+ private final int mMaxOverflowBubbles;
// State tracked during an operation -- keeps track of what listener events to dispatch.
private Update mStateChange;
@@ -129,6 +134,9 @@ public class BubbleData {
@Nullable
private Listener mListener;
+ @Nullable
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+
/**
* We track groups with summaries that aren't visibly displayed but still kept around because
* the bubble(s) associated with the summary still exist.
@@ -146,9 +154,16 @@ public class BubbleData {
public BubbleData(Context context) {
mContext = context;
mBubbles = new ArrayList<>();
+ mOverflowBubbles = new ArrayList<>();
mPendingBubbles = new ArrayList<>();
- mStateChange = new Update(mBubbles);
+ mStateChange = new Update(mBubbles, mOverflowBubbles);
mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
+ mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow);
+ }
+
+ public void setSuppressionChangedListener(
+ BubbleController.NotificationSuppressionChangedListener listener) {
+ mSuppressionListener = listener;
}
public boolean hasBubbles() {
@@ -184,6 +199,19 @@ public class BubbleData {
dispatchPendingChanges();
}
+ public void promoteBubbleFromOverflow(Bubble bubble) {
+ if (DEBUG_BUBBLE_DATA) {
+ Log.d(TAG, "promoteBubbleFromOverflow: " + bubble);
+ }
+ mOverflowBubbles.remove(bubble);
+ doAdd(bubble);
+ setSelectedBubbleInternal(bubble);
+ // Preserve new order for next repack, which sorts by last updated time.
+ bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
+ trim();
+ dispatchPendingChanges();
+ }
+
/**
* Constructs a new bubble or returns an existing one. Does not add new bubbles to
* bubble data, must go through {@link #notificationEntryUpdated(Bubble, boolean, boolean)}
@@ -199,7 +227,7 @@ public class BubbleData {
return b;
}
}
- bubble = new Bubble(entry);
+ bubble = new Bubble(entry, mSuppressionListener);
mPendingBubbles.add(bubble);
} else {
bubble.setEntry(entry);
@@ -238,11 +266,13 @@ public class BubbleData {
} else if (mSelectedBubble == null) {
setSelectedBubbleInternal(bubble);
}
+
boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble;
- bubble.setShowInShade(!isBubbleExpandedAndSelected && showInShade);
+ boolean suppress = isBubbleExpandedAndSelected || !showInShade || !bubble.showInShade();
+ bubble.setSuppressNotification(suppress);
bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */);
- dispatchPendingChanges();
+ dispatchPendingChanges();
}
public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
@@ -343,6 +373,7 @@ public class BubbleData {
mStateChange.orderChanged = true;
}
mStateChange.addedBubble = bubble;
+
if (!isExpanded()) {
mStateChange.orderChanged |= packGroup(findFirstIndexForGroup(bubble.getGroupId()));
// Top bubble becomes selected.
@@ -407,6 +438,17 @@ public class BubbleData {
mStateChange.orderChanged |= repackAll();
}
+ if (reason == BubbleController.DISMISS_AGED) {
+ if (DEBUG_BUBBLE_DATA) {
+ Log.d(TAG, "overflowing bubble: " + bubbleToRemove);
+ }
+ mOverflowBubbles.add(0, bubbleToRemove);
+ if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
+ // Remove oldest bubble.
+ mOverflowBubbles.remove(mOverflowBubbles.size() - 1);
+ }
+ }
+
// Note: If mBubbles.isEmpty(), then mSelectedBubble is now null.
if (Objects.equals(mSelectedBubble, bubbleToRemove)) {
// Move selection to the new bubble at the same position.
@@ -454,7 +496,7 @@ public class BubbleData {
if (mListener != null && mStateChange.anythingChanged()) {
mListener.applyUpdate(mStateChange);
}
- mStateChange = new Update(mBubbles);
+ mStateChange = new Update(mBubbles, mOverflowBubbles);
}
/**
@@ -689,12 +731,19 @@ public class BubbleData {
}
/**
- * The set of bubbles.
+ * The set of bubbles in row.
*/
@VisibleForTesting(visibility = PRIVATE)
public List<Bubble> getBubbles() {
return Collections.unmodifiableList(mBubbles);
}
+ /**
+ * The set of bubbles in overflow.
+ */
+ @VisibleForTesting(visibility = PRIVATE)
+ public List<Bubble> getOverflowBubbles() {
+ return Collections.unmodifiableList(mOverflowBubbles);
+ }
@VisibleForTesting(visibility = PRIVATE)
Bubble getBubbleWithKey(String key) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 48ce4e9b0097..50a50633f43c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -20,10 +20,13 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.ViewRootImpl.sNewInsetsMode;
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.ActivityView;
@@ -83,7 +86,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
private int mTaskId = -1;
- private PendingIntent mBubbleIntent;
+ private PendingIntent mPendingIntent;
private boolean mKeyboardVisible;
private boolean mNeedsNewHeight;
@@ -98,7 +101,9 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
private int[] mTempLoc = new int[2];
private int mExpandedViewTouchSlop;
- private Bubble mBubble;
+ @Nullable private Bubble mBubble;
+
+ private boolean mIsOverflow;
private BubbleController mBubbleController = Dependency.get(BubbleController.class);
private WindowManager mWindowManager;
@@ -125,7 +130,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
+ "bubble=" + getBubbleKey());
}
try {
- if (mBubble.usingShortcutInfo()) {
+ if (!mIsOverflow && mBubble.usingShortcutInfo()) {
mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
options, null /* sourceBounds */);
} else {
@@ -133,7 +138,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
// Apply flags to make behaviour match documentLaunchMode=always.
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- mActivityView.startActivity(mBubbleIntent, fillInIntent, options);
+ mActivityView.startActivity(mPendingIntent, fillInIntent, options);
}
} catch (RuntimeException e) {
// If there's a runtime exception here then there's something
@@ -141,7 +146,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
// the bubble again so we'll just remove it.
Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+ ", " + e.getMessage() + "; removing bubble");
- mBubbleController.removeBubble(mBubble.getKey(),
+ mBubbleController.removeBubble(getBubbleKey(),
BubbleController.DISMISS_INVALID_INTENT);
}
});
@@ -241,6 +246,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
true /* singleTaskInstance */);
+
// Set ActivityView's alpha value as zero, since there is no view content to be shown.
setContentVisibility(false);
addView(mActivityView);
@@ -334,7 +340,13 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
? insets.getDisplayCutout().getSafeInsetBottom()
: 0);
final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
- mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
+
+ // TODO: Temporary hack to offset the view until we can properly inset Bubbles again.
+ if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+ mStackView.animate().translationY(-insetsBottom);
+ } else {
+ mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
+ }
}
}
@@ -342,6 +354,15 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
mStackView = stackView;
}
+ public void setOverflow(boolean overflow) {
+ mIsOverflow = overflow;
+
+ Intent target = new Intent(mContext, BubbleOverflowActivity.class);
+ mPendingIntent = PendingIntent.getActivity(mContext, /* requestCode */ 0,
+ target, PendingIntent.FLAG_UPDATE_CURRENT);
+ mSettingsIcon.setVisibility(GONE);
+ }
+
/**
* Sets the bubble used to populate this view.
*/
@@ -350,14 +371,14 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
}
boolean isNew = mBubble == null;
- if (isNew || bubble.getKey().equals(mBubble.getKey())) {
+ if (isNew || bubble != null && bubble.getKey().equals(mBubble.getKey())) {
mBubble = bubble;
mSettingsIcon.setContentDescription(getResources().getString(
R.string.bubbles_settings_button_description, bubble.getAppName()));
if (isNew) {
- mBubbleIntent = mBubble.getBubbleIntent();
- if (mBubbleIntent != null || mBubble.getShortcutInfo() != null) {
+ mPendingIntent = mBubble.getBubbleIntent();
+ if (mPendingIntent != null || mBubble.getShortcutInfo() != null) {
setContentVisibility(false);
mActivityView.setVisibility(VISIBLE);
}
@@ -393,12 +414,16 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
return true;
}
+ // TODO(138116789) Fix overflow height.
void updateHeight() {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
}
if (usingActivityView()) {
- float desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
+ float desiredHeight = mMinHeight;
+ if (!mIsOverflow) {
+ desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
+ }
float height = Math.min(desiredHeight, getMaxExpandedHeight());
height = Math.max(height, mMinHeight);
LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
@@ -543,7 +568,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
private boolean usingActivityView() {
- return (mBubbleIntent != null || mBubble.getShortcutInfo() != null)
+ return (mPendingIntent != null || mBubble.getShortcutInfo() != null)
&& mActivityView != null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 4252f72b808e..006de8406ce2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -76,6 +76,9 @@ public class BubbleExperimentConfig {
private static final String ALLOW_BUBBLE_MENU = "allow_bubble_screenshot_menu";
private static final boolean ALLOW_BUBBLE_MENU_DEFAULT = false;
+ private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow";
+ private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false;
+
/**
* When true, if a notification has the information necessary to bubble (i.e. valid
* contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata}
@@ -141,6 +144,16 @@ public class BubbleExperimentConfig {
}
/**
+ * When true, show a menu when a bubble is long-pressed, which will allow the user to take
+ * actions on that bubble.
+ */
+ static boolean allowBubbleOverflow(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ALLOW_BUBBLE_OVERFLOW,
+ ALLOW_BUBBLE_OVERFLOW_DEFAULT ? 1 : 0) != 0;
+ }
+
+ /**
* If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds
* {@link android.app.Notification.BubbleMetadata} to the notification entry as long as
* the notification has necessary info for BubbleMetadata.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
index 4194352f93bd..5b9ea7dd5e3a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -431,7 +431,7 @@ public class BubbleFlyoutView extends FrameLayout {
final float interpolatedRadius = getInterpolatedRadius();
rectPath.addRoundRect(mBgRect, interpolatedRadius,
interpolatedRadius, Path.Direction.CW);
- outline.setConvexPath(rectPath);
+ outline.setPath(rectPath);
// Get rid of the triangle path once it has disappeared behind the flyout.
if (mPercentStillFlyout > 0.5f) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index d99607fd6236..bea55c820b40 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -1,15 +1,42 @@
+/*
+ * Copyright (C) 2020 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.bubbles;
+import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_OVERFLOW;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+
import android.app.Activity;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.systemui.R;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
import javax.inject.Inject;
/**
@@ -17,9 +44,13 @@ import javax.inject.Inject;
* Must be public to be accessible to androidx...AppComponentFactory
*/
public class BubbleOverflowActivity extends Activity {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
+
+ private BubbleController mBubbleController;
+ private BubbleOverflowAdapter mAdapter;
private RecyclerView mRecyclerView;
+ private List<Bubble> mOverflowBubbles = new ArrayList<>();
private int mMaxBubbles;
- private BubbleController mBubbleController;
@Inject
public BubbleOverflowActivity(BubbleController controller) {
@@ -35,17 +66,42 @@ public class BubbleOverflowActivity extends Activity {
mMaxBubbles = getResources().getInteger(R.integer.bubbles_max_rendered);
mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
mRecyclerView.setLayoutManager(
- new GridLayoutManager(getApplicationContext(), /* numberOfColumns */ mMaxBubbles));
+ new GridLayoutManager(getApplicationContext(),
+ getResources().getInteger(R.integer.bubbles_overflow_columns)));
+
+ mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
+ mBubbleController::promoteBubbleFromOverflow);
+ mRecyclerView.setAdapter(mAdapter);
+
+ updateData(mBubbleController.getOverflowBubbles());
+ mBubbleController.setOverflowCallback(() -> {
+ updateData(mBubbleController.getOverflowBubbles());
+ });
}
void setBackgroundColor() {
final TypedArray ta = getApplicationContext().obtainStyledAttributes(
- new int[] {android.R.attr.colorBackgroundFloating});
+ new int[]{android.R.attr.colorBackgroundFloating});
int bgColor = ta.getColor(0, Color.WHITE);
ta.recycle();
findViewById(android.R.id.content).setBackgroundColor(bgColor);
}
+ void updateData(List<Bubble> bubbles) {
+ mOverflowBubbles.clear();
+ if (bubbles.size() > mMaxBubbles) {
+ mOverflowBubbles.addAll(bubbles.subList(mMaxBubbles, bubbles.size()));
+ } else {
+ mOverflowBubbles.addAll(bubbles);
+ }
+ mAdapter.notifyDataSetChanged();
+
+ if (DEBUG_OVERFLOW) {
+ Log.d(TAG, "Updated overflow bubbles:\n" + BubbleDebugConfig.formatBubblesString(
+ mOverflowBubbles, /*selected*/ null));
+ }
+ }
+
@Override
public void onStart() {
super.onStart();
@@ -75,3 +131,48 @@ public class BubbleOverflowActivity extends Activity {
super.onDestroy();
}
}
+
+class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
+ private Consumer<Bubble> mPromoteBubbleFromOverflow;
+ private List<Bubble> mBubbles;
+
+ public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble) {
+ mBubbles = list;
+ mPromoteBubbleFromOverflow = promoteBubble;
+ }
+
+ @Override
+ public BubbleOverflowAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
+ int viewType) {
+ BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.bubble_view, parent, false);
+ view.setPadding(15, 15, 15, 15);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder vh, int index) {
+ Bubble bubble = mBubbles.get(index);
+
+ vh.mBadgedImageView.update(bubble);
+ vh.mBadgedImageView.setOnClickListener(view -> {
+ mBubbles.remove(bubble);
+ notifyDataSetChanged();
+ mPromoteBubbleFromOverflow.accept(bubble);
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mBubbles.size();
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ public BadgedImageView mBadgedImageView;
+
+ public ViewHolder(BadgedImageView v) {
+ super(v);
+ mBadgedImageView = v;
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 54a42a6ec212..6062a3d45be0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -33,6 +33,8 @@ import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
@@ -40,6 +42,9 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.InsetDrawable;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -58,6 +63,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -195,8 +201,6 @@ public class BubbleStackView extends FrameLayout {
private int mPointerHeight;
private int mStatusBarHeight;
private int mImeOffset;
- private int mBubbleMenuOffset = 252;
- private BubbleIconFactory mBubbleIconFactory;
private Bubble mExpandedBubble;
private boolean mIsExpanded;
@@ -320,6 +324,8 @@ public class BubbleStackView extends FrameLayout {
private Runnable mAfterMagnet;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+ private BubbleExpandedView mOverflowExpandedView;
+ private ImageView mOverflowBtn;
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer) {
@@ -369,8 +375,6 @@ public class BubbleStackView extends FrameLayout {
mBubbleContainer.setClipChildren(false);
addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- mBubbleIconFactory = new BubbleIconFactory(context);
-
mExpandedViewContainer = new FrameLayout(context);
mExpandedViewContainer.setElevation(elevation);
mExpandedViewContainer.setPadding(mExpandedViewPadding, mExpandedViewPadding,
@@ -405,8 +409,12 @@ public class BubbleStackView extends FrameLayout {
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mExpandedViewYAnim.addEndListener((anim, cancelled, value, velocity) -> {
- if (mIsExpanded && mExpandedBubble != null) {
- mExpandedBubble.getExpandedView().updateView();
+ if (mIsExpanded) {
+ if (mExpandedBubble == null) {
+ mOverflowExpandedView.updateView();
+ } else {
+ mExpandedBubble.getExpandedView().updateView();
+ }
}
});
@@ -414,6 +422,10 @@ public class BubbleStackView extends FrameLayout {
setFocusable(true);
mBubbleContainer.bringToFront();
+ if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
+ setUpOverflow();
+ }
+
setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
if (!mIsExpanded || mIsExpansionAnimating) {
return view.onApplyWindowInsets(insets);
@@ -421,7 +433,13 @@ public class BubbleStackView extends FrameLayout {
mExpandedAnimationController.updateYPosition(
// Update the insets after we're done translating otherwise position
// calculation for them won't be correct.
- () -> mExpandedBubble.getExpandedView().updateInsets(insets));
+ () -> {
+ if (mExpandedBubble == null) {
+ mOverflowExpandedView.updateInsets(insets);
+ } else {
+ mExpandedBubble.getExpandedView().updateInsets(insets);
+ }
+ });
return view.onApplyWindowInsets(insets);
});
@@ -433,7 +451,11 @@ public class BubbleStackView extends FrameLayout {
// Reposition & adjust the height for new orientation
if (mIsExpanded) {
mExpandedViewContainer.setTranslationY(getExpandedViewY());
- mExpandedBubble.getExpandedView().updateView();
+ if (mExpandedBubble == null) {
+ mOverflowExpandedView.updateView();
+ } else {
+ mExpandedBubble.getExpandedView().updateView();
+ }
}
// Need to update the padding around the view
@@ -499,6 +521,45 @@ public class BubbleStackView extends FrameLayout {
mBubbleMenuView = findViewById(R.id.bubble_menu_container);
}
+ private void setUpOverflow() {
+ mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
+ R.layout.bubble_expanded_view, this /* root */, false /* attachToRoot */);
+ mOverflowExpandedView.setOverflow(true);
+
+ mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
+ this /* root */,
+ false /* attachToRoot */);
+
+ mBubbleContainer.addView(mOverflowBtn, 0,
+ new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+
+ mOverflowBtn.setOnClickListener(v -> {
+ setSelectedBubble(null);
+ });
+
+ TypedArray ta = mContext.obtainStyledAttributes(
+ new int[]{android.R.attr.colorBackgroundFloating});
+ int bgColor = ta.getColor(0, Color.WHITE /* default */);
+ ta.recycle();
+
+ InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28);
+ ColorDrawable bg = new ColorDrawable(bgColor);
+ AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
+ mOverflowBtn.setImageDrawable(adaptiveIcon);
+
+ mOverflowBtn.setVisibility(GONE);
+ }
+
+ void showExpandedViewContents(int displayId) {
+ if (mOverflowExpandedView != null
+ && mOverflowExpandedView.getVirtualDisplayId() == displayId) {
+ mOverflowExpandedView.setContentVisibility(true);
+ } else if (mExpandedBubble != null
+ && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
+ mExpandedBubble.setContentVisibility(true);
+ }
+ }
+
private void setUpFlyout() {
if (mFlyout != null) {
removeView(mFlyout);
@@ -660,7 +721,7 @@ public class BubbleStackView extends FrameLayout {
private void updateSystemGestureExcludeRects() {
// Exclude the region occupied by the first BubbleView in the stack
Rect excludeZone = mSystemGestureExclusionRects.get(0);
- if (mBubbleContainer.getChildCount() > 0) {
+ if (getBubbleCount() > 0) {
View firstBubble = mBubbleContainer.getChildAt(0);
excludeZone.set(firstBubble.getLeft(), firstBubble.getTop(), firstBubble.getRight(),
firstBubble.getBottom());
@@ -721,7 +782,7 @@ public class BubbleStackView extends FrameLayout {
Log.d(TAG, "addBubble: " + bubble);
}
- if (mBubbleContainer.getChildCount() == 0) {
+ if (getBubbleCount() == 0) {
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
}
@@ -734,9 +795,10 @@ public class BubbleStackView extends FrameLayout {
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ViewClippingUtil.setClippingDeactivated(bubble.getIconView(), true, mClippingParameters);
animateInFlyoutForBubble(bubble);
+ updatePointerPosition();
+ updateOverflowBtnVisibility( /*apply */ true);
requestUpdate();
logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
- updatePointerPosition();
}
// via BubbleData.Listener
@@ -753,7 +815,29 @@ public class BubbleStackView extends FrameLayout {
} else {
Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
}
- updatePointerPosition();
+ updateOverflowBtnVisibility(/* apply */ true);
+ }
+
+ private void updateOverflowBtnVisibility(boolean apply) {
+ if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
+ return;
+ }
+ if (mIsExpanded) {
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "Show overflow button.");
+ }
+ mOverflowBtn.setVisibility(VISIBLE);
+ if (apply) {
+ mExpandedAnimationController.expandFromStack(() -> {
+ updatePointerPosition();
+ } /* after */);
+ }
+ } else {
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "Collapsed. Hide overflow button.");
+ }
+ mOverflowBtn.setVisibility(GONE);
+ }
}
// via BubbleData.Listener
@@ -794,7 +878,9 @@ public class BubbleStackView extends FrameLayout {
// expanded view becomes visible on the screen. See b/126856255
mExpandedViewContainer.setAlpha(0.0f);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
- if (previouslySelected != null) {
+ if (previouslySelected == null) {
+ mOverflowExpandedView.setContentVisibility(false);
+ } else {
previouslySelected.setContentVisibility(false);
}
updateExpandedBubble();
@@ -865,7 +951,7 @@ public class BubbleStackView extends FrameLayout {
if (mIsExpanded) {
if (isIntersecting(mBubbleContainer, x, y)) {
// Could be tapping or dragging a bubble while expanded
- for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ for (int i = 0; i < getBubbleCount(); i++) {
BadgedImageView view = (BadgedImageView) mBubbleContainer.getChildAt(i);
if (isIntersecting(view, x, y)) {
return view;
@@ -953,6 +1039,12 @@ public class BubbleStackView extends FrameLayout {
final Bubble previouslySelected = mExpandedBubble;
beforeExpandedViewAnimation();
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "animateCollapse");
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(this.getBubblesOnScreen(),
+ this.getExpandedBubble()));
+ }
+ updateOverflowBtnVisibility(/* apply */ false);
mBubbleContainer.cancelAllAnimations();
mExpandedAnimationController.collapseBackToStack(
mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
@@ -960,7 +1052,11 @@ public class BubbleStackView extends FrameLayout {
() -> {
mBubbleContainer.setActiveController(mStackAnimationController);
afterExpandedViewAnimation();
- previouslySelected.setContentVisibility(false);
+ if (previouslySelected == null) {
+ mOverflowExpandedView.setContentVisibility(false);
+ } else {
+ previouslySelected.setContentVisibility(false);
+ }
});
mExpandedViewXAnim.animateToFinalPosition(getCollapsedX());
@@ -975,12 +1071,12 @@ public class BubbleStackView extends FrameLayout {
beforeExpandedViewAnimation();
mBubbleContainer.setActiveController(mExpandedAnimationController);
+ updateOverflowBtnVisibility(/* apply */ false);
mExpandedAnimationController.expandFromStack(() -> {
updatePointerPosition();
afterExpandedViewAnimation();
} /* after */);
-
mExpandedViewContainer.setTranslationX(getCollapsedX());
mExpandedViewContainer.setTranslationY(getCollapsedY());
mExpandedViewContainer.setAlpha(0f);
@@ -1011,7 +1107,7 @@ public class BubbleStackView extends FrameLayout {
/** Return the BubbleView at the given index from the bubble container. */
public BadgedImageView getBubbleAt(int i) {
- return mBubbleContainer.getChildCount() > i
+ return getBubbleCount() > i
? (BadgedImageView) mBubbleContainer.getChildAt(i)
: null;
}
@@ -1063,9 +1159,11 @@ public class BubbleStackView extends FrameLayout {
Log.d(TAG, "onDragStart()");
}
if (mIsExpanded || mIsExpansionAnimating) {
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "mIsExpanded or mIsExpansionAnimating");
+ }
return;
}
-
hideBubbleMenu();
mStackAnimationController.cancelStackPositionAnimations();
mBubbleContainer.setActiveController(mStackAnimationController);
@@ -1473,7 +1571,7 @@ public class BubbleStackView extends FrameLayout {
return;
}
if (!mIsExpanded) {
- if (mBubbleContainer.getChildCount() > 0) {
+ if (getBubbleCount() > 0) {
mBubbleContainer.getChildAt(0).getBoundsOnScreen(outRect);
}
// Increase the touch target size of the bubble
@@ -1526,10 +1624,14 @@ public class BubbleStackView extends FrameLayout {
Log.d(TAG, "updateExpandedBubble()");
}
mExpandedViewContainer.removeAllViews();
- if (mExpandedBubble != null && mIsExpanded) {
- mExpandedViewContainer.addView(mExpandedBubble.getExpandedView());
- mExpandedBubble.getExpandedView().populateExpandedView();
- mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+ if (mIsExpanded) {
+ BubbleExpandedView bev = mOverflowExpandedView;
+ if (mExpandedBubble != null) {
+ bev = mExpandedBubble.getExpandedView();
+ }
+ mExpandedViewContainer.addView(bev);
+ bev.populateExpandedView();
+ mExpandedViewContainer.setVisibility(VISIBLE);
mExpandedViewContainer.setAlpha(1.0f);
}
}
@@ -1545,7 +1647,11 @@ public class BubbleStackView extends FrameLayout {
if (!mExpandedViewYAnim.isRunning()) {
// We're not animating so set the value
mExpandedViewContainer.setTranslationY(y);
- mExpandedBubble.getExpandedView().updateView();
+ if (mExpandedBubble == null) {
+ mOverflowExpandedView.updateView();
+ } else {
+ mExpandedBubble.getExpandedView().updateView();
+ }
} else {
// We are animating so update the value; there is an end listener on the animator
// that will ensure expandedeView.updateView gets called.
@@ -1559,7 +1665,7 @@ public class BubbleStackView extends FrameLayout {
/** Sets the appropriate Z-order and dot position for each bubble in the stack. */
private void updateBubbleZOrdersAndDotPosition(boolean animate) {
- int bubbleCount = mBubbleContainer.getChildCount();
+ int bubbleCount = getBubbleCount();
for (int i = 0; i < bubbleCount; i++) {
BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
bv.setZ((mMaxBubbles * mBubbleElevation) - i);
@@ -1571,22 +1677,16 @@ public class BubbleStackView extends FrameLayout {
}
private void updatePointerPosition() {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "updatePointerPosition()");
- }
-
Bubble expandedBubble = getExpandedBubble();
if (expandedBubble == null) {
return;
}
-
int index = getBubbleIndex(expandedBubble);
float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index);
float halfBubble = mBubbleSize / 2f;
float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble;
// Padding might be adjusted for insets, so get it directly from the view
bubbleCenter -= mExpandedViewContainer.getPaddingLeft();
-
expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
}
@@ -1594,6 +1694,10 @@ public class BubbleStackView extends FrameLayout {
* @return the number of bubbles in the stack view.
*/
public int getBubbleCount() {
+ if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
+ // Subtract 1 for the overflow button which is always in the bubble container.
+ return mBubbleContainer.getChildCount() - 1;
+ }
return mBubbleContainer.getChildCount();
}
@@ -1680,13 +1784,17 @@ public class BubbleStackView extends FrameLayout {
if (!isExpanded()) {
return false;
}
- return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
+ if (mExpandedBubble == null) {
+ return mOverflowExpandedView.performBackPressIfNeeded();
+ } else {
+ return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
+ }
}
/** For debugging only */
List<Bubble> getBubblesOnScreen() {
List<Bubble> bubbles = new ArrayList<>();
- for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ for (int i = 0; i < getBubbleCount(); i++) {
View child = mBubbleContainer.getChildAt(i);
if (child instanceof BadgedImageView) {
String key = ((BadgedImageView) child).getKey();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index e7055847f034..55a7a11987fc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -157,6 +157,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
PackageManager pm = c.getPackageManager();
ApplicationInfo appInfo;
Drawable badgedIcon;
+ Drawable appIcon;
try {
appInfo = pm.getApplicationInfo(
packageName,
@@ -167,7 +168,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
if (appInfo != null) {
info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
}
- Drawable appIcon = pm.getApplicationIcon(packageName);
+ appIcon = pm.getApplicationIcon(packageName);
badgedIcon = pm.getUserBadgedIcon(appIcon, sbn.getUser());
} catch (PackageManager.NameNotFoundException exception) {
// If we can't find package... don't think we should show the bubble.
@@ -178,6 +179,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
// Badged bubble image
Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
b.getEntry().getBubbleMetadata());
+ if (bubbleDrawable == null) {
+ // Default to app icon
+ bubbleDrawable = appIcon;
+ }
+
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon);
info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable,
badgeBitmapInfo).icon;
@@ -240,7 +246,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
bubbleMessage.senderName = sender != null
? sender.getName()
: null;
- bubbleMessage.senderAvatar = sender != null
+ bubbleMessage.senderAvatar = sender != null && sender.getIcon() != null
? sender.getIcon().loadDrawable(context)
: null;
return bubbleMessage;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 6528f3762473..aa549dc23f9b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -31,6 +31,7 @@ import androidx.dynamicanimation.animation.SpringForce;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleExperimentConfig;
import com.google.android.collect.Sets;
@@ -67,11 +68,13 @@ public class ExpandedAnimationController
private float mBubblePaddingTop;
/** Size of each bubble. */
private float mBubbleSizePx;
+ /** Space between bubbles in row above expanded view. */
+ private float mSpaceBetweenBubbles;
/** Height of the status bar. */
private float mStatusBarHeight;
/** Size of display. */
private Point mDisplaySize;
- /** Max number of bubbles shown in row above expanded view.*/
+ /** Max number of bubbles shown in row above expanded view. */
private int mBubblesMaxRendered;
/** What the current screen orientation is. */
private int mScreenOrientation;
@@ -81,7 +84,7 @@ public class ExpandedAnimationController
private boolean mAnimatingExpand = false;
private boolean mAnimatingCollapse = false;
- private Runnable mAfterExpand;
+ private @Nullable Runnable mAfterExpand;
private Runnable mAfterCollapse;
private PointF mCollapsePoint;
@@ -116,7 +119,7 @@ public class ExpandedAnimationController
/**
* Animates expanding the bubbles into a row along the top of the screen.
*/
- public void expandFromStack(Runnable after) {
+ public void expandFromStack(@Nullable Runnable after) {
mAnimatingCollapse = false;
mAnimatingExpand = true;
mAfterExpand = after;
@@ -358,10 +361,10 @@ public class ExpandedAnimationController
}
final WindowInsets insets = mLayout.getRootWindowInsets();
return mBubblePaddingTop + Math.max(
- mStatusBarHeight,
- insets.getDisplayCutout() != null
- ? insets.getDisplayCutout().getSafeInsetTop()
- : 0);
+ mStatusBarHeight,
+ insets.getDisplayCutout() != null
+ ? insets.getDisplayCutout().getSafeInsetTop()
+ : 0);
}
/** Description of current animation controller state. */
@@ -384,6 +387,11 @@ public class ExpandedAnimationController
res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
+ // Includes overflow button.
+ float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
+ - (mBubblesMaxRendered + 1) * mBubbleSizePx;
+ mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
+
// Ensure that all child views are at 1x scale, and visible, in case they were animating
// in.
mLayout.setVisibility(View.VISIBLE);
@@ -494,7 +502,7 @@ public class ExpandedAnimationController
* @return Bubble left x from left edge of screen.
*/
public float getBubbleLeft(int index) {
- final float bubbleFromRowLeft = index * (mBubbleSizePx + getSpaceBetweenBubbles());
+ final float bubbleFromRowLeft = index * (mBubbleSizePx + mSpaceBetweenBubbles);
return getRowLeft() + bubbleFromRowLeft;
}
@@ -505,7 +513,7 @@ public class ExpandedAnimationController
*
* @return the desired width to display the expanded bubbles in.
*/
- private float getWidthForDisplayingBubbles() {
+ public float getWidthForDisplayingBubbles() {
final float availableWidth = getAvailableScreenWidth(true /* includeStableInsets */);
if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
// display size y in landscape will be the smaller dimension of the screen
@@ -519,7 +527,7 @@ public class ExpandedAnimationController
* Determines the available screen width without the cutout.
*
* @param subtractStableInsets Whether or not stable insets should also be removed from the
- * returned width.
+ * returned width.
* @return the total screen width available accounting for cutouts and insets,
* iff {@param includeStableInsets} is true.
*/
@@ -546,35 +554,13 @@ public class ExpandedAnimationController
if (mLayout == null) {
return 0;
}
-
- int bubbleCount = mLayout.getChildCount();
-
- final float totalBubbleWidth = bubbleCount * mBubbleSizePx;
- final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles();
- final float rowWidth = totalGapWidth + totalBubbleWidth;
+ float rowWidth = (mLayout.getChildCount() * mBubbleSizePx)
+ + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles);
// This display size we're using includes the size of the insets, we want the true
// center of the display minus the notch here, which means we should include the
// stable insets (e.g. status bar, nav bar) in this calculation.
final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f;
- final float halfRow = rowWidth / 2f;
- final float rowLeft = trueCenter - halfRow;
-
- return rowLeft;
- }
-
- /**
- * @return Space between bubbles in row above expanded view.
- */
- private float getSpaceBetweenBubbles() {
- final float rowMargins = mExpandedViewPadding * 2;
- final float maxRowWidth = getWidthForDisplayingBubbles() - rowMargins;
-
- final float totalBubbleWidth = mBubblesMaxRendered * mBubbleSizePx;
- final float totalGapWidth = maxRowWidth - totalBubbleWidth;
-
- final int gapCount = mBubblesMaxRendered - 1;
- final float gapWidth = totalGapWidth / gapCount;
- return gapWidth;
+ return trueCenter - (rowWidth / 2f);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
index e6cdf50580d8..53841e2f144b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
@@ -18,4 +18,8 @@ package com.android.systemui.controls
import android.service.controls.Control
-data class ControlStatus(val control: Control, val favorite: Boolean, val removed: Boolean = false) \ No newline at end of file
+data class ControlStatus(
+ val control: Control,
+ val favorite: Boolean,
+ val removed: Boolean = false
+) \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
index 265ddd8043b6..588ef5c4e68f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
@@ -22,7 +22,7 @@ import com.android.settingslib.applications.DefaultAppInfo
class ControlsServiceInfo(
context: Context,
- serviceInfo: ServiceInfo
+ val serviceInfo: ServiceInfo
) : DefaultAppInfo(
context,
context.packageManager,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
new file mode 100644
index 000000000000..4f39f2255a75
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 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.controls
+
+import android.os.UserHandle
+
+interface UserAwareController {
+
+ fun changeUser(newUser: UserHandle) {}
+ val currentUserId: Int
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index 6b7fc4b7e827..12c3ce9c69ee 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -19,8 +19,9 @@ package com.android.systemui.controls.controller
import android.content.ComponentName
import android.service.controls.Control
import android.service.controls.actions.ControlAction
+import com.android.systemui.controls.UserAwareController
-interface ControlsBindingController {
+interface ControlsBindingController : UserAwareController {
fun bindAndLoad(component: ComponentName, callback: (List<Control>) -> Unit)
fun bindServices(components: List<ComponentName>)
fun subscribe(controls: List<ControlInfo>)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 80e48b925fc9..0a2a9255c3ea 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -19,8 +19,12 @@ package com.android.systemui.controls.controller
import android.content.ComponentName
import android.content.Context
import android.os.IBinder
+import android.os.UserHandle
import android.service.controls.Control
-import android.service.controls.IControlsProviderCallback
+import android.service.controls.IControlsActionCallback
+import android.service.controls.IControlsLoadCallback
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
import android.service.controls.actions.ControlAction
import android.util.ArrayMap
import android.util.Log
@@ -47,32 +51,52 @@ open class ControlsBindingControllerImpl @Inject constructor(
private val refreshing = AtomicBoolean(false)
+ private var currentUser = context.user
+
+ override val currentUserId: Int
+ get() = currentUser.identifier
+
@GuardedBy("componentMap")
private val tokenMap: MutableMap<IBinder, ControlsProviderLifecycleManager> =
ArrayMap<IBinder, ControlsProviderLifecycleManager>()
@GuardedBy("componentMap")
- private val componentMap: MutableMap<ComponentName, ControlsProviderLifecycleManager> =
- ArrayMap<ComponentName, ControlsProviderLifecycleManager>()
+ private val componentMap: MutableMap<Key, ControlsProviderLifecycleManager> =
+ ArrayMap<Key, ControlsProviderLifecycleManager>()
- private val serviceCallback = object : IControlsProviderCallback.Stub() {
- override fun onLoad(token: IBinder, controls: MutableList<Control>) {
+ private val loadCallbackService = object : IControlsLoadCallback.Stub() {
+ override fun accept(token: IBinder, controls: MutableList<Control>) {
backgroundExecutor.execute(OnLoadRunnable(token, controls))
}
+ }
- override fun onRefreshState(token: IBinder, controlStates: List<Control>) {
+ private val actionCallbackService = object : IControlsActionCallback.Stub() {
+ override fun accept(
+ token: IBinder,
+ controlId: String,
+ @ControlAction.ResponseResult response: Int
+ ) {
+ backgroundExecutor.execute(OnActionResponseRunnable(token, controlId, response))
+ }
+ }
+
+ private val subscriberService = object : IControlsSubscriber.Stub() {
+ override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
+ backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
+ }
+
+ override fun onNext(token: IBinder, c: Control) {
if (!refreshing.get()) {
Log.d(TAG, "Refresh outside of window for token:$token")
} else {
- backgroundExecutor.execute(OnRefreshStateRunnable(token, controlStates))
+ backgroundExecutor.execute(OnNextRunnable(token, c))
}
}
+ override fun onError(token: IBinder, s: String) {
+ backgroundExecutor.execute(OnErrorRunnable(token, s))
+ }
- override fun onControlActionResponse(
- token: IBinder,
- controlId: String,
- @ControlAction.ResponseResult response: Int
- ) {
- backgroundExecutor.execute(OnActionResponseRunnable(token, controlId, response))
+ override fun onComplete(token: IBinder) {
+ backgroundExecutor.execute(OnCompleteRunnable(token))
}
}
@@ -82,7 +106,10 @@ open class ControlsBindingControllerImpl @Inject constructor(
return ControlsProviderLifecycleManager(
context,
backgroundExecutor,
- serviceCallback,
+ loadCallbackService,
+ actionCallbackService,
+ subscriberService,
+ currentUser,
component
)
}
@@ -90,7 +117,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
private fun retrieveLifecycleManager(component: ComponentName):
ControlsProviderLifecycleManager {
synchronized(componentMap) {
- val provider = componentMap.getOrPut(component) {
+ val provider = componentMap.getOrPut(Key(component, currentUser)) {
createProviderManager(component)
}
tokenMap.putIfAbsent(provider.token, provider)
@@ -117,7 +144,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
val providersWithFavorites = controlsByComponentName.keys
synchronized(componentMap) {
componentMap.forEach {
- if (it.key !in providersWithFavorites) {
+ if (it.key.component !in providersWithFavorites) {
backgroundExecutor.execute { it.value.unbindService() }
}
}
@@ -143,10 +170,40 @@ open class ControlsBindingControllerImpl @Inject constructor(
override fun bindServices(components: List<ComponentName>) {
components.forEach {
val provider = retrieveLifecycleManager(it)
- backgroundExecutor.execute { provider.bindPermanently() }
+ backgroundExecutor.execute { provider.bindService() }
+ }
+ }
+
+ override fun changeUser(newUser: UserHandle) {
+ if (newUser == currentUser) return
+ synchronized(componentMap) {
+ unbindAllProvidersLocked() // unbind all providers from the old user
+ }
+ refreshing.set(false)
+ currentUser = newUser
+ }
+
+ private fun unbindAllProvidersLocked() {
+ componentMap.values.forEach {
+ if (it.user == currentUser) {
+ it.unbindService()
+ }
}
}
+ override fun toString(): String {
+ return StringBuilder(" ControlsBindingController:\n").apply {
+ append(" refreshing=${refreshing.get()}\n")
+ append(" currentUser=$currentUser\n")
+ append(" Providers:\n")
+ synchronized(componentMap) {
+ componentMap.values.forEach {
+ append(" $it\n")
+ }
+ }
+ }.toString()
+ }
+
private abstract inner class CallbackRunnable(val token: IBinder) : Runnable {
protected val provider: ControlsProviderLifecycleManager? =
synchronized(componentMap) {
@@ -163,6 +220,10 @@ open class ControlsBindingControllerImpl @Inject constructor(
Log.e(TAG, "No provider found for token:$token")
return
}
+ if (provider.user != currentUser) {
+ Log.e(TAG, "User ${provider.user} is not current user")
+ return
+ }
synchronized(componentMap) {
if (token !in tokenMap.keys) {
Log.e(TAG, "Provider for token:$token does not exist anymore")
@@ -172,20 +233,59 @@ open class ControlsBindingControllerImpl @Inject constructor(
provider.lastLoadCallback?.invoke(list) ?: run {
Log.w(TAG, "Null callback")
}
- provider.maybeUnbindAndRemoveCallback()
+ provider.unbindService()
}
}
- private inner class OnRefreshStateRunnable(
+ private inner class OnNextRunnable(
token: IBinder,
- val list: List<Control>
+ val control: Control
) : CallbackRunnable(token) {
override fun run() {
if (!refreshing.get()) {
Log.d(TAG, "onRefresh outside of window from:${provider?.componentName}")
}
+ if (provider?.user != currentUser) {
+ Log.e(TAG, "User ${provider?.user} is not current user")
+ return
+ }
+ provider?.let {
+ lazyController.get().refreshStatus(it.componentName, control)
+ }
+ }
+ }
+
+ private inner class OnSubscribeRunnable(
+ token: IBinder,
+ val subscription: IControlsSubscription
+ ) : CallbackRunnable(token) {
+ override fun run() {
+ if (!refreshing.get()) {
+ Log.d(TAG, "onRefresh outside of window from '${provider?.componentName}'")
+ }
+ provider?.let {
+ it.startSubscription(subscription)
+ }
+ }
+ }
+
+ private inner class OnCompleteRunnable(
+ token: IBinder
+ ) : CallbackRunnable(token) {
+ override fun run() {
+ provider?.let {
+ Log.i(TAG, "onComplete receive from '${provider.componentName}'")
+ }
+ }
+ }
+
+ private inner class OnErrorRunnable(
+ token: IBinder,
+ val error: String
+ ) : CallbackRunnable(token) {
+ override fun run() {
provider?.let {
- lazyController.get().refreshStatus(it.componentName, list)
+ Log.e(TAG, "onError receive from '${provider.componentName}': $error")
}
}
}
@@ -196,9 +296,15 @@ open class ControlsBindingControllerImpl @Inject constructor(
@ControlAction.ResponseResult val response: Int
) : CallbackRunnable(token) {
override fun run() {
+ if (provider?.user != currentUser) {
+ Log.e(TAG, "User ${provider?.user} is not current user")
+ return
+ }
provider?.let {
lazyController.get().onActionResponse(it.componentName, controlId, response)
}
}
}
-} \ No newline at end of file
+}
+
+private data class Key(val component: ComponentName, val user: UserHandle) \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 4d958224e917..b02de4500043 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -20,8 +20,9 @@ import android.content.ComponentName
import android.service.controls.Control
import android.service.controls.actions.ControlAction
import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.UserAwareController
-interface ControlsController {
+interface ControlsController : UserAwareController {
val available: Boolean
fun getFavoriteControls(): List<ControlInfo>
@@ -30,11 +31,11 @@ interface ControlsController {
fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean)
fun unsubscribe()
fun action(controlInfo: ControlInfo, action: ControlAction)
- fun refreshStatus(componentName: ComponentName, controls: List<Control>)
+ fun refreshStatus(componentName: ComponentName, control: Control)
fun onActionResponse(
componentName: ComponentName,
controlId: String,
@ControlAction.ResponseResult response: Int
)
fun clearFavorites()
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 7e328e467129..6ff1cf8474d3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -17,10 +17,13 @@
package com.android.systemui.controls.controller
import android.app.PendingIntent
+import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.IntentFilter
import android.os.Environment
+import android.os.UserHandle
import android.provider.Settings
import android.service.controls.Control
import android.service.controls.actions.ControlAction
@@ -29,14 +32,17 @@ import android.util.Log
import com.android.internal.annotations.GuardedBy
import com.android.systemui.DumpController
import com.android.systemui.Dumpable
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.management.ControlsFavoritingActivity
+import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.FileDescriptor
import java.io.PrintWriter
import java.util.Optional
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@@ -46,35 +52,101 @@ class ControlsControllerImpl @Inject constructor (
@Background private val executor: DelayableExecutor,
private val uiController: ControlsUiController,
private val bindingController: ControlsBindingController,
- private val optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
+ private val listingController: ControlsListingController,
+ broadcastDispatcher: BroadcastDispatcher,
+ optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
dumpController: DumpController
) : Dumpable, ControlsController {
companion object {
private const val TAG = "ControlsControllerImpl"
const val CONTROLS_AVAILABLE = "systemui.controls_available"
+ const val USER_CHANGE_RETRY_DELAY = 500L // ms
}
- override val available = Settings.Secure.getInt(
+ // Map of map: ComponentName -> (String -> ControlInfo).
+ // Only for current user
+ @GuardedBy("currentFavorites")
+ private val currentFavorites = ArrayMap<ComponentName, MutableMap<String, ControlInfo>>()
+
+ private var userChanging = true
+ override var available = Settings.Secure.getInt(
context.contentResolver, CONTROLS_AVAILABLE, 0) != 0
- val persistenceWrapper = optionalWrapper.orElseGet {
+ private set
+
+ private var currentUser = context.user
+ override val currentUserId
+ get() = currentUser.identifier
+
+ private val persistenceWrapper = optionalWrapper.orElseGet {
ControlsFavoritePersistenceWrapper(
Environment.buildPath(
- context.filesDir,
- ControlsFavoritePersistenceWrapper.FILE_NAME),
+ context.filesDir,
+ ControlsFavoritePersistenceWrapper.FILE_NAME
+ ),
executor
)
}
- // Map of map: ComponentName -> (String -> ControlInfo)
- @GuardedBy("currentFavorites")
- private val currentFavorites = ArrayMap<ComponentName, MutableMap<String, ControlInfo>>()
+ private fun setValuesForUser(newUser: UserHandle) {
+ Log.d(TAG, "Changing to user: $newUser")
+ currentUser = newUser
+ val userContext = context.createContextAsUser(currentUser, 0)
+ val fileName = Environment.buildPath(
+ userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME)
+ persistenceWrapper.changeFile(fileName)
+ available = Settings.Secure.getIntForUser(
+ context.contentResolver, CONTROLS_AVAILABLE, 0) != 0
+ synchronized(currentFavorites) {
+ currentFavorites.clear()
+ }
+ if (available) {
+ loadFavorites()
+ }
+ bindingController.changeUser(newUser)
+ listingController.changeUser(newUser)
+ userChanging = false
+ }
+
+ private val userSwitchReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_USER_SWITCHED) {
+ userChanging = true
+ val newUser =
+ UserHandle.of(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, sendingUserId))
+ if (currentUser == newUser) {
+ userChanging = false
+ return
+ }
+ setValuesForUser(newUser)
+ }
+ }
+ }
init {
+ dumpController.registerDumpable(this)
if (available) {
- dumpController.registerDumpable(this)
loadFavorites()
}
+ userChanging = false
+ broadcastDispatcher.registerReceiver(
+ userSwitchReceiver,
+ IntentFilter(Intent.ACTION_USER_SWITCHED),
+ executor,
+ UserHandle.ALL
+ )
+ }
+
+ private fun confirmAvailability(): Boolean {
+ if (userChanging) {
+ Log.w(TAG, "Controls not available while user is changing")
+ return false
+ }
+ if (!available) {
+ Log.d(TAG, "Controls not available")
+ return false
+ }
+ return true
}
private fun loadFavorites() {
@@ -91,8 +163,18 @@ class ControlsControllerImpl @Inject constructor (
componentName: ComponentName,
callback: (List<ControlStatus>) -> Unit
) {
- if (!available) {
- Log.d(TAG, "Controls not available")
+ if (!confirmAvailability()) {
+ if (userChanging) {
+ // Try again later, userChanging should not last forever. If so, we have bigger
+ // problems
+ executor.executeDelayed(
+ { loadForComponent(componentName, callback) },
+ USER_CHANGE_RETRY_DELAY,
+ TimeUnit.MILLISECONDS
+ )
+ } else {
+ callback(emptyList())
+ }
return
}
bindingController.bindAndLoad(componentName) {
@@ -158,10 +240,7 @@ class ControlsControllerImpl @Inject constructor (
}
override fun subscribeToFavorites() {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return
- }
+ if (!confirmAvailability()) return
// Make a copy of the favorites list
val favorites = synchronized(currentFavorites) {
currentFavorites.flatMap { it.value.values.toList() }
@@ -170,18 +249,12 @@ class ControlsControllerImpl @Inject constructor (
}
override fun unsubscribe() {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return
- }
+ if (!confirmAvailability()) return
bindingController.unsubscribe()
}
override fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean) {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return
- }
+ if (!confirmAvailability()) return
var changed = false
val listOfControls = synchronized(currentFavorites) {
if (state) {
@@ -210,45 +283,41 @@ class ControlsControllerImpl @Inject constructor (
}
}
- override fun refreshStatus(componentName: ComponentName, controls: List<Control>) {
- if (!available) {
+ override fun refreshStatus(componentName: ComponentName, control: Control) {
+ if (!confirmAvailability()) {
Log.d(TAG, "Controls not available")
return
}
executor.execute {
synchronized(currentFavorites) {
- val changed = updateFavoritesLocked(componentName, controls)
+ val changed = updateFavoritesLocked(componentName, listOf(control))
if (changed) {
persistenceWrapper.storeFavorites(favoritesAsListLocked())
}
}
}
- uiController.onRefreshState(componentName, controls)
+ uiController.onRefreshState(componentName, listOf(control))
}
override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return
- }
+ if (!confirmAvailability()) return
uiController.onActionResponse(componentName, controlId, response)
}
override fun getFavoriteControls(): List<ControlInfo> {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return emptyList()
- }
+ if (!confirmAvailability()) return emptyList()
synchronized(currentFavorites) {
return favoritesAsListLocked()
}
}
override fun action(controlInfo: ControlInfo, action: ControlAction) {
+ if (!confirmAvailability()) return
bindingController.action(controlInfo, action)
}
override fun clearFavorites() {
+ if (!confirmAvailability()) return
val changed = synchronized(currentFavorites) {
currentFavorites.isNotEmpty().also {
currentFavorites.clear()
@@ -261,6 +330,9 @@ class ControlsControllerImpl @Inject constructor (
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println("ControlsController state:")
+ pw.println(" Available: $available")
+ pw.println(" Changing users: $userChanging")
+ pw.println(" Current user: ${currentUser.identifier}")
pw.println(" Favorites:")
synchronized(currentFavorites) {
currentFavorites.forEach {
@@ -269,5 +341,6 @@ class ControlsControllerImpl @Inject constructor (
}
}
}
+ pw.println(bindingController.toString())
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
index 6f2d71fd0f59..7d1df14bbf1b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
@@ -16,7 +16,6 @@
package com.android.systemui.controls.controller
-import android.app.ActivityManager
import android.content.ComponentName
import android.util.AtomicFile
import android.util.Log
@@ -32,8 +31,8 @@ import java.io.FileNotFoundException
import java.io.IOException
class ControlsFavoritePersistenceWrapper(
- val file: File,
- val executor: DelayableExecutor
+ private var file: File,
+ private var executor: DelayableExecutor
) {
companion object {
@@ -47,11 +46,13 @@ class ControlsFavoritePersistenceWrapper(
private const val TAG_TYPE = "type"
}
- val currentUser: Int
- get() = ActivityManager.getCurrentUser()
+ fun changeFile(fileName: File) {
+ file = fileName
+ }
fun storeFavorites(list: List<ControlInfo>) {
executor.execute {
+ Log.d(TAG, "Saving data to file: $file")
val atomicFile = AtomicFile(file)
val writer = try {
atomicFile.startWrite()
@@ -98,6 +99,7 @@ class ControlsFavoritePersistenceWrapper(
return emptyList()
}
try {
+ Log.d(TAG, "Reading data from file: $file")
val parser = Xml.newPullParser()
parser.setInput(reader, null)
return parseXml(parser)
@@ -111,7 +113,7 @@ class ControlsFavoritePersistenceWrapper(
}
private fun parseXml(parser: XmlPullParser): List<ControlInfo> {
- var type: Int = 0
+ var type = 0
val infos = mutableListOf<ControlInfo>()
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -123,9 +125,9 @@ class ControlsFavoritePersistenceWrapper(
parser.getAttributeValue(null, TAG_COMPONENT))
val id = parser.getAttributeValue(null, TAG_ID)
val title = parser.getAttributeValue(null, TAG_TITLE)
- val type = parser.getAttributeValue(null, TAG_TYPE)?.toInt()
- if (component != null && id != null && title != null && type != null) {
- infos.add(ControlInfo(component, id, title, type))
+ val deviceType = parser.getAttributeValue(null, TAG_TYPE)?.toInt()
+ if (component != null && id != null && title != null && deviceType != null) {
+ infos.add(ControlInfo(component, id, title, deviceType))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 79057ad25b20..b4bd82c84e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -24,12 +24,15 @@ import android.os.Binder
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
+import android.os.UserHandle
import android.service.controls.Control
-import android.service.controls.ControlsProviderService.CALLBACK_BINDER
import android.service.controls.ControlsProviderService.CALLBACK_BUNDLE
import android.service.controls.ControlsProviderService.CALLBACK_TOKEN
+import android.service.controls.IControlsActionCallback
+import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsProvider
-import android.service.controls.IControlsProviderCallback
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
import android.service.controls.actions.ControlAction
import android.util.ArraySet
import android.util.Log
@@ -41,19 +44,22 @@ typealias LoadCallback = (List<Control>) -> Unit
class ControlsProviderLifecycleManager(
private val context: Context,
private val executor: DelayableExecutor,
- private val serviceCallback: IControlsProviderCallback.Stub,
+ private val loadCallbackService: IControlsLoadCallback.Stub,
+ private val actionCallbackService: IControlsActionCallback.Stub,
+ private val subscriberService: IControlsSubscriber.Stub,
+ val user: UserHandle,
val componentName: ComponentName
) : IBinder.DeathRecipient {
var lastLoadCallback: LoadCallback? = null
private set
val token: IBinder = Binder()
- private var unbindImmediate = false
+ @GuardedBy("subscriptions")
+ private val subscriptions = mutableListOf<IControlsSubscription>()
private var requiresBound = false
- private var isBound = false
@GuardedBy("queuedMessages")
private val queuedMessages: MutableSet<Message> = ArraySet()
- private var wrapper: ControlsProviderServiceWrapper? = null
+ private var wrapper: ServiceWrapper? = null
private var bindTryCount = 0
private val TAG = javaClass.simpleName
private var onLoadCanceller: Runnable? = null
@@ -61,12 +67,12 @@ class ControlsProviderLifecycleManager(
companion object {
private const val MSG_LOAD = 0
private const val MSG_SUBSCRIBE = 1
- private const val MSG_UNSUBSCRIBE = 2
- private const val MSG_ON_ACTION = 3
- private const val MSG_UNBIND = 4
+ private const val MSG_ACTION = 2
+ private const val MSG_UNBIND = 3
private const val BIND_RETRY_DELAY = 1000L // ms
private const val LOAD_TIMEOUT = 5000L // ms
private const val MAX_BIND_RETRIES = 5
+ private const val MAX_CONTROLS_REQUEST = 100000L
private const val DEBUG = true
private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
Context.BIND_WAIVE_PRIORITY
@@ -75,7 +81,6 @@ class ControlsProviderLifecycleManager(
private val intent = Intent().apply {
component = componentName
putExtra(CALLBACK_BUNDLE, Bundle().apply {
- putBinder(CALLBACK_BINDER, serviceCallback)
putBinder(CALLBACK_TOKEN, token)
})
}
@@ -91,35 +96,27 @@ class ControlsProviderLifecycleManager(
}
bindTryCount++
try {
- isBound = context.bindService(intent, serviceConnection, BIND_FLAGS)
+ context.bindServiceAsUser(intent, serviceConnection, BIND_FLAGS, user)
} catch (e: SecurityException) {
Log.e(TAG, "Failed to bind to service", e)
- isBound = false
}
} else {
if (DEBUG) {
Log.d(TAG, "Unbinding service $intent")
}
bindTryCount = 0
- wrapper = null
- if (isBound) {
+ wrapper?.run {
context.unbindService(serviceConnection)
- isBound = false
}
+ wrapper = null
}
}
- fun bindPermanently() {
- unbindImmediate = false
- unqueueMessage(Message.Unbind)
- bindService(true)
- }
-
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
if (DEBUG) Log.d(TAG, "onServiceConnected $name")
bindTryCount = 0
- wrapper = ControlsProviderServiceWrapper(IControlsProvider.Stub.asInterface(service))
+ wrapper = ServiceWrapper(IControlsProvider.Stub.asInterface(service))
try {
service.linkToDeath(this@ControlsProviderLifecycleManager, 0)
} catch (_: RemoteException) {}
@@ -128,7 +125,7 @@ class ControlsProviderLifecycleManager(
override fun onServiceDisconnected(name: ComponentName?) {
if (DEBUG) Log.d(TAG, "onServiceDisconnected $name")
- isBound = false
+ wrapper = null
bindService(false)
}
}
@@ -147,11 +144,13 @@ class ControlsProviderLifecycleManager(
load()
}
queue.filter { it is Message.Subscribe }.flatMap { (it as Message.Subscribe).list }.run {
- subscribe(this)
+ if (this.isNotEmpty()) {
+ subscribe(this)
+ }
}
queue.filter { it is Message.Action }.forEach {
val msg = it as Message.Action
- onAction(msg.id, msg.action)
+ action(msg.id, msg.action)
}
}
@@ -182,87 +181,104 @@ class ControlsProviderLifecycleManager(
if (DEBUG) {
Log.d(TAG, "load $componentName")
}
- if (!(wrapper?.load() ?: false)) {
+ if (!(wrapper?.load(loadCallbackService) ?: false)) {
queueMessage(Message.Load)
binderDied()
}
}
+ private fun invokeOrQueue(f: () -> Unit, msg: Message) {
+ wrapper?.run {
+ f()
+ } ?: run {
+ queueMessage(msg)
+ bindService(true)
+ }
+ }
+
fun maybeBindAndLoad(callback: LoadCallback) {
unqueueMessage(Message.Unbind)
lastLoadCallback = callback
onLoadCanceller = executor.executeDelayed({
// Didn't receive a response in time, log and send back empty list
Log.d(TAG, "Timeout waiting onLoad for $componentName")
- serviceCallback.onLoad(token, emptyList())
+ loadCallbackService.accept(token, emptyList())
}, LOAD_TIMEOUT, TimeUnit.MILLISECONDS)
- if (isBound) {
- load()
- } else {
- queueMessage(Message.Load)
- unbindImmediate = true
- bindService(true)
- }
+
+ invokeOrQueue(::load, Message.Load)
}
fun maybeBindAndSubscribe(controlIds: List<String>) {
- if (isBound) {
- subscribe(controlIds)
- } else {
- queueMessage(Message.Subscribe(controlIds))
- bindService(true)
- }
+ invokeOrQueue({ subscribe(controlIds) }, Message.Subscribe(controlIds))
}
private fun subscribe(controlIds: List<String>) {
if (DEBUG) {
Log.d(TAG, "subscribe $componentName - $controlIds")
}
- if (!(wrapper?.subscribe(controlIds) ?: false)) {
+ if (!(wrapper?.subscribe(controlIds, subscriberService) ?: false)) {
queueMessage(Message.Subscribe(controlIds))
binderDied()
}
}
fun maybeBindAndSendAction(controlId: String, action: ControlAction) {
- if (isBound) {
- onAction(controlId, action)
- } else {
- queueMessage(Message.Action(controlId, action))
- bindService(true)
- }
+ invokeOrQueue({ action(controlId, action) }, Message.Action(controlId, action))
}
- private fun onAction(controlId: String, action: ControlAction) {
+ private fun action(controlId: String, action: ControlAction) {
if (DEBUG) {
Log.d(TAG, "onAction $componentName - $controlId")
}
- if (!(wrapper?.onAction(controlId, action) ?: false)) {
+ if (!(wrapper?.action(controlId, action, actionCallbackService) ?: false)) {
queueMessage(Message.Action(controlId, action))
binderDied()
}
}
+ fun startSubscription(subscription: IControlsSubscription) {
+ synchronized(subscriptions) {
+ subscriptions.add(subscription)
+ }
+ wrapper?.request(subscription, MAX_CONTROLS_REQUEST)
+ }
+
fun unsubscribe() {
if (DEBUG) {
Log.d(TAG, "unsubscribe $componentName")
}
unqueueMessage(Message.Subscribe(emptyList())) // Removes all subscribe messages
- wrapper?.unsubscribe()
+
+ val subs = synchronized(subscriptions) {
+ ArrayList(subscriptions).also {
+ subscriptions.clear()
+ }
+ }
+
+ subs.forEach {
+ wrapper?.cancel(it)
+ }
+ }
+
+ fun bindService() {
+ unqueueMessage(Message.Unbind)
+ bindService(true)
}
- fun maybeUnbindAndRemoveCallback() {
+ fun unbindService() {
lastLoadCallback = null
onLoadCanceller?.run()
onLoadCanceller = null
- if (unbindImmediate) {
- bindService(false)
- }
+
+ bindService(false)
}
- fun unbindService() {
- unbindImmediate = true
- maybeUnbindAndRemoveCallback()
+ override fun toString(): String {
+ return StringBuilder("ControlsProviderLifecycleManager(").apply {
+ append("component=$componentName")
+ append(", user=$user")
+ append(")")
+ }.toString()
}
sealed class Message {
@@ -277,7 +293,7 @@ class ControlsProviderLifecycleManager(
override val type = MSG_SUBSCRIBE
}
class Action(val id: String, val action: ControlAction) : Message() {
- override val type = MSG_ON_ACTION
+ override val type = MSG_ACTION
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
index 882a10d54431..5c812b1347e5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderServiceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
@@ -17,12 +17,17 @@
package com.android.systemui.controls.controller
import android.service.controls.actions.ControlAction
+import android.service.controls.IControlsActionCallback
+import android.service.controls.IControlsLoadCallback
import android.service.controls.IControlsProvider
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
+import android.service.controls.actions.ControlActionWrapper
import android.util.Log
-class ControlsProviderServiceWrapper(val service: IControlsProvider) {
+class ServiceWrapper(val service: IControlsProvider) {
companion object {
- private const val TAG = "ControlsProviderServiceWrapper"
+ private const val TAG = "ServiceWrapper"
}
private fun callThroughService(block: () -> Unit): Boolean {
@@ -30,32 +35,42 @@ class ControlsProviderServiceWrapper(val service: IControlsProvider) {
block()
return true
} catch (ex: Exception) {
- Log.d(TAG, "Caught exception from ControlsProviderService", ex)
+ Log.e(TAG, "Caught exception from ControlsProviderService", ex)
return false
}
}
- fun load(): Boolean {
+ fun load(cb: IControlsLoadCallback): Boolean {
return callThroughService {
- service.load()
+ service.load(cb)
}
}
- fun subscribe(controlIds: List<String>): Boolean {
+ fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber): Boolean {
return callThroughService {
- service.subscribe(controlIds)
+ service.subscribe(controlIds, subscriber)
}
}
- fun unsubscribe(): Boolean {
+ fun request(subscription: IControlsSubscription, num: Long): Boolean {
return callThroughService {
- service.unsubscribe()
+ subscription.request(num)
}
}
- fun onAction(controlId: String, action: ControlAction): Boolean {
+ fun cancel(subscription: IControlsSubscription): Boolean {
return callThroughService {
- service.onAction(controlId, action)
+ subscription.cancel()
}
}
-} \ No newline at end of file
+
+ fun action(
+ controlId: String,
+ action: ControlAction,
+ cb: IControlsActionCallback
+ ): Boolean {
+ return callThroughService {
+ service.action(controlId, ControlActionWrapper(action), cb)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index d62bb4def3aa..22c69086cf8c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -23,6 +23,7 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import com.android.settingslib.widget.CandidateInfo
import com.android.systemui.R
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 01c4fef67fd4..7ee4fd5b059e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -22,15 +22,18 @@ import android.os.Bundle
import android.view.LayoutInflater
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.controller.ControlInfo
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.CurrentUserTracker
import java.util.concurrent.Executor
import javax.inject.Inject
class ControlsFavoritingActivity @Inject constructor(
@Main private val executor: Executor,
- private val controller: ControlsControllerImpl
+ private val controller: ControlsControllerImpl,
+ broadcastDispatcher: BroadcastDispatcher
) : Activity() {
companion object {
@@ -42,11 +45,24 @@ class ControlsFavoritingActivity @Inject constructor(
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: ControlAdapter
+ private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
+ private val startingUser = controller.currentUserId
+
+ override fun onUserSwitched(newUserId: Int) {
+ if (newUserId != startingUser) {
+ stopTracking()
+ finish()
+ }
+ }
+ }
+
+ private var component: ComponentName? = null
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val app = intent.getCharSequenceExtra(EXTRA_APP)
- val component = intent.getParcelableExtra<ComponentName>(EXTRA_COMPONENT)
+ component = intent.getParcelableExtra<ComponentName>(EXTRA_COMPONENT)
// If we have no component name, there's not much we can do.
val callback = component?.let {
@@ -68,6 +84,11 @@ class ControlsFavoritingActivity @Inject constructor(
}
setContentView(recyclerView)
+ currentUserTracker.startTracking()
+ }
+
+ override fun onResume() {
+ super.onResume()
component?.let {
controller.loadForComponent(it) {
executor.execute {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
index 09e0ce9fea8d..34db684022fb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
@@ -18,14 +18,17 @@ package com.android.systemui.controls.management
import android.content.ComponentName
import com.android.settingslib.widget.CandidateInfo
+import com.android.systemui.controls.UserAwareController
import com.android.systemui.statusbar.policy.CallbackController
interface ControlsListingController :
- CallbackController<ControlsListingController.ControlsListingCallback> {
+ CallbackController<ControlsListingController.ControlsListingCallback>,
+ UserAwareController {
fun getCurrentServices(): List<CandidateInfo>
fun getAppLabel(name: ComponentName): CharSequence? = ""
+ @FunctionalInterface
interface ControlsListingCallback {
fun onServicesUpdated(list: List<CandidateInfo>)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 937216230123..882382cc4ade 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -19,6 +19,7 @@ package com.android.systemui.controls.management
import android.content.ComponentName
import android.content.Context
import android.content.pm.ServiceInfo
+import android.os.UserHandle
import android.service.controls.ControlsProviderService
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
@@ -31,6 +32,16 @@ import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
+private fun createServiceListing(context: Context): ServiceListing {
+ return ServiceListing.Builder(context).apply {
+ setIntentAction(ControlsProviderService.SERVICE_CONTROLS)
+ setPermission("android.permission.BIND_CONTROLS")
+ setNoun("Controls Provider")
+ setSetting("controls_providers")
+ setTag("controls_providers")
+ }.build()
+}
+
/**
* Provides a listing of components to be used as ControlsServiceProvider.
*
@@ -43,41 +54,55 @@ import javax.inject.Singleton
class ControlsListingControllerImpl @VisibleForTesting constructor(
private val context: Context,
@Background private val backgroundExecutor: Executor,
- private val serviceListing: ServiceListing
+ private val serviceListingBuilder: (Context) -> ServiceListing
) : ControlsListingController {
@Inject
constructor(context: Context, executor: Executor): this(
context,
executor,
- ServiceListing.Builder(context)
- .setIntentAction(ControlsProviderService.CONTROLS_ACTION)
- .setPermission("android.permission.BIND_CONTROLS")
- .setNoun("Controls Provider")
- .setSetting("controls_providers")
- .setTag("controls_providers")
- .build()
+ ::createServiceListing
)
+ private var serviceListing = serviceListingBuilder(context)
+
companion object {
private const val TAG = "ControlsListingControllerImpl"
}
private var availableServices = emptyList<ServiceInfo>()
- init {
- serviceListing.addCallback {
- Log.d(TAG, "ServiceConfig reloaded")
- availableServices = it.toList()
-
- backgroundExecutor.execute {
- callbacks.forEach {
- it.onServicesUpdated(getCurrentServices())
- }
+ override var currentUserId = context.userId
+ private set
+
+ private val serviceListingCallback = ServiceListing.Callback {
+ Log.d(TAG, "ServiceConfig reloaded")
+ availableServices = it.toList()
+
+ backgroundExecutor.execute {
+ callbacks.forEach {
+ it.onServicesUpdated(getCurrentServices())
}
}
}
+ init {
+ serviceListing.addCallback(serviceListingCallback)
+ }
+
+ override fun changeUser(newUser: UserHandle) {
+ backgroundExecutor.execute {
+ callbacks.clear()
+ availableServices = emptyList()
+ serviceListing.setListening(false)
+ serviceListing.removeCallback(serviceListingCallback)
+ currentUserId = newUser.identifier
+ val contextForUser = context.createContextAsUser(newUser, 0)
+ serviceListing = serviceListingBuilder(contextForUser)
+ serviceListing.addCallback(serviceListingCallback)
+ }
+ }
+
// All operations in background thread
private val callbacks = mutableSetOf<ControlsListingController.ControlsListingCallback>()
@@ -91,6 +116,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
*/
override fun addCallback(listener: ControlsListingController.ControlsListingCallback) {
backgroundExecutor.execute {
+ Log.d(TAG, "Subscribing callback")
callbacks.add(listener)
if (callbacks.size == 1) {
serviceListing.setListening(true)
@@ -108,6 +134,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
*/
override fun removeCallback(listener: ControlsListingController.ControlsListingCallback) {
backgroundExecutor.execute {
+ Log.d(TAG, "Unsubscribing callback")
callbacks.remove(listener)
if (callbacks.size == 0) {
serviceListing.setListening(false)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 69af516b4ac9..5ff949c98806 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -22,7 +22,10 @@ import android.os.Bundle
import android.view.LayoutInflater
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.util.LifecycleActivity
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -32,7 +35,9 @@ import javax.inject.Inject
*/
class ControlsProviderSelectorActivity @Inject constructor(
@Main private val executor: Executor,
- private val listingController: ControlsListingController
+ @Background private val backExecutor: Executor,
+ private val listingController: ControlsListingController,
+ broadcastDispatcher: BroadcastDispatcher
) : LifecycleActivity() {
companion object {
@@ -40,6 +45,16 @@ class ControlsProviderSelectorActivity @Inject constructor(
}
private lateinit var recyclerView: RecyclerView
+ private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
+ private val startingUser = listingController.currentUserId
+
+ override fun onUserSwitched(newUserId: Int) {
+ if (newUserId != startingUser) {
+ stopTracking()
+ finish()
+ }
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -50,6 +65,7 @@ class ControlsProviderSelectorActivity @Inject constructor(
recyclerView.layoutManager = LinearLayoutManager(applicationContext)
setContentView(recyclerView)
+ currentUserTracker.startTracking()
}
/**
@@ -57,13 +73,17 @@ class ControlsProviderSelectorActivity @Inject constructor(
* @param component a component name for a [ControlsProviderService]
*/
fun launchFavoritingActivity(component: ComponentName?) {
- component?.let {
- val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java).apply {
- putExtra(ControlsFavoritingActivity.EXTRA_APP, listingController.getAppLabel(it))
- putExtra(ControlsFavoritingActivity.EXTRA_COMPONENT, it)
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
+ backExecutor.execute {
+ component?.let {
+ val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java)
+ .apply {
+ putExtra(ControlsFavoritingActivity.EXTRA_APP,
+ listingController.getAppLabel(it))
+ putExtra(ControlsFavoritingActivity.EXTRA_COMPONENT, it)
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
+ }
+ startActivity(intent)
}
- startActivity(intent)
}
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
new file mode 100644
index 000000000000..44f3fbc58eaf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+interface Behavior {
+ fun apply(cvh: ControlViewHolder, cws: ControlWithState)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
new file mode 100644
index 000000000000..5519e32d6c10
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.drawable.ClipDrawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.Icon
+import android.graphics.drawable.LayerDrawable
+import android.service.controls.Control
+import android.service.controls.actions.ControlAction
+import android.service.controls.templates.ControlTemplate
+import android.service.controls.templates.TemperatureControlTemplate
+import android.service.controls.templates.ToggleRangeTemplate
+import android.service.controls.templates.ToggleTemplate
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.R
+
+public const val MIN_LEVEL = 0
+public const val MAX_LEVEL = 10000
+
+class ControlViewHolder(
+ val layout: ViewGroup,
+ val controlsController: ControlsController
+) {
+ val icon: ImageView = layout.requireViewById(R.id.icon)
+ val status: TextView = layout.requireViewById(R.id.status)
+ val statusExtra: TextView = layout.requireViewById(R.id.status_extra)
+ val title: TextView = layout.requireViewById(R.id.title)
+ val subtitle: TextView = layout.requireViewById(R.id.subtitle)
+ val context: Context = layout.getContext()
+ val clipLayer: ClipDrawable
+ val gd: GradientDrawable
+ lateinit var cws: ControlWithState
+
+ init {
+ val ld = layout.getBackground() as LayerDrawable
+ ld.mutate()
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
+ gd = clipLayer.getDrawable() as GradientDrawable
+ }
+
+ fun bindData(cws: ControlWithState) {
+ this.cws = cws
+
+ val (status, template) = cws.control?.let {
+ title.setText(it.getTitle())
+ subtitle.setText(it.getSubtitle())
+ Pair(it.getStatus(), it.getControlTemplate())
+ } ?: run {
+ title.setText(cws.ci.controlTitle)
+ subtitle.setText("")
+ Pair(Control.STATUS_UNKNOWN, ControlTemplate.NO_TEMPLATE)
+ }
+
+ cws.control?.let { c ->
+ layout.setOnLongClickListener(View.OnLongClickListener() {
+ val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+ context.sendBroadcast(closeDialog)
+
+ c.getAppIntent().send()
+ true
+ })
+ }
+
+ findBehavior(status, template).apply(this, cws)
+ }
+
+ fun action(action: ControlAction) {
+ controlsController.action(cws.ci, action)
+ }
+
+ private fun findBehavior(status: Int, template: ControlTemplate): Behavior {
+ return when {
+ status == Control.STATUS_UNKNOWN -> UnknownBehavior()
+ template is ToggleTemplate -> ToggleBehavior()
+ template is ToggleRangeTemplate -> ToggleRangeBehavior()
+ template is TemperatureControlTemplate -> TemperatureControlBehavior()
+ else -> {
+ object : Behavior {
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ cvh.status.setText(cws.control?.getStatusText())
+ cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
+ }
+ }
+ }
+ }
+ }
+
+ internal fun applyRenderInfo(ri: RenderInfo) {
+ val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
+ val bg = context.getResources().getColorStateList(ri.background, context.getTheme())
+ status.setTextColor(fg)
+ statusExtra.setTextColor(fg)
+
+ icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
+ icon.setImageTintList(fg)
+
+ gd.setColor(bg)
+ }
+
+ fun setEnabled(enabled: Boolean) {
+ status.setEnabled(enabled)
+ icon.setEnabled(enabled)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt
new file mode 100644
index 000000000000..816f0b2cb1d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+import android.service.controls.Control
+
+import com.android.systemui.controls.controller.ControlInfo
+
+/**
+ * A container for:
+ * <ul>
+ * <li>ControlInfo - Basic cached info about a Control
+ * <li>Control - Actual Control parcelable received directly from
+ * the participating application
+ * </ul>
+ */
+data class ControlWithState(val ci: ControlInfo, val control: Control?)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 0270c2b6b1b3..b07a75d5e757 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -19,12 +19,15 @@ package com.android.systemui.controls.ui
import android.content.ComponentName
import android.service.controls.Control
import android.service.controls.actions.ControlAction
+import android.view.ViewGroup
interface ControlsUiController {
+ fun show(parent: ViewGroup)
+ fun hide()
fun onRefreshState(componentName: ComponentName, controls: List<Control>)
fun onActionResponse(
componentName: ComponentName,
controlId: String,
@ControlAction.ResponseResult response: Int
)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 0ace1263b49b..a777faf57fce 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -16,19 +16,215 @@
package com.android.systemui.controls.ui
+import android.accounts.Account
+import android.accounts.AccountManager
import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.IBinder
import android.service.controls.Control
+import android.service.controls.TokenProvider
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import android.widget.Space
+import android.widget.TextView
+
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.R
+
+import dagger.Lazy
+
+import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
+private const val TAG = "ControlsUi"
+
+// TEMP CODE for MOCK
+private const val TOKEN = "https://www.googleapis.com/auth/assistant"
+private const val SCOPE = "oauth2:" + TOKEN
+private var tokenProviderConnection: TokenProviderConnection? = null
+class TokenProviderConnection(val cc: ControlsController, val context: Context)
+ : ServiceConnection {
+ private var mTokenProvider: TokenProvider? = null
+
+ override fun onServiceConnected(cName: ComponentName, binder: IBinder) {
+ Thread({
+ Log.i(TAG, "TokenProviderConnection connected")
+ mTokenProvider = TokenProvider.Stub.asInterface(binder)
+
+ val mLastAccountName = mTokenProvider?.getAccountName()
+
+ if (mLastAccountName == null || mLastAccountName.isEmpty()) {
+ Log.e(TAG, "NO ACCOUNT IS SET. Open HomeMock app")
+ } else {
+ mTokenProvider?.setAuthToken(getAuthToken(mLastAccountName))
+ cc.subscribeToFavorites()
+ }
+ }, "TokenProviderThread").start()
+ }
+
+ override fun onServiceDisconnected(cName: ComponentName) {
+ mTokenProvider = null
+ }
+
+ fun getAuthToken(accountName: String): String? {
+ val am = AccountManager.get(context)
+ val accounts = am.getAccountsByType("com.google")
+ if (accounts == null || accounts.size == 0) {
+ Log.w(TAG, "No com.google accounts found")
+ return null
+ }
+
+ var account: Account? = null
+ for (a in accounts) {
+ if (a.name.equals(accountName)) {
+ account = a
+ break
+ }
+ }
+
+ if (account == null) {
+ account = accounts[0]
+ }
+
+ try {
+ return am.blockingGetAuthToken(account!!, SCOPE, true)
+ } catch (e: Throwable) {
+ Log.e(TAG, "Error getting auth token", e)
+ return null
+ }
+ }
+}
+
@Singleton
-class ControlsUiControllerImpl @Inject constructor() : ControlsUiController {
+class ControlsUiControllerImpl @Inject constructor (
+ val controlsController: Lazy<ControlsController>,
+ val context: Context,
+ @Main val uiExecutor: Executor
+) : ControlsUiController {
+
+ private lateinit var controlInfos: List<ControlInfo>
+ private val controlsById = mutableMapOf<Pair<ComponentName, String>, ControlWithState>()
+ private val controlViewsById = mutableMapOf<String, ControlViewHolder>()
+ private lateinit var parent: ViewGroup
+
+ override fun show(parent: ViewGroup) {
+ Log.d(TAG, "show()")
+
+ this.parent = parent
+
+ controlInfos = controlsController.get().getFavoriteControls()
+
+ controlInfos.map {
+ ControlWithState(it, null)
+ }.associateByTo(controlsById) { Pair(it.ci.component, it.ci.controlId) }
+
+ if (controlInfos.isEmpty()) {
+ showInitialSetupView()
+ } else {
+ showControlsView()
+ }
+
+ // Temp code to pass auth
+ tokenProviderConnection = TokenProviderConnection(controlsController.get(), context)
+ val serviceIntent = Intent()
+ serviceIntent.setComponent(ComponentName("com.android.systemui.home.mock",
+ "com.android.systemui.home.mock.AuthService"))
+ if (!context.bindService(serviceIntent, tokenProviderConnection!!,
+ Context.BIND_AUTO_CREATE)) {
+ controlsController.get().subscribeToFavorites()
+ }
+ }
+
+ private fun showInitialSetupView() {
+ val inflater = LayoutInflater.from(context)
+ inflater.inflate(R.layout.controls_no_favorites, parent, true)
+
+ val textView = parent.requireViewById(R.id.controls_title) as TextView
+ textView.setOnClickListener(launchSelectorActivityListener(context))
+ }
+
+ private fun launchSelectorActivityListener(context: Context): (View) -> Unit {
+ return { _ ->
+ val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+ context.sendBroadcast(closeDialog)
+
+ val i = Intent()
+ i.setComponent(ComponentName(context, ControlsProviderSelectorActivity::class.java))
+ i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(i)
+ }
+ }
+
+ private fun showControlsView() {
+ val inflater = LayoutInflater.from(context)
+ inflater.inflate(R.layout.controls_with_favorites, parent, true)
+
+ val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
+ var lastRow: ViewGroup = createRow(inflater, listView)
+ controlInfos.forEach {
+ Log.d(TAG, "favorited control id: " + it.controlId)
+ if (lastRow.getChildCount() == 2) {
+ lastRow = createRow(inflater, listView)
+ }
+ val item = inflater.inflate(
+ R.layout.controls_base_item, lastRow, false) as ViewGroup
+ lastRow.addView(item)
+ val cvh = ControlViewHolder(item, controlsController.get())
+ cvh.bindData(controlsById.get(Pair(it.component, it.controlId))!!)
+ controlViewsById.put(it.controlId, cvh)
+ }
+
+ if ((controlInfos.size % 2) == 1) {
+ lastRow.addView(Space(context), LinearLayout.LayoutParams(0, 0, 1f))
+ }
+
+ val moreImageView = parent.requireViewById(R.id.controls_more) as View
+ moreImageView.setOnClickListener(launchSelectorActivityListener(context))
+ }
+
+ override fun hide() {
+ Log.d(TAG, "hide()")
+ controlsController.get().unsubscribe()
+ context.unbindService(tokenProviderConnection)
+ tokenProviderConnection = null
+
+ parent.removeAllViews()
+ controlsById.clear()
+ controlViewsById.clear()
+ }
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
- TODO("not implemented")
+ Log.d(TAG, "onRefreshState()")
+ controls.forEach { c ->
+ controlsById.get(Pair(componentName, c.getControlId()))?.let {
+ Log.d(TAG, "onRefreshState() for id: " + c.getControlId())
+ val cws = ControlWithState(it.ci, c)
+ controlsById.put(Pair(componentName, c.getControlId()), cws)
+
+ uiExecutor.execute {
+ controlViewsById.get(c.getControlId())?.bindData(cws)
+ }
+ }
+ }
}
override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
+ Log.d(TAG, "onActionResponse()")
TODO("not implemented")
}
-} \ No newline at end of file
+
+ private fun createRow(inflater: LayoutInflater, parent: ViewGroup): ViewGroup {
+ val row = inflater.inflate(R.layout.controls_row, parent, false) as ViewGroup
+ parent.addView(row)
+ return row
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
new file mode 100644
index 000000000000..24c8020529ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+import android.service.controls.DeviceTypes
+import android.service.controls.templates.TemperatureControlTemplate
+
+import com.android.systemui.R
+
+data class IconState(val disabledResourceId: Int, val enabledResourceId: Int) {
+ operator fun get(state: Boolean): Int {
+ return if (state) {
+ enabledResourceId
+ } else {
+ disabledResourceId
+ }
+ }
+}
+
+data class RenderInfo(val iconResourceId: Int, val foreground: Int, val background: Int) {
+
+ companion object {
+ fun lookup(deviceType: Int, enabled: Boolean): RenderInfo {
+ val iconState = deviceIconMap.getValue(deviceType)
+ val (fg, bg) = deviceColorMap.getValue(deviceType)
+ return RenderInfo(iconState[enabled], fg, bg)
+ }
+
+ fun lookup(deviceType: Int, offset: Int, enabled: Boolean): RenderInfo {
+ val key = deviceType * BUCKET_SIZE + offset
+ return lookup(key, enabled)
+ }
+ }
+}
+
+private const val BUCKET_SIZE = 1000
+private const val THERMOSTAT_RANGE = DeviceTypes.TYPE_THERMOSTAT * BUCKET_SIZE
+
+public val deviceColorMap = mapOf<Int, Pair<Int, Int>>(
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to
+ Pair(R.color.thermo_heat_foreground, R.color.thermo_heat_background),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to
+ Pair(R.color.thermo_cool_foreground, R.color.thermo_cool_background),
+ DeviceTypes.TYPE_LIGHT to Pair(R.color.light_foreground, R.color.light_background)
+).withDefault {
+ Pair(R.color.control_foreground, R.color.control_background)
+}
+
+public val deviceIconMap = mapOf<Int, IconState>(
+ THERMOSTAT_RANGE to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT_COOL) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_ECO) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ DeviceTypes.TYPE_THERMOSTAT to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ DeviceTypes.TYPE_LIGHT to IconState(
+ R.drawable.ic_light_off_gm2_24px,
+ R.drawable.ic_lightbulb_outline_gm2_24px
+ ),
+ DeviceTypes.TYPE_CAMERA to IconState(
+ R.drawable.ic_videocam_gm2_24px,
+ R.drawable.ic_videocam_gm2_24px
+ ),
+ DeviceTypes.TYPE_LOCK to IconState(
+ R.drawable.ic_lock_open_gm2_24px,
+ R.drawable.ic_lock_gm2_24px
+ ),
+ DeviceTypes.TYPE_SWITCH to IconState(
+ R.drawable.ic_switches_gm2_24px,
+ R.drawable.ic_switches_gm2_24px
+ ),
+ DeviceTypes.TYPE_OUTLET to IconState(
+ R.drawable.ic_power_off_gm2_24px,
+ R.drawable.ic_power_gm2_24px
+ )
+).withDefault {
+ IconState(
+ R.drawable.ic_light_off_gm2_24px,
+ R.drawable.ic_lightbulb_outline_gm2_24px
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
new file mode 100644
index 000000000000..ae0ebbb9e1bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.service.controls.Control
+import android.service.controls.templates.TemperatureControlTemplate
+import android.widget.TextView
+
+import com.android.systemui.R
+
+class TemperatureControlBehavior : Behavior {
+ lateinit var clipLayer: Drawable
+ lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
+ lateinit var template: TemperatureControlTemplate
+ lateinit var status: TextView
+ lateinit var context: Context
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+ this.cvh = cvh
+ status = cvh.status
+
+ status.setText(control.getStatusText())
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+ template = control.getControlTemplate() as TemperatureControlTemplate
+
+ val activeMode = template.getCurrentActiveMode()
+ val enabled = activeMode != 0 && activeMode != TemperatureControlTemplate.MODE_OFF
+ val deviceType = control.getDeviceType()
+
+ clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL)
+ cvh.setEnabled(enabled)
+ cvh.applyRenderInfo(RenderInfo.lookup(deviceType, activeMode, enabled))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
new file mode 100644
index 000000000000..7cd3ab795678
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.view.View
+import android.widget.TextView
+import android.service.controls.Control
+import android.service.controls.actions.BooleanAction
+import android.service.controls.templates.ToggleTemplate
+
+import com.android.systemui.R
+
+class ToggleBehavior : Behavior {
+ lateinit var clipLayer: Drawable
+ lateinit var template: ToggleTemplate
+ lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
+ lateinit var context: Context
+ lateinit var status: TextView
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+ this.cvh = cvh
+ status = cvh.status
+
+ status.setText(control.getStatusText())
+
+ cvh.layout.setOnClickListener(View.OnClickListener() { toggle() })
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+ template = control.getControlTemplate() as ToggleTemplate
+
+ val checked = template.isChecked()
+ val deviceType = control.getDeviceType()
+
+ clipLayer.setLevel(if (checked) MAX_LEVEL else MIN_LEVEL)
+ cvh.setEnabled(checked)
+ cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked))
+ }
+
+ fun toggle() {
+ cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
+
+ val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
+ clipLayer.setLevel(nextLevel)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
new file mode 100644
index 000000000000..a6918f50a977
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.view.MotionEvent
+import android.view.View
+import android.widget.TextView
+import android.service.controls.Control
+import android.service.controls.actions.BooleanAction
+import android.service.controls.actions.FloatAction
+import android.service.controls.templates.RangeTemplate
+import android.service.controls.templates.ToggleRangeTemplate
+import android.util.TypedValue
+
+import com.android.systemui.R
+
+class ToggleRangeBehavior : Behavior {
+ lateinit var clipLayer: Drawable
+ lateinit var template: ToggleRangeTemplate
+ lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
+ lateinit var rangeTemplate: RangeTemplate
+ lateinit var statusExtra: TextView
+ lateinit var status: TextView
+ lateinit var context: Context
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+ this.cvh = cvh
+
+ statusExtra = cvh.statusExtra
+ status = cvh.status
+
+ status.setText(control.getStatusText())
+
+ context = status.getContext()
+
+ cvh.layout.setOnTouchListener(ToggleRangeTouchListener())
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+ template = control.getControlTemplate() as ToggleRangeTemplate
+ rangeTemplate = template.getRange()
+
+ val checked = template.isChecked()
+ val deviceType = control.getDeviceType()
+
+ updateRange((rangeTemplate.getCurrentValue() / 100.0f), checked)
+
+ cvh.setEnabled(checked)
+ cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked))
+ }
+
+ fun toggle() {
+ cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
+
+ val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
+ clipLayer.setLevel(nextLevel)
+ }
+
+ fun beginUpdateRange() {
+ status.setVisibility(View.GONE)
+ statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+ .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
+ }
+
+ fun updateRange(f: Float, checked: Boolean) {
+ clipLayer.setLevel(if (checked) (MAX_LEVEL * f).toInt() else MIN_LEVEL)
+
+ if (checked && f < 100.0f && f > 0.0f) {
+ statusExtra.setText("" + (f * 100.0).toInt() + "%")
+ statusExtra.setVisibility(View.VISIBLE)
+ } else {
+ statusExtra.setText("")
+ statusExtra.setVisibility(View.GONE)
+ }
+ }
+
+ fun endUpdateRange(f: Float) {
+ statusExtra.setText(" - " + (f * 100.0).toInt() + "%")
+
+ val newValue = rangeTemplate.getMinValue() +
+ (f * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
+
+ statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+ .getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
+ status.setVisibility(View.VISIBLE)
+
+ cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(newValue)))
+ }
+
+ fun findNearestStep(value: Float): Float {
+ var minDiff = 1000f
+
+ var f = rangeTemplate.getMinValue()
+ while (f <= rangeTemplate.getMaxValue()) {
+ val currentDiff = Math.abs(value - f)
+ if (currentDiff < minDiff) {
+ minDiff = currentDiff
+ } else {
+ return f - rangeTemplate.getStepValue()
+ }
+
+ f += rangeTemplate.getStepValue()
+ }
+
+ return rangeTemplate.getMaxValue()
+ }
+
+ inner class ToggleRangeTouchListener() : View.OnTouchListener {
+ private var initialTouchX: Float = 0.0f
+ private var initialTouchY: Float = 0.0f
+ private var isDragging: Boolean = false
+ private val minDragDiff = 20
+
+ override fun onTouch(v: View, e: MotionEvent): Boolean {
+ when (e.getActionMasked()) {
+ MotionEvent.ACTION_DOWN -> setupTouch(e)
+ MotionEvent.ACTION_MOVE -> detectDrag(v, e)
+ MotionEvent.ACTION_UP -> endTouch(v, e)
+ }
+
+ return true
+ }
+
+ private fun setupTouch(e: MotionEvent) {
+ initialTouchX = e.getX()
+ initialTouchY = e.getY()
+ }
+
+ private fun detectDrag(v: View, e: MotionEvent) {
+ val xDiff = Math.abs(e.getX() - initialTouchX)
+ val yDiff = Math.abs(e.getY() - initialTouchY)
+
+ if (xDiff < minDragDiff) {
+ isDragging = false
+ } else {
+ if (!isDragging) {
+ this@ToggleRangeBehavior.beginUpdateRange()
+ }
+ v.getParent().requestDisallowInterceptTouchEvent(true)
+ isDragging = true
+ if (yDiff > xDiff) {
+ endTouch(v, e)
+ } else {
+ val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
+ this@ToggleRangeBehavior.updateRange(percent, true)
+ }
+ }
+ }
+
+ private fun endTouch(v: View, e: MotionEvent) {
+ if (!isDragging) {
+ this@ToggleRangeBehavior.toggle()
+ } else {
+ val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
+ this@ToggleRangeBehavior.endUpdateRange(percent)
+ }
+
+ initialTouchX = 0.0f
+ initialTouchY = 0.0f
+ isDragging = false
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
new file mode 100644
index 000000000000..5a6e5b481544
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+class UnknownBehavior : Behavior {
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ cvh.status.setText("Loading...")
+ cvh.setEnabled(false)
+ cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 99dd5e2356d6..5de88e17d320 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -21,6 +21,7 @@ import com.android.systemui.ScreenDecorations;
import com.android.systemui.SizeCompatModeActivityController;
import com.android.systemui.SliceBroadcastRelayHandler;
import com.android.systemui.SystemUI;
+import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.globalactions.GlobalActionsComponent;
@@ -36,6 +37,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarModule;
import com.android.systemui.statusbar.tv.TvStatusBar;
import com.android.systemui.theme.ThemeOverlayController;
+import com.android.systemui.toast.ToastUI;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.volume.VolumeUI;
@@ -140,12 +142,24 @@ public abstract class SystemUIBinder {
@ClassKey(StatusBar.class)
public abstract SystemUI bindsStatusBar(StatusBar sysui);
+ /** Inject into SystemActions. */
+ @Binds
+ @IntoMap
+ @ClassKey(SystemActions.class)
+ public abstract SystemUI bindSystemActions(SystemActions sysui);
+
/** Inject into ThemeOverlayController. */
@Binds
@IntoMap
@ClassKey(ThemeOverlayController.class)
public abstract SystemUI bindThemeOverlayController(ThemeOverlayController sysui);
+ /** Inject into ToastUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(ToastUI.class)
+ public abstract SystemUI bindToastUI(ToastUI service);
+
/** Inject into TvStatusBar. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 36a845002deb..7b541991088c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -27,6 +27,7 @@ import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.DumpController;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
@@ -37,6 +38,7 @@ import com.android.systemui.statusbar.StatusBarWindowBlurController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
@@ -59,9 +61,13 @@ import dagger.Provides;
* A dagger module for injecting components of System UI that are not overridden by the System UI
* implementation.
*/
-@Module(includes = {AssistModule.class,
- ConcurrencyModule.class,
- PeopleHubModule.class},
+@Module(includes = {
+ AssistModule.class,
+ ConcurrencyModule.class,
+ LogModule.class,
+ NotificationsModule.class,
+ PeopleHubModule.class,
+ },
subcomponents = {StatusBarComponent.class, NotificationRowComponent.class})
public abstract class SystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e14581ffcde8..e8509b366c5b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -26,6 +26,7 @@ import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.InjectionInflationController;
@@ -105,4 +106,9 @@ public interface SystemUIRootComponent {
* Member injection into the supplied argument.
*/
void inject(ContentProvider contentProvider);
+
+ /**
+ * Member injection into the supplied argument.
+ */
+ void inject(KeyguardSliceProvider keyguardSliceProvider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java b/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java
deleted file mode 100644
index d2fe39424875..000000000000
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.doze;
-
-import android.annotation.IntDef;
-
-import com.android.systemui.log.RichEvent;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * An event related to dozing. {@link DozeLog} stores and prints these events for debugging
- * and triaging purposes.
- */
-public class DozeEvent extends RichEvent {
- /**
- * Initializes a doze event
- */
- public DozeEvent init(@EventType int type, String reason) {
- super.init(DEBUG, type, reason);
- return this;
- }
-
- /**
- * Event labels for each doze event
- * Index corresponds to the integer associated with each {@link EventType}
- */
- @Override
- public String[] getEventLabels() {
- final String[] events = new String[]{
- "PickupWakeup",
- "PulseStart",
- "PulseFinish",
- "NotificationPulse",
- "Dozing",
- "Fling",
- "EmergencyCall",
- "KeyguardBouncerChanged",
- "ScreenOn",
- "ScreenOff",
- "MissedTick",
- "TimeTickScheduled",
- "KeyguardVisibilityChanged",
- "DozeStateChanged",
- "WakeDisplay",
- "ProximityResult",
- "PulseDropped",
- "PulseDisabledByProx",
- "SensorTriggered"
- };
-
- if (events.length != TOTAL_EVENT_TYPES) {
- throw new IllegalStateException("DozeEvent events.length should match TOTAL_EVENT_TYPES"
- + " events.length=" + events.length
- + " TOTAL_EVENT_LENGTH=" + TOTAL_EVENT_TYPES);
- }
- return events;
- }
-
- /**
- * Converts the reason (integer) to a user-readable string
- */
- public static String reasonToString(@Reason int pulseReason) {
- switch (pulseReason) {
- case PULSE_REASON_INTENT: return "intent";
- case PULSE_REASON_NOTIFICATION: return "notification";
- case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
- case REASON_SENSOR_PICKUP: return "pickup";
- case REASON_SENSOR_DOUBLE_TAP: return "doubletap";
- case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
- case PULSE_REASON_DOCKING: return "docking";
- case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
- case REASON_SENSOR_WAKE_UP: return "wakeup";
- case REASON_SENSOR_TAP: return "tap";
- default: throw new IllegalArgumentException("invalid reason: " + pulseReason);
- }
- }
-
- @IntDef({PICKUP_WAKEUP, PULSE_START, PULSE_FINISH, NOTIFICATION_PULSE, DOZING, FLING,
- EMERGENCY_CALL, KEYGUARD_BOUNCER_CHANGED, SCREEN_ON, SCREEN_OFF, MISSED_TICK,
- TIME_TICK_SCHEDULED, KEYGUARD_VISIBILITY_CHANGE, DOZE_STATE_CHANGED, WAKE_DISPLAY,
- PROXIMITY_RESULT, PULSE_DROPPED, PULSE_DISABLED_BY_PROX, SENSOR_TRIGGERED})
- /**
- * Types of DozeEvents
- */
- @Retention(RetentionPolicy.SOURCE)
- public @interface EventType {}
- public static final int PICKUP_WAKEUP = 0;
- public static final int PULSE_START = 1;
- public static final int PULSE_FINISH = 2;
- public static final int NOTIFICATION_PULSE = 3;
- public static final int DOZING = 4;
- public static final int FLING = 5;
- public static final int EMERGENCY_CALL = 6;
- public static final int KEYGUARD_BOUNCER_CHANGED = 7;
- public static final int SCREEN_ON = 8;
- public static final int SCREEN_OFF = 9;
- public static final int MISSED_TICK = 10;
- public static final int TIME_TICK_SCHEDULED = 11;
- public static final int KEYGUARD_VISIBILITY_CHANGE = 12;
- public static final int DOZE_STATE_CHANGED = 13;
- public static final int WAKE_DISPLAY = 14;
- public static final int PROXIMITY_RESULT = 15;
- public static final int PULSE_DROPPED = 16;
- public static final int PULSE_DISABLED_BY_PROX = 17;
- public static final int SENSOR_TRIGGERED = 18;
- public static final int TOTAL_EVENT_TYPES = 19;
-
- public static final int TOTAL_REASONS = 10;
- @IntDef({PULSE_REASON_NONE, PULSE_REASON_INTENT, PULSE_REASON_NOTIFICATION,
- PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP,
- PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP,
- PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Reason {}
- public static final int PULSE_REASON_NONE = -1;
- public static final int PULSE_REASON_INTENT = 0;
- public static final int PULSE_REASON_NOTIFICATION = 1;
- public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
- public static final int REASON_SENSOR_PICKUP = 3;
- public static final int REASON_SENSOR_DOUBLE_TAP = 4;
- public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
- public static final int PULSE_REASON_DOCKING = 6;
- public static final int REASON_SENSOR_WAKE_UP = 7;
- public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
- public static final int REASON_SENSOR_TAP = 9;
-}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 43db85bd91ec..6f655bb0b209 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -118,6 +118,7 @@ public class DozeFactory {
new DozeWallpaperState(mWallpaperManager, mBiometricUnlockController,
mDozeParameters),
new DozeDockHandler(config, machine, mDockManager),
+ new DozeSuppressedHandler(dozeService, config, machine),
new DozeAuthRemover(dozeService)
});
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index fe504216b166..96ae6c9ec4a2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -16,15 +16,20 @@
package com.android.systemui.doze;
+import android.annotation.IntDef;
import android.util.TimeUtils;
+import androidx.annotation.NonNull;
+
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.DumpController;
-import com.android.systemui.log.SysuiLog;
+import com.android.systemui.Dumpable;
+import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Date;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -32,13 +37,11 @@ import javax.inject.Singleton;
/**
* Logs doze events for debugging and triaging purposes. Logs are dumped in bugreports or on demand:
* adb shell dumpsys activity service com.android.systemui/.SystemUIService \
- * dependency DumpController DozeLog
+ * dependency DumpController DozeLog,DozeStats
*/
@Singleton
-public class DozeLog extends SysuiLog<DozeEvent> {
- private static final String TAG = "DozeLog";
-
- private DozeEvent mRecycledEvent;
+public class DozeLog implements Dumpable {
+ private final DozeLogger mLogger;
private boolean mPulsing;
private long mSince;
@@ -51,8 +54,11 @@ public class DozeLog extends SysuiLog<DozeEvent> {
private SummaryStats[][] mProxStats; // [reason][near/far]
@Inject
- public DozeLog(KeyguardUpdateMonitor keyguardUpdateMonitor, DumpController dumpController) {
- super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS);
+ public DozeLog(
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DumpController dumpController,
+ DozeLogger logger) {
+ mLogger = logger;
mSince = System.currentTimeMillis();
mPickupPulseNearVibrationStats = new SummaryStats();
mPickupPulseNotNearVibrationStats = new SummaryStats();
@@ -60,8 +66,8 @@ public class DozeLog extends SysuiLog<DozeEvent> {
mScreenOnPulsingStats = new SummaryStats();
mScreenOnNotPulsingStats = new SummaryStats();
mEmergencyCallStats = new SummaryStats();
- mProxStats = new SummaryStats[DozeEvent.TOTAL_REASONS][2];
- for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
+ mProxStats = new SummaryStats[TOTAL_REASONS][2];
+ for (int i = 0; i < TOTAL_REASONS; i++) {
mProxStats[i][0] = new SummaryStats();
mProxStats[i][1] = new SummaryStats();
}
@@ -69,42 +75,42 @@ public class DozeLog extends SysuiLog<DozeEvent> {
if (keyguardUpdateMonitor != null) {
keyguardUpdateMonitor.registerCallback(mKeyguardCallback);
}
+
+ dumpController.registerDumpable("DumpStats", this);
}
/**
* Appends pickup wakeup event to the logs
*/
public void tracePickupWakeUp(boolean withinVibrationThreshold) {
- log(DozeEvent.PICKUP_WAKEUP, "withinVibrationThreshold=" + withinVibrationThreshold);
- if (mEnabled) {
- (withinVibrationThreshold ? mPickupPulseNearVibrationStats
- : mPickupPulseNotNearVibrationStats).append();
- }
+ mLogger.logPickupWakeup(withinVibrationThreshold);
+ (withinVibrationThreshold ? mPickupPulseNearVibrationStats
+ : mPickupPulseNotNearVibrationStats).append();
}
/**
* Appends pulse started event to the logs.
* @param reason why the pulse started
*/
- public void tracePulseStart(@DozeEvent.Reason int reason) {
- log(DozeEvent.PULSE_START, DozeEvent.reasonToString(reason));
- if (mEnabled) mPulsing = true;
+ public void tracePulseStart(@Reason int reason) {
+ mLogger.logPulseStart(reason);
+ mPulsing = true;
}
/**
* Appends pulse finished event to the logs
*/
public void tracePulseFinish() {
- log(DozeEvent.PULSE_FINISH);
- if (mEnabled) mPulsing = false;
+ mLogger.logPulseFinish();
+ mPulsing = false;
}
/**
* Appends pulse event to the logs
*/
public void traceNotificationPulse() {
- log(DozeEvent.NOTIFICATION_PULSE);
- if (mEnabled) mNotificationPulseStats.append();
+ mLogger.logNotificationPulse();
+ mNotificationPulseStats.append();
}
/**
@@ -112,8 +118,8 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param dozing true if dozing, else false
*/
public void traceDozing(boolean dozing) {
- log(DozeEvent.DOZING, "dozing=" + dozing);
- if (mEnabled) mPulsing = false;
+ mLogger.logDozing(dozing);
+ mPulsing = false;
}
/**
@@ -121,18 +127,15 @@ public class DozeLog extends SysuiLog<DozeEvent> {
*/
public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
boolean screenOnFromTouch) {
- log(DozeEvent.FLING, "expand=" + expand
- + " aboveThreshold=" + aboveThreshold
- + " thresholdNeeded=" + thresholdNeeded
- + " screenOnFromTouch=" + screenOnFromTouch);
+ mLogger.logFling(expand, aboveThreshold, thresholdNeeded, screenOnFromTouch);
}
/**
* Appends emergency call event to the logs
*/
public void traceEmergencyCall() {
- log(DozeEvent.EMERGENCY_CALL);
- if (mEnabled) mEmergencyCallStats.append();
+ mLogger.logEmergencyCall();
+ mEmergencyCallStats.append();
}
/**
@@ -140,18 +143,16 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param showing true if the keyguard bouncer is showing, else false
*/
public void traceKeyguardBouncerChanged(boolean showing) {
- log(DozeEvent.KEYGUARD_BOUNCER_CHANGED, "showing=" + showing);
+ mLogger.logKeyguardBouncerChanged(showing);
}
/**
* Appends screen-on event to the logs
*/
public void traceScreenOn() {
- log(DozeEvent.SCREEN_ON, "pulsing=" + mPulsing);
- if (mEnabled) {
- (mPulsing ? mScreenOnPulsingStats : mScreenOnNotPulsingStats).append();
- mPulsing = false;
- }
+ mLogger.logScreenOn(mPulsing);
+ (mPulsing ? mScreenOnPulsingStats : mScreenOnNotPulsingStats).append();
+ mPulsing = false;
}
/**
@@ -159,7 +160,7 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param why reason the screen is off
*/
public void traceScreenOff(int why) {
- log(DozeEvent.SCREEN_OFF, "why=" + why);
+ mLogger.logScreenOff(why);
}
/**
@@ -167,7 +168,7 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param delay of the missed tick
*/
public void traceMissedTick(String delay) {
- log(DozeEvent.MISSED_TICK, "delay=" + delay);
+ mLogger.logMissedTick(delay);
}
/**
@@ -176,9 +177,7 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param triggerAt time tick trigger at
*/
public void traceTimeTickScheduled(long when, long triggerAt) {
- log(DozeEvent.TIME_TICK_SCHEDULED,
- "scheduledAt=" + DATE_FORMAT.format(new Date(when))
- + " triggerAt=" + DATE_FORMAT.format(new Date(triggerAt)));
+ mLogger.logTimeTickScheduled(when, triggerAt);
}
/**
@@ -186,8 +185,8 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param showing whether the keyguard is now showing
*/
public void traceKeyguard(boolean showing) {
- log(DozeEvent.KEYGUARD_VISIBILITY_CHANGE, "showing=" + showing);
- if (mEnabled && !showing) mPulsing = false;
+ mLogger.logKeyguardVisibilityChange(showing);
+ if (!showing) mPulsing = false;
}
/**
@@ -195,7 +194,7 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param state new DozeMachine state
*/
public void traceState(DozeMachine.State state) {
- log(DozeEvent.DOZE_STATE_CHANGED, state.name());
+ mLogger.logDozeStateChanged(state);
}
/**
@@ -203,31 +202,22 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param wake if we're waking up or sleeping.
*/
public void traceWakeDisplay(boolean wake) {
- log(DozeEvent.WAKE_DISPLAY, "wake=" + wake);
+ mLogger.logWakeDisplay(wake);
}
/**
* Appends proximity result event to the logs
* @param near true if near, else false
- * @param millis
* @param reason why proximity result was triggered
*/
- public void traceProximityResult(boolean near, long millis, @DozeEvent.Reason int reason) {
- log(DozeEvent.PROXIMITY_RESULT,
- " reason=" + DozeEvent.reasonToString(reason)
- + " near=" + near
- + " millis=" + millis);
- if (mEnabled) mProxStats[reason][near ? 0 : 1].append();
+ public void traceProximityResult(boolean near, long millis, @Reason int reason) {
+ mLogger.logProximityResult(near, millis, reason);
+ mProxStats[reason][near ? 0 : 1].append();
}
- /**
- * Prints doze log timeline and consolidated stats
- * @param pw
- */
- public void dump(PrintWriter pw) {
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
synchronized (DozeLog.class) {
- super.dump(null, pw, null); // prints timeline
-
pw.print(" Doze summary stats (for ");
TimeUtils.formatDuration(System.currentTimeMillis() - mSince, pw);
pw.println("):");
@@ -237,32 +227,19 @@ public class DozeLog extends SysuiLog<DozeEvent> {
mScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
mScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
mEmergencyCallStats.dump(pw, "Emergency call");
- for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
- final String reason = DozeEvent.reasonToString(i);
+ for (int i = 0; i < TOTAL_REASONS; i++) {
+ final String reason = reasonToString(i);
mProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
mProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
}
}
}
- private void log(@DozeEvent.EventType int eventType) {
- log(eventType, "");
- }
-
- private void log(@DozeEvent.EventType int eventType, String msg) {
- if (mRecycledEvent != null) {
- mRecycledEvent = log(mRecycledEvent.init(eventType, msg));
- } else {
- mRecycledEvent = log(new DozeEvent().init(eventType, msg));
- }
- }
-
/**
* Appends pulse dropped event to logs
*/
public void tracePulseDropped(boolean pulsePending, DozeMachine.State state, boolean blocked) {
- log(DozeEvent.PULSE_DROPPED, "pulsePending=" + pulsePending + " state="
- + state.name() + " blocked=" + blocked);
+ mLogger.logPulseDropped(pulsePending, state, blocked);
}
/**
@@ -270,7 +247,7 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param reason why the pulse was dropped
*/
public void tracePulseDropped(String reason) {
- log(DozeEvent.PULSE_DROPPED, "why=" + reason);
+ mLogger.logPulseDropped(reason);
}
/**
@@ -278,15 +255,23 @@ public class DozeLog extends SysuiLog<DozeEvent> {
* @param disabled
*/
public void tracePulseTouchDisabledByProx(boolean disabled) {
- log(DozeEvent.PULSE_DISABLED_BY_PROX, "disabled=" + disabled);
+ mLogger.logPulseTouchDisabledByProx(disabled);
}
/**
* Appends sensor triggered event to logs
* @param reason why the sensor was triggered
*/
- public void traceSensor(@DozeEvent.Reason int reason) {
- log(DozeEvent.SENSOR_TRIGGERED, "type=" + DozeEvent.reasonToString(reason));
+ public void traceSensor(@Reason int reason) {
+ mLogger.logSensorTriggered(reason);
+ }
+
+ /**
+ * Appends doze suppressed event to the logs
+ * @param suppressedState The {@link DozeMachine.State} that was suppressed
+ */
+ public void traceDozeSuppressed(DozeMachine.State suppressedState) {
+ mLogger.logDozeSuppressed(suppressedState);
}
private class SummaryStats {
@@ -339,6 +324,42 @@ public class DozeLog extends SysuiLog<DozeEvent> {
}
};
- private static final int MAX_DOZE_DEBUG_LOGS = 400;
- private static final int MAX_DOZE_LOGS = 50;
+ /**
+ * Converts the reason (integer) to a user-readable string
+ */
+ public static String reasonToString(@Reason int pulseReason) {
+ switch (pulseReason) {
+ case PULSE_REASON_INTENT: return "intent";
+ case PULSE_REASON_NOTIFICATION: return "notification";
+ case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
+ case REASON_SENSOR_PICKUP: return "pickup";
+ case REASON_SENSOR_DOUBLE_TAP: return "doubletap";
+ case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
+ case PULSE_REASON_DOCKING: return "docking";
+ case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
+ case REASON_SENSOR_WAKE_UP: return "wakeup";
+ case REASON_SENSOR_TAP: return "tap";
+ default: throw new IllegalArgumentException("invalid reason: " + pulseReason);
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({PULSE_REASON_NONE, PULSE_REASON_INTENT, PULSE_REASON_NOTIFICATION,
+ PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP,
+ PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP,
+ PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP})
+ public @interface Reason {}
+ public static final int PULSE_REASON_NONE = -1;
+ public static final int PULSE_REASON_INTENT = 0;
+ public static final int PULSE_REASON_NOTIFICATION = 1;
+ public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
+ public static final int REASON_SENSOR_PICKUP = 3;
+ public static final int REASON_SENSOR_DOUBLE_TAP = 4;
+ public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
+ public static final int PULSE_REASON_DOCKING = 6;
+ public static final int REASON_SENSOR_WAKE_UP = 7;
+ public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
+ public static final int REASON_SENSOR_TAP = 9;
+
+ public static final int TOTAL_REASONS = 10;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
new file mode 100644
index 000000000000..732745a1158b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze
+
+import com.android.systemui.doze.DozeLog.Reason
+import com.android.systemui.doze.DozeLog.reasonToString
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.ERROR
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.DozeLog
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+import javax.inject.Inject
+
+/** Interface for logging messages to the [DozeLog]. */
+class DozeLogger @Inject constructor(
+ @DozeLog private val buffer: LogBuffer
+) {
+ fun logPickupWakeup(isWithinVibrationThreshold: Boolean) {
+ buffer.log(TAG, DEBUG, {
+ bool1 = isWithinVibrationThreshold
+ }, {
+ "PickupWakeup withinVibrationThreshold=$bool1"
+ })
+ }
+
+ fun logPulseStart(@Reason reason: Int) {
+ buffer.log(TAG, INFO, {
+ int1 = reason
+ }, {
+ "Pulse start, reason=${reasonToString(int1)}"
+ })
+ }
+
+ fun logPulseFinish() {
+ buffer.log(TAG, INFO, {}, { "Pulse finish" })
+ }
+
+ fun logNotificationPulse() {
+ buffer.log(TAG, INFO, {}, { "Notification pulse" })
+ }
+
+ fun logDozing(isDozing: Boolean) {
+ buffer.log(TAG, INFO, {
+ bool1 = isDozing
+ }, {
+ "Dozing=$bool1"
+ })
+ }
+
+ fun logFling(
+ expand: Boolean,
+ aboveThreshold: Boolean,
+ thresholdNeeded: Boolean,
+ screenOnFromTouch: Boolean
+ ) {
+ buffer.log(TAG, DEBUG, {
+ bool1 = expand
+ bool2 = aboveThreshold
+ bool3 = thresholdNeeded
+ bool4 = screenOnFromTouch
+ }, {
+ "Fling expand=$bool1 aboveThreshold=$bool2 thresholdNeeded=$bool3 " +
+ "screenOnFromTouch=$bool4"
+ })
+ }
+
+ fun logEmergencyCall() {
+ buffer.log(TAG, INFO, {}, { "Emergency call" })
+ }
+
+ fun logKeyguardBouncerChanged(isShowing: Boolean) {
+ buffer.log(TAG, INFO, {
+ bool1 = isShowing
+ }, {
+ "Keyguard bouncer changed, showing=$bool1"
+ })
+ }
+
+ fun logScreenOn(isPulsing: Boolean) {
+ buffer.log(TAG, INFO, {
+ bool1 = isPulsing
+ }, {
+ "Screen on, pulsing=$bool1"
+ })
+ }
+
+ fun logScreenOff(why: Int) {
+ buffer.log(TAG, INFO, {
+ int1 = why
+ }, {
+ "Screen off, why=$int1"
+ })
+ }
+
+ fun logMissedTick(delay: String) {
+ buffer.log(TAG, ERROR, {
+ str1 = delay
+ }, {
+ "Missed AOD time tick by $str1"
+ })
+ }
+
+ fun logTimeTickScheduled(whenAt: Long, triggerAt: Long) {
+ buffer.log(TAG, DEBUG, {
+ long1 = whenAt
+ long2 = triggerAt
+ }, {
+ "Time tick scheduledAt=${DATE_FORMAT.format(Date(long1))} " +
+ "triggerAt=${DATE_FORMAT.format(Date(long2))}"
+ })
+ }
+
+ fun logKeyguardVisibilityChange(isShowing: Boolean) {
+ buffer.log(TAG, INFO, {
+ bool1 = isShowing
+ }, {
+ "Keyguard visibility change, isShowing=$bool1"
+ })
+ }
+
+ fun logDozeStateChanged(state: DozeMachine.State) {
+ buffer.log(TAG, INFO, {
+ str1 = state.name
+ }, {
+ "Doze state changed to $str1"
+ })
+ }
+
+ fun logWakeDisplay(isAwake: Boolean) {
+ buffer.log(TAG, DEBUG, {
+ bool1 = isAwake
+ }, {
+ "Display wakefulness changed, isAwake=$bool1"
+ })
+ }
+
+ fun logProximityResult(isNear: Boolean, millis: Long, @Reason reason: Int) {
+ buffer.log(TAG, DEBUG, {
+ bool1 = isNear
+ long1 = millis
+ int1 = reason
+ }, {
+ "Proximity result reason=${reasonToString(int1)} near=$bool1 millis=$long1"
+ })
+ }
+
+ fun logPulseDropped(pulsePending: Boolean, state: DozeMachine.State, blocked: Boolean) {
+ buffer.log(TAG, INFO, {
+ bool1 = pulsePending
+ str1 = state.name
+ bool2 = blocked
+ }, {
+ "Pulse dropped, pulsePending=$bool1 state=$str1 blocked=$bool2"
+ })
+ }
+
+ fun logPulseDropped(reason: String) {
+ buffer.log(TAG, INFO, {
+ str1 = reason
+ }, {
+ "Pulse dropped, why=$str1"
+ })
+ }
+
+ fun logPulseTouchDisabledByProx(disabled: Boolean) {
+ buffer.log(TAG, DEBUG, {
+ bool1 = disabled
+ }, {
+ "Pulse touch modified by prox, disabled=$bool1"
+ })
+ }
+
+ fun logSensorTriggered(@Reason reason: Int) {
+ buffer.log(TAG, DEBUG, {
+ int1 = reason
+ }, {
+ "Sensor triggered, type=${reasonToString(int1)}"
+ })
+ }
+
+ fun logDozeSuppressed(state: DozeMachine.State) {
+ buffer.log(TAG, INFO, {
+ str1 = state.name
+ }, {
+ "Doze state suppressed, state=$str1"
+ })
+ }
+}
+
+private const val TAG = "DozeLog"
+
+val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.S", Locale.US)
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 40603ab24c1e..6e81d3a11098 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -102,6 +102,10 @@ public class DozeMachine {
}
}
+ boolean isAlwaysOn() {
+ return this == DOZE_AOD || this == DOZE_AOD_DOCKED;
+ }
+
int screenState(DozeParameters parameters) {
switch (this) {
case UNINITIALIZED:
@@ -169,7 +173,7 @@ public class DozeMachine {
@MainThread
public void requestState(State requestedState) {
Preconditions.checkArgument(requestedState != State.DOZE_REQUEST_PULSE);
- requestState(requestedState, DozeEvent.PULSE_REASON_NONE);
+ requestState(requestedState, DozeLog.PULSE_REASON_NONE);
}
@MainThread
@@ -271,7 +275,7 @@ public class DozeMachine {
if (newState == State.DOZE_REQUEST_PULSE) {
mPulseReason = pulseReason;
} else if (oldState == State.DOZE_PULSE_DONE) {
- mPulseReason = DozeEvent.PULSE_REASON_NONE;
+ mPulseReason = DozeLog.PULSE_REASON_NONE;
}
}
@@ -324,6 +328,11 @@ public class DozeMachine {
if (mState == State.FINISH) {
return State.FINISH;
}
+ if (mConfig.dozeSuppressed(UserHandle.USER_CURRENT) && requestedState.isAlwaysOn()) {
+ Log.i(TAG, "Doze is suppressed. Suppressing state: " + requestedState);
+ mDozeLog.traceDozeSuppressed(requestedState);
+ return State.DOZE;
+ }
if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING
|| mState == State.DOZE_AOD || mState == State.DOZE)
&& requestedState == State.DOZE_PULSE_DONE) {
@@ -368,7 +377,7 @@ public class DozeMachine {
nextState = State.DOZE;
}
- transitionTo(nextState, DozeEvent.PULSE_REASON_NONE);
+ transitionTo(nextState, DozeLog.PULSE_REASON_NONE);
break;
default:
break;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 3abeea91cdee..e50f1fb2a3ee 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -18,7 +18,6 @@ package com.android.systemui.doze;
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
-import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
@@ -90,10 +89,10 @@ public class DozeScreenState implements DozeMachine.Part {
}
final boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
- final boolean pulseEnding = oldState == DOZE_PULSE_DONE && isAlwaysOnState(newState);
+ final boolean pulseEnding = oldState == DOZE_PULSE_DONE && newState.isAlwaysOn();
final boolean turningOn = (oldState == DOZE_AOD_PAUSED || oldState == DOZE)
- && isAlwaysOnState(newState);
- final boolean turningOff = (isAlwaysOnState(oldState) && newState == DOZE)
+ && newState.isAlwaysOn();
+ final boolean turningOff = (newState.isAlwaysOn() && newState == DOZE)
|| (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED);
final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
if (messagePending || justInitialized || pulseEnding || turningOn) {
@@ -132,10 +131,6 @@ public class DozeScreenState implements DozeMachine.Part {
}
}
- private boolean isAlwaysOnState(DozeMachine.State state) {
- return state == DOZE_AOD || state == DOZE_AOD_DOCKED;
- }
-
private void applyPendingScreenState() {
applyScreenState(mPendingScreenState);
mPendingScreenState = Display.STATE_UNKNOWN;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index d008e665d171..44e5d3de5ca7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -97,14 +97,14 @@ public class DozeSensors {
mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
null /* setting */,
dozeParameters.getPulseOnSigMotion(),
- DozeEvent.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */,
+ DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */,
false /* touchscreen */, dozeLog),
mPickupSensor = new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
Settings.Secure.DOZE_PICK_UP_GESTURE,
true /* settingDef */,
config.dozePickupSensorAvailable(),
- DozeEvent.REASON_SENSOR_PICKUP, false /* touchCoords */,
+ DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */,
false /* touchscreen */,
false /* ignoresSetting */,
dozeLog),
@@ -112,7 +112,7 @@ public class DozeSensors {
findSensorWithType(config.doubleTapSensorType()),
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
true /* configured */,
- DozeEvent.REASON_SENSOR_DOUBLE_TAP,
+ DozeLog.REASON_SENSOR_DOUBLE_TAP,
dozeParameters.doubleTapReportsTouchCoordinates(),
true /* touchscreen */,
dozeLog),
@@ -120,7 +120,7 @@ public class DozeSensors {
findSensorWithType(config.tapSensorType()),
Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
true /* configured */,
- DozeEvent.REASON_SENSOR_TAP,
+ DozeLog.REASON_SENSOR_TAP,
false /* reports touch coordinates */,
true /* touchscreen */,
dozeLog),
@@ -129,7 +129,7 @@ public class DozeSensors {
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
false /* settingDef */,
true /* configured */,
- DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS,
+ DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
true /* reports touch coordinates */,
true /* touchscreen */,
dozeLog),
@@ -137,7 +137,7 @@ public class DozeSensors {
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
mConfig.wakeScreenGestureAvailable() && alwaysOn,
- DozeEvent.REASON_SENSOR_WAKE_UP,
+ DozeLog.REASON_SENSOR_WAKE_UP,
false /* reports touch coordinates */,
false /* touchscreen */,
dozeLog),
@@ -145,7 +145,7 @@ public class DozeSensors {
new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
mConfig.wakeScreenGestureAvailable(),
- DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
+ DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
false /* reports touch coordinates */,
false /* touchscreen */,
mConfig.getWakeLockScreenDebounce(),
@@ -525,7 +525,7 @@ public class DozeSensors {
/**
* Called when a sensor requests a pulse
- * @param pulseReason Requesting sensor, e.g. {@link DozeEvent#REASON_SENSOR_PICKUP}
+ * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP}
* @param screenX the location on the screen where the sensor fired or -1
* if the sensor doesn't support reporting screen locations.
* @param screenY the location on the screen where the sensor fired or -1
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressedHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressedHandler.java
new file mode 100644
index 000000000000..3a5c1a0890f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressedHandler.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import static java.util.Objects.requireNonNull;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Handles updating the doze state when doze is suppressed. */
+public final class DozeSuppressedHandler implements DozeMachine.Part {
+
+ private static final String TAG = DozeSuppressedHandler.class.getSimpleName();
+ private static final boolean DEBUG = DozeService.DEBUG;
+
+ private final ContentResolver mResolver;
+ private final AmbientDisplayConfiguration mConfig;
+ private final DozeMachine mMachine;
+ private final DozeSuppressedSettingObserver mSettingObserver;
+ private final Handler mHandler = new Handler();
+
+ public DozeSuppressedHandler(Context context, AmbientDisplayConfiguration config,
+ DozeMachine machine) {
+ this(context, config, machine, null);
+ }
+
+ @VisibleForTesting
+ DozeSuppressedHandler(Context context, AmbientDisplayConfiguration config, DozeMachine machine,
+ DozeSuppressedSettingObserver observer) {
+ mResolver = context.getContentResolver();
+ mConfig = requireNonNull(config);
+ mMachine = requireNonNull(machine);
+ if (observer == null) {
+ mSettingObserver = new DozeSuppressedSettingObserver(mHandler);
+ } else {
+ mSettingObserver = observer;
+ }
+ }
+
+ @Override
+ public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+ switch (newState) {
+ case INITIALIZED:
+ mSettingObserver.register();
+ break;
+ case FINISH:
+ mSettingObserver.unregister();
+ break;
+ default:
+ // no-op
+ }
+ }
+
+ /**
+ * Listens to changes to the DOZE_SUPPRESSED secure setting and updates the doze state
+ * accordingly.
+ */
+ final class DozeSuppressedSettingObserver extends ContentObserver {
+ private boolean mRegistered;
+
+ private DozeSuppressedSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ if (userId != ActivityManager.getCurrentUser()) {
+ return;
+ }
+ final DozeMachine.State nextState;
+ if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)
+ && !mConfig.dozeSuppressed(UserHandle.USER_CURRENT)) {
+ nextState = DozeMachine.State.DOZE_AOD;
+ } else {
+ nextState = DozeMachine.State.DOZE;
+ }
+ mMachine.requestState(nextState);
+ }
+
+ void register() {
+ if (mRegistered) {
+ return;
+ }
+ mResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.SUPPRESS_DOZE),
+ false, this, UserHandle.USER_CURRENT);
+ Log.d(TAG, "Register");
+ mRegistered = true;
+ }
+
+ void unregister() {
+ if (!mRegistered) {
+ return;
+ }
+ mResolver.unregisterContentObserver(this);
+ Log.d(TAG, "Unregister");
+ mRegistered = false;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 722dc038f853..305a4c870d91 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -127,7 +127,12 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
}
- requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
+ if (mConfig.dozeSuppressed(UserHandle.USER_CURRENT)) {
+ runIfNotNull(onPulseSuppressedListener);
+ mDozeLog.tracePulseDropped("dozeSuppressed");
+ return;
+ }
+ requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
onPulseSuppressedListener);
mDozeLog.traceNotificationPulse();
}
@@ -163,12 +168,12 @@ public class DozeTriggers implements DozeMachine.Part {
@VisibleForTesting
void onSensor(int pulseReason, float screenX, float screenY, float[] rawValues) {
- boolean isDoubleTap = pulseReason == DozeEvent.REASON_SENSOR_DOUBLE_TAP;
- boolean isTap = pulseReason == DozeEvent.REASON_SENSOR_TAP;
- boolean isPickup = pulseReason == DozeEvent.REASON_SENSOR_PICKUP;
- boolean isLongPress = pulseReason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS;
- boolean isWakeDisplay = pulseReason == DozeEvent.REASON_SENSOR_WAKE_UP;
- boolean isWakeLockScreen = pulseReason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+ boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP;
+ boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
+ boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
+ boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
+ boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+ boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
if (isWakeDisplay) {
@@ -281,9 +286,9 @@ public class DozeTriggers implements DozeMachine.Part {
// Logs AOD open due to sensor wake up.
mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
.setType(MetricsEvent.TYPE_OPEN)
- .setSubtype(DozeEvent.REASON_SENSOR_WAKE_UP));
+ .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
}
- }, true /* alreadyPerformedProxCheck */, DozeEvent.REASON_SENSOR_WAKE_UP);
+ }, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
} else {
boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
@@ -292,7 +297,7 @@ public class DozeTriggers implements DozeMachine.Part {
// Logs AOD close due to sensor wake up.
mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
.setType(MetricsEvent.TYPE_CLOSE)
- .setSubtype(DozeEvent.REASON_SENSOR_WAKE_UP));
+ .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
}
}
}
@@ -361,7 +366,7 @@ public class DozeTriggers implements DozeMachine.Part {
// When already pulsing we're allowed to show the wallpaper directly without
// requesting a new pulse.
if (mMachine.getState() == DozeMachine.State.DOZE_PULSING
- && reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
mMachine.requestState(DozeMachine.State.DOZE_PULSING_BRIGHT);
return;
}
@@ -426,7 +431,7 @@ public class DozeTriggers implements DozeMachine.Part {
public void onReceive(Context context, Intent intent) {
if (PULSE_ACTION.equals(intent.getAction())) {
if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
- requestPulse(DozeEvent.PULSE_REASON_INTENT, false, /* performedProxCheck */
+ requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
null /* onPulseSupressedListener */);
}
if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index a6aa90916c25..1c056215f1cb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -100,7 +100,7 @@ public class DozeUi implements DozeMachine.Part {
public void onPulseStarted() {
try {
mMachine.requestState(
- reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
+ reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
? DozeMachine.State.DOZE_PULSING_BRIGHT
: DozeMachine.State.DOZE_PULSING);
} catch (IllegalStateException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 37351985b3bd..45c07a3e4693 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -40,6 +40,7 @@ import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
@@ -93,13 +94,13 @@ import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.BlurUtils;
-import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.EmergencyDialerConstants;
@@ -183,6 +184,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final IStatusBarService mStatusBarService;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private GlobalActionsPanelPlugin mPanelPlugin;
+ private ControlsUiController mControlsUiController;
/**
* @param context everything needs a context :(
@@ -200,7 +202,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
TelecomManager telecomManager, MetricsLogger metricsLogger,
BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
- NotificationShadeWindowController notificationShadeWindowController) {
+ NotificationShadeWindowController notificationShadeWindowController,
+ ControlsUiController controlsUiController) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -220,6 +223,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mControlsUiController = controlsUiController;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -457,7 +461,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
mBlurUtils, mSysuiColorExtractor, mStatusBarService,
- mNotificationShadeWindowController, isControlsEnabled(mContext));
+ mNotificationShadeWindowController,
+ shouldShowControls() ? mControlsUiController : null);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.setKeyguardShowing(mKeyguardShowing);
@@ -531,7 +536,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@Override
public boolean shouldBeSeparated() {
- return !isControlsEnabled(mContext);
+ return !shouldShowControls();
}
@Override
@@ -1128,7 +1133,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
* A single press action maintains no state, just responds to a press
* and takes an action.
*/
- private static abstract class SinglePressAction implements Action {
+
+ private abstract class SinglePressAction implements Action {
private final int mIconResId;
private final Drawable mIcon;
private final int mMessageResId;
@@ -1167,7 +1173,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
protected int getActionLayoutId(Context context) {
- if (isControlsEnabled(context)) {
+ if (shouldShowControls()) {
return com.android.systemui.R.layout.global_actions_grid_item_v2;
}
return com.android.systemui.R.layout.global_actions_grid_item;
@@ -1543,13 +1549,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private boolean mHadTopUi;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final BlurUtils mBlurUtils;
- private final boolean mControlsEnabled;
+
+ private ControlsUiController mControlsUiController;
+ private ViewGroup mControlsView;
ActionsDialog(Context context, MyAdapter adapter,
GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- boolean controlsEnabled) {
+ ControlsUiController controlsUiController) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
mContext = context;
mAdapter = adapter;
@@ -1557,7 +1565,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
- mControlsEnabled = controlsEnabled;
+ mControlsUiController = controlsUiController;
// Window initialization
Window window = getWindow();
@@ -1577,7 +1585,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- window.setFitWindowInsetsTypes(0 /* types */);
+ window.getAttributes().setFitInsetsTypes(0 /* types */);
setTitle(R.string.global_actions);
mPanelController = plugin;
@@ -1631,14 +1639,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
panelContainer.addView(mPanelController.getPanelContent(), panelParams);
- mBackgroundDrawable = mPanelController.getBackgroundDrawable();
- mScrimAlpha = 1f;
}
}
private void initializeLayout() {
setContentView(getGlobalActionsLayoutId(mContext));
fixNavBarClipping();
+ mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls);
mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view);
mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
((View) mGlobalActionsLayout.getParent()).setOnClickListener(view -> dismiss());
@@ -1659,7 +1666,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
if (mBackgroundDrawable == null) {
mBackgroundDrawable = new ScrimDrawable();
- mScrimAlpha = ScrimController.GRADIENT_SCRIM_ALPHA;
+ mScrimAlpha = 0.8f;
}
getWindow().setBackgroundDrawable(mBackgroundDrawable);
}
@@ -1674,7 +1681,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
private int getGlobalActionsLayoutId(Context context) {
- if (mControlsEnabled) {
+ if (mControlsUiController != null) {
return com.android.systemui.R.layout.global_actions_grid_v2;
}
@@ -1718,7 +1725,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
if (!(mBackgroundDrawable instanceof ScrimDrawable)) {
return;
}
- ((ScrimDrawable) mBackgroundDrawable).setColor(colors.getMainColor(), animate);
+ ((ScrimDrawable) mBackgroundDrawable).setColor(Color.BLACK, animate);
View decorView = getWindow().getDecorView();
if (colors.supportsDarkText()) {
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
@@ -1758,6 +1765,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mBlurUtils.radiusForRatio(animatedValue));
})
.start();
+ if (mControlsUiController != null) {
+ mControlsUiController.show(mControlsView);
+ }
}
@Override
@@ -1766,6 +1776,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return;
}
mShowing = false;
+ if (mControlsUiController != null) mControlsUiController.hide();
mGlobalActionsLayout.setTranslationX(0);
mGlobalActionsLayout.setTranslationY(0);
mGlobalActionsLayout.setAlpha(1);
@@ -1790,6 +1801,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
void dismissImmediately() {
mShowing = false;
+ if (mControlsUiController != null) mControlsUiController.hide();
dismissPanel();
resetOrientation();
completeDismiss();
@@ -1886,8 +1898,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return true;
}
- private static boolean isControlsEnabled(Context context) {
- return Settings.Secure.getInt(
- context.getContentResolver(), "systemui.controls_available", 0) == 1;
+ private boolean shouldShowControls() {
+ return isCurrentUserOwner()
+ && !mKeyguardManager.isDeviceLocked()
+ && Settings.Secure.getInt(mContext.getContentResolver(),
+ "systemui.controls_available", 0) == 1;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c911bf28effd..c9c38d31e865 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -30,17 +30,17 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.internal.R;
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.ScrimDrawable;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -59,6 +59,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension;
+ private final BlurUtils mBlurUtils;
private GlobalActionsPanelPlugin mPlugin;
private final CommandQueue mCommandQueue;
private GlobalActionsDialog mGlobalActionsDialog;
@@ -68,13 +69,14 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
@Inject
public GlobalActionsImpl(Context context, CommandQueue commandQueue,
- Lazy<GlobalActionsDialog> globalActionsDialogLazy) {
+ Lazy<GlobalActionsDialog> globalActionsDialogLazy, BlurUtils blurUtils) {
mContext = context;
mGlobalActionsDialogLazy = globalActionsDialogLazy;
mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mPluginManager = Dependency.get(PluginManager.class);
mCommandQueue = commandQueue;
+ mBlurUtils = blurUtils;
mCommandQueue.addCallback(this);
mPanelExtension = Dependency.get(ExtensionController.class)
.newExtension(GlobalActionsPanelPlugin.class)
@@ -110,7 +112,6 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
@Override
public void showShutdownUi(boolean isReboot, String reason) {
ScrimDrawable background = new ScrimDrawable();
- background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
Dialog d = new Dialog(mContext,
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
@@ -126,7 +127,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
window.getAttributes().height = ViewGroup.LayoutParams.MATCH_PARENT;
window.getAttributes().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- window.setFitWindowInsetsTypes(0 /* types */);
+ window.getAttributes().setFitInsetsTypes(0 /* types */);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
@@ -160,8 +161,13 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
reasonView.setText(rebootReasonMessage);
}
- GradientColors colors = Dependency.get(SysuiColorExtractor.class).getNeutralColors();
- background.setColor(colors.getMainColor(), false);
+ if (mBlurUtils.supportsBlursOnWindows()) {
+ background.setAlpha((int) (ScrimController.GRADIENT_SCRIM_ALPHA_BUSY * 255));
+ mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
+ mBlurUtils.radiusForRatio(1));
+ } else {
+ background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
+ }
d.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index e66a9fadb937..2cc3d9e22a7d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -52,13 +52,13 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIAppComponentFactory;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -68,6 +68,8 @@ import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
+import javax.inject.Inject;
+
/**
* Simple Slice provider that shows the current date.
*/
@@ -108,26 +110,31 @@ public class KeyguardSliceProvider extends SliceProvider implements
private final Handler mHandler;
private final Handler mMediaHandler;
private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
- private DozeParameters mDozeParameters;
+ @Inject
+ public DozeParameters mDozeParameters;
@VisibleForTesting
protected SettableWakeLock mMediaWakeLock;
- @VisibleForTesting
- protected ZenModeController mZenModeController;
+ @Inject
+ public ZenModeController mZenModeController;
private String mDatePattern;
private DateFormat mDateFormat;
private String mLastText;
private boolean mRegistered;
private String mNextAlarm;
- private NextAlarmController mNextAlarmController;
- @VisibleForTesting
- protected AlarmManager mAlarmManager;
- @VisibleForTesting
- protected ContentResolver mContentResolver;
+ @Inject
+ public NextAlarmController mNextAlarmController;
+ @Inject
+ public AlarmManager mAlarmManager;
+ @Inject
+ public ContentResolver mContentResolver;
private AlarmManager.AlarmClockInfo mNextAlarmInfo;
private PendingIntent mPendingIntent;
- protected NotificationMediaManager mMediaManager;
- private StatusBarStateController mStatusBarStateController;
- private KeyguardBypassController mKeyguardBypassController;
+ @Inject
+ public NotificationMediaManager mMediaManager;
+ @Inject
+ public StatusBarStateController mStatusBarStateController;
+ @Inject
+ public KeyguardBypassController mKeyguardBypassController;
private CharSequence mMediaTitle;
private CharSequence mMediaArtist;
protected boolean mDozing;
@@ -188,26 +195,6 @@ public class KeyguardSliceProvider extends SliceProvider implements
mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
}
- /**
- * Initialize dependencies that don't exist during {@link android.content.ContentProvider}
- * instantiation.
- *
- * @param mediaManager {@link NotificationMediaManager} singleton.
- * @param statusBarStateController {@link StatusBarStateController} singleton.
- */
- public void initDependencies(
- NotificationMediaManager mediaManager,
- StatusBarStateController statusBarStateController,
- KeyguardBypassController keyguardBypassController,
- DozeParameters dozeParameters) {
- mMediaManager = mediaManager;
- mMediaManager.addCallback(this);
- mStatusBarStateController = statusBarStateController;
- mStatusBarStateController.addCallback(this);
- mKeyguardBypassController = keyguardBypassController;
- mDozeParameters = dozeParameters;
- }
-
@AnyThread
@Override
public Slice onBindSlice(Uri sliceUri) {
@@ -310,25 +297,19 @@ public class KeyguardSliceProvider extends SliceProvider implements
@Override
public boolean onCreateSliceProvider() {
- if (mContextAvailableCallback != null) {
- mContextAvailableCallback.onContextAvailable(getContext());
- }
+ mContextAvailableCallback.onContextAvailable(getContext());
+ inject();
synchronized (KeyguardSliceProvider.sInstanceLock) {
KeyguardSliceProvider oldInstance = KeyguardSliceProvider.sInstance;
if (oldInstance != null) {
oldInstance.onDestroy();
}
-
- mAlarmManager = getContext().getSystemService(AlarmManager.class);
- mContentResolver = getContext().getContentResolver();
- mNextAlarmController = new NextAlarmControllerImpl(getContext());
- mNextAlarmController.addCallback(this);
- mZenModeController = Dependency.get(ZenModeController.class);
- mZenModeController.addCallback(this);
mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
- mMediaWakeLock = new SettableWakeLock(WakeLock.createPartial(getContext(), "media"),
- "media");
+ mMediaManager.addCallback(this);
+ mStatusBarStateController.addCallback(this);
+ mNextAlarmController.addCallback(this);
+ mZenModeController.addCallback(this);
KeyguardSliceProvider.sInstance = this;
registerClockUpdate();
updateClockLocked();
@@ -337,6 +318,13 @@ public class KeyguardSliceProvider extends SliceProvider implements
}
@VisibleForTesting
+ protected void inject() {
+ SystemUIFactory.getInstance().getRootComponent().inject(this);
+ mMediaWakeLock = new SettableWakeLock(WakeLock.createPartial(getContext(), "media"),
+ "media");
+ }
+
+ @VisibleForTesting
protected void onDestroy() {
synchronized (KeyguardSliceProvider.sInstanceLock) {
mNextAlarmController.removeCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 50a89ad9af10..15e3a0a2e023 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -53,6 +54,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -371,6 +373,7 @@ public class KeyguardViewMediator extends SystemUI {
private boolean mPulsing;
private boolean mLockLater;
+ private boolean mShowHomeOverLockscreen;
private boolean mWakeAndUnlocking;
private IKeyguardDrawnCallback mDrawnCallback;
@@ -703,6 +706,20 @@ public class KeyguardViewMediator extends SystemUI {
mStatusBarKeyguardViewManagerLazy = statusBarKeyguardViewManagerLazy;
mDismissCallbackRegistry = dismissCallbackRegistry;
mUiBgExecutor = uiBgExecutor;
+ mShowHomeOverLockscreen = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
+ /* defaultValue = */ true);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (properties.getKeyset().contains(NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN)) {
+ mShowHomeOverLockscreen = properties.getBoolean(
+ NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, true /* defaultValue */);
+ }
+ }
+ });
}
public void userActivity() {
@@ -1973,7 +1990,10 @@ public class KeyguardViewMediator extends SystemUI {
// windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
- flags |= StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT;
+ if (!mShowHomeOverLockscreen) {
+ flags |= StatusBarManager.DISABLE_HOME;
+ }
+ flags |= StatusBarManager.DISABLE_RECENT;
}
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
new file mode 100644
index 000000000000..18c7baec1f74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import android.util.Log
+import com.android.systemui.DumpController
+import com.android.systemui.Dumpable
+import com.android.systemui.log.dagger.LogModule
+import java.text.SimpleDateFormat
+import java.util.ArrayDeque
+import java.util.Locale
+
+/**
+ * A simple ring buffer of recyclable log messages
+ *
+ * The goal of this class is to enable logging that is both extremely chatty and extremely
+ * lightweight. If done properly, logging a message will not result in any heap allocations or
+ * string generation. Messages are only converted to strings if the log is actually dumped (usually
+ * as the result of taking a bug report).
+ *
+ * You can dump the entire buffer at any time by running:
+ *
+ * ```
+ * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService \
+ * dependency DumpController <bufferName>
+ * ```
+ *
+ * where `bufferName` is the (case-sensitive) [name] passed to the constructor.
+ *
+ * By default, only messages of WARN level or higher are echoed to logcat, but this can be adjusted
+ * locally (usually for debugging purposes).
+ *
+ * To enable logcat echoing for an entire buffer:
+ *
+ * ```
+ * $ adb shell settings put global systemui/buffer/<bufferName> <level>
+ * ```
+ *
+ * To enable logcat echoing for a specific tag:
+ *
+ * ```
+ * $ adb shell settings put global systemui/tag/<tag> <level>
+ * ```
+ *
+ * In either case, `level` can be any of `verbose`, `debug`, `info`, `warn`, `error`, `assert`, or
+ * the first letter of any of the previous.
+ *
+ * Buffers are provided by [LogModule].
+ *
+ * @param name The name of this buffer
+ * @param maxLogs The maximum number of messages to keep in memory at any one time, including the
+ * unused pool.
+ * @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to
+ * sequential calls to [document] that aren't immediately followed by a matching call to [push].
+ */
+class LogBuffer(
+ private val name: String,
+ private val maxLogs: Int,
+ private val poolSize: Int,
+ private val logcatEchoTracker: LogcatEchoTracker
+) {
+ private val buffer: ArrayDeque<LogMessageImpl> = ArrayDeque()
+
+ fun attach(dumpController: DumpController) {
+ dumpController.registerDumpable(name, onDump)
+ }
+
+ /**
+ * Logs a message to the log buffer
+ *
+ * May also log the message to logcat if echoing is enabled for this buffer or tag.
+ *
+ * The actual string of the log message is not constructed until it is needed. To accomplish
+ * this, logging a message is a two-step process. First, a fresh instance of [LogMessage] is
+ * obtained and is passed to the [initializer]. The initializer stores any relevant data on the
+ * message's fields. The message is then inserted into the buffer where it waits until it is
+ * either pushed out by newer messages or it needs to printed. If and when this latter moment
+ * occurs, the [printer] function is called on the message. It reads whatever data the
+ * initializer stored and converts it to a human-readable log message.
+ *
+ * @param tag A string of at most 23 characters, used for grouping logs into categories or
+ * subjects. If this message is echoed to logcat, this will be the tag that is used.
+ * @param level Which level to log the message at, both to the buffer and to logcat if it's
+ * echoed. In general, a module should split most of its logs into either INFO or DEBUG level.
+ * INFO level should be reserved for information that other parts of the system might care
+ * about, leaving the specifics of code's day-to-day operations to DEBUG.
+ * @param initializer A function that will be called immediately to store relevant data on the
+ * log message. The value of `this` will be the LogMessage to be initialized.
+ * @param printer A function that will be called if and when the message needs to be dumped to
+ * logcat or a bug report. It should read the data stored by the initializer and convert it to
+ * a human-readable string. The value of `this` will be the LogMessage to be printed.
+ * **IMPORTANT:** The printer should ONLY ever reference fields on the LogMessage and NEVER any
+ * variables in its enclosing scope. Otherwise, the runtime will need to allocate a new instance
+ * of the printer for each call, thwarting our attempts at avoiding any sort of allocation.
+ */
+ inline fun log(
+ tag: String,
+ level: LogLevel,
+ initializer: LogMessage.() -> Unit,
+ noinline printer: LogMessage.() -> String
+ ) {
+ val message = obtain(tag, level, printer)
+ initializer(message)
+ push(message)
+ }
+
+ /**
+ * Same as [log], but doesn't push the message to the buffer. Useful if you need to supply a
+ * "reason" for doing something (the thing you supply the reason to will presumably call [push]
+ * on that message at some point).
+ */
+ inline fun document(
+ tag: String,
+ level: LogLevel,
+ initializer: LogMessage.() -> Unit,
+ noinline printer: LogMessage.() -> String
+ ): LogMessage {
+ val message = obtain(tag, level, printer)
+ initializer(message)
+ return message
+ }
+
+ /**
+ * Obtains an instance of [LogMessageImpl], usually from the object pool. If the pool has been
+ * exhausted, creates a new instance.
+ *
+ * In general, you should call [log] or [document] instead of this method.
+ */
+ fun obtain(
+ tag: String,
+ level: LogLevel,
+ printer: (LogMessage) -> String
+ ): LogMessageImpl {
+ val message = synchronized(buffer) {
+ if (buffer.size > maxLogs - poolSize) {
+ buffer.removeFirst()
+ } else {
+ LogMessageImpl.create()
+ }
+ }
+ message.reset(tag, level, System.currentTimeMillis(), printer)
+ return message
+ }
+
+ /**
+ * Pushes a message into buffer, possibly evicting an older message if the buffer is full.
+ */
+ fun push(message: LogMessage) {
+ synchronized(buffer) {
+ if (buffer.size == maxLogs) {
+ Log.e(TAG, "LogBuffer $name has exceeded its pool size")
+ buffer.removeFirst()
+ }
+ buffer.add(message as LogMessageImpl)
+ if (logcatEchoTracker.isBufferLoggable(name, message.level) ||
+ logcatEchoTracker.isTagLoggable(message.tag, message.level)) {
+ echoToLogcat(message)
+ }
+ }
+ }
+
+ /** Converts the entire buffer to a newline-delimited string */
+ fun dump(): String {
+ synchronized(buffer) {
+ val sb = StringBuilder()
+ for (message in buffer) {
+ dumpMessage(message, sb)
+ }
+ return sb.toString()
+ }
+ }
+
+ private fun dumpMessage(message: LogMessage, sb: StringBuilder) {
+ sb.append(DATE_FORMAT.format(message.timestamp))
+ .append(" ").append(message.level)
+ .append(" ").append(message.tag)
+ .append(" ").append(message.printer(message))
+ .append("\n")
+ }
+
+ private fun echoToLogcat(message: LogMessage) {
+ val strMessage = message.printer(message)
+ when (message.level) {
+ LogLevel.VERBOSE -> Log.v(message.tag, strMessage)
+ LogLevel.DEBUG -> Log.d(message.tag, strMessage)
+ LogLevel.INFO -> Log.i(message.tag, strMessage)
+ LogLevel.WARNING -> Log.w(message.tag, strMessage)
+ LogLevel.ERROR -> Log.e(message.tag, strMessage)
+ LogLevel.WTF -> Log.wtf(message.tag, strMessage)
+ }
+ }
+
+ private val onDump = Dumpable { _, pw, _ ->
+ pw.println(dump())
+ }
+}
+
+private const val TAG = "LogBuffer"
+private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.S", Locale.US)
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt b/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
new file mode 100644
index 000000000000..7b9af0f91200
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import android.util.Log
+
+/**
+ * Enum version of @Log.Level
+ */
+enum class LogLevel(@Log.Level val nativeLevel: Int) {
+ VERBOSE(Log.VERBOSE),
+ DEBUG(Log.DEBUG),
+ INFO(Log.INFO),
+ WARNING(Log.WARN),
+ ERROR(Log.ERROR),
+ WTF(Log.ASSERT)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
new file mode 100644
index 000000000000..2a0a2aa6fb38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+/**
+ * Generic data class for storing messages logged to a [LogBuffer]
+ *
+ * Each LogMessage has a few standard fields ([level], [tag], and [timestamp]). The rest are generic
+ * data slots that may or may not be used, depending on the nature of the specific message being
+ * logged.
+ *
+ * When a message is logged, the code doing the logging stores data in one or more of the generic
+ * fields ([str1], [int1], etc). When it comes time to dump the message to logcat/bugreport/etc, the
+ * [printer] function reads the data stored in the generic fields and converts that to a human-
+ * readable string. Thus, for every log type there must be a specialized initializer function that
+ * stores data specific to that log type and a specialized printer function that prints that data.
+ *
+ * See [LogBuffer.log] for more information.
+ */
+interface LogMessage {
+ val level: LogLevel
+ val tag: String
+ val timestamp: Long
+ val printer: LogMessage.() -> String
+
+ var str1: String?
+ var str2: String?
+ var str3: String?
+ var int1: Int
+ var int2: Int
+ var long1: Long
+ var long2: Long
+ var double1: Double
+ var bool1: Boolean
+ var bool2: Boolean
+ var bool3: Boolean
+ var bool4: Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt b/packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt
new file mode 100644
index 000000000000..d33ac4b4a80b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+/**
+ * Recyclable implementation of [LogMessage].
+ */
+data class LogMessageImpl(
+ override var level: LogLevel,
+ override var tag: String,
+ override var timestamp: Long,
+ override var printer: LogMessage.() -> String,
+ override var str1: String?,
+ override var str2: String?,
+ override var str3: String?,
+ override var int1: Int,
+ override var int2: Int,
+ override var long1: Long,
+ override var long2: Long,
+ override var double1: Double,
+ override var bool1: Boolean,
+ override var bool2: Boolean,
+ override var bool3: Boolean,
+ override var bool4: Boolean
+) : LogMessage {
+
+ fun reset(
+ tag: String,
+ level: LogLevel,
+ timestamp: Long,
+ renderer: LogMessage.() -> String
+ ) {
+ this.level = level
+ this.tag = tag
+ this.timestamp = timestamp
+ this.printer = renderer
+ str1 = null
+ str2 = null
+ str3 = null
+ int1 = 0
+ int2 = 0
+ long1 = 0
+ long2 = 0
+ double1 = 0.0
+ bool1 = false
+ bool2 = false
+ bool3 = false
+ bool4 = false
+ }
+
+ companion object Factory {
+ fun create(): LogMessageImpl {
+ return LogMessageImpl(
+ LogLevel.DEBUG,
+ DEFAULT_TAG,
+ 0,
+ DEFAULT_RENDERER,
+ null,
+ null,
+ null,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0.0,
+ false,
+ false,
+ false,
+ false)
+ }
+ }
+}
+
+private const val DEFAULT_TAG = "UnknownTag"
+private val DEFAULT_RENDERER: LogMessage.() -> String = { "Unknown message: $this" }
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt
new file mode 100644
index 000000000000..3022f4b42a42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+/**
+ * Keeps track of which [LogBuffer] messages should also appear in logcat.
+ */
+interface LogcatEchoTracker {
+ /**
+ * Whether [bufferName] should echo messages of [level] or higher to logcat.
+ */
+ fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean
+
+ /**
+ * Whether [tagName] should echo messages of [level] or higher to logcat.
+ */
+ fun isTagLoggable(tagName: String, level: LogLevel): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
new file mode 100644
index 000000000000..23942e1d6e3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+
+/**
+ * Version of [LogcatEchoTracker] for debuggable builds
+ *
+ * The log level of individual buffers or tags can be controlled via global settings:
+ *
+ * ```
+ * # Echo any message to <bufferName> of <level> or higher
+ * $ adb shell settings put global systemui/buffer/<bufferName> <level>
+ *
+ * # Echo any message of <tag> and of <level> or higher
+ * $ adb shell settings put global systemui/tag/<tag> <level>
+ * ```
+ */
+class LogcatEchoTrackerDebug private constructor(
+ private val contentResolver: ContentResolver
+) : LogcatEchoTracker {
+ private val cachedBufferLevels: MutableMap<String, LogLevel> = mutableMapOf()
+ private val cachedTagLevels: MutableMap<String, LogLevel> = mutableMapOf()
+
+ companion object Factory {
+ @JvmStatic
+ fun create(
+ contentResolver: ContentResolver,
+ mainLooper: Looper
+ ): LogcatEchoTrackerDebug {
+ val tracker = LogcatEchoTrackerDebug(contentResolver)
+ tracker.attach(mainLooper)
+ return tracker
+ }
+ }
+
+ private fun attach(mainLooper: Looper) {
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(BUFFER_PATH),
+ true,
+ object : ContentObserver(Handler(mainLooper)) {
+ override fun onChange(selfChange: Boolean, uri: Uri) {
+ super.onChange(selfChange, uri)
+ cachedBufferLevels.clear()
+ }
+ })
+
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(TAG_PATH),
+ true,
+ object : ContentObserver(Handler(mainLooper)) {
+ override fun onChange(selfChange: Boolean, uri: Uri) {
+ super.onChange(selfChange, uri)
+ cachedTagLevels.clear()
+ }
+ })
+ }
+
+ /**
+ * Whether [bufferName] should echo messages of [level] or higher to logcat.
+ */
+ @Synchronized
+ override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean {
+ return level.ordinal >= getLogLevel(bufferName, BUFFER_PATH, cachedBufferLevels).ordinal
+ }
+
+ /**
+ * Whether [tagName] should echo messages of [level] or higher to logcat.
+ */
+ @Synchronized
+ override fun isTagLoggable(tagName: String, level: LogLevel): Boolean {
+ return level >= getLogLevel(tagName, TAG_PATH, cachedTagLevels)
+ }
+
+ private fun getLogLevel(
+ name: String,
+ path: String,
+ cache: MutableMap<String, LogLevel>
+ ): LogLevel {
+ return cache[name] ?: readSetting("$path/$name").also { cache[name] = it }
+ }
+
+ private fun readSetting(path: String): LogLevel {
+ return try {
+ parseProp(Settings.Global.getString(contentResolver, path))
+ } catch (_: Settings.SettingNotFoundException) {
+ DEFAULT_LEVEL
+ }
+ }
+
+ private fun parseProp(propValue: String?): LogLevel {
+ return when (propValue?.toLowerCase()) {
+ "verbose" -> LogLevel.VERBOSE
+ "v" -> LogLevel.VERBOSE
+ "debug" -> LogLevel.DEBUG
+ "d" -> LogLevel.DEBUG
+ "info" -> LogLevel.INFO
+ "i" -> LogLevel.INFO
+ "warning" -> LogLevel.WARNING
+ "warn" -> LogLevel.WARNING
+ "w" -> LogLevel.WARNING
+ "error" -> LogLevel.ERROR
+ "e" -> LogLevel.ERROR
+ "assert" -> LogLevel.WTF
+ "wtf" -> LogLevel.WTF
+ else -> DEFAULT_LEVEL
+ }
+ }
+}
+
+private val DEFAULT_LEVEL = LogLevel.WARNING
+private const val BUFFER_PATH = "systemui/buffer"
+private const val TAG_PATH = "systemui/tag"
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
new file mode 100644
index 000000000000..394f624a3e58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+/**
+ * Production version of [LogcatEchoTracker] that isn't configurable.
+ */
+class LogcatEchoTrackerProd : LogcatEchoTracker {
+ override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean {
+ return level >= LogLevel.WARNING
+ }
+
+ override fun isTagLoggable(tagName: String, level: LogLevel): Boolean {
+ return level >= LogLevel.WARNING
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
index 4e15668f6a34..9ee3e6765e4a 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
@@ -48,7 +48,7 @@ import java.util.Locale;
*/
public class SysuiLog<E extends Event> implements Dumpable {
public static final SimpleDateFormat DATE_FORMAT =
- new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US);
+ new SimpleDateFormat("MM-dd HH:mm:ss.S", Locale.US);
protected final Object mDataLock = new Object();
private final String mId;
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java
new file mode 100644
index 000000000000..7c5f4025117f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for dozing-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface DozeLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
new file mode 100644
index 000000000000..e119beff3c6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import android.content.ContentResolver;
+import android.os.Build;
+import android.os.Looper;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.LogcatEchoTracker;
+import com.android.systemui.log.LogcatEchoTrackerDebug;
+import com.android.systemui.log.LogcatEchoTrackerProd;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger module for providing instances of {@link LogBuffer}.
+ */
+@Module
+public class LogModule {
+ /** Provides a logging buffer for doze-related logs. */
+ @Provides
+ @Singleton
+ @DozeLog
+ public static LogBuffer provideDozeLogBuffer(
+ LogcatEchoTracker bufferFilter,
+ DumpController dumpController) {
+ LogBuffer buffer = new LogBuffer("DozeLog", 100, 10, bufferFilter);
+ buffer.attach(dumpController);
+ return buffer;
+ }
+
+ /** Provides a logging buffer for all logs related to the data layer of notifications. */
+ @Provides
+ @Singleton
+ @NotificationLog
+ public static LogBuffer provideNotificationsLogBuffer(
+ LogcatEchoTracker bufferFilter,
+ DumpController dumpController) {
+ LogBuffer buffer = new LogBuffer("NotifLog2", 1000, 10, bufferFilter);
+ buffer.attach(dumpController);
+ return buffer;
+ }
+
+ /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
+ @Provides
+ @Singleton
+ public static LogcatEchoTracker provideLogcatEchoTracker(
+ ContentResolver contentResolver,
+ @Main Looper looper) {
+ if (Build.IS_DEBUGGABLE) {
+ return LogcatEchoTrackerDebug.create(contentResolver, looper);
+ } else {
+ return new LogcatEchoTrackerProd();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java
new file mode 100644
index 000000000000..a0b686487bec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for notification-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface NotificationLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
index d1d9b3de7e69..92aa02050d28 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
@@ -20,13 +20,13 @@ import android.content.Context;
import android.content.res.Configuration;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.wm.DisplayWindowController;
+import com.android.systemui.wm.DisplayController;
import java.io.PrintWriter;
public interface BasePipManager {
void initialize(Context context, BroadcastDispatcher broadcastDispatcher,
- DisplayWindowController displayWindowController);
+ DisplayController displayController);
void showPictureInPictureMenu();
default void expandPip() {}
default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 8e34a90e7b51..6f03f18ef64b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -261,8 +261,6 @@ public class PipBoundsHandler {
mPinnedStackController.startAnimation(destinationBounds, sourceRectHint,
-1 /* animationDuration */);
mLastDestinationBounds.set(destinationBounds);
- mPinnedStackController.reportBounds(defaultBounds,
- getMovementBounds(defaultBounds));
} catch (RemoteException e) {
Log.e(TAG, "Failed to start PiP animation from SysUI", e);
}
@@ -317,7 +315,6 @@ public class PipBoundsHandler {
outBounds.set(postChangeStackBounds);
mLastDestinationBounds.set(outBounds);
mPinnedStackController.resetBoundsAnimation(outBounds);
- mPinnedStackController.reportBounds(outBounds, getMovementBounds(outBounds));
t.setBounds(pinnedStackInfo.stackToken, outBounds);
} catch (RemoteException e) {
Log.e(TAG, "Failed to resize PiP on display rotation", e);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 29de90bb2824..cecdc9cbb4f3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -28,7 +28,7 @@ import android.os.UserManager;
import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.wm.DisplayWindowController;
+import com.android.systemui.wm.DisplayController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -45,17 +45,17 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks {
private final CommandQueue mCommandQueue;
private BasePipManager mPipManager;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final DisplayWindowController mDisplayWindowController;
+ private final DisplayController mDisplayController;
private boolean mSupportsPip;
@Inject
public PipUI(Context context, CommandQueue commandQueue,
BroadcastDispatcher broadcastDispatcher,
- DisplayWindowController displayWindowController) {
+ DisplayController displayController) {
super(context);
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
- mDisplayWindowController = displayWindowController;
+ mDisplayController = displayController;
}
@Override
@@ -75,7 +75,7 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks {
mPipManager = pm.hasSystemFeature(FEATURE_LEANBACK_ONLY)
? com.android.systemui.pip.tv.PipManager.getInstance()
: com.android.systemui.pip.phone.PipManager.getInstance();
- mPipManager.initialize(mContext, mBroadcastDispatcher, mDisplayWindowController);
+ mPipManager.initialize(mContext, mBroadcastDispatcher, mDisplayController);
mCommandQueue.addCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 750cc607abe3..b7258117c48c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -109,7 +109,7 @@ public class PipDismissViewController {
lp.setTitle("pip-dismiss-overlay");
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.setFitWindowInsetsTypes(0 /* types */);
+ lp.setFitInsetsTypes(0 /* types */);
mWindowManager.addView(mDismissView, lp);
}
mDismissView.animate().cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index f39d1ecb0585..239ef3638ff6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -46,7 +46,8 @@ import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.wm.DisplayWindowController;
+import com.android.systemui.wm.DisplayChangeController;
+import com.android.systemui.wm.DisplayController;
import java.io.PrintWriter;
@@ -79,7 +80,7 @@ public class PipManager implements BasePipManager {
/**
* Handler for display rotation changes.
*/
- private final DisplayWindowController.OnDisplayWindowRotationController mRotationController = (
+ private final DisplayChangeController.OnDisplayChangingListener mRotationController = (
int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> {
final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mTmpNormalBounds,
displayId, fromRotation, toRotation, t);
@@ -230,7 +231,7 @@ public class PipManager implements BasePipManager {
* Initializes {@link PipManager}.
*/
public void initialize(Context context, BroadcastDispatcher broadcastDispatcher,
- DisplayWindowController displayWindowController) {
+ DisplayController displayController) {
mContext = context;
mActivityManager = ActivityManager.getService();
mActivityTaskManager = ActivityTaskManager.getService();
@@ -251,7 +252,7 @@ public class PipManager implements BasePipManager {
mMenuController, mInputConsumerController, mPipBoundsHandler);
mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
mTouchHandler.getMotionHelper());
- displayWindowController.addRotationController(mRotationController);
+ displayController.addDisplayChangingController(mRotationController);
// If SystemUI restart, and it already existed a pinned stack,
// register the pip input consumer to ensure touch can send to it.
@@ -297,6 +298,13 @@ public class PipManager implements BasePipManager {
}
/**
+ * Sets a customized touch gesture that replaces the default one.
+ */
+ public void setTouchGesture(PipTouchGesture gesture) {
+ mTouchHandler.setTouchGesture(gesture);
+ }
+
+ /**
* Sets both shelf visibility and its height.
*/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java
index e8e8a4d3215a..72335dbed115 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java
@@ -24,19 +24,19 @@ public abstract class PipTouchGesture {
/**
* Handle the touch down.
*/
- void onDown(PipTouchState touchState) {}
+ public void onDown(PipTouchState touchState) {}
/**
* Handle the touch move, and return whether the event was consumed.
*/
- boolean onMove(PipTouchState touchState) {
+ public boolean onMove(PipTouchState touchState) {
return false;
}
/**
* Handle the touch up, and return whether the gesture was consumed.
*/
- boolean onUp(PipTouchState touchState) {
+ public boolean onUp(PipTouchState touchState) {
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 09f163810d27..65cc666d5164 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -126,8 +126,8 @@ public class PipTouchHandler {
// Touch state
private final PipTouchState mTouchState;
private final FlingAnimationUtils mFlingAnimationUtils;
- private final PipTouchGesture[] mGestures;
private final PipMotionHelper mMotionHelper;
+ private PipTouchGesture mGesture;
// Temp vars
private final Rect mTmpBounds = new Rect();
@@ -185,9 +185,7 @@ public class PipTouchHandler {
mSnapAlgorithm = new PipSnapAlgorithm(mContext);
mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(),
2.5f);
- mGestures = new PipTouchGesture[] {
- mDefaultMovementGesture
- };
+ mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
mTouchState = new PipTouchState(mViewConfig, mHandler,
@@ -210,6 +208,10 @@ public class PipTouchHandler {
this::onAccessibilityShowMenu, mHandler);
}
+ public void setTouchGesture(PipTouchGesture gesture) {
+ mGesture = gesture;
+ }
+
public void setTouchEnabled(boolean enabled) {
mTouchState.setAllowTouches(enabled);
}
@@ -363,17 +365,12 @@ public class PipTouchHandler {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
mMotionHelper.synchronizePinnedStackBounds();
-
- for (PipTouchGesture gesture : mGestures) {
- gesture.onDown(mTouchState);
- }
+ mGesture.onDown(mTouchState);
break;
}
case MotionEvent.ACTION_MOVE: {
- for (PipTouchGesture gesture : mGestures) {
- if (gesture.onMove(mTouchState)) {
- break;
- }
+ if (mGesture.onMove(mTouchState)) {
+ break;
}
shouldDeliverToMenu = !mTouchState.isDragging();
@@ -384,10 +381,8 @@ public class PipTouchHandler {
// dragging (ie. when the IME shows)
updateMovementBounds(mMenuState);
- for (PipTouchGesture gesture : mGestures) {
- if (gesture.onUp(mTouchState)) {
- break;
- }
+ if (mGesture.onUp(mTouchState)) {
+ break;
}
// Fall through to clean up
@@ -591,7 +586,7 @@ public class PipTouchHandler {
/**
* Gesture controlling normal movement of the PIP.
*/
- private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
+ private class DefaultPipTouchGesture extends PipTouchGesture {
// Whether the PiP was on the left side of the screen at the start of the gesture
private boolean mStartedOnLeft;
private final Point mStartPosition = new Point();
@@ -623,7 +618,7 @@ public class PipTouchHandler {
}
@Override
- boolean onMove(PipTouchState touchState) {
+ public boolean onMove(PipTouchState touchState) {
if (!touchState.isUserInteracting()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 1d92375a7541..7532f9f11296 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -55,7 +55,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.wm.DisplayWindowController;
+import com.android.systemui.wm.DisplayController;
import java.util.ArrayList;
import java.util.List;
@@ -230,7 +230,7 @@ public class PipManager implements BasePipManager {
* Initializes {@link PipManager}.
*/
public void initialize(Context context, BroadcastDispatcher broadcastDispatcher,
- DisplayWindowController displayWindowController) {
+ DisplayController displayController) {
if (mInitialized) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
index f66a1ece1868..f710f7fc47e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -68,7 +68,7 @@ class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTil
override fun updateResources(): Boolean {
with(mContext.resources) {
smallTileSize = getDimensionPixelSize(R.dimen.qs_quick_tile_size)
- cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) / 2
+ cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal)
cellMarginVertical = getDimensionPixelSize(R.dimen.new_qs_vertical_margin)
}
requestLayout()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 1077834e7146..9e3e94ce4186 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -36,7 +36,6 @@ import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
-import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -45,7 +44,6 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.RemoteViews;
import android.widget.TextView;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -72,8 +70,6 @@ public class QSMediaPlayer {
private View mSeamless;
private MediaSession.Token mToken;
private MediaController mController;
- private int mWidth;
- private int mHeight;
private int mForegroundColor;
private int mBackgroundColor;
private ComponentName mRecvComponent;
@@ -158,16 +154,11 @@ public class QSMediaPlayer {
*
* @param context
* @param parent
- * @param width
- * @param height
*/
- public QSMediaPlayer(Context context, ViewGroup parent, int width, int height) {
+ public QSMediaPlayer(Context context, ViewGroup parent) {
mContext = context;
LayoutInflater inflater = LayoutInflater.from(mContext);
mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qs_media_panel, parent, false);
-
- mWidth = width;
- mHeight = height;
}
public View getView() {
@@ -217,27 +208,18 @@ public class QSMediaPlayer {
Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
// Album art
- addAlbumArtBackground(mMediaMetadata, bgColor, mWidth, mHeight);
+ addAlbumArt(mMediaMetadata, bgColor);
- // Reuse notification header instead of reimplementing everything
- RemoteViews headerRemoteView = builder.makeNotificationHeader();
LinearLayout headerView = mMediaNotifView.findViewById(R.id.header);
- View result = headerRemoteView.apply(mContext, headerView);
- result.setPadding(0, 0, 0, 0);
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, 75);
- result.setLayoutParams(lp);
- headerView.removeAllViews();
- headerView.addView(result);
// App icon
- ImageView appIcon = headerView.findViewById(com.android.internal.R.id.icon);
+ ImageView appIcon = headerView.findViewById(R.id.icon);
Drawable iconDrawable = icon.loadDrawable(mContext);
iconDrawable.setTint(iconColor);
appIcon.setImageDrawable(iconDrawable);
// App title
- TextView appName = headerView.findViewById(com.android.internal.R.id.app_name_text);
+ TextView appName = headerView.findViewById(R.id.app_name);
String appNameString = builder.loadHeaderAppName();
appName.setText(appNameString);
appName.setTextColor(iconColor);
@@ -254,25 +236,8 @@ public class QSMediaPlayer {
}
});
- // Separator
- TextView separator = headerView.findViewById(com.android.internal.R.id.header_text_divider);
- separator.setTextColor(iconColor);
-
- // Album name
- TextView albumName = headerView.findViewById(com.android.internal.R.id.header_text);
- String albumString = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
- if (TextUtils.isEmpty(albumString)) {
- albumName.setVisibility(View.GONE);
- separator.setVisibility(View.GONE);
- } else {
- albumName.setText(albumString);
- albumName.setTextColor(iconColor);
- albumName.setVisibility(View.VISIBLE);
- separator.setVisibility(View.VISIBLE);
- }
-
// Transfer chip
- mSeamless = headerView.findViewById(com.android.internal.R.id.media_seamless);
+ mSeamless = headerView.findViewById(R.id.media_seamless);
mSeamless.setVisibility(View.VISIBLE);
updateChip(device);
ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
@@ -284,13 +249,13 @@ public class QSMediaPlayer {
});
// Artist name
- TextView artistText = mMediaNotifView.findViewById(R.id.header_title);
+ TextView artistText = headerView.findViewById(R.id.header_artist);
String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
artistText.setText(artistName);
artistText.setTextColor(iconColor);
// Song name
- TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
+ TextView titleText = headerView.findViewById(R.id.header_text);
String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
titleText.setText(songName);
titleText.setTextColor(iconColor);
@@ -363,34 +328,25 @@ public class QSMediaPlayer {
return (state.getState() == PlaybackState.STATE_PLAYING);
}
- private void addAlbumArtBackground(MediaMetadata metadata, int bgColor, int width, int height) {
+ private void addAlbumArt(MediaMetadata metadata, int bgColor) {
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
+ ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
if (albumArt != null) {
Log.d(TAG, "updating album art");
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
- Bitmap scaled = scaleBitmap(original, width, height);
- Canvas canvas = new Canvas(scaled);
-
- // Add translucent layer over album art to improve contrast
- Paint p = new Paint();
- p.setStyle(Paint.Style.FILL);
- p.setColor(bgColor);
- p.setAlpha(200);
- canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p);
-
+ int albumSize = (int) mContext.getResources().getDimension(R.dimen.qs_media_album_size);
+ Bitmap scaled = scaleBitmap(original, albumSize, albumSize);
RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
mContext.getResources(), scaled);
roundedDrawable.setCornerRadius(radius);
-
- mMediaNotifView.setBackground(roundedDrawable);
+ albumView.setImageDrawable(roundedDrawable);
} else {
Log.e(TAG, "No album art available");
- GradientDrawable rect = new GradientDrawable();
- rect.setCornerRadius(radius);
- rect.setColor(bgColor);
- mMediaNotifView.setBackground(rect);
+ albumView.setImageDrawable(null);
}
+
+ mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(bgColor));
}
private Bitmap scaleBitmap(Bitmap original, int width, int height) {
@@ -423,8 +379,8 @@ public class QSMediaPlayer {
rect.setStroke(2, mForegroundColor);
rect.setColor(mBackgroundColor);
- ImageView iconView = mSeamless.findViewById(com.android.internal.R.id.media_seamless_image);
- TextView deviceName = mSeamless.findViewById(com.android.internal.R.id.media_seamless_text);
+ ImageView iconView = mSeamless.findViewById(R.id.media_seamless_image);
+ TextView deviceName = mSeamless.findViewById(R.id.media_seamless_text);
deviceName.setTextColor(fgTintList);
if (device != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 4fcd9b7dd941..7f141a7d5568 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -31,7 +31,6 @@ import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.service.quicksettings.Tile;
import android.util.AttributeSet;
@@ -62,7 +61,6 @@ import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSliderView;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.phone.NPVPluginManager;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
import com.android.systemui.tuner.TunerService;
@@ -97,6 +95,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>();
private LocalMediaManager mLocalMediaManager;
private MediaDevice mDevice;
+ private boolean mUpdateCarousel = false;
protected boolean mExpanded;
protected boolean mListening;
@@ -120,7 +119,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private FrameLayout mPluginFrame;
private final PluginManager mPluginManager;
- private NPVPluginManager mNPVPluginManager;
private final LocalMediaManager.DeviceCallback mDeviceCallback =
new LocalMediaManager.DeviceCallback() {
@@ -187,7 +185,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
HorizontalScrollView mediaScrollView = (HorizontalScrollView) LayoutInflater.from(
mContext).inflate(R.layout.media_carousel, this, false);
mMediaCarousel = mediaScrollView.findViewById(R.id.media_carousel);
- addView(mediaScrollView);
+ addView(mediaScrollView, 0);
} else {
mMediaCarousel = null;
}
@@ -202,14 +200,23 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
updateResources();
mPluginManager = pluginManager;
- if (mPluginManager != null && Settings.System.getInt(
- mContext.getContentResolver(), "npv_plugin_flag", 0) == 2) {
- mPluginFrame = (FrameLayout) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_expanded_plugin_frame, this, false);
- addView(mPluginFrame);
- mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
- }
+ }
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+ if (!isVisible && mUpdateCarousel) {
+ for (QSMediaPlayer player : mMediaPlayers) {
+ if (player.isPlaying()) {
+ LayoutParams lp = (LayoutParams) player.getView().getLayoutParams();
+ mMediaCarousel.removeView(player.getView());
+ mMediaCarousel.addView(player.getView(), 0, lp);
+ ((HorizontalScrollView) mMediaCarousel.getParent()).fullScroll(View.FOCUS_LEFT);
+ mUpdateCarousel = false;
+ break;
+ }
+ }
+ }
}
/**
@@ -249,7 +256,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
}
- int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height);
int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width);
int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
LayoutParams lp = new LayoutParams(playerWidth, ViewGroup.LayoutParams.MATCH_PARENT);
@@ -258,8 +264,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (player == null) {
Log.d(TAG, "creating new player");
-
- player = new QSMediaPlayer(mContext, this, playerWidth, playerHeight);
+ player = new QSMediaPlayer(mContext, this);
if (player.isPlaying()) {
mMediaCarousel.addView(player.getView(), 0, lp); // add in front
@@ -268,9 +273,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
mMediaPlayers.add(player);
} else if (player.isPlaying()) {
- // move it to the front
- mMediaCarousel.removeView(player.getView());
- mMediaCarousel.addView(player.getView(), 0, lp);
+ mUpdateCarousel = true;
}
Log.d(TAG, "setting player session");
@@ -560,7 +563,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (mListening) {
refreshAllTiles();
}
- if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
}
public void setListening(boolean listening, boolean expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index b682cb09b598..476af20b78f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -31,7 +31,6 @@ import android.text.SpannableStringBuilder;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.Log;
-import android.util.StatsLog;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
@@ -41,6 +40,7 @@ import android.view.Window;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -116,13 +116,14 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic
@Override
public void onClick(View v) {
+ if (!hasFooter()) return;
mHandler.sendEmptyMessage(H.CLICK);
}
private void handleClick() {
showDeviceMonitoringDialog();
DevicePolicyEventLogger
- .createEvent(StatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED)
+ .createEvent(FrameworkStatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED)
.write();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index cec1cb2fb53b..9018a375c365 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -23,13 +23,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaController;
@@ -45,9 +39,6 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-import androidx.core.graphics.drawable.RoundedBitmapDrawable;
-import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
-
import com.android.systemui.R;
import java.util.List;
@@ -63,7 +54,6 @@ public class QuickQSMediaPlayer {
private LinearLayout mMediaNotifView;
private MediaSession.Token mToken;
private MediaController mController;
- private int mBackgroundColor;
private int mForegroundColor;
private ComponentName mRecvComponent;
@@ -142,7 +132,6 @@ public class QuickQSMediaPlayer {
View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) {
mToken = token;
mForegroundColor = iconColor;
- mBackgroundColor = bgColor;
String oldPackage = "";
if (mController != null) {
@@ -185,8 +174,7 @@ public class QuickQSMediaPlayer {
}
});
- // Album art
- addAlbumArtBackground(mMediaMetadata, mBackgroundColor);
+ mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(bgColor));
// App icon
ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
@@ -194,14 +182,8 @@ public class QuickQSMediaPlayer {
iconDrawable.setTint(mForegroundColor);
appIcon.setImageDrawable(iconDrawable);
- // Artist name
- TextView appText = mMediaNotifView.findViewById(R.id.header_title);
- String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
- appText.setText(artistName);
- appText.setTextColor(mForegroundColor);
-
// Song name
- TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
+ TextView titleText = mMediaNotifView.findViewById(R.id.header_title);
String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
titleText.setText(songName);
titleText.setTextColor(mForegroundColor);
@@ -277,54 +259,4 @@ public class QuickQSMediaPlayer {
public boolean hasMediaSession() {
return mController != null && mController.getPlaybackState() != null;
}
-
- private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) {
- Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
- float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
- Rect bounds = new Rect();
- mMediaNotifView.getBoundsOnScreen(bounds);
- int width = bounds.width();
- int height = bounds.height();
- if (albumArt != null && width > 0 && height > 0) {
- Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
- Bitmap scaled = scaleBitmap(original, width, height);
- Canvas canvas = new Canvas(scaled);
-
- // Add translucent layer over album art to improve contrast
- Paint p = new Paint();
- p.setStyle(Paint.Style.FILL);
- p.setColor(bgColor);
- p.setAlpha(200);
- canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p);
-
- RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
- mContext.getResources(), scaled);
- roundedDrawable.setCornerRadius(radius);
-
- mMediaNotifView.setBackground(roundedDrawable);
- } else {
- Log.e(TAG, "No album art available");
- GradientDrawable rect = new GradientDrawable();
- rect.setCornerRadius(radius);
- rect.setColor(bgColor);
- mMediaNotifView.setBackground(rect);
- }
- }
-
- private Bitmap scaleBitmap(Bitmap original, int width, int height) {
- Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(cropped);
-
- float scale = (float) cropped.getWidth() / (float) original.getWidth();
- float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f;
- Matrix transformation = new Matrix();
- transformation.postTranslate(0, dy);
- transformation.preScale(scale, scale);
-
- Paint paint = new Paint();
- paint.setFilterBitmap(true);
- canvas.drawBitmap(original, transformation, paint);
-
- return cropped;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index b05d4fdf7db7..e9207016eb68 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -85,20 +85,21 @@ public class QuickQSPanel extends QSPanel {
mHorizontalLinearLayout.setClipChildren(false);
mHorizontalLinearLayout.setClipToPadding(false);
+ int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
+ mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout);
+ LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
+ lp2.setMarginEnd(marginSize);
+ lp2.setMarginStart(0);
+ mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp2);
+
mTileLayout = new DoubleLineTileLayout(context);
mMediaTileLayout = mTileLayout;
mRegularTileLayout = new HeaderTileLayout(context);
LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
- lp.setMarginEnd(10);
- lp.setMarginStart(0);
+ lp.setMarginEnd(0);
+ lp.setMarginStart(marginSize);
mHorizontalLinearLayout.addView((View) mTileLayout, lp);
- mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout);
- LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
- lp2.setMarginEnd(0);
- lp2.setMarginStart(25);
- mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp2);
-
sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
mTileLayout.setListening(mListening);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 37743ec55517..9f59277c918a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs;
+import static com.android.systemui.util.Utils.useQsMediaPlayer;
+
import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
@@ -42,7 +44,8 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
public TileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusableInTouchMode(true);
- mLessRows = Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0;
+ mLessRows = (Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0)
+ || useQsMediaPlayer(context);
updateResources();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 2b53727f237e..554672d88052 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -16,7 +16,6 @@ package com.android.systemui.qs.tileimpl;
import android.content.Context;
import android.os.Build;
-import android.provider.Settings;
import android.util.Log;
import android.view.ContextThemeWrapper;
@@ -33,7 +32,6 @@ import com.android.systemui.qs.tiles.BluetoothTile;
import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
-import com.android.systemui.qs.tiles.ControlsTile;
import com.android.systemui.qs.tiles.DataSaverTile;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.FlashlightTile;
@@ -60,7 +58,6 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<WifiTile> mWifiTileProvider;
private final Provider<BluetoothTile> mBluetoothTileProvider;
- private final Provider<ControlsTile> mControlsTileProvider;
private final Provider<CellularTile> mCellularTileProvider;
private final Provider<DndTile> mDndTileProvider;
private final Provider<ColorInversionTile> mColorInversionTileProvider;
@@ -85,7 +82,6 @@ public class QSFactoryImpl implements QSFactory {
@Inject
public QSFactoryImpl(Provider<WifiTile> wifiTileProvider,
Provider<BluetoothTile> bluetoothTileProvider,
- Provider<ControlsTile> controlsTileProvider,
Provider<CellularTile> cellularTileProvider,
Provider<DndTile> dndTileProvider,
Provider<ColorInversionTile> colorInversionTileProvider,
@@ -106,7 +102,6 @@ public class QSFactoryImpl implements QSFactory {
Provider<ScreenRecordTile> screenRecordTileProvider) {
mWifiTileProvider = wifiTileProvider;
mBluetoothTileProvider = bluetoothTileProvider;
- mControlsTileProvider = controlsTileProvider;
mCellularTileProvider = cellularTileProvider;
mDndTileProvider = dndTileProvider;
mColorInversionTileProvider = colorInversionTileProvider;
@@ -146,11 +141,6 @@ public class QSFactoryImpl implements QSFactory {
return mWifiTileProvider.get();
case "bt":
return mBluetoothTileProvider.get();
- case "controls":
- if (Settings.System.getInt(mHost.getContext().getContentResolver(),
- "npv_plugin_flag", 0) == 3) {
- return mControlsTileProvider.get();
- } else return null;
case "cell":
return mCellularTileProvider.get();
case "dnd":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index d4e9fdff1b24..781fdf076db8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -194,8 +194,8 @@ public class CastTile extends QSTileImpl<BooleanState> {
if (device.state == CastDevice.STATE_CONNECTED) {
state.value = true;
state.secondaryLabel = getDeviceName(device);
- state.contentDescription = state.contentDescription + "," +
- mContext.getString(R.string.accessibility_cast_name, state.label);
+ state.contentDescription = state.contentDescription + ","
+ + mContext.getString(R.string.accessibility_cast_name, state.label);
connecting = false;
break;
} else if (device.state == CastDevice.STATE_CONNECTING) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
deleted file mode 100644
index 39ae66e7607a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.android.systemui.R;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.NPVPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSTile.BooleanState;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.shared.plugins.PluginManager;
-
-import javax.inject.Inject;
-
-
-/**
- * Temporary control test for prototyping
- */
-public class ControlsTile extends QSTileImpl<BooleanState> {
- private ControlsDetailAdapter mDetailAdapter;
- private final ActivityStarter mActivityStarter;
- private PluginManager mPluginManager;
- private NPVPlugin mPlugin;
- private Intent mHomeAppIntent;
-
- @Inject
- public ControlsTile(QSHost host,
- ActivityStarter activityStarter,
- PluginManager pluginManager) {
- super(host);
- mActivityStarter = activityStarter;
- mPluginManager = pluginManager;
- mDetailAdapter = (ControlsDetailAdapter) createDetailAdapter();
-
- mHomeAppIntent = new Intent(Intent.ACTION_VIEW);
- mHomeAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mHomeAppIntent.setComponent(new ComponentName("com.google.android.apps.chromecast.app",
- "com.google.android.apps.chromecast.app.DiscoveryActivity"));
- }
-
- @Override
- public DetailAdapter getDetailAdapter() {
- return mDetailAdapter;
- }
-
- @Override
- public BooleanState newTileState() {
- return new BooleanState();
- }
-
- @Override
- public void handleSetListening(boolean listening) {
-
- }
-
- @Override
- public void setDetailListening(boolean listening) {
- if (mPlugin == null) return;
-
- mPlugin.setListening(listening);
- }
-
- @Override
- protected void handleClick() {
- showDetail(true);
- }
-
- @Override
- public Intent getLongClickIntent() {
- return mHomeAppIntent;
- }
-
- @Override
- protected void handleSecondaryClick() {
- showDetail(true);
- }
-
- @Override
- public CharSequence getTileLabel() {
- return "Controls";
- }
-
- @Override
- protected void handleUpdateState(BooleanState state, Object arg) {
- state.icon = ResourceIcon.get(R.drawable.ic_lightbulb_outline_gm2_24px);
- state.label = "Controls";
- }
-
- @Override
- public boolean supportsDetailView() {
- return getDetailAdapter() != null && mQSSettingsPanelOption == QSSettingsPanel.OPEN_CLICK;
- }
-
- @Override
- public int getMetricsCategory() {
- return -1;
- }
-
- @Override
- protected String composeChangeAnnouncement() {
- if (mState.value) {
- return "On";
- } else {
- return "Off";
- }
- }
-
- @Override
- public boolean isAvailable() {
- return true;
- }
-
- @Override
- protected DetailAdapter createDetailAdapter() {
- mDetailAdapter = new ControlsDetailAdapter();
- return mDetailAdapter;
- }
-
- private class ControlsDetailAdapter implements DetailAdapter {
- private View mDetailView;
- protected FrameLayout mHomeControlsLayout;
-
- public CharSequence getTitle() {
- return "Controls";
- }
-
- public Boolean getToggleState() {
- return null;
- }
-
- public boolean getToggleEnabled() {
- return false;
- }
-
- public View createDetailView(Context context, View convertView, final ViewGroup parent) {
- if (convertView != null) return convertView;
-
- mHomeControlsLayout = (FrameLayout) LayoutInflater.from(context).inflate(
- R.layout.home_controls, parent, false);
- mHomeControlsLayout.setVisibility(View.VISIBLE);
- parent.addView(mHomeControlsLayout);
-
- mPluginManager.addPluginListener(
- new PluginListener<NPVPlugin>() {
- @Override
- public void onPluginConnected(NPVPlugin plugin,
- Context pluginContext) {
- mPlugin = plugin;
- mPlugin.attachToRoot(mHomeControlsLayout);
- mPlugin.setListening(true);
- }
-
- @Override
- public void onPluginDisconnected(NPVPlugin plugin) {
- mPlugin.setListening(false);
- mHomeControlsLayout.removeAllViews();
-
- }
- }, NPVPlugin.class, false);
- return mHomeControlsLayout;
- }
-
- public Intent getSettingsIntent() {
- return mHomeAppIntent;
- }
-
- public void setToggleState(boolean state) {
-
- }
-
- public int getMetricsCategory() {
- return -1;
- }
-
- public boolean hasHeader() {
- return false;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 569f660d1797..573ea4dd85de 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -29,6 +29,7 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WIN
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
@@ -38,6 +39,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
@@ -55,6 +58,7 @@ import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipUI;
@@ -64,6 +68,7 @@ import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.phone.NavigationBarFragment;
import com.android.systemui.statusbar.phone.NavigationBarView;
@@ -115,6 +120,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private final DeviceProvisionedController mDeviceProvisionedController;
private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
private final Intent mQuickStepIntent;
+ private final ScreenshotHelper mScreenshotHelper;
private Region mActiveNavBarRegion;
@@ -365,6 +371,13 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
+ @Override
+ public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
+ Insets visibleInsets, int taskId) {
+ mScreenshotHelper.provideScreenshot(screenImage, locationInScreen, visibleInsets,
+ taskId, mHandler, null);
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -474,7 +487,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
- public OverviewProxyService(Context context, DeviceProvisionedController provisionController,
+ public OverviewProxyService(Context context, CommandQueue commandQueue,
+ DeviceProvisionedController provisionController,
NavigationBarController navBarController, NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
PipUI pipUI, Optional<Divider> dividerOptional,
@@ -518,6 +532,16 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
// Listen for status bar state changes
statusBarWinController.registerCallback(mStatusBarWindowCallback);
+ mScreenshotHelper = new ScreenshotHelper(context);
+
+ // Listen for tracing state changes
+ commandQueue.addCallback(new CommandQueue.Callbacks() {
+ @Override
+ public void onTracingStateChanged(boolean enabled) {
+ mSysUiState.setFlag(SYSUI_STATE_TRACING_ENABLED, enabled)
+ .commitUpdate(mContext.getDisplayId());
+ }
+ });
}
public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton,
@@ -561,14 +585,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
boolean bouncerShowing) {
- int displayId = mContext.getDisplayId();
-
mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
keyguardShowing && !keyguardOccluded)
.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
keyguardShowing && keyguardOccluded)
.setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
- .commitUpdate(displayId);
+ .commitUpdate(mContext.getDisplayId());
}
/**
@@ -589,10 +611,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
- public float getBackButtonAlpha() {
- return mNavBarButtonAlpha;
- }
-
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
mHandler.post(()-> {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 1d649eee4d57..fe84d81836e8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -138,7 +138,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("ScreenPinningConfirmation");
lp.gravity = Gravity.FILL;
- lp.setFitWindowInsetsTypes(0 /* types */);
+ lp.setFitInsetsTypes(0 /* types */);
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java b/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java
index de8e6ea4a0cb..7ebebaaef122 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java
@@ -70,6 +70,6 @@ public class TriangleShape extends PathShape {
@Override
public void getOutline(@NonNull Outline outline) {
- outline.setConvexPath(mTriangularPath);
+ outline.setPath(mTriangularPath);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 1b321685e88b..b091ad8c0db8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -34,6 +34,7 @@ import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.provider.MediaStore;
import android.provider.Settings;
@@ -72,9 +73,6 @@ public class RecordingService extends Service {
private static final String ACTION_START = "com.android.systemui.screenrecord.START";
private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP";
- private static final String ACTION_PAUSE = "com.android.systemui.screenrecord.PAUSE";
- private static final String ACTION_RESUME = "com.android.systemui.screenrecord.RESUME";
- private static final String ACTION_CANCEL = "com.android.systemui.screenrecord.CANCEL";
private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE";
@@ -94,6 +92,7 @@ public class RecordingService extends Service {
private boolean mUseAudio;
private boolean mShowTaps;
+ private boolean mOriginalShowTaps;
private File mTempFile;
@Inject
@@ -145,38 +144,11 @@ public class RecordingService extends Service {
}
break;
- case ACTION_CANCEL:
- stopRecording();
-
- // Delete temp file
- if (!mTempFile.delete()) {
- Log.e(TAG, "Error canceling screen recording!");
- Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
- .show();
- } else {
- Toast.makeText(this, R.string.screenrecord_cancel_success, Toast.LENGTH_LONG)
- .show();
- }
-
- // Close quick shade
- sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- break;
-
case ACTION_STOP:
stopRecording();
saveRecording(notificationManager);
break;
- case ACTION_PAUSE:
- mMediaRecorder.pause();
- setNotificationActions(true, notificationManager);
- break;
-
- case ACTION_RESUME:
- mMediaRecorder.resume();
- setNotificationActions(false, notificationManager);
- break;
-
case ACTION_SHARE:
Uri shareUri = Uri.parse(intent.getStringExtra(EXTRA_PATH));
@@ -233,9 +205,14 @@ public class RecordingService extends Service {
*/
private void startRecording() {
try {
- mTempFile = File.createTempFile("temp", ".mp4");
+ File cacheDir = getCacheDir();
+ cacheDir.mkdirs();
+ mTempFile = File.createTempFile("temp", ".mp4", cacheDir);
Log.d(TAG, "Writing video output to: " + mTempFile.getAbsolutePath());
+ mOriginalShowTaps = 1 == Settings.System.getInt(
+ getApplicationContext().getContentResolver(),
+ Settings.System.SHOW_TOUCHES, 0);
setTapsVisible(mShowTaps);
// Set up media recorder
@@ -295,50 +272,33 @@ public class RecordingService extends Service {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
getString(R.string.screenrecord_name),
- NotificationManager.IMPORTANCE_LOW);
+ NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(getString(R.string.screenrecord_channel_description));
channel.enableVibration(true);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
+ Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ getResources().getString(R.string.screenrecord_name));
+
mRecordingNotificationBuilder = new Notification.Builder(this, CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_android)
+ .setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(getResources().getString(R.string.screenrecord_name))
+ .setContentText(getResources().getString(R.string.screenrecord_stop_text))
.setUsesChronometer(true)
- .setOngoing(true);
- setNotificationActions(false, notificationManager);
- Notification notification = mRecordingNotificationBuilder.build();
- startForeground(NOTIFICATION_ID, notification);
- }
-
- private void setNotificationActions(boolean isPaused, NotificationManager notificationManager) {
- String pauseString = getResources()
- .getString(isPaused ? R.string.screenrecord_resume_label
- : R.string.screenrecord_pause_label);
- Intent pauseIntent = isPaused ? getResumeIntent(this) : getPauseIntent(this);
-
- mRecordingNotificationBuilder.setActions(
- new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android),
- getResources().getString(R.string.screenrecord_stop_label),
- PendingIntent
- .getService(this, REQUEST_CODE, getStopIntent(this),
- PendingIntent.FLAG_UPDATE_CURRENT))
- .build(),
- new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android), pauseString,
- PendingIntent.getService(this, REQUEST_CODE, pauseIntent,
+ .setColorized(true)
+ .setColor(getResources().getColor(R.color.GM2_red_700))
+ .setOngoing(true)
+ .setContentIntent(
+ PendingIntent.getService(
+ this, REQUEST_CODE, getStopIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT))
- .build(),
- new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android),
- getResources().getString(R.string.screenrecord_cancel_label),
- PendingIntent
- .getService(this, REQUEST_CODE, getCancelIntent(this),
- PendingIntent.FLAG_UPDATE_CURRENT))
- .build());
+ .addExtras(extras);
notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build());
+ Notification notification = mRecordingNotificationBuilder.build();
+ startForeground(NOTIFICATION_ID, notification);
}
private Notification createSaveNotification(Uri uri) {
@@ -347,7 +307,7 @@ public class RecordingService extends Service {
.setDataAndType(uri, "video/mp4");
Notification.Action shareAction = new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android),
+ Icon.createWithResource(this, R.drawable.ic_screenrecord),
getResources().getString(R.string.screenrecord_share_label),
PendingIntent.getService(
this,
@@ -357,7 +317,7 @@ public class RecordingService extends Service {
.build();
Notification.Action deleteAction = new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android),
+ Icon.createWithResource(this, R.drawable.ic_screenrecord),
getResources().getString(R.string.screenrecord_delete_label),
PendingIntent.getService(
this,
@@ -366,8 +326,12 @@ public class RecordingService extends Service {
PendingIntent.FLAG_UPDATE_CURRENT))
.build();
+ Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ getResources().getString(R.string.screenrecord_name));
+
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_android)
+ .setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(getResources().getString(R.string.screenrecord_name))
.setContentText(getResources().getString(R.string.screenrecord_save_message))
.setContentIntent(PendingIntent.getActivity(
@@ -377,7 +341,8 @@ public class RecordingService extends Service {
Intent.FLAG_GRANT_READ_URI_PERMISSION))
.addAction(shareAction)
.addAction(deleteAction)
- .setAutoCancel(true);
+ .setAutoCancel(true)
+ .addExtras(extras);
// Add thumbnail if available
Bitmap thumbnailBitmap = null;
@@ -400,7 +365,7 @@ public class RecordingService extends Service {
}
private void stopRecording() {
- setTapsVisible(false);
+ setTapsVisible(mOriginalShowTaps);
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
@@ -459,18 +424,6 @@ public class RecordingService extends Service {
return new Intent(context, RecordingService.class).setAction(ACTION_STOP);
}
- private static Intent getPauseIntent(Context context) {
- return new Intent(context, RecordingService.class).setAction(ACTION_PAUSE);
- }
-
- private static Intent getResumeIntent(Context context) {
- return new Intent(context, RecordingService.class).setAction(ACTION_RESUME);
- }
-
- private static Intent getCancelIntent(Context context) {
- return new Intent(context, RecordingService.class).setAction(ACTION_CANCEL);
- }
-
private static Intent getShareIntent(Context context, String path) {
return new Intent(context, RecordingService.class).setAction(ACTION_SHARE)
.putExtra(EXTRA_PATH, path);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 50e9a51478ed..880b8f8776e8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -29,6 +29,7 @@ import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.Notification;
@@ -38,6 +39,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PointF;
@@ -240,7 +242,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
PixelFormat.TRANSLUCENT);
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.setFitWindowInsetsTypes(0 /* types */);
+ mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mDisplayMetrics = new DisplayMetrics();
@@ -300,8 +302,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
int width = crop.width();
int height = crop.height();
- // Take the screenshot
- mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot);
+ takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, null);
+ }
+
+ private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) {
+ mScreenBitmap = screenshot;
if (mScreenBitmap == null) {
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
@@ -317,7 +322,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
// Start the post-screenshot animation
- startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
+ startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+ screenRect);
}
void takeScreenshot(Consumer<Uri> finisher) {
@@ -327,9 +333,16 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
}
+ void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
+ Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
+ // TODO use taskId and visibleInsets
+ takeScreenshot(screenshot, finisher, screenshotScreenBounds);
+ }
+
/**
* Displays a screenshot selector
*/
+ @SuppressLint("ClickableViewAccessibility")
void takeScreenshotPartial(final Consumer<Uri> finisher) {
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
@@ -402,7 +415,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
/**
* Starts the animation after taking the screenshot
*/
- private void startAnimation(final Consumer<Uri> finisher, int w, int h) {
+ private void startAnimation(final Consumer<Uri> finisher, int w, int h,
+ @Nullable Rect screenRect) {
// If power save is on, show a toast so there is some visual indication that a screenshot
// has been taken.
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -422,7 +436,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotAnimation.removeAllListeners();
}
- ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
+ ValueAnimator screenshotDropInAnim = screenRect != null ? createRectAnimation(screenRect)
+ : createScreenshotDropInAnimation();
ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h);
mScreenshotAnimation = new AnimatorSet();
mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
@@ -460,6 +475,46 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
});
}
+ private ValueAnimator createRectAnimation(Rect rect) {
+ mScreenshotView.setAdjustViewBounds(true);
+ mScreenshotView.setMaxHeight(rect.height());
+ mScreenshotView.setMaxWidth(rect.width());
+
+ final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
+ / SCREENSHOT_DROP_IN_DURATION);
+ final float flashDurationPct = 2f * flashPeakDurationPct;
+ final Interpolator scaleInterpolator = x -> {
+ // We start scaling when the flash is at it's peak
+ if (x < flashPeakDurationPct) {
+ return 0;
+ }
+ return (x - flashDurationPct) / (1f - flashDurationPct);
+ };
+
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mBackgroundView.setAlpha(0f);
+ mBackgroundView.setVisibility(View.VISIBLE);
+ mScreenshotView.setAlpha(0f);
+ mScreenshotView.setElevation(0f);
+ mScreenshotView.setTranslationX(0f);
+ mScreenshotView.setTranslationY(0f);
+ mScreenshotView.setScaleX(1f);
+ mScreenshotView.setScaleY(1f);
+ mScreenshotView.setVisibility(View.VISIBLE);
+ }
+ });
+ anim.addUpdateListener(animation -> {
+ float t = (Float) animation.getAnimatedValue();
+ mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
+ mScreenshotView.setAlpha(t);
+ });
+ return anim;
+ }
+
private ValueAnimator createScreenshotDropInAnimation() {
final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
/ SCREENSHOT_DROP_IN_DURATION);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
index 16447fbdd4aa..f3614ffbdb1b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
@@ -30,6 +30,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -156,7 +157,7 @@ public class GlobalScreenshotLegacy {
PixelFormat.TRANSLUCENT);
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.setFitWindowInsetsTypes(0 /* types */);
+ mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mDisplayMetrics = new DisplayMetrics();
@@ -205,8 +206,13 @@ public class GlobalScreenshotLegacy {
int width = crop.width();
int height = crop.height();
- // Take the screenshot
- mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot);
+ takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher,
+ statusBarVisible, navBarVisible, null);
+ }
+
+ private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, boolean statusBarVisible,
+ boolean navBarVisible, Rect screenboundsOfBitmap) {
+ mScreenBitmap = screenshot;
if (mScreenBitmap == null) {
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
@@ -220,7 +226,7 @@ public class GlobalScreenshotLegacy {
// Start the post-screenshot animation
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
- statusBarVisible, navBarVisible);
+ statusBarVisible, navBarVisible, screenboundsOfBitmap);
}
void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) {
@@ -229,6 +235,12 @@ public class GlobalScreenshotLegacy {
new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
}
+ void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
+ Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
+ // TODO use taskId and visibleInsets
+ takeScreenshot(screenshot, finisher, false, false, screenshotScreenBounds);
+ }
+
/**
* Displays a screenshot selector
*/
@@ -302,7 +314,7 @@ public class GlobalScreenshotLegacy {
* Starts the animation after taking the screenshot
*/
private void startAnimation(final Consumer<Uri> finisher, int w, int h,
- boolean statusBarVisible, boolean navBarVisible) {
+ boolean statusBarVisible, boolean navBarVisible, @Nullable Rect screenBoundsOfBitmap) {
// If power save is on, show a toast so there is some visual indication that a screenshot
// has been taken.
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -323,7 +335,8 @@ public class GlobalScreenshotLegacy {
}
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
+ ValueAnimator screenshotDropInAnim = screenBoundsOfBitmap != null
+ ? createRectAnimation(screenBoundsOfBitmap) : createScreenshotDropInAnimation();
ValueAnimator screenshotFadeOutAnim =
createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible);
mScreenshotAnimation = new AnimatorSet();
@@ -430,6 +443,53 @@ public class GlobalScreenshotLegacy {
return anim;
}
+ /**
+ * If a bitmap was supplied to be used as the screenshot, animated from where that bitmap was
+ * on screen, rather than using the whole screen.
+ */
+ private ValueAnimator createRectAnimation(Rect rect) {
+ mScreenshotView.setAdjustViewBounds(true);
+ mScreenshotView.setMaxHeight(rect.height());
+ mScreenshotView.setMaxWidth(rect.width());
+
+ final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
+ / SCREENSHOT_DROP_IN_DURATION);
+ final float flashDurationPct = 2f * flashPeakDurationPct;
+ final Interpolator scaleInterpolator = x -> {
+ // We start scaling when the flash is at it's peak
+ if (x < flashPeakDurationPct) {
+ return 0;
+ }
+ return (x - flashDurationPct) / (1f - flashDurationPct);
+ };
+
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mBackgroundView.setAlpha(0f);
+ mBackgroundView.setVisibility(View.VISIBLE);
+ mScreenshotView.setAlpha(0f);
+ mScreenshotView.setElevation(0f);
+ mScreenshotView.setTranslationX(0f);
+ mScreenshotView.setTranslationY(0f);
+ mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
+ mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
+ mScreenshotView.setVisibility(View.VISIBLE);
+ }
+ });
+ anim.addUpdateListener(animation -> {
+ float t = (Float) animation.getAnimatedValue();
+ float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
+ - scaleInterpolator.getInterpolation(t)
+ * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
+ mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
+ mScreenshotView.setAlpha(t);
+ });
+ return anim;
+ }
+
private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
boolean navBarVisible) {
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e6082dddd6c7..e7e1ba8c28b4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -106,7 +106,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Initialize screenshot notification smart actions provider.
mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, false);
+ SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
if (mSmartActionsEnabled) {
mSmartActionsProvider =
SystemUIFactory.getInstance()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 9570b5a3b57c..4ac59df07eb9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -22,6 +22,9 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREEN
import android.app.Service;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -85,6 +88,22 @@ public class TakeScreenshotService extends Service {
finisher, msg.arg1 > 0, msg.arg2 > 0);
}
break;
+ case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
+ Bitmap screenshot = msg.getData().getParcelable(
+ WindowManager.PARCEL_KEY_SCREENSHOT_BITMAP);
+ Rect screenBounds = msg.getData().getParcelable(
+ WindowManager.PARCEL_KEY_SCREENSHOT_BOUNDS);
+ Insets insets = msg.getData().getParcelable(
+ WindowManager.PARCEL_KEY_SCREENSHOT_INSETS);
+ int taskId = msg.getData().getInt(WindowManager.PARCEL_KEY_SCREENSHOT_TASK_ID);
+ if (useCornerFlow) {
+ mScreenshot.handleImageAsScreenshot(
+ screenshot, screenBounds, insets, taskId, finisher);
+ } else {
+ mScreenshotLegacy.handleImageAsScreenshot(
+ screenshot, screenBounds, insets, taskId, finisher);
+ }
+ break;
default:
Log.d(TAG, "Invalid screenshot option: " + msg.what);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 7c0f4f942bce..64f083024ce1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -25,6 +25,8 @@ import static android.view.Display.INVALID_DISPLAY;
import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
+import android.annotation.Nullable;
+import android.app.ITransientNotificationCallback;
import android.app.StatusBarManager;
import android.app.StatusBarManager.Disable2Flags;
import android.app.StatusBarManager.DisableFlags;
@@ -51,6 +53,7 @@ import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.tracing.ProtoTracer;
import java.util.ArrayList;
@@ -119,6 +122,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT;
private static final int MSG_SHOW_INATTENTIVE_SLEEP_WARNING = 51 << MSG_SHIFT;
private static final int MSG_DISMISS_INATTENTIVE_SLEEP_WARNING = 52 << MSG_SHIFT;
+ private static final int MSG_SHOW_TOAST = 53 << MSG_SHIFT;
+ private static final int MSG_HIDE_TOAST = 54 << MSG_SHIFT;
+ private static final int MSG_TRACING_STATE_CHANGED = 55 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -139,6 +145,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
* event.
*/
private int mLastUpdatedImeDisplayId = INVALID_DISPLAY;
+ private ProtoTracer mProtoTracer;
/**
* These methods are called back on the main thread.
@@ -308,9 +315,32 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
* due to prolonged user inactivity should be dismissed.
*/
default void dismissInattentiveSleepWarning(boolean animated) { }
+
+ /**
+ * @see IStatusBar#showToast(String, IBinder, CharSequence, IBinder, int,
+ * ITransientNotificationCallback)
+ */
+ default void showToast(String packageName, IBinder token, CharSequence text,
+ IBinder windowToken, int duration,
+ @Nullable ITransientNotificationCallback callback) { }
+
+ /**
+ * @see IStatusBar#hideToast(String, IBinder) (String, IBinder)
+ */
+ default void hideToast(String packageName, IBinder token) { }
+
+ /**
+ * @param enabled
+ */
+ default void onTracingStateChanged(boolean enabled) { }
}
public CommandQueue(Context context) {
+ this(context, null);
+ }
+
+ public CommandQueue(Context context, ProtoTracer protoTracer) {
+ mProtoTracer = protoTracer;
context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler);
// We always have default display.
setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE);
@@ -761,6 +791,31 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
@Override
+ public void showToast(String packageName, IBinder token, CharSequence text,
+ IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) {
+ synchronized (mLock) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ args.arg2 = token;
+ args.arg3 = text;
+ args.arg4 = windowToken;
+ args.arg5 = callback;
+ args.argi1 = duration;
+ mHandler.obtainMessage(MSG_SHOW_TOAST, args).sendToTarget();
+ }
+ }
+
+ @Override
+ public void hideToast(String packageName, IBinder token) {
+ synchronized (mLock) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ args.arg2 = token;
+ mHandler.obtainMessage(MSG_HIDE_TOAST, args).sendToTarget();
+ }
+ }
+
+ @Override
public void onBiometricAuthenticated() {
synchronized (mLock) {
mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
@@ -875,6 +930,26 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
}
+ @Override
+ public void startTracing() {
+ synchronized (mLock) {
+ if (mProtoTracer != null) {
+ mProtoTracer.start();
+ }
+ mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, true).sendToTarget();
+ }
+ }
+
+ @Override
+ public void stopTracing() {
+ synchronized (mLock) {
+ if (mProtoTracer != null) {
+ mProtoTracer.stop();
+ }
+ mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, false).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -1178,6 +1253,35 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
mCallbacks.get(i).dismissInattentiveSleepWarning((Boolean) msg.obj);
}
break;
+ case MSG_SHOW_TOAST: {
+ args = (SomeArgs) msg.obj;
+ String packageName = (String) args.arg1;
+ IBinder token = (IBinder) args.arg2;
+ CharSequence text = (CharSequence) args.arg3;
+ IBinder windowToken = (IBinder) args.arg4;
+ ITransientNotificationCallback callback =
+ (ITransientNotificationCallback) args.arg5;
+ int duration = args.argi1;
+ for (Callbacks callbacks : mCallbacks) {
+ callbacks.showToast(packageName, token, text, windowToken, duration,
+ callback);
+ }
+ break;
+ }
+ case MSG_HIDE_TOAST: {
+ args = (SomeArgs) msg.obj;
+ String packageName = (String) args.arg1;
+ IBinder token = (IBinder) args.arg2;
+ for (Callbacks callbacks : mCallbacks) {
+ callbacks.hideToast(packageName, token);
+ }
+ break;
+ }
+ case MSG_TRACING_STATE_CHANGED:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onTracingStateChanged((Boolean) msg.obj);
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index ac05c53c38dd..6839921e90a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -56,7 +56,7 @@ public class FeatureFlags {
}
public boolean isNewNotifPipelineEnabled() {
- return getDeviceConfigFlag("notification.newpipeline.enabled", false);
+ return getDeviceConfigFlag("notification.newpipeline.enabled", true);
}
public boolean isNewNotifPipelineRenderingEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 976531d8b49d..12298817d5a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -79,6 +79,7 @@ public class NotificationLockscreenUserManagerImpl implements
private final DeviceProvisionedController mDeviceProvisionedController;
private final KeyguardStateController mKeyguardStateController;
+ private final Object mLock = new Object();
// Lazy
private NotificationEntryManager mEntryManager;
@@ -181,6 +182,7 @@ public class NotificationLockscreenUserManagerImpl implements
protected final Context mContext;
private final Handler mMainHandler;
protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
+ protected final ArrayList<UserInfo> mCurrentManagedProfiles = new ArrayList<>();
protected int mCurrentUserId = 0;
protected NotificationPresenter mPresenter;
@@ -280,7 +282,8 @@ public class NotificationLockscreenUserManagerImpl implements
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter);
+ mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter,
+ null /* executor */, UserHandle.ALL);
IntentFilter internalFilter = new IntentFilter();
internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
@@ -300,7 +303,7 @@ public class NotificationLockscreenUserManagerImpl implements
}
public boolean isCurrentProfile(int userId) {
- synchronized (mCurrentProfiles) {
+ synchronized (mLock) {
return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
}
}
@@ -417,6 +420,20 @@ public class NotificationLockscreenUserManagerImpl implements
return mUsersAllowingPrivateNotifications.get(userHandle);
}
+ /**
+ * If all managed profiles (work profiles) can show private data in public (secure & locked.)
+ */
+ public boolean allowsManagedPrivateNotificationsInPublic() {
+ synchronized (mLock) {
+ for (UserInfo profile : mCurrentManagedProfiles) {
+ if (!userAllowsPrivateNotificationsInPublic(profile.id)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
private boolean adminAllowsKeyguardFeature(int userHandle, int feature) {
if (userHandle == UserHandle.USER_ALL) {
return true;
@@ -495,11 +512,15 @@ public class NotificationLockscreenUserManagerImpl implements
}
private void updateCurrentProfilesCache() {
- synchronized (mCurrentProfiles) {
+ synchronized (mLock) {
mCurrentProfiles.clear();
+ mCurrentManagedProfiles.clear();
if (mUserManager != null) {
for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
mCurrentProfiles.put(user.id, user);
+ if (UserManager.USER_TYPE_PROFILE_MANAGED.equals(user.userType)) {
+ mCurrentManagedProfiles.add(user);
+ }
}
}
}
@@ -510,10 +531,29 @@ public class NotificationLockscreenUserManagerImpl implements
});
}
+ /**
+ * If any of the profiles are in public mode.
+ */
public boolean isAnyProfilePublicMode() {
- for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
- if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
- return true;
+ synchronized (mLock) {
+ for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+ if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * If any managed/work profiles are in public mode.
+ */
+ public boolean isAnyManagedProfilePublicMode() {
+ synchronized (mLock) {
+ for (int i = mCurrentManagedProfiles.size() - 1; i >= 0; i--) {
+ if (isLockscreenPublicMode(mCurrentManagedProfiles.get(i).id)) {
+ return true;
+ }
}
}
return false;
@@ -620,9 +660,17 @@ public class NotificationLockscreenUserManagerImpl implements
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 + " ");
+ synchronized (mLock) {
+ for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+ final int userId = mCurrentProfiles.valueAt(i).id;
+ pw.print("" + userId + " ");
+ }
+ }
+ pw.print(" mCurrentManagedProfiles=");
+ synchronized (mLock) {
+ for (UserInfo userInfo : mCurrentManagedProfiles) {
+ pw.print("" + userInfo.id + " ");
+ }
}
pw.println();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 667e721ae37d..f3783c83a301 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -25,8 +25,10 @@ import android.app.KeyguardManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
+import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.content.Intent;
+import android.net.Uri;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -345,7 +347,8 @@ public class NotificationRemoteInputManager implements Dumpable {
});
mSmartReplyController.setCallback((entry, reply) -> {
StatusBarNotification newSbn =
- rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */);
+ rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */,
+ null /* mimeType */, null /* uri */);
mEntryManager.updateNotification(newSbn, null /* ranking */);
});
}
@@ -527,28 +530,36 @@ public class NotificationRemoteInputManager implements Dumpable {
StatusBarNotification rebuildNotificationForCanceledSmartReplies(
NotificationEntry entry) {
return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
- false /* showSpinner */);
+ false /* showSpinner */, null /* mimeType */, null /* uri */);
}
@VisibleForTesting
StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry,
- CharSequence remoteInputText, boolean showSpinner) {
+ CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri) {
StatusBarNotification sbn = entry.getSbn();
Notification.Builder b = Notification.Builder
.recoverBuilder(mContext, sbn.getNotification().clone());
- if (remoteInputText != null) {
- CharSequence[] oldHistory = sbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
- CharSequence[] newHistory;
- if (oldHistory == null) {
- newHistory = new CharSequence[1];
+ if (remoteInputText != null || uri != null) {
+ RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[])
+ sbn.getNotification().extras.getParcelableArray(
+ Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ RemoteInputHistoryItem[] newHistoryItems;
+
+ if (oldHistoryItems == null) {
+ newHistoryItems = new RemoteInputHistoryItem[1];
} else {
- newHistory = new CharSequence[oldHistory.length + 1];
- System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
+ newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1];
+ System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length);
}
- newHistory[0] = String.valueOf(remoteInputText);
- b.setRemoteInputHistory(newHistory);
+ RemoteInputHistoryItem newItem;
+ if (uri != null) {
+ newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText);
+ } else {
+ newItem = new RemoteInputHistoryItem(remoteInputText);
+ }
+ newHistoryItems[0] = newItem;
+ b.setRemoteInputHistory(newHistoryItems);
}
b.setShowRemoteInputSpinner(showSpinner);
b.setHideSmartReplies(true);
@@ -631,8 +642,11 @@ public class NotificationRemoteInputManager implements Dumpable {
if (TextUtils.isEmpty(remoteInputText)) {
remoteInputText = entry.remoteInputTextWhenReset;
}
+ String remoteInputMimeType = entry.remoteInputMimeType;
+ Uri remoteInputUri = entry.remoteInputUri;
StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry,
- remoteInputText, false /* showSpinner */);
+ remoteInputText, false /* showSpinner */, remoteInputMimeType,
+ remoteInputUri);
entry.onRemoteInputInserted();
if (newSbn == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 6b0b5dfeaf40..8d4a9efbcd7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -140,7 +140,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
boolean hideMedia = Utils.useQsMediaPlayer(mContext);
if (ent.isRowDismissed() || ent.isRowRemoved()
|| (ent.isMediaNotification() && hideMedia)
- || mBubbleController.isBubbleNotificationSuppressedFromShade(ent.getKey())) {
+ || mBubbleController.isBubbleNotificationSuppressedFromShade(ent)) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
index ec8dbead7de2..493482aacce5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar;
import android.content.Context;
import com.android.systemui.statusbar.notification.row.NotificationRowModule;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.tracing.ProtoTracer;
import javax.inject.Singleton;
@@ -35,8 +37,8 @@ public class StatusBarDependenciesModule {
*/
@Provides
@Singleton
- public CommandQueue provideCommandQueue(Context context) {
- return new CommandQueue(context);
+ public CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) {
+ return new CommandQueue(context, protoTracer);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
index 015c32348bb0..269a7a59f1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
@@ -43,10 +43,10 @@ class BypassHeadsUpNotifier @Inject constructor(
private val headsUpManager: HeadsUpManagerPhone,
private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
private val mediaManager: NotificationMediaManager,
+ private val entryManager: NotificationEntryManager,
tunerService: TunerService
) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener {
- private lateinit var entryManager: NotificationEntryManager
private var currentMediaEntry: NotificationEntry? = null
private var enabled = true
@@ -70,8 +70,7 @@ class BypassHeadsUpNotifier @Inject constructor(
}, Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING)
}
- fun setUp(entryManager: NotificationEntryManager) {
- this.entryManager = entryManager
+ fun setUp() {
mediaManager.addCallback(this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 56ad0e1df36f..b048d032feaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -44,10 +44,11 @@ public abstract class ListEntry {
/**
* Should return the "representative entry" for this ListEntry. For NotificationEntries, its
- * the entry itself. For groups, it should be the summary. This method exists to interface with
+ * the entry itself. For groups, it should be the summary (but if a summary doesn't exist,
+ * this can return null). This method exists to interface with
* legacy code that expects groups to also be NotificationEntries.
*/
- public abstract NotificationEntry getRepresentativeEntry();
+ public abstract @Nullable NotificationEntry getRepresentativeEntry();
@Nullable public GroupEntry getParent() {
return mParent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index c488c6bb8721..1b6170326bac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -44,17 +44,18 @@ import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
-import android.util.Log;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.notification.collection.coalescer.CoalescedEvent;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.Assert;
@@ -91,13 +92,15 @@ import javax.inject.Singleton;
* {@link #addNotificationLifetimeExtender(NotifLifetimeExtender)}).
*
* Interested parties can register listeners
- * ({@link #addCollectionListener(NotifCollectionListener)}) to be informed when notifications are
- * added, updated, or removed.
+ * ({@link #addCollectionListener(NotifCollectionListener)}) to be informed when notifications
+ * events occur.
*/
@MainThread
@Singleton
public class NotifCollection implements Dumpable {
private final IStatusBarService mStatusBarService;
+ private final FeatureFlags mFeatureFlags;
+ private final NotifCollectionLogger mLogger;
private final Map<String, NotificationEntry> mNotificationSet = new ArrayMap<>();
private final Collection<NotificationEntry> mReadOnlyNotificationSet =
@@ -111,10 +114,16 @@ public class NotifCollection implements Dumpable {
private boolean mAmDispatchingToOtherCode;
@Inject
- public NotifCollection(IStatusBarService statusBarService, DumpController dumpController) {
+ public NotifCollection(
+ IStatusBarService statusBarService,
+ DumpController dumpController,
+ FeatureFlags featureFlags,
+ NotifCollectionLogger logger) {
Assert.isMainThread();
mStatusBarService = statusBarService;
+ mLogger = logger;
dumpController.registerDumpable(TAG, this);
+ mFeatureFlags = featureFlags;
}
/** Initializes the NotifCollection and registers it to receive notification events. */
@@ -184,8 +193,8 @@ public class NotifCollection implements Dumpable {
private void onNotificationGroupPosted(List<CoalescedEvent> batch) {
Assert.isMainThread();
- Log.d(TAG, "POSTED GROUP " + batch.get(0).getSbn().getGroupKey()
- + " (" + batch.size() + " events)");
+ mLogger.logNotifGroupPosted(batch.get(0).getSbn().getGroupKey(), batch.size());
+
for (CoalescedEvent event : batch) {
postNotification(event.getSbn(), event.getRanking(), null);
}
@@ -198,13 +207,14 @@ public class NotifCollection implements Dumpable {
int reason) {
Assert.isMainThread();
- Log.d(TAG, "REMOVED " + sbn.getKey() + " reason=" + reason);
+ mLogger.logNotifRemoved(sbn.getKey(), reason);
removeNotification(sbn.getKey(), rankingMap, reason, null);
}
private void onNotificationRankingUpdate(RankingMap rankingMap) {
Assert.isMainThread();
applyRanking(rankingMap);
+ dispatchNotificationRankingUpdate(rankingMap);
rebuildList();
}
@@ -216,10 +226,12 @@ public class NotifCollection implements Dumpable {
if (entry == null) {
// A new notification!
- Log.d(TAG, "POSTED " + sbn.getKey());
+ mLogger.logNotifPosted(sbn.getKey());
entry = new NotificationEntry(sbn, ranking);
mNotificationSet.put(sbn.getKey(), entry);
+ dispatchOnEntryInit(entry);
+
if (rankingMap != null) {
applyRanking(rankingMap);
}
@@ -228,7 +240,7 @@ public class NotifCollection implements Dumpable {
} else {
// Update to an existing entry
- Log.d(TAG, "UPDATED " + sbn.getKey());
+ mLogger.logNotifUpdated(sbn.getKey());
// Notification is updated so it is essentially re-added and thus alive again. Don't
// need to keep its lifetime extended.
@@ -287,6 +299,7 @@ public class NotifCollection implements Dumpable {
}
dispatchOnEntryRemoved(entry, reason, dismissedByUserStats != null /* removedByUser */);
+ dispatchOnEntryCleanUp(entry);
}
rebuildList();
@@ -295,15 +308,26 @@ public class NotifCollection implements Dumpable {
private void applyRanking(@NonNull RankingMap rankingMap) {
for (NotificationEntry entry : mNotificationSet.values()) {
if (!isLifetimeExtended(entry)) {
- Ranking ranking = requireRanking(rankingMap, entry.getKey());
- entry.setRanking(ranking);
-
- // TODO: (b/145659174) update the sbn's overrideGroupKey in
- // NotificationEntry.setRanking instead of here once we fully migrate to the
- // NewNotifPipeline
- final String newOverrideGroupKey = ranking.getOverrideGroupKey();
- if (!Objects.equals(entry.getSbn().getOverrideGroupKey(), newOverrideGroupKey)) {
- entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
+
+ // TODO: (b/148791039) We should crash if we are ever handed a ranking with
+ // incomplete entries. Right now, there's a race condition in NotificationListener
+ // that means this might occur when SystemUI is starting up.
+ Ranking ranking = new Ranking();
+ if (rankingMap.getRanking(entry.getKey(), ranking)) {
+ entry.setRanking(ranking);
+
+ // TODO: (b/145659174) update the sbn's overrideGroupKey in
+ // NotificationEntry.setRanking instead of here once we fully migrate to the
+ // NewNotifPipeline
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ final String newOverrideGroupKey = ranking.getOverrideGroupKey();
+ if (!Objects.equals(entry.getSbn().getOverrideGroupKey(),
+ newOverrideGroupKey)) {
+ entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
+ }
+ }
+ } else {
+ mLogger.logRankingMissing(entry.getKey(), rankingMap);
}
}
}
@@ -365,6 +389,14 @@ public class NotifCollection implements Dumpable {
return ranking;
}
+ private void dispatchOnEntryInit(NotificationEntry entry) {
+ mAmDispatchingToOtherCode = true;
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryInit(entry);
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
private void dispatchOnEntryAdded(NotificationEntry entry) {
mAmDispatchingToOtherCode = true;
for (NotifCollectionListener listener : mNotifCollectionListeners) {
@@ -381,6 +413,14 @@ public class NotifCollection implements Dumpable {
mAmDispatchingToOtherCode = false;
}
+ private void dispatchNotificationRankingUpdate(RankingMap map) {
+ mAmDispatchingToOtherCode = true;
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onRankingUpdate(map);
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
private void dispatchOnEntryRemoved(
NotificationEntry entry,
@CancellationReason int reason,
@@ -392,6 +432,14 @@ public class NotifCollection implements Dumpable {
mAmDispatchingToOtherCode = false;
}
+ private void dispatchOnEntryCleanUp(NotificationEntry entry) {
+ mAmDispatchingToOtherCode = true;
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryCleanUp(entry);
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
private final BatchableNotificationHandler mNotifHandler = new BatchableNotificationHandler() {
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
index 0377f57a7a9c..9142388374e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -90,7 +90,8 @@ public class NotifPipeline {
}
/**
- * Registers a listener to be informed when notifications are added, removed or updated.
+ * Registers a listener to be informed when there is a notification entry event such as an add,
+ * update, or remove.
*/
public void addCollectionListener(NotifCollectionListener listener) {
mNotifCollection.addCollectionListener(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 2fcfb8c811aa..1f77ec229041 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -37,8 +37,10 @@ import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
import android.app.NotificationManager.Policy;
import android.app.Person;
+import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
@@ -120,6 +122,8 @@ public final class NotificationEntry extends ListEntry {
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
public CharSequence remoteInputText;
+ public String remoteInputMimeType;
+ public Uri remoteInputUri;
private Notification.BubbleMetadata mBubbleMetadata;
/**
@@ -595,8 +599,8 @@ public final class NotificationEntry extends ListEntry {
return false;
}
Bundle extras = mSbn.getNotification().extras;
- CharSequence[] replyTexts = extras.getCharSequenceArray(
- Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ RemoteInputHistoryItem[] replyTexts = (RemoteInputHistoryItem[]) extras.getParcelableArray(
+ Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
if (!ArrayUtils.isEmpty(replyTexts)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 97f8ec5f5bb7..be5c535b7f47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -32,19 +32,20 @@ import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.Pair;
+import androidx.annotation.NonNull;
+
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState;
+import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderLogger;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
-import com.android.systemui.statusbar.notification.logging.NotifEvent;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.util.Assert;
import com.android.systemui.util.time.SystemClock;
@@ -69,7 +70,7 @@ import javax.inject.Singleton;
@Singleton
public class ShadeListBuilder implements Dumpable {
private final SystemClock mSystemClock;
- private final NotifLog mNotifLog;
+ private final ShadeListBuilderLogger mLogger;
private List<ListEntry> mNotifList = new ArrayList<>();
private List<ListEntry> mNewNotifList = new ArrayList<>();
@@ -93,16 +94,17 @@ public class ShadeListBuilder implements Dumpable {
new ArrayList<>();
@Nullable private OnRenderListListener mOnRenderListListener;
- private final List<ListEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
+ private List<ListEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
+ private List<ListEntry> mReadOnlyNewNotifList = Collections.unmodifiableList(mNewNotifList);
@Inject
public ShadeListBuilder(
SystemClock systemClock,
- NotifLog notifLog,
+ ShadeListBuilderLogger logger,
DumpController dumpController) {
Assert.isMainThread();
mSystemClock = systemClock;
- mNotifLog = notifLog;
+ mLogger = logger;
dumpController.registerDumpable(TAG, this);
}
@@ -205,8 +207,7 @@ public class ShadeListBuilder implements Dumpable {
Assert.isMainThread();
mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
- mNotifLog.log(NotifEvent.ON_BUILD_LIST, "Request received from "
- + "NotifCollection");
+ mLogger.logOnBuildList();
mAllEntries = entries;
buildList();
}
@@ -215,21 +216,15 @@ public class ShadeListBuilder implements Dumpable {
private void onPreGroupFilterInvalidated(NotifFilter filter) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.PRE_GROUP_FILTER_INVALIDATED, String.format(
- "Filter \"%s\" invalidated; pipeline state is %d",
- filter.getName(),
- mPipelineState.getState()));
+ mLogger.logPreGroupFilterInvalidated(filter.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_PRE_GROUP_FILTERING);
}
- private void onPromoterInvalidated(NotifPromoter filter) {
+ private void onPromoterInvalidated(NotifPromoter promoter) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.PROMOTER_INVALIDATED, String.format(
- "NotifPromoter \"%s\" invalidated; pipeline state is %d",
- filter.getName(),
- mPipelineState.getState()));
+ mLogger.logPromoterInvalidated(promoter.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_TRANSFORMING);
}
@@ -237,10 +232,7 @@ public class ShadeListBuilder implements Dumpable {
private void onNotifSectionInvalidated(NotifSection section) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.SECTION_INVALIDATED, String.format(
- "Section \"%s\" invalidated; pipeline state is %d",
- section.getName(),
- mPipelineState.getState()));
+ mLogger.logNotifSectionInvalidated(section.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_SORTING);
}
@@ -248,10 +240,7 @@ public class ShadeListBuilder implements Dumpable {
private void onPreRenderFilterInvalidated(NotifFilter filter) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.PRE_RENDER_FILTER_INVALIDATED, String.format(
- "Filter \"%s\" invalidated; pipeline state is %d",
- filter.getName(),
- mPipelineState.getState()));
+ mLogger.logPreRenderFilterInvalidated(filter.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_PRE_RENDER_FILTERING);
}
@@ -259,26 +248,12 @@ public class ShadeListBuilder implements Dumpable {
private void onNotifComparatorInvalidated(NotifComparator comparator) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.COMPARATOR_INVALIDATED, String.format(
- "Comparator \"%s\" invalidated; pipeline state is %d",
- comparator.getName(),
- mPipelineState.getState()));
+ mLogger.logNotifComparatorInvalidated(comparator.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_SORTING);
}
/**
- * Points mNotifList to the list stored in mNewNotifList.
- * Reuses the (emptied) mNotifList as mNewNotifList.
- */
- private void applyNewNotifList() {
- mNotifList.clear();
- List<ListEntry> emptyList = mNotifList;
- mNotifList = mNewNotifList;
- mNewNotifList = emptyList;
- }
-
- /**
* The core algorithm of the pipeline. See the top comment in {@link NotifPipeline} for
* details on our contracts with other code.
*
@@ -288,7 +263,7 @@ public class ShadeListBuilder implements Dumpable {
* if we detect that behavior, we should crash instantly.
*/
private void buildList() {
- mNotifLog.log(NotifEvent.START_BUILD_LIST, "Run #" + mIterationCount + "...");
+ mLogger.logStartBuildList(mIterationCount);
mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
mPipelineState.setState(STATE_BUILD_STARTED);
@@ -334,20 +309,37 @@ public class ShadeListBuilder implements Dumpable {
freeEmptyGroups();
// Step 6: Dispatch the new list, first to any listeners and then to the view layer
- mNotifLog.log(NotifEvent.DISPATCH_FINAL_LIST, "List finalized, is:\n"
- + ListDumper.dumpTree(mNotifList, false, "\t\t"));
+ if (mIterationCount % 10 == 0) {
+ mLogger.logFinalList(mNotifList);
+ }
dispatchOnBeforeRenderList(mReadOnlyNotifList);
if (mOnRenderListListener != null) {
mOnRenderListListener.onRenderList(mReadOnlyNotifList);
}
// Step 7: We're done!
- mNotifLog.log(NotifEvent.LIST_BUILD_COMPLETE,
- "Notif list build #" + mIterationCount + " completed");
+ mLogger.logEndBuildList(mIterationCount);
mPipelineState.setState(STATE_IDLE);
mIterationCount++;
}
+ /**
+ * Points mNotifList to the list stored in mNewNotifList.
+ * Reuses the (emptied) mNotifList as mNewNotifList.
+ *
+ * Accordingly, updates the ReadOnlyNotifList pointers.
+ */
+ private void applyNewNotifList() {
+ mNotifList.clear();
+ List<ListEntry> emptyList = mNotifList;
+ mNotifList = mNewNotifList;
+ mNewNotifList = emptyList;
+
+ List<ListEntry> readOnlyNotifList = mReadOnlyNotifList;
+ mReadOnlyNotifList = mReadOnlyNewNotifList;
+ mReadOnlyNewNotifList = readOnlyNotifList;
+ }
+
private void resetNotifs() {
for (GroupEntry group : mGroups.values()) {
group.setPreviousParent(group.getParent());
@@ -429,11 +421,10 @@ public class ShadeListBuilder implements Dumpable {
if (existingSummary == null) {
group.setSummary(entry);
} else {
- mNotifLog.log(NotifEvent.WARN, String.format(
- "Duplicate summary for group '%s': '%s' vs. '%s'",
+ mLogger.logDuplicateSummary(
group.getKey(),
existingSummary.getKey(),
- entry.getKey()));
+ entry.getKey());
// Use whichever one was posted most recently
if (entry.getSbn().getPostTime()
@@ -452,8 +443,7 @@ public class ShadeListBuilder implements Dumpable {
final String topLevelKey = entry.getKey();
if (mGroups.containsKey(topLevelKey)) {
- mNotifLog.log(NotifEvent.WARN,
- "Duplicate non-group top-level key: " + topLevelKey);
+ mLogger.logDuplicateTopLevelKey(topLevelKey);
} else {
entry.setParent(ROOT_ENTRY);
out.add(entry);
@@ -617,24 +607,22 @@ public class ShadeListBuilder implements Dumpable {
private void logParentingChanges() {
for (NotificationEntry entry : mAllEntries) {
if (entry.getParent() != entry.getPreviousParent()) {
- mNotifLog.log(NotifEvent.PARENT_CHANGED, String.format(
- "%s: parent changed from %s to %s",
+ mLogger.logParentChanged(
entry.getKey(),
entry.getPreviousParent() == null
- ? "null" : entry.getPreviousParent().getKey(),
+ ? null : entry.getPreviousParent().getKey(),
entry.getParent() == null
- ? "null" : entry.getParent().getKey()));
+ ? null : entry.getParent().getKey());
}
}
for (GroupEntry group : mGroups.values()) {
if (group.getParent() != group.getPreviousParent()) {
- mNotifLog.log(NotifEvent.PARENT_CHANGED, String.format(
- "%s: parent changed from %s to %s",
+ mLogger.logParentChanged(
group.getKey(),
group.getPreviousParent() == null
- ? "null" : group.getPreviousParent().getKey(),
+ ? null : group.getPreviousParent().getKey(),
group.getParent() == null
- ? "null" : group.getParent().getKey()));
+ ? null : group.getParent().getKey());
}
}
}
@@ -684,23 +672,10 @@ public class ShadeListBuilder implements Dumpable {
NotifFilter filter = findRejectingFilter(entry, now, filters);
if (filter != entry.mExcludingFilter) {
- if (entry.mExcludingFilter == null) {
- mNotifLog.log(NotifEvent.FILTER_CHANGED, String.format(
- "%s: filtered out by '%s'",
- entry.getKey(),
- filter.getName()));
- } else if (filter == null) {
- mNotifLog.log(NotifEvent.FILTER_CHANGED, String.format(
- "%s: no longer filtered out (previous filter was '%s')",
- entry.getKey(),
- entry.mExcludingFilter.getName()));
- } else {
- mNotifLog.log(NotifEvent.FILTER_CHANGED, String.format(
- "%s: filter changed: '%s' -> '%s'",
- entry.getKey(),
- entry.mExcludingFilter,
- filter));
- }
+ mLogger.logFilterChanged(
+ entry.getKey(),
+ entry.mExcludingFilter != null ? entry.mExcludingFilter.getName() : null,
+ filter != null ? filter.getName() : null);
// Note that groups and summaries can also be filtered out later if they're part of a
// malformed group. We currently don't have a great way to track that beyond parenting
@@ -728,23 +703,10 @@ public class ShadeListBuilder implements Dumpable {
NotifPromoter promoter = findPromoter(entry);
if (promoter != entry.mNotifPromoter) {
- if (entry.mNotifPromoter == null) {
- mNotifLog.log(NotifEvent.PROMOTER_CHANGED, String.format(
- "%s: Entry promoted to top level by '%s'",
- entry.getKey(),
- promoter.getName()));
- } else if (promoter == null) {
- mNotifLog.log(NotifEvent.PROMOTER_CHANGED, String.format(
- "%s: Entry is no longer promoted to top level (previous promoter was '%s')",
- entry.getKey(),
- entry.mNotifPromoter.getName()));
- } else {
- mNotifLog.log(NotifEvent.PROMOTER_CHANGED, String.format(
- "%s: Top-level promoter changed: '%s' -> '%s'",
- entry.getKey(),
- entry.mNotifPromoter,
- promoter));
- }
+ mLogger.logPromoterChanged(
+ entry.getKey(),
+ entry.mNotifPromoter != null ? entry.mNotifPromoter.getName() : null,
+ promoter != null ? promoter.getName() : null);
entry.mNotifPromoter = promoter;
}
@@ -767,21 +729,12 @@ public class ShadeListBuilder implements Dumpable {
final Integer sectionIndex = sectionWithIndex.second;
if (section != entry.mNotifSection) {
- if (entry.mNotifSection == null) {
- mNotifLog.log(NotifEvent.SECTION_CHANGED, String.format(
- "%s: sectioned by '%s' [index=%d].",
- entry.getKey(),
- section.getName(),
- sectionIndex));
- } else {
- mNotifLog.log(NotifEvent.SECTION_CHANGED, String.format(
- "%s: section changed: '%s' [index=%d] -> '%s [index=%d]'.",
- entry.getKey(),
- entry.mNotifSection,
- entry.getSection(),
- section,
- sectionIndex));
- }
+ mLogger.logSectionChanged(
+ entry.getKey(),
+ entry.mNotifSection != null ? entry.mNotifSection.getName() : null,
+ entry.getSection(),
+ section.getName(),
+ sectionIndex);
entry.mNotifSection = section;
entry.setSection(sectionIndex);
@@ -826,7 +779,7 @@ public class ShadeListBuilder implements Dumpable {
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) {
pw.println("\t" + TAG + " shade notifications:");
if (getShadeList().size() == 0) {
pw.println("\t\t None");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index f5890386a14f..98c45ffd6afb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -16,11 +16,6 @@
package com.android.systemui.statusbar.notification.collection.coalescer;
-import static com.android.systemui.statusbar.notification.logging.NotifEvent.BATCH_MAX_TIMEOUT;
-import static com.android.systemui.statusbar.notification.logging.NotifEvent.COALESCED_EVENT;
-import static com.android.systemui.statusbar.notification.logging.NotifEvent.EARLY_BATCH_EMIT;
-import static com.android.systemui.statusbar.notification.logging.NotifEvent.EMIT_EVENT_BATCH;
-
import static java.util.Objects.requireNonNull;
import android.annotation.MainThread;
@@ -35,7 +30,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
@@ -71,7 +65,7 @@ import javax.inject.Inject;
public class GroupCoalescer implements Dumpable {
private final DelayableExecutor mMainExecutor;
private final SystemClock mClock;
- private final NotifLog mLog;
+ private final GroupCoalescerLogger mLogger;
private final long mMinGroupLingerDuration;
private final long mMaxGroupLingerDuration;
@@ -83,8 +77,9 @@ public class GroupCoalescer implements Dumpable {
@Inject
public GroupCoalescer(
@Main DelayableExecutor mainExecutor,
- SystemClock clock, NotifLog log) {
- this(mainExecutor, clock, log, MIN_GROUP_LINGER_DURATION, MAX_GROUP_LINGER_DURATION);
+ SystemClock clock,
+ GroupCoalescerLogger logger) {
+ this(mainExecutor, clock, logger, MIN_GROUP_LINGER_DURATION, MAX_GROUP_LINGER_DURATION);
}
/**
@@ -98,12 +93,12 @@ public class GroupCoalescer implements Dumpable {
GroupCoalescer(
@Main DelayableExecutor mainExecutor,
SystemClock clock,
- NotifLog log,
+ GroupCoalescerLogger logger,
long minGroupLingerDuration,
long maxGroupLingerDuration) {
mMainExecutor = mainExecutor;
mClock = clock;
- mLog = log;
+ mLogger = logger;
mMinGroupLingerDuration = minGroupLingerDuration;
mMaxGroupLingerDuration = maxGroupLingerDuration;
}
@@ -129,7 +124,7 @@ public class GroupCoalescer implements Dumpable {
final boolean shouldCoalesce = handleNotificationPosted(sbn, rankingMap);
if (shouldCoalesce) {
- mLog.log(COALESCED_EVENT, String.format("Coalesced notification %s", sbn.getKey()));
+ mLogger.logEventCoalesced(sbn.getKey());
mHandler.onNotificationRankingUpdate(rankingMap);
} else {
mHandler.onNotificationPosted(sbn, rankingMap);
@@ -164,15 +159,11 @@ public class GroupCoalescer implements Dumpable {
final CoalescedEvent event = mCoalescedEvents.get(sbn.getKey());
final EventBatch batch = mBatches.get(sbn.getGroupKey());
if (event != null) {
- mLog.log(EARLY_BATCH_EMIT,
- String.format("Modification of %s triggered early emit of batched group %s",
- sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey));
+ mLogger.logEarlyEmit(sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey);
emitBatch(requireNonNull(event.getBatch()));
} else if (batch != null
&& mClock.uptimeMillis() - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
- mLog.log(BATCH_MAX_TIMEOUT,
- String.format("Modification of %s triggered timeout emit of batched group %s",
- sbn.getKey(), batch.mGroupKey));
+ mLogger.logMaxBatchTimeout(sbn.getKey(), batch.mGroupKey);
emitBatch(batch);
}
}
@@ -253,7 +244,7 @@ public class GroupCoalescer implements Dumpable {
}
events.sort(mEventComparator);
- mLog.log(EMIT_EVENT_BATCH, "Emitting event batch for group " + batch.mGroupKey);
+ mLogger.logEmitBatch(batch.mGroupKey);
mHandler.onNotificationBatchPosted(events);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
new file mode 100644
index 000000000000..6e8788db59d7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coalescer
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class GroupCoalescerLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logEventCoalesced(key: String) {
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = key
+ }, {
+ "COALESCED: $str1"
+ })
+ }
+
+ fun logEmitBatch(groupKey: String) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = groupKey
+ }, {
+ "Emitting event batch for group $str1"
+ })
+ }
+
+ fun logEarlyEmit(modifiedKey: String, groupKey: String) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = modifiedKey
+ str2 = groupKey
+ }, {
+ "Modification of notif $str1 triggered early emit of batched group $str2"
+ })
+ }
+
+ fun logMaxBatchTimeout(modifiedKey: String, groupKey: String) {
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = modifiedKey
+ str2 = groupKey
+ }, {
+ "Modification of notif $str1 triggered TIMEOUT emit of batched group $str2"
+ })
+ }
+}
+
+private const val TAG = "GroupCoalescer" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
index da119c1502c6..854444fc8bb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import android.app.Notification;
-import android.os.Handler;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -30,6 +29,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.HashMap;
import java.util.Map;
@@ -47,6 +48,8 @@ import javax.inject.Singleton;
* frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceController
* frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener
* frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender
+ *
+ * TODO: AppOps stuff should be spun off into its own coordinator
*/
@Singleton
public class ForegroundCoordinator implements Coordinator {
@@ -54,7 +57,7 @@ public class ForegroundCoordinator implements Coordinator {
private final ForegroundServiceController mForegroundServiceController;
private final AppOpsController mAppOpsController;
- private final Handler mMainHandler;
+ private final DelayableExecutor mMainExecutor;
private NotifPipeline mNotifPipeline;
@@ -62,10 +65,10 @@ public class ForegroundCoordinator implements Coordinator {
public ForegroundCoordinator(
ForegroundServiceController foregroundServiceController,
AppOpsController appOpsController,
- @Main Handler mainHandler) {
+ @Main DelayableExecutor mainExecutor) {
mForegroundServiceController = foregroundServiceController;
mAppOpsController = appOpsController;
- mMainHandler = mainHandler;
+ mMainExecutor = mainExecutor;
}
@Override
@@ -78,11 +81,11 @@ public class ForegroundCoordinator implements Coordinator {
// listen for new notifications to add appOps
mNotifPipeline.addCollectionListener(mNotifCollectionListener);
- // when appOps change, update any relevant notifications to update appOps for
- mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
-
// filter out foreground service notifications that aren't necessary anymore
mNotifPipeline.addPreGroupFilter(mNotifFilter);
+
+ // when appOps change, update any relevant notifications to update appOps for
+ mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
}
/**
@@ -93,7 +96,8 @@ public class ForegroundCoordinator implements Coordinator {
public boolean shouldFilterOut(NotificationEntry entry, long now) {
StatusBarNotification sbn = entry.getSbn();
if (mForegroundServiceController.isDisclosureNotification(sbn)
- && !mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId())) {
+ && !mForegroundServiceController.isDisclosureNeededForUser(
+ sbn.getUser().getIdentifier())) {
return true;
}
@@ -102,7 +106,7 @@ public class ForegroundCoordinator implements Coordinator {
Notification.EXTRA_FOREGROUND_APPS);
if (apps != null && apps.length >= 1) {
if (!mForegroundServiceController.isSystemAlertWarningNeeded(
- sbn.getUserId(), apps[0])) {
+ sbn.getUser().getIdentifier(), apps[0])) {
return true;
}
}
@@ -119,7 +123,7 @@ public class ForegroundCoordinator implements Coordinator {
new NotifLifetimeExtender() {
private static final int MIN_FGS_TIME_MS = 5000;
private OnEndLifetimeExtensionCallback mEndCallback;
- private Map<String, Runnable> mEndRunnables = new HashMap<>();
+ private Map<NotificationEntry, Runnable> mEndRunnables = new HashMap<>();
@Override
public String getName() {
@@ -142,16 +146,18 @@ public class ForegroundCoordinator implements Coordinator {
final boolean extendLife = currTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS;
if (extendLife) {
- if (!mEndRunnables.containsKey(entry.getKey())) {
- final Runnable runnable = new Runnable() {
- @Override
- public void run() {
- mEndCallback.onEndLifetimeExtension(mForegroundLifetimeExtender, entry);
- }
+ if (!mEndRunnables.containsKey(entry)) {
+ final Runnable endExtensionRunnable = () -> {
+ mEndRunnables.remove(entry);
+ mEndCallback.onEndLifetimeExtension(
+ mForegroundLifetimeExtender,
+ entry);
};
- mEndRunnables.put(entry.getKey(), runnable);
- mMainHandler.postDelayed(runnable,
+
+ final Runnable cancelRunnable = mMainExecutor.executeDelayed(
+ endExtensionRunnable,
MIN_FGS_TIME_MS - (currTime - entry.getSbn().getPostTime()));
+ mEndRunnables.put(entry, cancelRunnable);
}
}
@@ -160,9 +166,9 @@ public class ForegroundCoordinator implements Coordinator {
@Override
public void cancelLifetimeExtension(NotificationEntry entry) {
- if (mEndRunnables.containsKey(entry.getKey())) {
- Runnable endRunnable = mEndRunnables.remove(entry.getKey());
- mMainHandler.removeCallbacks(endRunnable);
+ Runnable cancelRunnable = mEndRunnables.remove(entry);
+ if (cancelRunnable != null) {
+ cancelRunnable.run();
}
}
};
@@ -184,25 +190,32 @@ public class ForegroundCoordinator implements Coordinator {
private void tagForeground(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
// note: requires that the ForegroundServiceController is updating their appOps first
- ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(sbn.getUserId(),
- sbn.getPackageName());
+ ArraySet<Integer> activeOps =
+ mForegroundServiceController.getAppOps(
+ sbn.getUser().getIdentifier(),
+ sbn.getPackageName());
if (activeOps != null) {
- synchronized (entry.mActiveAppOps) {
- entry.mActiveAppOps.clear();
- entry.mActiveAppOps.addAll(activeOps);
- }
+ entry.mActiveAppOps.clear();
+ entry.mActiveAppOps.addAll(activeOps);
}
}
};
+ private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
+ mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active));
+ }
+
/**
* Update the appOp for the posted notification associated with the current foreground service
+ *
* @param code code for appOp to add/remove
* @param uid of user the notification is sent to
* @param packageName package that created the notification
* @param active whether the appOpCode is active or not
*/
- private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
+ private void handleAppOpsChanged(int code, int uid, String packageName, boolean active) {
+ Assert.isMainThread();
+
int userId = UserHandle.getUserId(uid);
// Update appOp if there's an associated posted notification:
@@ -214,15 +227,13 @@ public class ForegroundCoordinator implements Coordinator {
&& uid == entry.getSbn().getUid()
&& packageName.equals(entry.getSbn().getPackageName())) {
boolean changed;
- synchronized (entry.mActiveAppOps) {
- if (active) {
- changed = entry.mActiveAppOps.add(code);
- } else {
- changed = entry.mActiveAppOps.remove(code);
- }
+ if (active) {
+ changed = entry.mActiveAppOps.add(code);
+ } else {
+ changed = entry.mActiveAppOps.remove(code);
}
if (changed) {
- mMainHandler.post(mNotifFilter::invalidateList);
+ mNotifFilter.invalidateList();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
new file mode 100644
index 000000000000..6e15043973f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import javax.inject.Inject
+
+class ShadeListBuilderLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logOnBuildList() {
+ buffer.log(TAG, INFO, {
+ }, {
+ "Request received from NotifCollection"
+ })
+ }
+
+ fun logStartBuildList(iterationCount: Int) {
+ buffer.log(TAG, INFO, {
+ int1 = iterationCount
+ }, {
+ "Starting to build shade list (run #$int1)"
+ })
+ }
+
+ fun logEndBuildList(iterationCount: Int) {
+ buffer.log(TAG, INFO, {
+ int1 = iterationCount
+ }, {
+ "Finished building shade list (run #$int1)"
+ })
+ }
+
+ fun logPreGroupFilterInvalidated(filterName: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = filterName
+ int1 = pipelineState
+ }, {
+ """Pre-group NotifFilter "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logPromoterInvalidated(name: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = name
+ int1 = pipelineState
+ }, {
+ """NotifPromoter "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logNotifSectionInvalidated(name: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = name
+ int1 = pipelineState
+ }, {
+ """NotifSection "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logNotifComparatorInvalidated(name: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = name
+ int1 = pipelineState
+ }, {
+ """NotifComparator "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logPreRenderFilterInvalidated(name: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = name
+ int1 = pipelineState
+ }, {
+ """Pre-render NotifFilter "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logDuplicateSummary(groupKey: String, existingKey: String, newKey: String) {
+ buffer.log(TAG, WARNING, {
+ str1 = groupKey
+ str2 = existingKey
+ str3 = newKey
+ }, {
+ """Duplicate summary for group "$str1": "$str2" vs. "$str3""""
+ })
+ }
+
+ fun logDuplicateTopLevelKey(topLevelKey: String) {
+ buffer.log(TAG, WARNING, {
+ str1 = topLevelKey
+ }, {
+ "Duplicate top-level key: $str1"
+ })
+ }
+
+ fun logParentChanged(key: String, prevParent: String?, newParent: String?) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ str2 = prevParent
+ str3 = newParent
+ }, {
+ "Parent change for $str1: $str2 -> $str3"
+ })
+ }
+
+ fun logFilterChanged(
+ key: String,
+ prevFilter: String?,
+ newFilter: String?
+ ) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ str2 = prevFilter
+ str3 = newFilter
+ }, {
+ "Filter changed for $str1: $str2 -> $str3"
+ })
+ }
+
+ fun logPromoterChanged(
+ key: String,
+ prevPromoter: String?,
+ newPromoter: String?
+ ) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ str2 = prevPromoter
+ str3 = newPromoter
+ }, {
+ "Promoter changed for $str1: $str2 -> $str3"
+ })
+ }
+
+ fun logSectionChanged(
+ key: String,
+ prevSection: String?,
+ prevIndex: Int,
+ section: String,
+ index: Int
+ ) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ str2 = section
+ int1 = index
+ str3 = prevSection
+ int2 = prevIndex
+ }, {
+ if (str3 == null) {
+ "Section assigned for $str1: '$str2' (#$int1)"
+ } else {
+ "Section changed for $str1: '$str3' (#$int2) -> '$str2' (#$int1)"
+ }
+ })
+ }
+
+ fun logFinalList(entries: List<ListEntry>) {
+ buffer.log(TAG, DEBUG, {
+ int1 = entries.size
+ }, {
+ "List is finalized ($int1 top-level entries):"
+ })
+ if (entries.isEmpty()) {
+ buffer.log(TAG, DEBUG, {}, { "(empty list)" })
+ }
+ for (i in entries.indices) {
+ val entry = entries[i]
+ buffer.log(TAG, DEBUG, {
+ int1 = i
+ str1 = entry.key
+ }, {
+ "[$int1] $str1"
+ })
+
+ if (entry is GroupEntry) {
+ entry.summary?.let {
+ buffer.log(TAG, DEBUG, {
+ str1 = it.key
+ }, {
+ " [*] $str1 (summary)"
+ })
+ }
+ for (j in entry.children.indices) {
+ val child = entry.children[j]
+ buffer.log(TAG, DEBUG, {
+ int1 = j
+ str1 = child.key
+ }, {
+ " [$int1] $str1"
+ })
+ }
+ }
+ }
+ }
+}
+
+private const val TAG = "ShadeListBuilder" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index 9cbc7d7efa66..ff6da12bd0bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -16,15 +16,27 @@
package com.android.systemui.statusbar.notification.collection.notifcollection;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import android.service.notification.NotificationListenerService;
+
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
- * Listener interface for {@link NotifCollection}.
+ * Listener interface for {@link NotificationEntry} events.
*/
public interface NotifCollectionListener {
/**
+ * Called whenever a new {@link NotificationEntry} is initialized. This should be used for
+ * initializing any decorated state tied to the notification.
+ *
+ * Do not reference other registered {@link NotifCollectionListener} implementations here as
+ * there is no guarantee of order and they may not have had a chance to initialize yet. Instead,
+ * use {@link #onEntryAdded} which is called after all initialization.
+ */
+ default void onEntryInit(NotificationEntry entry) {
+ }
+
+ /**
* Called whenever a notification with a new key is posted.
*/
default void onEntryAdded(NotificationEntry entry) {
@@ -47,4 +59,23 @@ public interface NotifCollectionListener {
@CancellationReason int reason,
boolean removedByUser) {
}
+
+ /**
+ * Called whenever a {@link NotificationEntry} is considered deleted. This should be used for
+ * cleaning up any state tied to the notification.
+ *
+ * This is the deletion parallel of {@link #onEntryInit} and similarly means that you cannot
+ * expect other {@link NotifCollectionListener} implementations to still have valid state for
+ * the entry during this call. Instead, use {@link #onEntryRemoved} which will be called before
+ * deletion.
+ */
+ default void onEntryCleanUp(NotificationEntry entry) {
+ }
+
+ /**
+ * Called whenever the RankingMap is updated by system server. By the time this listener is
+ * called, the Rankings of all entries will have been updated.
+ */
+ default void onRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
new file mode 100644
index 000000000000..0d0a46adb41f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.notifcollection
+
+import android.service.notification.NotificationListenerService.RankingMap
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class NotifCollectionLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logNotifPosted(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ }, {
+ "POSTED $str1"
+ })
+ }
+
+ fun logNotifGroupPosted(groupKey: String, batchSize: Int) {
+ buffer.log(TAG, INFO, {
+ str1 = groupKey
+ int1 = batchSize
+ }, {
+ "POSTED GROUP $str1 ($int1 events)"
+ })
+ }
+
+ fun logNotifUpdated(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ }, {
+ "UPDATED $str1"
+ })
+ }
+
+ fun logNotifRemoved(key: String, reason: Int) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ int1 = reason
+ }, {
+ "REMOVED $str1 reason=$int1"
+ })
+ }
+
+ fun logRankingMissing(key: String, rankingMap: RankingMap) {
+ buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
+ buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
+ for (entry in rankingMap.orderedKeys) {
+ buffer.log(TAG, DEBUG, { str1 = entry }, { " $str1" })
+ }
+ }
+}
+
+private const val TAG = "NotifCollection" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index b49611688bc7..0d9beaefbf8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -63,6 +63,10 @@ public class HighPriorityProvider {
}
final NotificationEntry notifEntry = entry.getRepresentativeEntry();
+ if (notifEntry == null) {
+ return false;
+ }
+
return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
|| hasHighPriorityCharacteristics(notifEntry)
|| hasHighPriorityChild(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
new file mode 100644
index 000000000000..c7666e47d4b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.dagger;
+
+import android.content.Context;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
+import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
+
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+
+/** Module for classes related to the notifications data pipeline */
+@Module
+public class NotificationsModule {
+ /** Initializes the notification data pipeline (can be disabled via config). */
+ @Singleton
+ @Provides
+ static NotificationsController provideNotificationsController(
+ Context context,
+ Lazy<NotificationsControllerImpl> realController,
+ Lazy<NotificationsControllerStub> stubController) {
+ if (context.getResources().getBoolean(R.bool.config_renderNotifications)) {
+ return realController.get();
+ } else {
+ return stubController.get();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
new file mode 100644
index 000000000000..9da8b8a3fd92
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.init
+
+import android.service.notification.StatusBarNotification
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.StatusBar
+import java.io.FileDescriptor
+import java.io.PrintWriter
+
+/**
+ * The master controller for all notifications-related work
+ *
+ * Split into two implementations: [NotificationsControllerImpl] (most cases) and
+ * [NotificationsControllerStub] (for builds that disable notification rendering).
+ */
+interface NotificationsController {
+ fun initialize(
+ statusBar: StatusBar,
+ presenter: NotificationPresenter,
+ listContainer: NotificationListContainer,
+ notificationActivityStarter: NotificationActivityStarter,
+ bindRowCallback: NotificationRowBinderImpl.BindRowCallback
+ )
+
+ fun requestNotificationUpdate(reason: String)
+ fun resetUserExpandedStates()
+ fun setNotificationSnoozed(sbn: StatusBarNotification, snoozeOption: SnoozeOption)
+ fun getActiveNotificationsCount(): Int
+ fun setNotificationSnoozed(sbn: StatusBarNotification, hoursToSnooze: Int)
+ fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>, dumpTruck: Boolean)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
new file mode 100644
index 000000000000..61e3192eba3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.init
+
+import android.service.notification.StatusBarNotification
+import com.android.systemui.bubbles.BubbleController
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.NotificationListener
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.NotificationClicker
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.NotificationListController
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
+import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper
+import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.RemoteInputUriController
+import dagger.Lazy
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.util.Optional
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Master controller for all notifications-related work
+ *
+ * At the moment exposes a number of event-handler-esque methods; these are for historical reasons.
+ * Once we migrate away from the need for such things, this class becomes primarily a place to do
+ * any initialization work that notifications require.
+ */
+@Singleton
+class NotificationsControllerImpl @Inject constructor(
+ private val featureFlags: FeatureFlags,
+ private val notificationListener: NotificationListener,
+ private val entryManager: NotificationEntryManager,
+ private val newNotifPipeline: Lazy<NotifPipelineInitializer>,
+ private val deviceProvisionedController: DeviceProvisionedController,
+ private val notificationRowBinder: NotificationRowBinderImpl,
+ private val remoteInputUriController: RemoteInputUriController,
+ private val bubbleController: BubbleController,
+ private val groupManager: NotificationGroupManager,
+ private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper,
+ private val headsUpManager: HeadsUpManager
+) : NotificationsController {
+
+ override fun initialize(
+ statusBar: StatusBar,
+ presenter: NotificationPresenter,
+ listContainer: NotificationListContainer,
+ notificationActivityStarter: NotificationActivityStarter,
+ bindRowCallback: NotificationRowBinderImpl.BindRowCallback
+ ) {
+ notificationListener.registerAsSystemService()
+
+ val listController =
+ NotificationListController(
+ entryManager,
+ listContainer,
+ deviceProvisionedController)
+ listController.bind()
+
+ notificationRowBinder.setNotificationClicker(
+ NotificationClicker(
+ Optional.of(statusBar),
+ bubbleController,
+ notificationActivityStarter))
+ notificationRowBinder.setUpWithPresenter(
+ presenter,
+ listContainer,
+ headsUpManager,
+ bindRowCallback)
+
+ if (featureFlags.isNewNotifPipelineEnabled) {
+ newNotifPipeline.get().initialize(notificationListener, notificationRowBinder)
+ }
+
+ if (featureFlags.isNewNotifPipelineRenderingEnabled) {
+ // TODO
+ } else {
+ notificationRowBinder.setInflationCallback(entryManager)
+
+ remoteInputUriController.attach(entryManager)
+ groupAlertTransferHelper.bind(entryManager, groupManager)
+ headsUpManager.addListener(groupManager)
+ headsUpManager.addListener(groupAlertTransferHelper)
+ groupManager.setHeadsUpManager(headsUpManager)
+ groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
+
+ entryManager.attach(notificationListener)
+ }
+ }
+
+ override fun dump(
+ fd: FileDescriptor,
+ pw: PrintWriter,
+ args: Array<String>,
+ dumpTruck: Boolean
+ ) {
+ if (dumpTruck) {
+ entryManager.dump(pw, " ")
+ }
+ groupManager.dump(fd, pw, args)
+ }
+
+ // TODO: Convert all functions below this line into listeners instead of public methods
+
+ override fun requestNotificationUpdate(reason: String) {
+ entryManager.updateNotifications(reason)
+ }
+
+ override fun resetUserExpandedStates() {
+ for (entry in entryManager.visibleNotifications) {
+ entry.resetUserExpansion()
+ }
+ }
+
+ override fun setNotificationSnoozed(sbn: StatusBarNotification, snoozeOption: SnoozeOption) {
+ if (snoozeOption.snoozeCriterion != null) {
+ notificationListener.snoozeNotification(sbn.key, snoozeOption.snoozeCriterion.id)
+ } else {
+ notificationListener.snoozeNotification(
+ sbn.key,
+ snoozeOption.minutesToSnoozeFor * 60 * 1000.toLong())
+ }
+ }
+
+ override fun getActiveNotificationsCount(): Int {
+ return entryManager.activeNotificationsCount
+ }
+
+ override fun setNotificationSnoozed(sbn: StatusBarNotification, hoursToSnooze: Int) {
+ notificationListener.snoozeNotification(
+ sbn.key,
+ hoursToSnooze * 60 * 60 * 1000.toLong())
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
new file mode 100644
index 000000000000..ded855dd84f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.init
+
+import android.service.notification.StatusBarNotification
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
+import com.android.systemui.statusbar.NotificationListener
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.StatusBar
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Implementation of [NotificationsController] that's used when notifications rendering is disabled.
+ */
+class NotificationsControllerStub @Inject constructor(
+ private val notificationListener: NotificationListener
+) : NotificationsController {
+
+ override fun initialize(
+ statusBar: StatusBar,
+ presenter: NotificationPresenter,
+ listContainer: NotificationListContainer,
+ notificationActivityStarter: NotificationActivityStarter,
+ bindRowCallback: NotificationRowBinderImpl.BindRowCallback
+ ) {
+ // Always connect the listener even if notification-handling is disabled. Being a listener
+ // grants special permissions and it's not clear if other things will break if we lose those
+ notificationListener.registerAsSystemService()
+ }
+
+ override fun requestNotificationUpdate(reason: String) {
+ }
+
+ override fun resetUserExpandedStates() {
+ }
+
+ override fun setNotificationSnoozed(sbn: StatusBarNotification, snoozeOption: SnoozeOption) {
+ }
+
+ override fun setNotificationSnoozed(sbn: StatusBarNotification, hoursToSnooze: Int) {
+ }
+
+ override fun getActiveNotificationsCount(): Int {
+ return 0
+ }
+
+ override fun dump(
+ fd: FileDescriptor,
+ pw: PrintWriter,
+ args: Array<String>,
+ dumpTruck: Boolean
+ ) {
+ pw.println()
+ pw.println("Notification handling disabled")
+ pw.println()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 315ea0a49bb7..4f27c0f04c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -35,6 +35,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.Window
import android.view.WindowInsets.Type
+import android.view.WindowInsets.Type.statusBars
import android.view.WindowManager
import android.widget.TextView
import com.android.internal.annotations.VisibleForTesting
@@ -288,13 +289,13 @@ class ChannelEditorDialogController @Inject constructor(
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
addFlags(wmFlags)
setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL)
- setFitWindowInsetsTypes(getFitWindowInsetsTypes() and Type.statusBars().inv())
setWindowAnimations(com.android.internal.R.style.Animation_InputMethod)
attributes = attributes.apply {
format = PixelFormat.TRANSLUCENT
title = ChannelEditorDialogController::class.java.simpleName
gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ fitInsetsTypes = attributes.fitInsetsTypes and statusBars().inv()
width = MATCH_PARENT
height = WRAP_CONTENT
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 6aadcb7b30e1..049cafa4ccde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -92,10 +92,8 @@ public abstract class ExpandableOutlineView extends ExpandableView {
outline.setRect(left, top, right, bottom);
} else {
Path clipPath = getClipPath(false /* ignoreTranslation */);
- if (clipPath != null && clipPath.isConvex()) {
- // The path might not be convex in border cases where the view is small and
- // clipped
- outline.setConvexPath(clipPath);
+ if (clipPath != null) {
+ outline.setPath(clipPath);
}
}
outline.setAlpha(mOutlineAlpha);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 18c755da53ff..6045524f30b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -29,6 +29,7 @@ import static com.android.systemui.statusbar.notification.row.NotificationConver
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_HOME;
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_MUTE;
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_SNOOZE;
+import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_UNBUBBLE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -121,7 +122,7 @@ public class NotificationConversationInfo extends LinearLayout implements
boolean mSkipPost = false;
private OnClickListener mOnBubbleClick = v -> {
- mSelectedAction = ACTION_BUBBLE;
+ mSelectedAction = mStartedAsBubble ? ACTION_UNBUBBLE : ACTION_BUBBLE;
if (mStartedAsBubble) {
mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
} else {
@@ -586,6 +587,7 @@ public class NotificationConversationInfo extends LinearLayout implements
static final int ACTION_SNOOZE = 3;
static final int ACTION_MUTE = 4;
static final int ACTION_DEMOTE = 5;
+ static final int ACTION_UNBUBBLE = 6;
private final INotificationManager mINotificationManager;
private final String mAppPkg;
@@ -606,9 +608,17 @@ public class NotificationConversationInfo extends LinearLayout implements
@Override
public void run() {
try {
+ boolean channelSettingChanged = mAction != ACTION_HOME && mAction != ACTION_SNOOZE;
switch (mAction) {
case ACTION_BUBBLE:
- mChannelToUpdate.setAllowBubbles(!mChannelToUpdate.canBubble());
+ case ACTION_UNBUBBLE:
+ boolean canBubble = mAction == ACTION_BUBBLE;
+ if (mChannelToUpdate.canBubble() != canBubble) {
+ channelSettingChanged = true;
+ mChannelToUpdate.setAllowBubbles(canBubble);
+ } else {
+ channelSettingChanged = false;
+ }
break;
case ACTION_FAVORITE:
// TODO: extend beyond DND
@@ -629,7 +639,7 @@ public class NotificationConversationInfo extends LinearLayout implements
}
- if (mAction != ACTION_HOME && mAction != ACTION_SNOOZE) {
+ if (channelSettingChanged) {
mINotificationManager.updateNotificationChannelForPackage(
mAppPkg, mAppUid, mChannelToUpdate);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index a3e13053d169..fa4bc2aba21a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -80,7 +80,15 @@ public class NotificationInlineImageResolver implements ImageResolver {
public Drawable loadImage(Uri uri) {
Drawable result = null;
try {
- result = hasCache() ? mImageCache.get(uri) : resolveImage(uri);
+ if (hasCache()) {
+ // if the uri isn't currently cached, try caching it first
+ if (!mImageCache.hasEntry(uri)) {
+ mImageCache.preload((uri));
+ }
+ result = mImageCache.get(uri);
+ } else {
+ result = resolveImage(uri);
+ }
} catch (IOException | SecurityException ex) {
Log.d(TAG, "loadImage: Can't load image from " + uri, ex);
}
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 11ead8bd2576..84a293eebc95 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
@@ -35,6 +35,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -56,6 +57,7 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.ContextThemeWrapper;
import android.view.InputDevice;
import android.view.LayoutInflater;
@@ -99,6 +101,7 @@ 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.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.RemoteInputController;
@@ -328,6 +331,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
return true;
}
};
+ private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() {
+ @Override
+ public void onUserChanged(int userId) {
+ updateSensitiveness(false /* animated */);
+ }
+ };
private StatusBar mStatusBar;
private int[] mTempInt2 = new int[2];
private boolean mGenerateChildOrderChangedEvent;
@@ -561,6 +570,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
addOnExpandedHeightChangedListener(mRoundnessManager::setExpanded);
+ mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
setOutlineProvider(mOutlineProvider);
// Blocking helper manager wants to know the expanded state, update as well.
@@ -4611,7 +4621,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void setHideSensitive(boolean hideSensitive, boolean animate) {
+ private void updateSensitiveness(boolean animate) {
+ boolean hideSensitive = mLockscreenUserManager.isAnyProfilePublicMode();
if (hideSensitive != mAmbientState.isHideSensitive()) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -5306,7 +5317,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private void onStatePostChange() {
boolean onKeyguard = onKeyguard();
- boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
if (mHeadsUpAppearanceController != null) {
mHeadsUpAppearanceController.onStateChanged();
@@ -5314,7 +5324,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
SysuiStatusBarStateController state = (SysuiStatusBarStateController)
Dependency.get(StatusBarStateController.class);
- setHideSensitive(publicMode, state.goingToFullShade() /* animate */);
+ updateSensitiveness(state.goingToFullShade() /* animate */);
setDimmed(onKeyguard, state.fromShadeLocked() /* animate */);
setExpandingEnabled(!onKeyguard);
ActivatableNotificationView activatedChild = getActivatedChild();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index afaa593b3bb9..e7d6eba1dcb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -22,7 +22,6 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
-import com.android.systemui.doze.DozeEvent;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -53,7 +52,7 @@ public class DozeScrimController implements StateListener {
public void onDisplayBlanked() {
if (DEBUG) {
Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
- + DozeEvent.reasonToString(mPulseReason));
+ + DozeLog.reasonToString(mPulseReason));
}
if (!mDozing) {
return;
@@ -74,8 +73,8 @@ public class DozeScrimController implements StateListener {
// Notifications should time out on their own. Pulses due to notifications should
// instead be managed externally based off the notification's lifetime.
// Dock also controls the time out by self.
- if (mPulseReason != DozeEvent.PULSE_REASON_NOTIFICATION
- && mPulseReason != DozeEvent.PULSE_REASON_DOCKING) {
+ if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION
+ && mPulseReason != DozeLog.PULSE_REASON_DOCKING) {
mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
mHandler.postDelayed(mPulseOutExtended,
mDozeParameters.getPulseVisibleDurationExtended());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index dcf7d9873ef9..02d6c2e7f758 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -31,7 +31,6 @@ import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.doze.DozeEvent;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.doze.DozeReceiver;
@@ -223,18 +222,18 @@ public final class DozeServiceHost implements DozeHost {
@Override
public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
- if (reason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS) {
+ if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:LONG_PRESS");
mAssistManagerLazy.get().startAssist(new Bundle());
return;
}
- if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
mScrimController.setWakeLockScreenSensorActive(true);
}
- boolean passiveAuthInterrupt = reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
+ boolean passiveAuthInterrupt = reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
&& mWakeLockScreenPerformsAuth;
// Set the state to pulsing, so ScrimController will know what to do once we ask it to
// execute the transition. The pulse callback will then be invoked when the scrims
@@ -334,7 +333,7 @@ public final class DozeServiceHost implements DozeHost {
@Override
public void extendPulse(int reason) {
- if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
mScrimController.setWakeLockScreenSensorActive(true);
}
if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index ef581db0b053..db692c8a8c89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -54,6 +54,10 @@ import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.android.systemui.tracing.ProtoTracer;
+import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
+import com.android.systemui.tracing.nano.SystemUiTraceProto;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -62,7 +66,7 @@ import java.util.concurrent.Executor;
* Utility class to handle edge swipes for back gesture
*/
public class EdgeBackGestureHandler implements DisplayListener,
- PluginListener<NavigationEdgeBackPlugin> {
+ PluginListener<NavigationEdgeBackPlugin>, ProtoTraceable<SystemUiTraceProto> {
private static final String TAG = "EdgeBackGestureHandler";
private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
@@ -161,6 +165,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
mMainExecutor = context.getMainExecutor();
mOverviewProxyService = overviewProxyService;
mPluginManager = pluginManager;
+ Dependency.get(ProtoTracer.class).add(this);
// Reduce the default touch slop to ensure that we can intercept the gesture
// before the app starts to react to it.
@@ -304,7 +309,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
layoutParams.setTitle(TAG + mContext.getDisplayId());
layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
layoutParams.windowAnimations = 0;
- layoutParams.setFitWindowInsetsTypes(0 /* types */);
+ layoutParams.setFitInsetsTypes(0 /* types */);
return layoutParams;
}
@@ -399,6 +404,8 @@ public class EdgeBackGestureHandler implements DisplayListener,
// forward touch
mEdgeBackPlugin.onMotionEvent(ev);
}
+
+ Dependency.get(ProtoTracer.class).update();
}
@Override
@@ -458,6 +465,14 @@ public class EdgeBackGestureHandler implements DisplayListener,
pw.println(" mEdgeWidth=" + mEdgeWidth);
}
+ @Override
+ public void writeToProto(SystemUiTraceProto proto) {
+ if (proto.edgeBackGestureHandler == null) {
+ proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto();
+ }
+ proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
+ }
+
class SysUiInputEventReceiver extends InputEventReceiver {
SysUiInputEventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index 783e7adf2a8b..16b5a2389ec6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -84,7 +84,7 @@ public class FloatingRotationButton implements RotationButton {
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("FloatingRotationButton");
- lp.setFitWindowInsetsTypes(0 /*types */);
+ lp.setFitInsetsTypes(0 /*types */);
switch (mWindowManager.getDefaultDisplay().getRotation()) {
case Surface.ROTATION_0:
lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index ad1aa8370495..b4d0d479ff39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -33,7 +33,7 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
-class KeyguardBypassController : Dumpable {
+open class KeyguardBypassController : Dumpable {
private val mKeyguardStateController: KeyguardStateController
private val statusBarStateController: StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
deleted file mode 100644
index 53601babfd56..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone
-
-import android.content.Context
-import android.view.View
-import android.view.ViewGroup.MarginLayoutParams
-import android.widget.FrameLayout
-import com.android.systemui.plugins.NPVPlugin
-import com.android.systemui.plugins.PluginListener
-import com.android.systemui.qs.TouchAnimator
-import com.android.systemui.shared.plugins.PluginManager
-
-/**
- * Manages the NPVPlugin view and state
- *
- * Abstracts NPVPlugin from NPV and helps animate on expansion and respond to changes in Config.
- */
-class NPVPluginManager(
- var parent: FrameLayout,
- val pluginManager: PluginManager
-) : PluginListener<NPVPlugin> {
-
- private var plugin: NPVPlugin? = null
- private var animator = createAnimator()
- private var yOffset = 0f
-
- private fun createAnimator() = TouchAnimator.Builder()
- .addFloat(parent, "alpha", 1f, 0f)
- .addFloat(parent, "scaleY", 1f, 0f)
- .build()
-
- init {
- pluginManager.addPluginListener(NPVPlugin.ACTION, this, NPVPlugin::class.java, false)
- parent.pivotY = 0f
- }
-
- override fun onPluginConnected(plugin: NPVPlugin, pluginContext: Context) {
- parent.removeAllViews()
- plugin.attachToRoot(parent)
- this.plugin = plugin
- parent.visibility = View.VISIBLE
- }
-
- fun changeVisibility(visibility: Int) {
- parent.visibility = if (plugin != null) visibility else View.GONE
- }
-
- fun destroy() {
- plugin?.onDestroy()
- pluginManager.removePluginListener(this)
- }
-
- override fun onPluginDisconnected(plugin: NPVPlugin) {
- if (this.plugin == plugin) {
- this.plugin = null
- parent.removeAllViews()
- parent.visibility = View.GONE
- }
- }
-
- fun setListening(listening: Boolean) {
- plugin?.setListening(listening)
- }
-
- fun setExpansion(expansion: Float, headerTranslation: Float, heightDiff: Float) {
- parent.setTranslationY(expansion * heightDiff + headerTranslation + yOffset)
- if (!expansion.isNaN()) animator.setPosition(expansion)
- }
-
- fun replaceFrameLayout(newParent: FrameLayout) {
- newParent.visibility = parent.visibility
- parent.removeAllViews()
- plugin?.attachToRoot(newParent)
- parent = newParent
- animator = createAnimator()
- }
-
- fun getHeight() =
- if (plugin != null) {
- parent.height + (parent.getLayoutParams() as MarginLayoutParams).topMargin
- } else 0
-
- fun setYOffset(y: Float) {
- yOffset = y
- parent.setTranslationY(yOffset)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 3e3ef0ccb8ce..9e64748f2e65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -302,14 +302,14 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_FORCE_OPAQUE,
- /* defaultValue = */ false);
+ /* defaultValue = */ true);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
mForceNavBarHandleOpaque = properties.getBoolean(
- NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ false);
+ NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index d6336ed3e18a..826af669cb39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -387,7 +387,7 @@ public class NavigationModeController implements Dumpable {
Log.d(TAG, "setModeOverlay: overlayPackage=" + overlayPkg
+ " userId=" + userId);
}
- } catch (RemoteException e) {
+ } catch (SecurityException | IllegalStateException | RemoteException e) {
Log.e(TAG, "Failed to enable overlay " + overlayPkg + " for user " + userId);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8c947edd9a83..77337e95bb95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -204,8 +204,8 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
}
int childCount = 0;
boolean hasBubbles = false;
- for (String key : group.children.keySet()) {
- if (!getBubbleController().isBubbleNotificationSuppressedFromShade(key)) {
+ for (NotificationEntry entry : group.children.values()) {
+ if (!getBubbleController().isBubbleNotificationSuppressedFromShade(entry)) {
childCount++;
} else {
hasBubbles = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 707138ee8dc0..b09ccffdc4de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -1,5 +1,6 @@
package com.android.systemui.statusbar.phone;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
@@ -10,6 +11,7 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
import com.android.internal.statusbar.StatusBarIcon;
@@ -21,6 +23,7 @@ import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -76,6 +79,19 @@ public class NotificationIconAreaController implements DarkReceiver,
private boolean mFullyHidden;
private boolean mAodIconsVisible;
private boolean mIsPulsing;
+ private boolean mShowLowPriority = true;
+
+ @VisibleForTesting
+ final NotificationListener.NotificationSettingsListener mSettingsListener =
+ new NotificationListener.NotificationSettingsListener() {
+ @Override
+ public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
+ mShowLowPriority = !hideSilentStatusIcons;
+ if (mNotificationScrollLayout != null) {
+ updateStatusBarIcons();
+ }
+ }
+ };
public NotificationIconAreaController(
Context context,
@@ -84,6 +100,7 @@ public class NotificationIconAreaController implements DarkReceiver,
NotificationWakeUpCoordinator wakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
NotificationMediaManager notificationMediaManager,
+ NotificationListener notificationListener,
DozeParameters dozeParameters) {
mStatusBar = statusBar;
mContrastColorUtil = ContrastColorUtil.getInstance(context);
@@ -95,6 +112,7 @@ public class NotificationIconAreaController implements DarkReceiver,
mWakeUpCoordinator = wakeUpCoordinator;
wakeUpCoordinator.addListener(this);
mBypassController = keyguardBypassController;
+ notificationListener.addNotificationSettingsListener(mSettingsListener);
initializeNotificationAreaViews(context);
reloadAodColor();
@@ -230,7 +248,7 @@ public class NotificationIconAreaController implements DarkReceiver,
}
protected boolean shouldShowNotificationIcon(NotificationEntry entry,
- boolean showAmbient, boolean hideDismissed,
+ boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon,
boolean hidePulsing, boolean onlyShowCenteredIcon) {
@@ -249,6 +267,9 @@ public class NotificationIconAreaController implements DarkReceiver,
if (hideCurrentMedia && entry.getKey().equals(mMediaManager.getMediaNotificationKey())) {
return false;
}
+ if (!showLowPriority && entry.getImportance() < NotificationManager.IMPORTANCE_DEFAULT) {
+ return false;
+ }
if (!entry.isTopLevelChild()) {
return false;
}
@@ -288,6 +309,7 @@ public class NotificationIconAreaController implements DarkReceiver,
private void updateShelfIcons() {
updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
true /* showAmbient */,
+ true /* showLowPriority */,
false /* hideDismissed */,
false /* hideRepliedMessages */,
false /* hideCurrentMedia */,
@@ -299,6 +321,7 @@ public class NotificationIconAreaController implements DarkReceiver,
public void updateStatusBarIcons() {
updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
false /* showAmbient */,
+ mShowLowPriority,
true /* hideDismissed */,
true /* hideRepliedMessages */,
false /* hideCurrentMedia */,
@@ -310,6 +333,7 @@ public class NotificationIconAreaController implements DarkReceiver,
private void updateCenterIcon() {
updateIconsForLayout(entry -> entry.centeredIcon, mCenteredIcon,
false /* showAmbient */,
+ true /* showLowPriority */,
false /* hideDismissed */,
false /* hideRepliedMessages */,
false /* hideCurrentMedia */,
@@ -321,6 +345,7 @@ public class NotificationIconAreaController implements DarkReceiver,
public void updateAodNotificationIcons() {
updateIconsForLayout(entry -> entry.aodIcon, mAodIcons,
false /* showAmbient */,
+ true /* showLowPriority */,
true /* hideDismissed */,
true /* hideRepliedMessages */,
true /* hideCurrentMedia */,
@@ -329,18 +354,24 @@ public class NotificationIconAreaController implements DarkReceiver,
false /* onlyShowCenteredIcon */);
}
+ @VisibleForTesting
+ boolean shouldShouldLowPriorityIcons() {
+ return mShowLowPriority;
+ }
+
/**
* Updates the notification icons for a host layout. This will ensure that the notification
* host layout will have the same icons like the ones in here.
* @param function A function to look up an icon view based on an entry
* @param hostLayout which layout should be updated
* @param showAmbient should ambient notification icons be shown
+ * @param showLowPriority should icons from silent notifications be shown
* @param hideDismissed should dismissed icons be hidden
* @param hideRepliedMessages should messages that have been replied to be hidden
* @param hidePulsing should pulsing notifications be hidden
*/
private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
- NotificationIconContainer hostLayout, boolean showAmbient,
+ NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia,
boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon) {
ArrayList<StatusBarIconView> toShow = new ArrayList<>(
@@ -351,7 +382,7 @@ public class NotificationIconAreaController implements DarkReceiver,
View view = mNotificationScrollLayout.getChildAt(i);
if (view instanceof ExpandableNotificationRow) {
NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
- if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed,
+ if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing,
onlyShowCenteredIcon)) {
StatusBarIconView iconView = function.apply(ent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 6112ae88f634..d2186f959aba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -25,10 +25,8 @@ import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.Fragment;
import android.app.StatusBarManager;
-import android.content.Context;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -40,8 +38,6 @@ import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType;
import android.os.PowerManager;
import android.os.SystemClock;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
import android.view.LayoutInflater;
@@ -57,7 +53,6 @@ import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
@@ -73,13 +68,10 @@ import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.HomeControlsPlugin;
-import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.qs.QSFragment;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
@@ -113,7 +105,6 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.InjectionInflationController;
-import com.android.systemui.util.Utils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -173,8 +164,6 @@ public class NotificationPanelViewController extends PanelViewController {
private final ConfigurationController mConfigurationController;
private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
- private double mQqsSplitFraction;
-
// Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
// changed.
private static final int CAP_HEIGHT = 1456;
@@ -258,7 +247,6 @@ public class NotificationPanelViewController extends PanelViewController {
private View mQsNavbarScrim;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private NotificationStackScrollLayout mNotificationStackScroller;
- private FrameLayout mHomeControlsLayout;
private boolean mAnimateNextPositionUpdate;
private int mTrackingPointer;
@@ -446,9 +434,6 @@ public class NotificationPanelViewController extends PanelViewController {
*/
private boolean mDelayShowingKeyguardStatusBar;
- private PluginManager mPluginManager;
- private FrameLayout mPluginFrame;
- private NPVPluginManager mNPVPluginManager;
private int mOldLayoutDirection;
@Inject
@@ -457,7 +442,7 @@ public class NotificationPanelViewController extends PanelViewController {
NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
KeyguardBypassController bypassController, FalsingManager falsingManager,
- PluginManager pluginManager, ShadeController shadeController,
+ ShadeController shadeController,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationEntryManager notificationEntryManager,
KeyguardStateController keyguardStateController,
@@ -523,7 +508,6 @@ public class NotificationPanelViewController extends PanelViewController {
});
mBottomAreaShadeAlphaAnimator.setDuration(160);
mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
- mPluginManager = pluginManager;
mShadeController = shadeController;
mLockscreenUserManager = notificationLockscreenUserManager;
mEntryManager = notificationEntryManager;
@@ -553,7 +537,6 @@ public class NotificationPanelViewController extends PanelViewController {
mBigClockContainer = mView.findViewById(R.id.big_clock_container);
keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
- mHomeControlsLayout = mView.findViewById(R.id.home_controls_layout);
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller);
mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
@@ -563,12 +546,6 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim);
mLastOrientation = mResources.getConfiguration().orientation;
- mPluginFrame = mView.findViewById(R.id.plugin_frame);
- if (Settings.System.getInt(mView.getContext().getContentResolver(), "npv_plugin_flag", 0)
- == 1) {
- mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
- }
-
initBottomArea();
@@ -592,19 +569,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
});
- mPluginManager.addPluginListener(new PluginListener<HomeControlsPlugin>() {
-
- @Override
- public void onPluginConnected(HomeControlsPlugin plugin, Context pluginContext) {
- plugin.sendParentGroup(mHomeControlsLayout);
- }
-
- @Override
- public void onPluginDisconnected(HomeControlsPlugin plugin) {
-
- }
- }, HomeControlsPlugin.class, false);
-
mView.setRtlChangeListener(layoutDirection -> {
if (layoutDirection != mOldLayoutDirection) {
mAffordanceHelper.onRtlPropertiesChanged();
@@ -637,9 +601,6 @@ public class NotificationPanelViewController extends PanelViewController {
com.android.internal.R.dimen.status_bar_height);
mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
- mQqsSplitFraction = ((float) mResources.getInteger(R.integer.qqs_split_fraction)) / (
- mResources.getInteger(R.integer.qqs_split_fraction) + mResources.getInteger(
- R.integer.qs_split_fraction));
}
/**
@@ -679,18 +640,6 @@ public class NotificationPanelViewController extends PanelViewController {
lp.gravity = panelGravity;
mNotificationStackScroller.setLayoutParams(lp);
}
- int sideMargin = mResources.getDimensionPixelOffset(R.dimen.notification_side_paddings);
- int topMargin = sideMargin;
- lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
- if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
- || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
- lp.width = qsWidth;
- lp.gravity = panelGravity;
- lp.leftMargin = sideMargin;
- lp.rightMargin = sideMargin;
- lp.topMargin = topMargin;
- mPluginFrame.setLayoutParams(lp);
- }
}
private void reInflateViews() {
@@ -732,41 +681,6 @@ public class NotificationPanelViewController extends PanelViewController {
if (mOnReinflationListener != null) {
mOnReinflationListener.run();
}
- reinflatePluginContainer();
- }
-
- private void reinflatePluginContainer() {
- int index = mView.indexOfChild(mPluginFrame);
- mView.removeView(mPluginFrame);
- mPluginFrame = (FrameLayout) mInjectionInflationController.injectable(
- LayoutInflater.from(mView.getContext())).inflate(
- R.layout.status_bar_expanded_plugin_frame, mView, false);
- mView.addView(mPluginFrame, index);
-
- Resources res = mView.getResources();
- int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width);
- int panelGravity = mView.getResources().getInteger(
- R.integer.notification_panel_layout_gravity);
- FrameLayout.LayoutParams lp;
- int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
- int topMargin = res.getDimensionPixelOffset(
- com.android.internal.R.dimen.quick_qs_total_height);
- if (Utils.useQsMediaPlayer(mView.getContext())) {
- topMargin = res.getDimensionPixelOffset(
- com.android.internal.R.dimen.quick_qs_total_height_with_media);
- }
- lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
- if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
- || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
- lp.width = qsWidth;
- lp.gravity = panelGravity;
- lp.leftMargin = sideMargin;
- lp.rightMargin = sideMargin;
- lp.topMargin = topMargin;
- mPluginFrame.setLayoutParams(lp);
- }
-
- if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame);
}
private void initBottomArea() {
@@ -1266,17 +1180,6 @@ public class NotificationPanelViewController extends PanelViewController {
// earlier so the state is already up to date when dragging down.
setListening(true);
}
- if (isQsSplitEnabled() && !mKeyguardShowing) {
- if (mQsExpandImmediate) {
- mNotificationStackScroller.setVisibility(View.GONE);
- mQsFrame.setVisibility(View.VISIBLE);
- mHomeControlsLayout.setVisibility(View.VISIBLE);
- } else {
- mNotificationStackScroller.setVisibility(View.VISIBLE);
- mQsFrame.setVisibility(View.GONE);
- mHomeControlsLayout.setVisibility(View.GONE);
- }
- }
return false;
}
@@ -1286,17 +1189,6 @@ public class NotificationPanelViewController extends PanelViewController {
|| y <= mQs.getView().getY() + mQs.getView().getHeight());
}
- private boolean isOnQsEndArea(float x) {
- if (!isQsSplitEnabled()) return false;
- if (mView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
- return x >= mQsFrame.getX() + mQqsSplitFraction * mQsFrame.getWidth()
- && x <= mQsFrame.getX() + mQsFrame.getWidth();
- } else {
- return x >= mQsFrame.getX()
- && x <= mQsFrame.getX() + (1 - mQqsSplitFraction) * mQsFrame.getWidth();
- }
- }
-
private boolean isOpenQsEvent(MotionEvent event) {
final int pointerCount = event.getPointerCount();
final int action = event.getActionMasked();
@@ -1317,9 +1209,7 @@ public class NotificationPanelViewController extends PanelViewController {
MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed(
MotionEvent.BUTTON_TERTIARY));
- final boolean onHeaderRight = isOnQsEndArea(event.getX());
-
- return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag || onHeaderRight;
+ return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
}
private void handleQsDown(MotionEvent event) {
@@ -1659,10 +1549,7 @@ public class NotificationPanelViewController extends PanelViewController {
mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
|| mQsExpansionFromOverscroll));
updateEmptyShadeView();
- if (mNPVPluginManager != null) {
- mNPVPluginManager.changeVisibility(
- (mBarState != StatusBarState.KEYGUARD) ? View.VISIBLE : View.INVISIBLE);
- }
+
mQsNavbarScrim.setVisibility(
mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
&& mQsScrimEnabled ? View.VISIBLE : View.INVISIBLE);
@@ -1718,9 +1605,6 @@ public class NotificationPanelViewController extends PanelViewController {
float qsExpansionFraction = getQsExpansionFraction();
mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
- if (mNPVPluginManager != null) {
- mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
- }
mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
}
@@ -2015,7 +1899,6 @@ public class NotificationPanelViewController extends PanelViewController {
targetHeight =
mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
setQsExpansion(targetHeight);
- mHomeControlsLayout.setTranslationY(targetHeight);
}
updateExpandedHeight(expandedHeight);
updateHeader();
@@ -2150,7 +2033,6 @@ public class NotificationPanelViewController extends PanelViewController {
appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
}
startHeight = -mQs.getQsMinExpansionHeight();
- if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight();
}
float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount))
+ mExpandOffset;
@@ -2294,7 +2176,6 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusBar.setListening(listening);
if (mQs == null) return;
mQs.setListening(listening);
- if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
}
@Override
@@ -3029,11 +2910,6 @@ public class NotificationPanelViewController extends PanelViewController {
mOnReinflationListener = onReinflationListener;
}
- public static boolean isQsSplitEnabled() {
- return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.QS_SPLIT_ENABLED, false);
- }
-
public void setAlpha(float alpha) {
mView.setAlpha(alpha);
}
@@ -3500,9 +3376,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
@Override
- public void onUiModeChanged() {
- reinflatePluginContainer();
- }
+ public void onUiModeChanged() {}
}
private class StatusBarStateListener implements StateListener {
@@ -3517,11 +3391,6 @@ public class NotificationPanelViewController extends PanelViewController {
mBarState = statusBarState;
mKeyguardShowing = keyguardShowing;
- if (mKeyguardShowing && isQsSplitEnabled()) {
- mNotificationStackScroller.setVisibility(View.VISIBLE);
- mQsFrame.setVisibility(View.VISIBLE);
- mHomeControlsLayout.setVisibility(View.GONE);
- }
if (oldState == StatusBarState.KEYGUARD && (goingToFullShade
|| statusBarState == StatusBarState.SHADE_LOCKED)) {
@@ -3544,7 +3413,6 @@ public class NotificationPanelViewController extends PanelViewController {
} else {
mKeyguardStatusBar.setAlpha(1f);
mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
- ((PhoneStatusBarView) mBar).maybeShowDivider(keyguardShowing);
if (keyguardShowing && oldState != mBarState) {
if (mQs != null) {
mQs.hideImmediately();
@@ -3622,10 +3490,6 @@ public class NotificationPanelViewController extends PanelViewController {
int oldMaxHeight = mQsMaxExpansionHeight;
if (mQs != null) {
mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
- if (mNPVPluginManager != null) {
- mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
- mQsMinExpansionHeight += mNPVPluginManager.getHeight();
- }
mQsMaxExpansionHeight = mQs.getDesiredHeight();
mNotificationStackScroller.setMaxTopPadding(
mQsMaxExpansionHeight + mQsNotificationTopPadding);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index fe4879b0b071..10b68b9a518d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -16,8 +16,10 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
@@ -175,11 +177,18 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
- mLp.setFitWindowInsetsTypes(0 /* types */);
+ mLp.setFitInsetsTypes(0 /* types */);
mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("NotificationShade");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+ // We use BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE here, however, there is special logic in
+ // window manager which disables the transient show behavior.
+ // TODO: Clean this up once that behavior moves into the Shell.
+ mLp.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
+ mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
mWindowManager.addView(mNotificationShadeView, mLp);
mLpChanged.copyFrom(mLp);
onThemeChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index 6979554303b3..7650a3ab3a4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowInsets.Type.systemBars;
+
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.LayoutRes;
@@ -81,7 +83,7 @@ public class NotificationShadeWindowView extends FrameLayout {
@Override
public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
- final Insets insets = windowInsets.getMaxInsets(WindowInsets.Type.systemBars());
+ final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
if (getFitsSystemWindows()) {
boolean paddingChanged = insets.top != getPaddingTop()
|| insets.bottom != getPaddingBottom();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 9522d3574e17..98188bd4e0ba 100755
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -38,7 +38,6 @@ import android.telephony.TelephonyManager;
import android.text.format.DateFormat;
import android.util.Log;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -176,7 +175,7 @@ public class PhoneStatusBarPolicy
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
@@ -613,7 +612,7 @@ public class PhoneStatusBarPolicy
case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION:
updateVolumeZen();
break;
- case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
+ case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index 96b4b22d0580..e8bc2f58adb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -32,7 +32,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
private final PhoneStatusBarView mView;
private final float mIconAlphaWhenOpaque;
- private View mLeftSide, mStatusIcons, mBattery, mClock, mDivider;
+ private View mLeftSide, mStatusIcons, mBattery, mClock;
private Animator mCurrentAnimation;
public PhoneStatusBarTransitions(PhoneStatusBarView view) {
@@ -46,7 +46,6 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
mLeftSide = mView.findViewById(R.id.status_bar_left_side);
mStatusIcons = mView.findViewById(R.id.statusIcons);
mBattery = mView.findViewById(R.id.battery);
- mDivider = mView.findViewById(R.id.divider);
applyModeBackground(-1, getMode(), false /*animate*/);
applyMode(getMode(), false /*animate*/);
}
@@ -89,7 +88,6 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
anims.playTogether(
animateTransitionTo(mLeftSide, newAlpha),
animateTransitionTo(mStatusIcons, newAlpha),
- animateTransitionTo(mDivider, newAlpha),
animateTransitionTo(mBattery, newAlphaBC)
);
if (isLightsOut(mode)) {
@@ -100,8 +98,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
} else {
mLeftSide.setAlpha(newAlpha);
mStatusIcons.setAlpha(newAlpha);
- mDivider.setAlpha(newAlpha);
mBattery.setAlpha(newAlphaBC);
}
}
-} \ No newline at end of file
+}
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 ffbbffc0d8d9..f3b0a79f9518 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -25,7 +25,6 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
-import android.provider.DeviceConfig;
import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Pair;
@@ -40,8 +39,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.systemui.DarkReceiverImpl;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
@@ -81,9 +78,6 @@ public class PhoneStatusBarView extends PanelBar {
@Nullable
private DisplayCutout mDisplayCutout;
- private DarkReceiverImpl mSplitDivider;
- private View mDividerContainer;
- private QsSplitPropertyListener mPropertyListener;
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
*/
@@ -115,10 +109,6 @@ public class PhoneStatusBarView extends PanelBar {
mBattery = findViewById(R.id.battery);
mCutoutSpace = findViewById(R.id.cutout_space_view);
mCenterIconSpace = findViewById(R.id.centered_icon_area);
- mSplitDivider = findViewById(R.id.divider);
- mDividerContainer = findViewById(R.id.divider_container);
- maybeShowDivider(true);
- mPropertyListener = new QsSplitPropertyListener(mDividerContainer);
updateResources();
}
@@ -128,26 +118,16 @@ public class PhoneStatusBarView extends PanelBar {
super.onAttachedToWindow();
// Always have Battery meters in the status bar observe the dark/light modes.
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
- Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSplitDivider);
- maybeShowDivider(true);
if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
updateLayoutForCutout();
}
- if (mPropertyListener != null) {
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- mContext.getMainExecutor(), mPropertyListener);
- }
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
- Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mSplitDivider);
mDisplayCutout = null;
- if (mPropertyListener != null) {
- DeviceConfig.removeOnPropertiesChangedListener(mPropertyListener);
- }
}
@Override
@@ -216,7 +196,6 @@ public class PhoneStatusBarView extends PanelBar {
public void onPanelPeeked() {
super.onPanelPeeked();
mBar.makeExpandedVisible(false);
- maybeShowDivider(!mBar.mPanelExpanded);
}
@Override
@@ -225,7 +204,6 @@ public class PhoneStatusBarView extends PanelBar {
// Close the status bar in the next frame so we can show the end of the animation.
post(mHideExpandedRunnable);
mIsFullyOpenedPanel = false;
- maybeShowDivider(!mBar.mPanelExpanded);
}
public void removePendingHideExpandedRunnables() {
@@ -239,7 +217,6 @@ public class PhoneStatusBarView extends PanelBar {
mPanel.getView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
mIsFullyOpenedPanel = true;
- maybeShowDivider(!mBar.mPanelExpanded);
}
@Override
@@ -263,28 +240,24 @@ public class PhoneStatusBarView extends PanelBar {
mBar.onTrackingStarted();
mScrimController.onTrackingStarted();
removePendingHideExpandedRunnables();
- maybeShowDivider(!mBar.mPanelExpanded);
}
@Override
public void onClosingFinished() {
super.onClosingFinished();
mBar.onClosingFinished();
- maybeShowDivider(!mBar.mPanelExpanded);
}
@Override
public void onTrackingStopped(boolean expand) {
super.onTrackingStopped(expand);
mBar.onTrackingStopped(expand);
- maybeShowDivider(!mBar.mPanelExpanded);
}
@Override
public void onExpandingFinished() {
super.onExpandingFinished();
mScrimController.onExpandingFinished();
- maybeShowDivider(!mBar.mPanelExpanded);
}
@Override
@@ -416,31 +389,4 @@ public class PhoneStatusBarView extends PanelBar {
protected boolean shouldPanelBeVisible() {
return mHeadsUpVisible || super.shouldPanelBeVisible();
}
-
- void maybeShowDivider(boolean showDivider) {
- int state =
- showDivider && NotificationPanelViewController.isQsSplitEnabled()
- ? View.VISIBLE : View.GONE;
- mDividerContainer.setVisibility(state);
- }
-
- private static class QsSplitPropertyListener implements
- DeviceConfig.OnPropertiesChangedListener {
- private final View mDivider;
-
- QsSplitPropertyListener(View divider) {
- mDivider = divider;
- }
-
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (properties.getNamespace().equals(DeviceConfig.NAMESPACE_SYSTEMUI)
- && properties.getKeyset().contains(
- SystemUiDeviceConfigFlags.QS_SPLIT_ENABLED)) {
- boolean splitEnabled = properties.getBoolean(
- SystemUiDeviceConfigFlags.QS_SPLIT_ENABLED, false);
- mDivider.setVisibility(splitEnabled ? VISIBLE : GONE);
- }
- }
- }
}
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 832005210353..dc502d86ee45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -144,7 +144,6 @@ import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -169,12 +168,10 @@ import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -192,15 +189,11 @@ import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationClicker;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.NotificationListController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -219,7 +212,6 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
@@ -357,7 +349,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private final Object mQueueLock = new Object();
- private final FeatureFlags mFeatureFlags;
private final StatusBarIconController mIconController;
private final PulseExpansionHandler mPulseExpansionHandler;
private final NotificationWakeUpCoordinator mWakeUpCoordinator;
@@ -366,7 +357,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private final HeadsUpManagerPhone mHeadsUpManager;
private final DynamicPrivacyController mDynamicPrivacyController;
private final BypassHeadsUpNotifier mBypassHeadsUpNotifier;
- private final Lazy<NotifPipelineInitializer> mNewNotifPipeline;
private final FalsingManager mFalsingManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final ConfigurationController mConfigurationController;
@@ -375,7 +365,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
private final Provider<StatusBarComponent.Builder> mStatusBarComponentBuilder;
private final PluginManager mPluginManager;
- private final RemoteInputUriController mRemoteInputUriController;
private final Optional<Divider> mDividerOptional;
private final StatusBarNotificationActivityStarter.Builder
mStatusBarNotificationActivityStarterBuilder;
@@ -388,8 +377,8 @@ public class StatusBar extends SystemUI implements DemoMode,
private final KeyguardDismissUtil mKeyguardDismissUtil;
private final ExtensionController mExtensionController;
private final UserInfoControllerImpl mUserInfoControllerImpl;
- private final NotificationRowBinderImpl mNotificationRowBinder;
private final DismissCallbackRegistry mDismissCallbackRegistry;
+ private NotificationsController mNotificationsController;
// expanded notifications
// the sliding/resizing panel within the notification window
@@ -413,8 +402,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
- private final NotificationEntryManager mEntryManager;
- private NotificationListController mNotificationListController;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final NotificationViewHierarchyManager mViewHierarchyManager;
private final KeyguardViewMediator mKeyguardViewMediator;
@@ -590,7 +577,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onStrongAuthStateChanged(int userId) {
super.onStrongAuthStateChanged(userId);
- mEntryManager.updateNotifications("onStrongAuthStateChanged");
+ mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
}
};
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
@@ -615,7 +602,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public StatusBar(
Context context,
- FeatureFlags featureFlags,
+ NotificationsController notificationsController,
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -627,13 +614,11 @@ public class StatusBar extends SystemUI implements DemoMode,
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NotifPipelineInitializer> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationEntryManager notificationEntryManager,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -654,12 +639,10 @@ public class StatusBar extends SystemUI implements DemoMode,
VibratorHelper vibratorHelper,
BubbleController bubbleController,
NotificationGroupManager groupManager,
- NotificationGroupAlertTransferHelper groupAlertTransferHelper,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
Lazy<AssistManager> assistManagerLazy,
- NotificationListener notificationListener,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
LockscreenLockIconController lockscreenLockIconController,
@@ -677,7 +660,6 @@ public class StatusBar extends SystemUI implements DemoMode,
Optional<Recents> recentsOptional,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
- RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
LightsOutNotifController lightsOutNotifController,
StatusBarNotificationActivityStarter.Builder
@@ -693,10 +675,9 @@ public class StatusBar extends SystemUI implements DemoMode,
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
- NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry) {
super(context);
- mFeatureFlags = featureFlags;
+ mNotificationsController = notificationsController;
mLightBarController = lightBarController;
mAutoHideController = autoHideController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -708,13 +689,11 @@ public class StatusBar extends SystemUI implements DemoMode,
mHeadsUpManager = headsUpManagerPhone;
mDynamicPrivacyController = dynamicPrivacyController;
mBypassHeadsUpNotifier = bypassHeadsUpNotifier;
- mNewNotifPipeline = newNotifPipeline;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
- mEntryManager = notificationEntryManager;
mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
mViewHierarchyManager = notificationViewHierarchyManager;
mKeyguardViewMediator = keyguardViewMediator;
@@ -735,12 +714,10 @@ public class StatusBar extends SystemUI implements DemoMode,
mVibratorHelper = vibratorHelper;
mBubbleController = bubbleController;
mGroupManager = groupManager;
- mGroupAlertTransferHelper = groupAlertTransferHelper;
mVisualStabilityManager = visualStabilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mNavigationBarController = navigationBarController;
mAssistManagerLazy = assistManagerLazy;
- mNotificationListener = notificationListener;
mConfigurationController = configurationController;
mNotificationShadeWindowController = notificationShadeWindowController;
mLockscreenLockIconController = lockscreenLockIconController;
@@ -758,7 +735,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mRecentsOptional = recentsOptional;
mStatusBarComponentBuilder = statusBarComponentBuilder;
mPluginManager = pluginManager;
- mRemoteInputUriController = remoteInputUriController;
mDividerOptional = dividerOptional;
mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
mShadeController = shadeController;
@@ -772,12 +748,11 @@ public class StatusBar extends SystemUI implements DemoMode,
mKeyguardDismissUtil = keyguardDismissUtil;
mExtensionController = extensionController;
mUserInfoControllerImpl = userInfoControllerImpl;
- mNotificationRowBinder = notificationRowBinder;
mDismissCallbackRegistry = dismissCallbackRegistry;
mBubbleExpandListener =
(isExpanding, key) -> {
- mEntryManager.updateNotifications("onBubbleExpandChanged");
+ mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
updateScrimController();
};
@@ -787,20 +762,12 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void start() {
- mNotificationListener.registerAsSystemService();
mScreenLifecycle.addObserver(mScreenObserver);
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
- mBypassHeadsUpNotifier.setUp(mEntryManager);
+ mBypassHeadsUpNotifier.setUp();
mBubbleController.setExpandListener(mBubbleExpandListener);
mActivityIntentHelper = new ActivityIntentHelper(mContext);
- KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
- if (sliceProvider != null) {
- sliceProvider.initDependencies(mMediaManager, mStatusBarStateController,
- mKeyguardBypassController, mDozeParameters);
- } else {
- Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
- }
mColorExtractor.addOnColorsChangedListener(this);
mStatusBarStateController.addCallback(this,
@@ -1068,12 +1035,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mConfigurationController.addCallback(mHeadsUpManager);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
- mHeadsUpManager.addListener(mGroupManager);
- mHeadsUpManager.addListener(mGroupAlertTransferHelper);
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
- mGroupManager.setHeadsUpManager(mHeadsUpManager);
- mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
createNavigationBar(result);
@@ -1251,16 +1214,10 @@ public class StatusBar extends SystemUI implements DemoMode,
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
mHeadsUpManager, mNotificationShadeWindowView, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
- mNotificationAlertingManager, mNotificationRowBinder, mKeyguardStateController,
+ mNotificationAlertingManager, mKeyguardStateController,
mKeyguardIndicationController,
this /* statusBar */, mShadeController, mCommandQueue, mInitController);
- mNotificationListController =
- new NotificationListController(
- mEntryManager,
- (NotificationListContainer) mStackScroller,
- mDeviceProvisionedController);
-
mNotificationShelf.setOnActivatedListener(mPresenter);
mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController);
@@ -1274,22 +1231,12 @@ public class StatusBar extends SystemUI implements DemoMode,
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
- if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- mNotificationRowBinder.setInflationCallback(mEntryManager);
- }
-
- mRemoteInputUriController.attach(mEntryManager);
-
- mNotificationRowBinder.setNotificationClicker(new NotificationClicker(
- Optional.of(this), mBubbleController, mNotificationActivityStarter));
-
- mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
- mNotificationListController.bind();
-
- if (mFeatureFlags.isNewNotifPipelineEnabled()) {
- mNewNotifPipeline.get().initialize(mNotificationListener, mNotificationRowBinder);
- }
- mEntryManager.attach(mNotificationListener);
+ mNotificationsController.initialize(
+ this,
+ mPresenter,
+ (NotificationListContainer) mStackScroller,
+ mNotificationActivityStarter,
+ mPresenter);
}
/**
@@ -1526,7 +1473,7 @@ public class StatusBar extends SystemUI implements DemoMode,
* @param reason why we're requesting a notification update
*/
public void requestNotificationUpdate(String reason) {
- mEntryManager.updateNotifications(reason);
+ mNotificationsController.requestNotificationUpdate(reason);
}
/**
@@ -1734,7 +1681,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- mEntryManager.updateNotifications("onHeadsUpStateChanged");
+ mNotificationsController.requestNotificationUpdate("onHeadsUpStateChanged");
if (mStatusBarStateController.isDozing() && isHeadsUp) {
entry.setPulseSuppressed(false);
mDozeServiceHost.fireNotificationPulse(entry);
@@ -2493,11 +2440,9 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarKeyguardViewManager.dump(pw);
}
- if (DUMPTRUCK) {
- synchronized (mEntryManager) {
- mEntryManager.dump(pw, " ");
- }
+ mNotificationsController.dump(fd, pw, args, DUMPTRUCK);
+ if (DUMPTRUCK) {
if (false) {
pw.println("see the logcat for a dump of the views we have created.");
// must happen on ui thread
@@ -2520,11 +2465,6 @@ public class StatusBar extends SystemUI implements DemoMode,
} else {
pw.println(" mHeadsUpManager: null");
}
- if (mGroupManager != null) {
- mGroupManager.dump(fd, pw, args);
- } else {
- pw.println(" mGroupManager: null");
- }
if (mBubbleController != null) {
mBubbleController.dump(fd, pw, args);
@@ -2755,9 +2695,7 @@ public class StatusBar extends SystemUI implements DemoMode,
};
public void resetUserExpandedStates() {
- for (NotificationEntry entry : mEntryManager.getVisibleNotifications()) {
- entry.resetUserExpansion();
- }
+ mNotificationsController.resetUserExpandedStates();
}
private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen) {
@@ -2863,7 +2801,7 @@ public class StatusBar extends SystemUI implements DemoMode,
try {
// consider the transition from peek to expanded to be a panel open,
// but not one that clears notification effects.
- int notificationLoad = mEntryManager.getActiveNotificationsCount();
+ int notificationLoad = mNotificationsController.getActiveNotificationsCount();
mBarService.onPanelRevealed(false, notificationLoad);
} catch (RemoteException ex) {
// Won't fail unless the world has ended.
@@ -2881,7 +2819,7 @@ public class StatusBar extends SystemUI implements DemoMode,
!mPresenter.isPresenterFullyCollapsed() &&
(mState == StatusBarState.SHADE
|| mState == StatusBarState.SHADE_LOCKED);
- int notificationLoad = mEntryManager.getActiveNotificationsCount();
+ int notificationLoad = mNotificationsController.getActiveNotificationsCount();
if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
notificationLoad = 1;
}
@@ -3522,7 +3460,7 @@ public class StatusBar extends SystemUI implements DemoMode,
updateQsExpansionEnabled();
mKeyguardViewMediator.setDozing(mDozing);
- mEntryManager.updateNotifications("onDozingChanged");
+ mNotificationsController.requestNotificationUpdate("onDozingChanged");
updateDozingState();
mDozeServiceHost.updateDozing();
updateScrimController();
@@ -3974,7 +3912,6 @@ public class StatusBar extends SystemUI implements DemoMode,
protected ViewGroup mStackScroller;
private final NotificationGroupManager mGroupManager;
- private final NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
// handling reordering
private final VisualStabilityManager mVisualStabilityManager;
@@ -4041,21 +3978,12 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- private final NotificationListener mNotificationListener;
-
public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
- if (snoozeOption.getSnoozeCriterion() != null) {
- mNotificationListener.snoozeNotification(sbn.getKey(),
- snoozeOption.getSnoozeCriterion().getId());
- } else {
- mNotificationListener.snoozeNotification(sbn.getKey(),
- snoozeOption.getMinutesToSnoozeFor() * 60 * 1000);
- }
+ mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
}
public void setNotificationSnoozed(StatusBarNotification sbn, int hoursToSnooze) {
- mNotificationListener.snoozeNotification(sbn.getKey(),
- hoursToSnooze * 60 * 60 * 1000);
+ mNotificationsController.setNotificationSnoozed(sbn, hoursToSnooze);
}
@Override
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 86a81ce3d1f1..6a046884e835 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.phone;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.ViewRootImpl.sNewInsetsMode;
+import static android.view.WindowInsets.Type.navigationBars;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_FADING;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -789,7 +792,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
@Override
public void run() {
- mStatusBar.getNavigationBarView().getRootView().setVisibility(View.VISIBLE);
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+ mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ .show(navigationBars());
+ } else {
+ mStatusBar.getNavigationBarView().getRootView().setVisibility(View.VISIBLE);
+ }
}
};
@@ -856,7 +864,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
} else {
mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
- mStatusBar.getNavigationBarView().getRootView().setVisibility(View.GONE);
+ if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+ mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ .hide(navigationBars());
+ } else {
+ mStatusBar.getNavigationBarView().getRootView().setVisibility(View.GONE);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index 7615bf826287..15a0e08e285f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -46,9 +46,7 @@ import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -61,12 +59,10 @@ import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
@@ -77,7 +73,6 @@ import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
@@ -105,7 +100,7 @@ public class StatusBarModule {
@Singleton
static StatusBar provideStatusBar(
Context context,
- FeatureFlags featureFlags,
+ NotificationsController notificationsController,
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -117,13 +112,11 @@ public class StatusBarModule {
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NotifPipelineInitializer> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationEntryManager notificationEntryManager,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -144,12 +137,10 @@ public class StatusBarModule {
VibratorHelper vibratorHelper,
BubbleController bubbleController,
NotificationGroupManager groupManager,
- NotificationGroupAlertTransferHelper groupAlertTransferHelper,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
Lazy<AssistManager> assistManagerLazy,
- NotificationListener notificationListener,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
LockscreenLockIconController lockscreenLockIconController,
@@ -167,7 +158,6 @@ public class StatusBarModule {
Optional<Recents> recentsOptional,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
- RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
LightsOutNotifController lightsOutNotifController,
StatusBarNotificationActivityStarter.Builder
@@ -183,11 +173,10 @@ public class StatusBarModule {
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
- NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry) {
return new StatusBar(
context,
- featureFlags,
+ notificationsController,
lightBarController,
autoHideController,
keyguardUpdateMonitor,
@@ -199,13 +188,11 @@ public class StatusBarModule {
headsUpManagerPhone,
dynamicPrivacyController,
bypassHeadsUpNotifier,
- newNotifPipeline,
falsingManager,
broadcastDispatcher,
remoteInputQuickSettingsDisabler,
notificationGutsManager,
notificationLogger,
- notificationEntryManager,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
@@ -226,12 +213,10 @@ public class StatusBarModule {
vibratorHelper,
bubbleController,
groupManager,
- groupAlertTransferHelper,
visualStabilityManager,
deviceProvisionedController,
navigationBarController,
assistManagerLazy,
- notificationListener,
configurationController,
notificationShadeWindowController,
lockscreenLockIconController,
@@ -249,7 +234,6 @@ public class StatusBarModule {
recentsOptional,
statusBarComponentBuilder,
pluginManager,
- remoteInputUriController,
dividerOptional,
lightsOutNotifController,
statusBarNotificationActivityStarterBuilder,
@@ -264,7 +248,6 @@ public class StatusBarModule {
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
- notificationRowBinder,
dismissCallbackRegistry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 1336b2de82d3..2485513abba5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -141,7 +141,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
ActivityLaunchAnimator activityLaunchAnimator,
DynamicPrivacyController dynamicPrivacyController,
NotificationAlertingManager notificationAlertingManager,
- NotificationRowBinderImpl notificationRowBinder,
KeyguardStateController keyguardStateController,
KeyguardIndicationController keyguardIndicationController,
StatusBar statusBar,
@@ -216,8 +215,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mEntryManager.addNotificationLifetimeExtender(mGutsManager);
mEntryManager.addNotificationLifetimeExtenders(
remoteInputManager.getLifetimeExtenders());
- notificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
- this);
mNotificationInterruptionStateProvider.setUpWithPresenter(
this, mHeadsUpManager, this::canHeadsUp);
mLockscreenUserManager.setUpWithPresenter(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index fb30bdec68b5..e448d0ac8649 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -107,6 +107,7 @@ public class StatusBarWindowController {
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
+ mLp.setFitInsetsTypes(0 /* types */);
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index d06164963f7b..5a7c5c9b5ebc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -56,6 +56,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
private static final int MAX_DOTS = 1;
private int mDotPadding;
+ private int mIconSpacing;
private int mStaticDotDiameter;
private int mUnderflowWidth;
private int mUnderflowStart = 0;
@@ -99,6 +100,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
mIconDotFrameWidth = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
+ mIconSpacing = getResources().getDimensionPixelSize(R.dimen.status_bar_system_icon_spacing);
int radius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
mStaticDotDiameter = 2 * radius;
mUnderflowWidth = mIconDotFrameWidth + (MAX_DOTS - 1) * (mStaticDotDiameter + mDotPadding);
@@ -163,20 +165,21 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
// Measure all children so that they report the correct width
int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
mNeedsUnderflow = mShouldRestrictIcons && visibleCount > MAX_ICONS;
- for (int i = 0; i < mMeasureViews.size(); i++) {
+ for (int i = 0; i < visibleCount; i++) {
// Walking backwards
View child = mMeasureViews.get(visibleCount - i - 1);
measureChild(child, childWidthSpec, heightMeasureSpec);
+ int spacing = i == visibleCount - 1 ? 0 : mIconSpacing;
if (mShouldRestrictIcons) {
if (i < maxVisible && trackWidth) {
- totalWidth += getViewTotalMeasuredWidth(child);
+ totalWidth += getViewTotalMeasuredWidth(child) + spacing;
} else if (trackWidth) {
// We've hit the icon limit; add space for dots
totalWidth += mUnderflowWidth;
trackWidth = false;
}
} else {
- totalWidth += getViewTotalMeasuredWidth(child);
+ totalWidth += getViewTotalMeasuredWidth(child) + spacing;
}
}
@@ -284,11 +287,15 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
continue;
}
+ // Move translationX to the spot within StatusIconContainer's layout to add the view
+ // without cutting off the child view.
+ translationX -= getViewTotalWidth(child);
childState.visibleState = STATE_ICON;
- childState.xTranslation = translationX - getViewTotalWidth(child);
+ childState.xTranslation = translationX;
mLayoutStates.add(0, childState);
- translationX -= getViewTotalWidth(child);
+ // Shift translationX over by mIconSpacing for the next view.
+ translationX -= mIconSpacing;
}
// Show either 1-MAX_ICONS icons, or (MAX_ICONS - 1) icons + overflow
@@ -306,7 +313,8 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
firstUnderflowIndex = i;
break;
}
- mUnderflowStart = (int) Math.max(contentStart, state.xTranslation - mUnderflowWidth);
+ mUnderflowStart = (int) Math.max(
+ contentStart, state.xTranslation - mUnderflowWidth - mIconSpacing);
visible++;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 28b6c389d4df..06105f532eb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -106,8 +106,8 @@ public class SystemUIDialog extends AlertDialog {
if (Dependency.get(KeyguardStateController.class).isShowing()) {
final Window window = dialog.getWindow();
window.setType(LayoutParams.TYPE_STATUS_BAR_PANEL);
- window.setFitWindowInsetsTypes(
- window.getFitWindowInsetsTypes() & ~Type.statusBars());
+ window.getAttributes().setFitInsetsTypes(
+ window.getAttributes().getFitInsetsTypes() & ~Type.statusBars());
} else {
dialog.getWindow().setType(LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
}
@@ -118,8 +118,8 @@ public class SystemUIDialog extends AlertDialog {
window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- window.setFitWindowInsetsTypes(
- window.getFitWindowInsetsTypes() & ~Type.statusBars());
+ window.getAttributes().setFitInsetsTypes(
+ window.getAttributes().getFitInsetsTypes() & ~Type.statusBars());
return dialog;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index e675a7f373b6..5dc91047770d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -29,7 +29,6 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
@@ -137,9 +136,9 @@ public class EmergencyCryptkeeperText extends TextView {
displayText = getContext().getText(
com.android.internal.R.string.emergency_calls_only);
Intent i = getContext().registerReceiver(null,
- new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION));
+ new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED));
if (i != null) {
- displayText = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
+ displayText = i.getStringExtra(TelephonyManager.EXTRA_PLMN);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 39cbdc83a295..0d5b3756b68a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -29,11 +29,13 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings.Global;
+import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
import android.telephony.CdmaEriInformation;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.CellSignalStrengthNr;
+import android.telephony.DataSpecificRegistrationInfo;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.feature.MmTelFeature;
@@ -51,9 +53,9 @@ import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import com.android.ims.FeatureConnector;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneConstants.DataState;
+import com.android.internal.telephony.TelephonyIntents;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.net.SignalStrengthUtil;
@@ -390,9 +392,9 @@ public class MobileSignalController extends SignalController<
private int getNumLevels() {
if (mInflateSignalStrengths) {
- return SignalStrength.NUM_SIGNAL_STRENGTH_BINS + 1;
+ return CellSignalStrength.getNumSignalStrengthLevels() + 1;
}
- return SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+ return CellSignalStrength.getNumSignalStrengthLevels();
}
@Override
@@ -586,12 +588,12 @@ public class MobileSignalController extends SignalController<
public void handleBroadcast(Intent intent) {
String action = intent.getAction();
- if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
- updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
- intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
- intent.getStringExtra(TelephonyIntents.EXTRA_DATA_SPN),
- intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
- intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
+ if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) {
+ updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false),
+ intent.getStringExtra(TelephonyManager.EXTRA_SPN),
+ intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN),
+ intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false),
+ intent.getStringExtra(TelephonyManager.EXTRA_PLMN));
notifyListenersIfNecessary();
} else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
updateDataSim();
@@ -852,10 +854,19 @@ public class MobileSignalController extends SignalController<
notifyListenersIfNecessary();
}
+ private int getNrState(ServiceState serviceState) {
+ NetworkRegistrationInfo nri = serviceState.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (nri != null) {
+ return nri.getNrState();
+ }
+ return NetworkRegistrationInfo.NR_STATE_NONE;
+ }
+
private MobileIconGroup getNr5GIconGroup() {
if (mServiceState == null) return null;
- int nrState = mServiceState.getNrState();
+ int nrState = getNrState(mServiceState);
if (nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED) {
// Check if the NR 5G is using millimeter wave and the icon is config.
if (mServiceState.getNrFrequencyRange() == ServiceState.FREQUENCY_RANGE_MMWAVE) {
@@ -1124,12 +1135,24 @@ public class MobileSignalController extends SignalController<
if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE) {
if (isCarrierSpecificDataIcon()) {
mCAPlus = true;
- } else if (mServiceState != null && mServiceState.isUsingCarrierAggregation()) {
+ } else if (mServiceState != null && isUsingCarrierAggregation(mServiceState)) {
mCA = true;
}
}
}
+ private boolean isUsingCarrierAggregation(ServiceState serviceState) {
+ NetworkRegistrationInfo nri = serviceState.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (nri != null) {
+ DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
+ if (dsri != null) {
+ return dsri.isUsingCarrierAggregation();
+ }
+ }
+ return false;
+ }
+
@Override
public void onDataActivity(int direction) {
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index bb3342d2d567..bb6676e38df6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -41,9 +41,9 @@ import android.os.Looper;
import android.os.PersistableBundle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
+import android.telephony.CellSignalStrength;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -319,11 +319,11 @@ public class NetworkControllerImpl extends BroadcastReceiver
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
filter.addAction(Intent.ACTION_SERVICE_STATE);
- filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
+ filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
@@ -530,7 +530,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mConfig = Config.readConfig(mContext);
mReceiverHandler.post(this::handleConfigurationChanged);
break;
- case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
+ case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
@@ -611,7 +611,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
@VisibleForTesting
void doUpdateMobileControllers() {
List<SubscriptionInfo> subscriptions = mSubscriptionManager
- .getActiveSubscriptionInfoList(false);
+ .getActiveAndHiddenSubscriptionInfoList();
if (subscriptions == null) {
subscriptions = Collections.emptyList();
}
@@ -1043,7 +1043,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
if (level != null) {
controller.getState().level = level.equals("null") ? -1
: Math.min(Integer.parseInt(level),
- SignalStrength.NUM_SIGNAL_STRENGTH_BINS);
+ CellSignalStrength.getNumSignalStrengthLevels());
controller.getState().connected = controller.getState().level >= 0;
}
if (args.containsKey("inflate")) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 307e3bcbaba5..408b3a619ff1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -161,6 +161,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
results);
+
+ mEntry.remoteInputText = mEditText.getText();
+ mEntry.remoteInputUri = null;
+ mEntry.remoteInputMimeType = null;
+
if (mEntry.editedSuggestionInfo == null) {
RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT);
} else {
@@ -177,6 +182,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results);
+ mEntry.remoteInputText = mContext.getString(R.string.remote_input_image_insertion_text);
+ mEntry.remoteInputMimeType = contentType;
+ mEntry.remoteInputUri = data;
+
return fillInIntent;
}
@@ -184,7 +193,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
- mEntry.remoteInputText = mEditText.getText();
mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime();
mController.addSpinning(mEntry.getKey(), mToken);
mController.removeRemoteInput(mEntry, mToken);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index d60afa3c5133..9ffa5900eac3 100755
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -58,7 +58,8 @@ public class WifiSignalController extends
mWifiTracker.setListening(true);
mHasMobileData = hasMobileData;
if (wifiManager != null) {
- wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback());
+ wifiManager.registerTrafficStateCallback(context.getMainExecutor(),
+ new WifiTrafficStateCallback());
}
mDefaultWifiIconGroup = new IconGroup(
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
index 41e026af7c72..665cb6307b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java
@@ -178,7 +178,7 @@ class ThemeOverlayManager {
} else {
mOverlayManager.setEnabled(pkg, false, userHandle);
}
- } catch (IllegalStateException e) {
+ } catch (SecurityException | IllegalStateException e) {
Log.e(TAG,
String.format("setEnabled failed: %s %s %b", pkg, userHandle, enabled), e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
new file mode 100644
index 000000000000..dea8c5d49dfc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2020 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.toast;
+
+import android.annotation.MainThread;
+import android.annotation.Nullable;
+import android.app.INotificationManager;
+import android.app.ITransientNotificationCallback;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.CommandQueue;
+
+import java.util.Objects;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controls display of text toasts.
+ */
+@Singleton
+public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
+ private static final String TAG = "ToastUI";
+
+ /**
+ * Values taken from {@link Toast}.
+ */
+ private static final long DURATION_SHORT = 4000;
+ private static final long DURATION_LONG = 7000;
+
+ private final CommandQueue mCommandQueue;
+ private final WindowManager mWindowManager;
+ private final INotificationManager mNotificationManager;
+ private final AccessibilityManager mAccessibilityManager;
+ private ToastEntry mCurrentToast;
+
+ @Inject
+ public ToastUI(Context context, CommandQueue commandQueue) {
+ super(context);
+ mCommandQueue = commandQueue;
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mNotificationManager = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mAccessibilityManager = AccessibilityManager.getInstance(context);
+ }
+
+ @Override
+ public void start() {
+ mCommandQueue.addCallback(this);
+ }
+
+ @Override
+ @MainThread
+ public void showToast(String packageName, IBinder token, CharSequence text,
+ IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) {
+ if (mCurrentToast != null) {
+ hideCurrentToast();
+ }
+ View view = getView(text);
+ LayoutParams params = getLayoutParams(windowToken, duration);
+ mCurrentToast = new ToastEntry(packageName, token, view, windowToken, callback);
+ try {
+ mWindowManager.addView(view, params);
+ } catch (WindowManager.BadTokenException e) {
+ Log.w(TAG, "Error while attempting to show toast from " + packageName, e);
+ return;
+ }
+ trySendAccessibilityEvent(view, packageName);
+ if (callback != null) {
+ try {
+ callback.onToastShown();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error calling back " + packageName + " to notify onToastShow()", e);
+ }
+ }
+ }
+
+ @Override
+ @MainThread
+ public void hideToast(String packageName, IBinder token) {
+ if (mCurrentToast == null || !Objects.equals(mCurrentToast.packageName, packageName)
+ || !Objects.equals(mCurrentToast.token, token)) {
+ Log.w(TAG, "Attempt to hide non-current toast from package " + packageName);
+ return;
+ }
+ hideCurrentToast();
+ }
+
+ @MainThread
+ private void hideCurrentToast() {
+ if (mCurrentToast.view.getParent() != null) {
+ mWindowManager.removeViewImmediate(mCurrentToast.view);
+ }
+ String packageName = mCurrentToast.packageName;
+ try {
+ mNotificationManager.finishToken(packageName, mCurrentToast.windowToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error finishing toast window token from package " + packageName, e);
+ }
+ if (mCurrentToast.callback != null) {
+ try {
+ mCurrentToast.callback.onToastHidden();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error calling back " + packageName + " to notify onToastHide()", e);
+ }
+ }
+ mCurrentToast = null;
+ }
+
+ private void trySendAccessibilityEvent(View view, String packageName) {
+ if (!mAccessibilityManager.isEnabled()) {
+ return;
+ }
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+ event.setClassName(Toast.class.getName());
+ event.setPackageName(packageName);
+ view.dispatchPopulateAccessibilityEvent(event);
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+
+ private View getView(CharSequence text) {
+ View view = LayoutInflater.from(mContext).inflate(
+ R.layout.transient_notification, null);
+ TextView textView = view.findViewById(com.android.internal.R.id.message);
+ textView.setText(text);
+ return view;
+ }
+
+ private LayoutParams getLayoutParams(IBinder windowToken, int duration) {
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.format = PixelFormat.TRANSLUCENT;
+ params.windowAnimations = com.android.internal.R.style.Animation_Toast;
+ params.type = WindowManager.LayoutParams.TYPE_TOAST;
+ params.setTitle("Toast");
+ params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ Configuration config = mContext.getResources().getConfiguration();
+ int specificGravity = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_toastDefaultGravity);
+ int gravity = Gravity.getAbsoluteGravity(specificGravity, config.getLayoutDirection());
+ params.gravity = gravity;
+ if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
+ params.horizontalWeight = 1.0f;
+ }
+ if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
+ params.verticalWeight = 1.0f;
+ }
+ params.x = 0;
+ params.y = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
+ params.verticalMargin = 0;
+ params.horizontalMargin = 0;
+ params.packageName = mContext.getPackageName();
+ params.hideTimeoutMilliseconds =
+ (duration == Toast.LENGTH_LONG) ? DURATION_LONG : DURATION_SHORT;
+ params.token = windowToken;
+ return params;
+ }
+
+ private static class ToastEntry {
+ public final String packageName;
+ public final IBinder token;
+ public final View view;
+ public final IBinder windowToken;
+
+ @Nullable
+ public final ITransientNotificationCallback callback;
+
+ private ToastEntry(String packageName, IBinder token, View view, IBinder windowToken,
+ @Nullable ITransientNotificationCallback callback) {
+ this.packageName = packageName;
+ this.token = token;
+ this.view = view;
+ this.windowToken = windowToken;
+ this.callback = callback;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java
new file mode 100644
index 000000000000..3bef044a2526
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tracing;
+
+import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_L;
+
+import android.content.Context;
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
+import com.android.systemui.shared.tracing.FrameProtoTracer;
+import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams;
+import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.systemui.tracing.nano.SystemUiTraceEntryProto;
+import com.android.systemui.tracing.nano.SystemUiTraceFileProto;
+
+import com.google.protobuf.nano.MessageNano;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Queue;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controller for coordinating winscope proto tracing.
+ */
+@Singleton
+public class ProtoTracer implements Dumpable, ProtoTraceParams<MessageNano, SystemUiTraceFileProto,
+ SystemUiTraceEntryProto, SystemUiTraceProto> {
+
+ private static final String TAG = "ProtoTracer";
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final Context mContext;
+ private final FrameProtoTracer<MessageNano, SystemUiTraceFileProto, SystemUiTraceEntryProto,
+ SystemUiTraceProto> mProtoTracer;
+
+ @Inject
+ public ProtoTracer(Context context, DumpController dumpController) {
+ mContext = context;
+ mProtoTracer = new FrameProtoTracer<>(this);
+ dumpController.registerDumpable(this);
+ }
+
+ @Override
+ public File getTraceFile() {
+ return new File(mContext.getFilesDir(), "sysui_trace.pb");
+ }
+
+ @Override
+ public SystemUiTraceFileProto getEncapsulatingTraceProto() {
+ return new SystemUiTraceFileProto();
+ }
+
+ @Override
+ public SystemUiTraceEntryProto updateBufferProto(SystemUiTraceEntryProto reuseObj,
+ ArrayList<ProtoTraceable<SystemUiTraceProto>> traceables) {
+ SystemUiTraceEntryProto proto = reuseObj != null
+ ? reuseObj
+ : new SystemUiTraceEntryProto();
+ proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
+ proto.systemUi = proto.systemUi != null ? proto.systemUi : new SystemUiTraceProto();
+ for (ProtoTraceable t : traceables) {
+ t.writeToProto(proto.systemUi);
+ }
+ return proto;
+ }
+
+ @Override
+ public byte[] serializeEncapsulatingProto(SystemUiTraceFileProto encapsulatingProto,
+ Queue<SystemUiTraceEntryProto> buffer) {
+ encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE;
+ encapsulatingProto.entry = buffer.toArray(new SystemUiTraceEntryProto[0]);
+ return MessageNano.toByteArray(encapsulatingProto);
+ }
+
+ @Override
+ public byte[] getProtoBytes(MessageNano proto) {
+ return MessageNano.toByteArray(proto);
+ }
+
+ @Override
+ public int getProtoSize(MessageNano proto) {
+ return proto.getCachedSize();
+ }
+
+ public void start() {
+ mProtoTracer.start();
+ }
+
+ public void stop() {
+ mProtoTracer.stop();
+ }
+
+ public boolean isEnabled() {
+ return mProtoTracer.isEnabled();
+ }
+
+ public void add(ProtoTraceable<SystemUiTraceProto> traceable) {
+ mProtoTracer.add(traceable);
+ }
+
+ public void remove(ProtoTraceable<SystemUiTraceProto> traceable) {
+ mProtoTracer.remove(traceable);
+ }
+
+ public void scheduleFrameUpdate() {
+ mProtoTracer.scheduleFrameUpdate();
+ }
+
+ public void update() {
+ mProtoTracer.update();
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("ProtoTracer:");
+ pw.print(" "); pw.println("enabled: " + mProtoTracer.isEnabled());
+ pw.print(" "); pw.println("usagePct: " + mProtoTracer.getBufferUsagePct());
+ pw.print(" "); pw.println("file: " + getTraceFile());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
new file mode 100644
index 000000000000..d940a6b5c460
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.systemui.tracing;
+
+option java_multiple_files = true;
+
+message SystemUiTraceProto {
+
+ optional EdgeBackGestureHandlerProto edge_back_gesture_handler = 1;
+}
+
+message EdgeBackGestureHandlerProto {
+
+ optional bool allow_gesture = 1;
+}
+
+/* represents a file full of system ui trace entries.
+ Encoded, it should start with 0x9 0x53 0x59 0x53 0x55 0x49 0x54 0x52 0x43 (.SYSUITRC), such
+ that they can be easily identified. */
+message SystemUiTraceFileProto {
+
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x55535953; /* SYSU (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x43525449; /* ITRC (little-endian ASCII) */
+ }
+
+ optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+ repeated SystemUiTraceEntryProto entry = 2;
+}
+
+/* one system ui trace entry. */
+message SystemUiTraceEntryProto {
+ /* required: elapsed realtime in nanos since boot of when this entry was logged */
+ optional fixed64 elapsed_realtime_nanos = 1;
+
+ optional SystemUiTraceProto system_ui = 3;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 47454cb5aca1..cfa2947eb862 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -129,8 +129,6 @@ public class Utils {
*/
public static boolean useQsMediaPlayer(Context context) {
int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
- flag |= Settings.System.getInt(context.getContentResolver(), "npv_plugin_flag", 0);
-
return flag > 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java
new file mode 100644
index 000000000000..66bc306d4a59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wm;
+
+import android.os.Handler;
+import android.os.RemoteException;
+import android.view.IDisplayWindowRotationCallback;
+import android.view.IDisplayWindowRotationController;
+import android.view.IWindowManager;
+import android.view.WindowContainerTransaction;
+
+import java.util.ArrayList;
+
+/**
+ * This module deals with display rotations coming from WM. When WM starts a rotation: after it has
+ * frozen the screen, it will call into this class. This will then call all registered local
+ * controllers and give them a chance to queue up task changes to be applied synchronously with that
+ * rotation.
+ */
+public class DisplayChangeController {
+
+ private final Handler mHandler;
+ private final IWindowManager mWmService;
+
+ private final ArrayList<OnDisplayChangingListener> mRotationListener =
+ new ArrayList<>();
+ private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>();
+
+ private final IDisplayWindowRotationController mDisplayRotationController =
+ new IDisplayWindowRotationController.Stub() {
+ @Override
+ public void onRotateDisplay(int displayId, final int fromRotation,
+ final int toRotation, IDisplayWindowRotationCallback callback) {
+ mHandler.post(() -> {
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ synchronized (mRotationListener) {
+ mTmpListeners.clear();
+ // Make a local copy in case the handlers add/remove themselves.
+ mTmpListeners.addAll(mRotationListener);
+ }
+ for (OnDisplayChangingListener c : mTmpListeners) {
+ c.onRotateDisplay(displayId, fromRotation, toRotation, t);
+ }
+ try {
+ callback.continueRotateDisplay(toRotation, t);
+ } catch (RemoteException e) {
+ }
+ });
+ }
+ };
+
+ public DisplayChangeController(Handler mainHandler, IWindowManager wmService) {
+ mHandler = mainHandler;
+ mWmService = wmService;
+ try {
+ mWmService.setDisplayWindowRotationController(mDisplayRotationController);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Unable to register rotation controller");
+ }
+ }
+
+ /**
+ * Adds a display rotation controller.
+ */
+ public void addRotationListener(OnDisplayChangingListener listener) {
+ synchronized (mRotationListener) {
+ mRotationListener.add(listener);
+ }
+ }
+
+ /**
+ * Removes a display rotation controller.
+ */
+ public void removeRotationListener(OnDisplayChangingListener listener) {
+ synchronized (mRotationListener) {
+ mRotationListener.remove(listener);
+ }
+ }
+
+ /**
+ * Give a listener a chance to queue up configuration changes to execute as part of a
+ * display rotation. The contents of {@link #onRotateDisplay} must run synchronously.
+ */
+ public interface OnDisplayChangingListener {
+ /**
+ * Called before the display is rotated. Contents of this method must run synchronously.
+ * @param displayId Id of display that is rotating.
+ * @param fromRotation starting rotation of the display.
+ * @param toRotation target rotation of the display (after rotating).
+ * @param t A task transaction to populate.
+ */
+ void onRotateDisplay(int displayId, int fromRotation, int toRotation,
+ WindowContainerTransaction t);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java
index 951d6dd4c3a3..bc24ad0118e7 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java
@@ -26,12 +26,10 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.IDisplayWindowListener;
-import android.view.IDisplayWindowRotationCallback;
-import android.view.IDisplayWindowRotationController;
import android.view.IWindowManager;
-import android.view.WindowContainerTransaction;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.wm.DisplayChangeController.OnDisplayChangingListener;
import java.util.ArrayList;
@@ -45,42 +43,16 @@ import javax.inject.Singleton;
* rotation.
*/
@Singleton
-public class DisplayWindowController {
- private static final String TAG = "DisplayWindowController";
+public class DisplayController {
+ private static final String TAG = "DisplayController";
private final Handler mHandler;
private final Context mContext;
private final IWindowManager mWmService;
-
- private final ArrayList<OnDisplayWindowRotationController> mRotationControllers =
- new ArrayList<>();
- private final ArrayList<OnDisplayWindowRotationController> mTmpControllers = new ArrayList<>();
+ private final DisplayChangeController mChangeController;
private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>();
- private final ArrayList<DisplayWindowListener> mDisplayChangedListeners = new ArrayList<>();
-
- private final IDisplayWindowRotationController mDisplayRotationController =
- new IDisplayWindowRotationController.Stub() {
- @Override
- public void onRotateDisplay(int displayId, final int fromRotation,
- final int toRotation, IDisplayWindowRotationCallback callback) {
- mHandler.post(() -> {
- WindowContainerTransaction t = new WindowContainerTransaction();
- synchronized (mRotationControllers) {
- mTmpControllers.clear();
- // Make a local copy in case the handlers add/remove themselves.
- mTmpControllers.addAll(mRotationControllers);
- }
- for (OnDisplayWindowRotationController c : mTmpControllers) {
- c.onRotateDisplay(displayId, fromRotation, toRotation, t);
- }
- try {
- callback.continueRotateDisplay(toRotation, t);
- } catch (RemoteException e) {
- }
- });
- }
- };
+ private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
/**
* Get's a display by id from DisplayManager.
@@ -160,14 +132,14 @@ public class DisplayWindowController {
};
@Inject
- public DisplayWindowController(Context context, @Main Handler mainHandler,
+ public DisplayController(Context context, @Main Handler mainHandler,
IWindowManager wmService) {
mHandler = mainHandler;
mContext = context;
mWmService = wmService;
+ mChangeController = new DisplayChangeController(mHandler, mWmService);
try {
mWmService.registerDisplayWindowListener(mDisplayContainerListener);
- mWmService.setDisplayWindowRotationController(mDisplayRotationController);
} catch (RemoteException e) {
throw new RuntimeException("Unable to register hierarchy listener");
}
@@ -193,7 +165,7 @@ public class DisplayWindowController {
* Add a display window-container listener. It will get notified whenever a display's
* configuration changes or when displays are added/removed from the WM hierarchy.
*/
- public void addDisplayWindowListener(DisplayWindowListener listener) {
+ public void addDisplayWindowListener(OnDisplaysChangedListener listener) {
synchronized (mDisplays) {
if (mDisplayChangedListeners.contains(listener)) {
return;
@@ -208,7 +180,7 @@ public class DisplayWindowController {
/**
* Remove a display window-container listener.
*/
- public void removeDisplayWindowListener(DisplayWindowListener listener) {
+ public void removeDisplayWindowListener(OnDisplaysChangedListener listener) {
synchronized (mDisplays) {
mDisplayChangedListeners.remove(listener);
}
@@ -217,19 +189,15 @@ public class DisplayWindowController {
/**
* Adds a display rotation controller.
*/
- public void addRotationController(OnDisplayWindowRotationController controller) {
- synchronized (mRotationControllers) {
- mRotationControllers.add(controller);
- }
+ public void addDisplayChangingController(OnDisplayChangingListener controller) {
+ mChangeController.addRotationListener(controller);
}
/**
* Removes a display rotation controller.
*/
- public void removeRotationController(OnDisplayWindowRotationController controller) {
- synchronized (mRotationControllers) {
- mRotationControllers.remove(controller);
- }
+ public void removeDisplayChangingController(OnDisplayChangingListener controller) {
+ mChangeController.removeRotationListener(controller);
}
private static class DisplayRecord {
@@ -244,36 +212,20 @@ public class DisplayWindowController {
*
* @see IDisplayWindowListener
*/
- public interface DisplayWindowListener {
+ public interface OnDisplaysChangedListener {
/**
* Called when a display has been added to the WM hierarchy.
*/
- void onDisplayAdded(int displayId);
+ default void onDisplayAdded(int displayId) {}
/**
* Called when a display's window-container configuration changes.
*/
- void onDisplayConfigurationChanged(int displayId, Configuration newConfig);
+ default void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {}
/**
* Called when a display is removed.
*/
- void onDisplayRemoved(int displayId);
- }
-
- /**
- * Give a controller a chance to queue up configuration changes to execute as part of a
- * display rotation. The contents of {@link #onRotateDisplay} must run synchronously.
- */
- public interface OnDisplayWindowRotationController {
- /**
- * Called before the display is rotated. Contents of this method must run synchronously.
- * @param displayId Id of display that is rotating.
- * @param fromRotation starting rotation of the display.
- * @param toRotation target rotation of the display (after rotating).
- * @param t A task transaction to populate.
- */
- void onRotateDisplay(int displayId, int fromRotation, int toRotation,
- WindowContainerTransaction t);
+ default void onDisplayRemoved(int displayId) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index d413308d4573..7dad05df8f2c 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -45,7 +45,7 @@ import javax.inject.Singleton;
* Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
*/
@Singleton
-public class DisplayImeController implements DisplayWindowController.DisplayWindowListener {
+public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayImeController";
static final int ANIMATION_DURATION_SHOW_MS = 275;
@@ -63,7 +63,7 @@ public class DisplayImeController implements DisplayWindowController.DisplayWind
final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
@Inject
- DisplayImeController(SystemWindows syswin, DisplayWindowController displayController,
+ public DisplayImeController(SystemWindows syswin, DisplayController displayController,
@Main Handler mainHandler) {
mHandler = mainHandler;
mSystemWindows = syswin;
@@ -315,19 +315,20 @@ public class DisplayImeController implements DisplayWindowController.DisplayWind
/**
* Called when the IME position is starting to animate.
*/
- void onImeStartPositioning(int displayId, int imeTop, int finalImeTop, boolean showing,
- SurfaceControl.Transaction t);
+ default void onImeStartPositioning(int displayId, int imeTop, int finalImeTop,
+ boolean showing, SurfaceControl.Transaction t) {}
/**
* Called when the ime position changed. This is expected to be a synchronous call on the
* animation thread. Operations can be added to the transaction to be applied in sync.
*/
- void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t);
+ default void onImePositionChanged(int displayId, int imeTop,
+ SurfaceControl.Transaction t) {}
/**
* Called when the IME position is done animating.
*/
- void onImeEndPositioning(int displayId, int imeTop, boolean showing,
- SurfaceControl.Transaction t);
+ default void onImeEndPositioning(int displayId, int imeTop, boolean showing,
+ SurfaceControl.Transaction t) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index 5aba013a7fb8..044a2a6cc4b6 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -64,11 +64,11 @@ public class SystemWindows {
final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
Context mContext;
IWindowSession mSession;
- DisplayWindowController mDisplayController;
+ DisplayController mDisplayController;
IWindowManager mWmService;
- private final DisplayWindowController.DisplayWindowListener mDisplayListener =
- new DisplayWindowController.DisplayWindowListener() {
+ private final DisplayController.OnDisplaysChangedListener mDisplayListener =
+ new DisplayController.OnDisplaysChangedListener() {
@Override
public void onDisplayAdded(int displayId) { }
@@ -86,7 +86,7 @@ public class SystemWindows {
};
@Inject
- public SystemWindows(Context context, DisplayWindowController displayController,
+ public SystemWindows(Context context, DisplayController displayController,
IWindowManager wmService) {
mContext = context;
mWmService = wmService;
@@ -239,12 +239,12 @@ public class SystemWindows {
Rect outVisibleInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- Point outSurfaceSize) {
+ Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight,
viewVisibility, flags, frameNumber, outFrame, outOverscanInsets,
outContentInsets, outVisibleInsets, outStableInsets,
cutout, mergedConfiguration, outSurfaceControl, outInsetsState,
- outSurfaceSize);
+ outSurfaceSize, outBLASTSurfaceControl);
if (res != 0) {
return res;
}