summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SystemUI/src')
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java226
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadButton.java48
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/Prefs.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java109
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java91
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java297
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java (renamed from packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java94
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetail.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java165
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt185
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt277
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java440
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java363
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java110
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/Execution.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java4
90 files changed, 2790 insertions, 1113 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 4b71a3a8fbdc..baf34589770c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -19,42 +19,22 @@ package com.android.keyguard;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.app.PendingIntent;
import android.app.WallpaperManager;
-import android.app.smartspace.SmartspaceConfig;
-import android.app.smartspace.SmartspaceManager;
-import android.app.smartspace.SmartspaceSession;
-import android.app.smartspace.SmartspaceTarget;
-import android.content.Intent;
-import android.content.pm.UserInfo;
import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin.IntentStarter;
import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -62,14 +42,10 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
-import com.android.systemui.util.settings.SecureSettings;
import java.util.Locale;
-import java.util.Optional;
import java.util.TimeZone;
-import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -85,9 +61,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final KeyguardSliceViewController mKeyguardSliceViewController;
private final NotificationIconAreaController mNotificationIconAreaController;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final Executor mUiExecutor;
private final BatteryController mBatteryController;
- private final FeatureFlags mFeatureFlags;
+ private final LockscreenSmartspaceController mSmartspaceController;
/**
* Clock for both small and large sizes
@@ -97,20 +72,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private AnimatableClockController mLargeClockViewController;
private FrameLayout mLargeClockFrame;
- private SmartspaceSession mSmartspaceSession;
- private SmartspaceSession.OnTargetsAvailableListener mSmartspaceCallback;
- private ConfigurationController mConfigurationController;
- private ActivityStarter mActivityStarter;
- private FalsingManager mFalsingManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mBypassController;
- private Handler mHandler;
- private UserTracker mUserTracker;
- private SecureSettings mSecureSettings;
- private ContentObserver mSettingsObserver;
- private boolean mShowSensitiveContentForCurrentUser;
- private boolean mShowSensitiveContentForManagedUser;
- private UserHandle mManagedUserHandle;
/**
* Listener for changes to the color palette.
@@ -118,59 +81,30 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* The color palette changes when the wallpaper is changed.
*/
private final ColorExtractor.OnColorsChangedListener mColorsListener =
- new ColorExtractor.OnColorsChangedListener() {
- @Override
- public void onColorsChanged(ColorExtractor extractor, int which) {
- if ((which & WallpaperManager.FLAG_LOCK) != 0) {
- mView.updateColors(getGradientColors());
- }
- }
- };
-
- private final ConfigurationController.ConfigurationListener mConfigurationListener =
- new ConfigurationController.ConfigurationListener() {
- @Override
- public void onThemeChanged() {
- updateWallpaperColor();
- }
- };
-
- private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
-
- private final StatusBarStateController.StateListener mStatusBarStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- if (mSmartspaceView != null) {
- mSmartspaceView.setDozeAmount(eased);
- }
+ (extractor, which) -> {
+ if ((which & WallpaperManager.FLAG_LOCK) != 0) {
+ mView.updateColors(getGradientColors());
}
};
+ private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
+
// If set, will replace keyguard_status_area
- private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView;
- private Optional<BcSmartspaceDataPlugin> mSmartspacePlugin;
+ private View mSmartspaceView;
@Inject
public KeyguardClockSwitchController(
KeyguardClockSwitch keyguardClockSwitch,
StatusBarStateController statusBarStateController,
- SysuiColorExtractor colorExtractor, ClockManager clockManager,
+ SysuiColorExtractor colorExtractor,
+ ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
BroadcastDispatcher broadcastDispatcher,
- FeatureFlags featureFlags,
- @Main Executor uiExecutor,
BatteryController batteryController,
- ConfigurationController configurationController,
- ActivityStarter activityStarter,
- FalsingManager falsingManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController bypassController,
- @Main Handler handler,
- UserTracker userTracker,
- SecureSettings secureSettings,
- Optional<BcSmartspaceDataPlugin> smartspacePlugin) {
+ LockscreenSmartspaceController smartspaceController) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -178,18 +112,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
mBroadcastDispatcher = broadcastDispatcher;
- mFeatureFlags = featureFlags;
- mUiExecutor = uiExecutor;
mBatteryController = batteryController;
- mConfigurationController = configurationController;
- mActivityStarter = activityStarter;
- mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
- mHandler = handler;
- mUserTracker = userTracker;
- mSecureSettings = secureSettings;
- mSmartspacePlugin = smartspacePlugin;
+ mSmartspaceController = smartspaceController;
}
/**
@@ -232,119 +158,33 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mBypassController);
mLargeClockViewController.init();
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mConfigurationController.addCallback(mConfigurationListener);
+ if (mSmartspaceController.isEnabled()) {
+ mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
- if (mFeatureFlags.isSmartspaceEnabled() && mSmartspacePlugin.isPresent()) {
- BcSmartspaceDataPlugin smartspaceDataPlugin = mSmartspacePlugin.get();
View ksa = mView.findViewById(R.id.keyguard_status_area);
int ksaIndex = mView.indexOfChild(ksa);
ksa.setVisibility(View.GONE);
- mSmartspaceView = smartspaceDataPlugin.getView(mView);
- mSmartspaceView.registerDataProvider(smartspaceDataPlugin);
- mSmartspaceView.setIntentStarter(new IntentStarter() {
- public void startIntent(View v, Intent i) {
- mActivityStarter.startActivity(i, true /* dismissShade */);
- }
-
- public void startPendingIntent(PendingIntent pi) {
- mActivityStarter.startPendingIntentDismissingKeyguard(pi);
- }
- });
- mSmartspaceView.setFalsingManager(mFalsingManager);
- updateWallpaperColor();
- View asView = (View) mSmartspaceView;
-
// Place smartspace view below normal clock...
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
MATCH_PARENT, WRAP_CONTENT);
lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view);
- mView.addView(asView, ksaIndex, lp);
+ mView.addView(mSmartspaceView, ksaIndex, lp);
int padding = getContext().getResources()
.getDimensionPixelSize(R.dimen.below_clock_padding_start);
- asView.setPadding(padding, 0, padding, 0);
+ mSmartspaceView.setPadding(padding, 0, padding, 0);
// ... but above the large clock
lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
- lp.addRule(RelativeLayout.BELOW, asView.getId());
+ lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
mLargeClockFrame.setLayoutParams(lp);
View nic = mView.findViewById(
R.id.left_aligned_notification_icon_container);
lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
- lp.addRule(RelativeLayout.BELOW, asView.getId());
+ lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
nic.setLayoutParams(lp);
-
- mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
- .createSmartspaceSession(
- new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
- mSmartspaceCallback = targets -> {
- targets.removeIf(this::filterSmartspaceTarget);
- smartspaceDataPlugin.onTargetsAvailable(targets);
- };
- mSmartspaceSession.addOnTargetsAvailableListener(mUiExecutor, mSmartspaceCallback);
- mSettingsObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- reloadSmartspace();
- }
- };
-
- getContext().getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
- true, mSettingsObserver, UserHandle.USER_ALL);
- reloadSmartspace();
- }
-
- float dozeAmount = mStatusBarStateController.getDozeAmount();
- mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
- }
-
- @VisibleForTesting
- boolean filterSmartspaceTarget(SmartspaceTarget t) {
- if (!t.isSensitive()) return false;
-
- if (t.getUserHandle().equals(mUserTracker.getUserHandle())) {
- return !mShowSensitiveContentForCurrentUser;
- }
- if (t.getUserHandle().equals(mManagedUserHandle)) {
- return !mShowSensitiveContentForManagedUser;
- }
-
- return false;
- }
-
- private void reloadSmartspace() {
- mManagedUserHandle = getWorkProfileUser();
- final String setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
-
- mShowSensitiveContentForCurrentUser =
- mSecureSettings.getIntForUser(setting, 0, mUserTracker.getUserId()) == 1;
- if (mManagedUserHandle != null) {
- int id = mManagedUserHandle.getIdentifier();
- mShowSensitiveContentForManagedUser =
- mSecureSettings.getIntForUser(setting, 0, id) == 1;
- }
-
- mSmartspaceSession.requestSmartspaceUpdate();
- }
-
- private UserHandle getWorkProfileUser() {
- for (UserInfo userInfo : mUserTracker.getUserProfiles()) {
- if (userInfo.isManagedProfile()) {
- return userInfo.getUserHandle();
- }
- }
- return null;
- }
-
- private void updateWallpaperColor() {
- if (mSmartspaceView != null) {
- int color = Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColor);
- mSmartspaceView.setPrimaryTextColor(color);
}
}
@@ -356,16 +196,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mColorExtractor.removeOnColorsChangedListener(mColorsListener);
mView.setClockPlugin(null, mStatusBarStateController.getState());
- if (mSmartspaceSession != null) {
- mSmartspaceSession.removeOnTargetsAvailableListener(mSmartspaceCallback);
- mSmartspaceSession.close();
- mSmartspaceSession = null;
- }
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
- mConfigurationController.removeCallback(mConfigurationListener);
+ mSmartspaceController.disconnect();
- if (mSettingsObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mSettingsObserver);
+ // TODO: This is an unfortunate necessity since smartspace plugin retains a single instance
+ // of the smartspace view -- if we don't remove the view, it can't be reused by a later
+ // instance of this class. In order to fix this, we need to modify the plugin so that
+ // (a) we get a new view each time and (b) we can properly clean up an old view by making
+ // it unregister itself as a plugin listener.
+ if (mSmartspaceView != null) {
+ mView.removeView(mSmartspaceView);
+ mSmartspaceView = null;
}
}
@@ -436,7 +276,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
scale, props, animate);
if (mSmartspaceView != null) {
- PropertyAnimator.setProperty((View) mSmartspaceView, AnimatableProperty.TRANSLATION_X,
+ PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
x, props, animate);
}
@@ -510,14 +350,4 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private int getCurrentLayoutDirection() {
return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
}
-
- @VisibleForTesting
- ConfigurationController.ConfigurationListener getConfigurationListener() {
- return mConfigurationListener;
- }
-
- @VisibleForTesting
- ContentObserver getSettingsObserver() {
- return mSettingsObserver;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 3d42da2e5158..97d3a5a4cd18 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -28,7 +28,6 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -167,7 +166,6 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private final TelephonyManager mTelephonyManager;
private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
private final FalsingCollector mFalsingCollector;
- private final boolean mIsNewLayoutEnabled;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -177,8 +175,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
@Main Resources resources, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
- EmergencyButtonController.Factory emergencyButtonControllerFactory,
- FeatureFlags featureFlags) {
+ EmergencyButtonController.Factory emergencyButtonControllerFactory) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -190,7 +187,6 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
mTelephonyManager = telephonyManager;
mEmergencyButtonControllerFactory = emergencyButtonControllerFactory;
mFalsingCollector = falsingCollector;
- mIsNewLayoutEnabled = featureFlags.isKeyguardLayoutEnabled();
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -216,20 +212,19 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
- mIsNewLayoutEnabled);
+ mLiftToActivateListener, emergencyButtonController, mFalsingCollector);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
- emergencyButtonController, mIsNewLayoutEnabled);
+ emergencyButtonController);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
- emergencyButtonController, mIsNewLayoutEnabled);
+ emergencyButtonController);
}
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 09fb8efba4e8..0b8868f6d3c4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -169,20 +169,6 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
}
/**
- * By default, the new layout will be enabled. When false, revert to the old style.
- */
- public void setIsNewLayoutEnabled(boolean isEnabled) {
- if (!isEnabled) {
- for (int i = 0; i < mButtons.length; i++) {
- mButtons[i].disableNewLayout();
- }
- mDeleteButton.disableNewLayout();
- mOkButton.disableNewLayout();
- reloadColors();
- }
- }
-
- /**
* Reload colors from resources.
**/
public void reloadColors() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index a456d42f5be5..262bed3f695c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -35,12 +35,11 @@ public class KeyguardPinViewController
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
EmergencyButtonController emergencyButtonController,
- FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
+ FalsingCollector falsingCollector) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- view.setIsNewLayoutEnabled(isNewLayoutEnabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 90d191f0f418..90d140eabb20 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -80,14 +80,13 @@ public class KeyguardSimPinViewController
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
- EmergencyButtonController emergencyButtonController, boolean isNewLayoutEnabled) {
+ EmergencyButtonController emergencyButtonController) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
- view.setIsNewLayoutEnabled(isNewLayoutEnabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 6e868b9f0ab6..ff680a56efe7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -85,14 +85,13 @@ public class KeyguardSimPukViewController
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
- EmergencyButtonController emergencyButtonController, boolean isNewLayoutEnabled) {
+ EmergencyButtonController emergencyButtonController) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
- view.setIsNewLayoutEnabled(isNewLayoutEnabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index abdd770c37d4..e6298a426c5b 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.RippleDrawable;
import android.view.ContextThemeWrapper;
@@ -40,17 +39,14 @@ class NumPadAnimator {
private ValueAnimator mContractAnimator;
private GradientDrawable mBackground;
private RippleDrawable mRipple;
- private GradientDrawable mRippleMask;
private int mNormalColor;
private int mHighlightColor;
private int mStyle;
- NumPadAnimator(Context context, LayerDrawable drawable, @StyleRes int style) {
- LayerDrawable ld = (LayerDrawable) drawable.mutate();
- mBackground = (GradientDrawable) ld.findDrawableByLayerId(R.id.background);
- mRipple = (RippleDrawable) ld.findDrawableByLayerId(R.id.ripple);
- mRippleMask = (GradientDrawable) mRipple.findDrawableByLayerId(android.R.id.mask);
+ NumPadAnimator(Context context, final RippleDrawable drawable, @StyleRes int style) {
mStyle = style;
+ mRipple = (RippleDrawable) drawable.mutate();
+ mBackground = (GradientDrawable) mRipple.findDrawableByLayerId(R.id.background);
reloadColors(context);
@@ -62,7 +58,7 @@ class NumPadAnimator {
mExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator anim) {
mBackground.setCornerRadius((float) anim.getAnimatedValue());
- mRippleMask.setCornerRadius((float) anim.getAnimatedValue());
+ mRipple.invalidateSelf();
}
});
@@ -73,7 +69,7 @@ class NumPadAnimator {
mContractAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator anim) {
mBackground.setCornerRadius((float) anim.getAnimatedValue());
- mRippleMask.setCornerRadius((float) anim.getAnimatedValue());
+ mRipple.invalidateSelf();
}
});
mAnimator.playSequentially(mExpandAnimator, mContractAnimator);
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index b76499a39ff8..096597afa889 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -16,37 +16,22 @@
package com.android.keyguard;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.VectorDrawable;
+import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
import android.view.MotionEvent;
-import androidx.annotation.Nullable;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.R;
-
/**
* Similar to the {@link NumPadKey}, but displays an image.
*/
public class NumPadButton extends AlphaOptimizedImageButton {
- @Nullable
private NumPadAnimator mAnimator;
public NumPadButton(Context context, AttributeSet attrs) {
super(context, attrs);
- Drawable background = getBackground();
- if (background instanceof LayerDrawable) {
- mAnimator = new NumPadAnimator(context, (LayerDrawable) background,
- attrs.getStyleAttribute());
- } else {
- mAnimator = null;
- }
+ mAnimator = new NumPadAnimator(context, (RippleDrawable) getBackground(),
+ attrs.getStyleAttribute());
}
@Override
@@ -56,7 +41,7 @@ public class NumPadButton extends AlphaOptimizedImageButton {
// Set width/height to the same value to ensure a smooth circle for the bg, but shrink
// the height to match the old pin bouncer
int width = getMeasuredWidth();
- int height = mAnimator == null ? (int) (width * .75f) : width;
+ int height = width;
setMeasuredDimension(getMeasuredWidth(), height);
}
@@ -65,13 +50,13 @@ public class NumPadButton extends AlphaOptimizedImageButton {
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- if (mAnimator != null) mAnimator.onLayout(b - t);
+ mAnimator.onLayout(b - t);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- if (mAnimator != null) mAnimator.start();
+ mAnimator.start();
}
return super.onTouchEvent(event);
}
@@ -80,25 +65,6 @@ public class NumPadButton extends AlphaOptimizedImageButton {
* Reload colors from resources.
**/
public void reloadColors() {
- if (mAnimator != null) {
- mAnimator.reloadColors(getContext());
- } else {
- // Needed for old style pin
- int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary)
- .getDefaultColor();
- ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor));
- }
- }
-
- /**
- * By default, the new layout will be enabled. Invoking will revert to the old style
- */
- public void disableNewLayout() {
- if (mAnimator != null) {
- mAnimator = null;
- ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
- setBackground(getContext().getResources().getDrawable(
- R.drawable.ripple_drawable_pin, ctw.getTheme()));
- }
+ mAnimator.reloadColors(getContext());
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 89c1a7fe21ca..35ace0d1a404 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -18,12 +18,10 @@ package com.android.keyguard;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.RippleDrawable;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -32,8 +30,6 @@ import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
-import androidx.annotation.Nullable;
-
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -51,7 +47,6 @@ public class NumPadKey extends ViewGroup {
private int mTextViewResId;
private PasswordTextView mTextView;
- @Nullable
private NumPadAnimator mAnimator;
private View.OnClickListener mListener = new View.OnClickListener() {
@@ -131,26 +126,8 @@ public class NumPadKey extends ViewGroup {
setContentDescription(mDigitText.getText().toString());
- Drawable background = getBackground();
- if (background instanceof LayerDrawable) {
- mAnimator = new NumPadAnimator(context, (LayerDrawable) background,
- R.style.NumPadKey);
- } else {
- mAnimator = null;
- }
- }
-
- /**
- * By default, the new layout will be enabled. Invoking will revert to the old style
- */
- public void disableNewLayout() {
- findViewById(R.id.klondike_text).setVisibility(View.VISIBLE);
- if (mAnimator != null) {
- mAnimator = null;
- ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.NumPadKey);
- setBackground(getContext().getResources().getDrawable(
- R.drawable.ripple_drawable_pin, ctw.getTheme()));
- }
+ mAnimator = new NumPadAnimator(context, (RippleDrawable) getBackground(),
+ R.style.NumPadKey);
}
/**
@@ -164,14 +141,14 @@ public class NumPadKey extends ViewGroup {
mDigitText.setTextColor(textColor);
mKlondikeText.setTextColor(klondikeColor);
- if (mAnimator != null) mAnimator.reloadColors(getContext());
+ mAnimator.reloadColors(getContext());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
doHapticKeyClick();
- if (mAnimator != null) mAnimator.start();
+ mAnimator.start();
}
return super.onTouchEvent(event);
@@ -185,7 +162,7 @@ public class NumPadKey extends ViewGroup {
// Set width/height to the same value to ensure a smooth circle for the bg, but shrink
// the height to match the old pin bouncer
int width = getMeasuredWidth();
- int height = mAnimator == null ? (int) (width * .75f) : width;
+ int height = width;
setMeasuredDimension(getMeasuredWidth(), height);
}
@@ -206,7 +183,7 @@ public class NumPadKey extends ViewGroup {
left = centerX - mKlondikeText.getMeasuredWidth() / 2;
mKlondikeText.layout(left, top, left + mKlondikeText.getMeasuredWidth(), bottom);
- if (mAnimator != null) mAnimator.onLayout(b - t);
+ mAnimator.onLayout(b - t);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 76cec0b33477..64a683e78953 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -305,10 +305,15 @@ public class ImageWallpaper extends WallpaperService {
continue;
}
Rect subImage = new Rect(
- Math.round(area.left * b.getWidth()),
- Math.round(area.top * b.getHeight()),
- Math.round(area.right * b.getWidth()),
- Math.round(area.bottom * b.getHeight()));
+ (int) Math.floor(area.left * b.getWidth()),
+ (int) Math.floor(area.top * b.getHeight()),
+ (int) Math.ceil(area.right * b.getWidth()),
+ (int) Math.ceil(area.bottom * b.getHeight()));
+ if (subImage.isEmpty()) {
+ // Do not notify client. treat it as too small to sample
+ colors.add(null);
+ continue;
+ }
Bitmap colorImg = Bitmap.createBitmap(b,
subImage.left, subImage.top, subImage.width(), subImage.height());
WallpaperColors color = WallpaperColors.fromBitmap(colorImg);
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 7f183798709c..bf09975bb034 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -73,7 +73,8 @@ public final class Prefs {
Key.TOUCHED_RINGER_TOGGLE,
Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
Key.HAS_SEEN_REVERSE_BOTTOM_SHEET,
- Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
+ Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT,
+ Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP
})
// TODO: annotate these with their types so {@link PrefsCommandLine} can know how to set them
public @interface Key {
@@ -122,6 +123,8 @@ public final class Prefs {
String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet";
String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
+ String HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP =
+ "HasSeenAccessibilityFloatingMenuDockTooltip";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 4b8ec5022573..0213335d6372 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -55,8 +55,6 @@ import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Secure;
@@ -91,11 +89,13 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -126,7 +126,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
@VisibleForTesting
protected boolean mIsRegistered;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final Handler mMainHandler;
+ private final Executor mMainExecutor;
private final TunerService mTunerService;
private final SecureSettings mSecureSettings;
private DisplayManager.DisplayListener mDisplayListener;
@@ -157,6 +157,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private WindowManager mWindowManager;
private int mRotation;
private SecureSetting mColorInversionSetting;
+ private DelayableExecutor mExecutor;
private Handler mHandler;
private boolean mPendingRotationChange;
private boolean mIsRoundedCornerMultipleRadius;
@@ -216,7 +217,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
@Inject
public ScreenDecorations(Context context,
- @Main Handler handler,
+ @Main Executor mainExecutor,
SecureSettings secureSettings,
BroadcastDispatcher broadcastDispatcher,
TunerService tunerService,
@@ -224,7 +225,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
PrivacyDotViewController dotViewController,
ThreadFactory threadFactory) {
super(context);
- mMainHandler = handler;
+ mMainExecutor = mainExecutor;
mSecureSettings = secureSettings;
mBroadcastDispatcher = broadcastDispatcher;
mTunerService = tunerService;
@@ -239,17 +240,10 @@ public class ScreenDecorations extends SystemUI implements Tunable {
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler = startHandlerThread();
- mHandler.post(this::startOnScreenDecorationsThread);
- mDotViewController.setUiExecutor(
- mThreadFactory.buildDelayableExecutorOnLooper(mHandler.getLooper()));
- }
-
- @VisibleForTesting
- Handler startHandlerThread() {
- HandlerThread thread = new HandlerThread("ScreenDecorations");
- thread.start();
- return thread.getThreadHandler();
+ mHandler = mThreadFactory.builderHandlerOnNewThread("ScreenDecorations");
+ mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler);
+ mExecutor.execute(this::startOnScreenDecorationsThread);
+ mDotViewController.setUiExecutor(mExecutor);
}
private void startOnScreenDecorationsThread() {
@@ -336,7 +330,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
mDisplayManager.getDisplay(DEFAULT_DISPLAY).getMetrics(metrics);
mDensity = metrics.density;
- mMainHandler.post(() -> mTunerService.addTunable(this, SIZE));
+ mExecutor.execute(() -> mTunerService.addTunable(this, SIZE));
// Watch color inversion and invert the overlay as needed.
if (mColorInversionSetting == null) {
@@ -355,10 +349,10 @@ public class ScreenDecorations extends SystemUI implements Tunable {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
mBroadcastDispatcher.registerReceiver(mUserSwitchIntentReceiver, filter,
- new HandlerExecutor(mHandler), UserHandle.ALL);
+ mExecutor, UserHandle.ALL);
mIsRegistered = true;
} else {
- mMainHandler.post(() -> mTunerService.removeTunable(this));
+ mMainExecutor.execute(() -> mTunerService.removeTunable(this));
if (mColorInversionSetting != null) {
mColorInversionSetting.setListening(false);
@@ -571,7 +565,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
Resources res = mContext.getResources();
boolean enabled = res.getBoolean(R.bool.config_enableDisplayCutoutProtection);
if (enabled) {
- mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mHandler::post);
+ mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mExecutor);
mCameraListener.addTransitionCallback(mCameraTransitionCallback);
mCameraListener.startListening();
}
@@ -628,7 +622,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
int oldRotation = mRotation;
mPendingRotationChange = false;
updateOrientation();
@@ -836,7 +830,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (mOverlays == null) return;
if (SIZE.equals(key)) {
Point size = mRoundedDefault;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
index b69001d1ba07..c472457b0206 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -63,17 +63,15 @@ abstract class DisplayIdIndexSupplier<T> {
}
/**
- * Gets the object by the element index.
+ * Returns the object with the given display id.
*
- * <p> If the index is bigger than the array size, an {@link ArrayIndexOutOfBoundsException} is
- * thrown for apps targeting {@link android.os.Build.VERSION_CODES#Q} and later </p>
*
- * @param index the element index
+ * @param displayId the logical display Id
* @return T
- * @see SparseArray#valueAt(int)
*/
- public T valueAt(int index) {
- return mSparseArray.valueAt(index);
+ @Nullable
+ public T valueAt(int displayId) {
+ return mSparseArray.get(displayId);
}
@NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
index 4c892e29f386..4b30ec3e6f6f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
@@ -113,7 +113,7 @@ class MagnificationGestureDetector {
final float rawX = event.getRawX();
final float rawY = event.getRawY();
boolean handled = false;
- switch (event.getAction()) {
+ switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mPointerDown.set(rawX, rawY);
mHandler.postAtTime(mCancelTapGestureRunnable,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 4f5fdc90e929..cee395bd9a6a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -18,10 +18,11 @@ package com.android.systemui.accessibility;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -37,9 +38,13 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
import javax.inject.Inject;
/**
@@ -52,34 +57,33 @@ import javax.inject.Inject;
public class WindowMagnification extends SystemUI implements WindowMagnifierCallback,
CommandQueue.Callbacks {
private static final String TAG = "WindowMagnification";
- private static final int CONFIG_MASK =
- ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION
- | ActivityInfo.CONFIG_LOCALE;
private final ModeSwitchesController mModeSwitchesController;
private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
private final CommandQueue mCommandQueue;
+ private final OverviewProxyService mOverviewProxyService;
private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
private Configuration mLastConfiguration;
+ private SysUiState mSysUiState;
private static class AnimationControllerSupplier extends
DisplayIdIndexSupplier<WindowMagnificationAnimationController> {
private final Context mContext;
private final Handler mHandler;
- private final NavigationModeController mNavigationModeController;
private final WindowMagnifierCallback mWindowMagnifierCallback;
+ private final SysUiState mSysUiState;
AnimationControllerSupplier(Context context, Handler handler,
- NavigationModeController navigationModeController,
- WindowMagnifierCallback windowMagnifierCallback, DisplayManager displayManager) {
+ WindowMagnifierCallback windowMagnifierCallback,
+ DisplayManager displayManager, SysUiState sysUiState) {
super(displayManager);
mContext = context;
mHandler = handler;
- mNavigationModeController = navigationModeController;
mWindowMagnifierCallback = windowMagnifierCallback;
+ mSysUiState = sysUiState;
}
@Override
@@ -89,10 +93,7 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
final WindowMagnificationController controller = new WindowMagnificationController(
mContext,
mHandler, new SfVsyncFrameCallbackProvider(), null,
- new SurfaceControl.Transaction(), mWindowMagnifierCallback);
- final int navBarMode = mNavigationModeController.addListener(
- controller::onNavigationModeChanged);
- controller.onNavigationModeChanged(navBarMode);
+ new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState);
return new WindowMagnificationAnimationController(windowContext, controller);
}
}
@@ -103,24 +104,22 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
@Inject
public WindowMagnification(Context context, @Main Handler mainHandler,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
- NavigationModeController navigationModeController) {
+ SysUiState sysUiState, OverviewProxyService overviewProxyService) {
super(context);
mHandler = mainHandler;
mLastConfiguration = new Configuration(context.getResources().getConfiguration());
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mCommandQueue = commandQueue;
mModeSwitchesController = modeSwitchesController;
+ mSysUiState = sysUiState;
+ mOverviewProxyService = overviewProxyService;
mAnimationControllerSupplier = new AnimationControllerSupplier(context,
- mHandler, navigationModeController, this,
- context.getSystemService(DisplayManager.class));
+ mHandler, this, context.getSystemService(DisplayManager.class), sysUiState);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
final int configDiff = newConfig.diff(mLastConfiguration);
- if ((configDiff & CONFIG_MASK) == 0) {
- return;
- }
mLastConfiguration.setTo(newConfig);
mAnimationControllerSupplier.forEach(
animationController -> animationController.onConfigurationChanged(configDiff));
@@ -132,6 +131,28 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
@Override
public void start() {
mCommandQueue.addCallback(this);
+ mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateSysUiStateFlag();
+ }
+ }
+ });
+ }
+
+ private void updateSysUiStateFlag() {
+ //TODO(b/187510533): support multi-display once SysuiState supports it.
+ final WindowMagnificationAnimationController controller =
+ mAnimationControllerSupplier.valueAt(Display.DEFAULT_DISPLAY);
+ if (controller != null) {
+ controller.updateSysUiStateFlag();
+ } else {
+ // The instance is initialized when there is an IPC request. Considering
+ // self-crash cases, we need to reset the flag in such situation.
+ mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
+ .commitUpdate(Display.DEFAULT_DISPLAY);
+ }
}
@MainThread
@@ -210,6 +231,13 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
}
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(TAG);
+ mAnimationControllerSupplier.forEach(
+ animationController -> animationController.dump(pw));
+ }
+
private void setWindowMagnificationConnection() {
if (mWindowMagnificationConnectionImpl == null) {
mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 5758b1575f5a..36fef3ed6b51 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -31,6 +31,7 @@ import android.view.animation.AccelerateInterpolator;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -269,6 +270,14 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
mController.enableWindowMagnification(sentScale, centerX, centerY);
}
+ public void updateSysUiStateFlag() {
+ mController.updateSysUIStateFlag();
+ }
+
+ void dump(PrintWriter pw) {
+ mController.dump(pw);
+ }
+
private static ValueAnimator newValueAnimator(Resources resources) {
final ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index ae16703405a5..fcb090a76d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -16,9 +16,10 @@
package com.android.systemui.accessibility;
+import static android.view.WindowInsets.Type.systemGestures;
import static android.view.WindowManager.LayoutParams;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -28,6 +29,7 @@ import android.annotation.UiContext;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -52,14 +54,17 @@ import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.Locale;
@@ -111,6 +116,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private final View.OnLayoutChangeListener mMirrorSurfaceViewLayoutChangeListener;
private final Runnable mMirrorViewRunnable;
private final Runnable mUpdateStateDescriptionRunnable;
+ private final Runnable mWindowInsetChangeRunnable;
private View mMirrorView;
private SurfaceView mMirrorSurfaceView;
private int mMirrorSurfaceMargin;
@@ -119,9 +125,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private int mOuterBorderSize;
// The boundary of magnification frame.
private final Rect mMagnificationFrameBoundary = new Rect();
-
- private int mNavBarMode;
- private int mNavGestureHeight;
+ // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid.
+ private int mSystemGestureTop = -1;
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
private final MagnificationGestureDetector mGestureDetector;
@@ -130,6 +135,9 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private Locale mLocale;
private NumberFormat mPercentFormat;
private float mBounceEffectAnimationScale;
+ private SysUiState mSysUiState;
+ // Set it to true when the view is overlapped with the gesture insets at the bottom.
+ private boolean mOverlapWithGestureInsets;
@Nullable
private MirrorWindowControl mMirrorWindowControl;
@@ -137,11 +145,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
WindowMagnificationController(@UiContext Context context, @NonNull Handler handler,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
- @NonNull WindowMagnifierCallback callback) {
+ @NonNull WindowMagnifierCallback callback, SysUiState sysUiState) {
mContext = context;
mHandler = handler;
mSfVsyncFrameProvider = sfVsyncFrameProvider;
mWindowMagnifierCallback = callback;
+ mSysUiState = sysUiState;
final Display display = mContext.getDisplay();
mDisplayId = mContext.getDisplayId();
@@ -170,13 +179,18 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mMirrorViewRunnable = () -> {
if (mMirrorView != null) {
mMirrorView.getBoundsOnScreen(mMirrorViewBounds);
+ updateSystemUIStateIfNeeded();
mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(
mDisplayId, mMirrorViewBounds);
}
};
mMirrorViewLayoutChangeListener =
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ if (!mHandler.hasCallbacks(mMirrorViewRunnable)) {
mHandler.post(mMirrorViewRunnable);
+ }
+ };
+
mMirrorSurfaceViewLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
-> applyTapExcludeRegion();
@@ -199,6 +213,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mMirrorView.setStateDescription(formatStateDescription(mScale));
}
};
+ mWindowInsetChangeRunnable = this::onWindowInsetChanged;
}
private void updateDimensions() {
@@ -210,7 +225,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
R.dimen.magnification_drag_view_size);
mOuterBorderSize = mResources.getDimensionPixelSize(
R.dimen.magnification_outer_border_margin);
- updateNavigationBarDimensions();
}
private void computeBounceAnimationScale() {
@@ -220,16 +234,16 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mBounceEffectAnimationScale = Math.min(animationScaleMax, ANIMATION_BOUNCE_EFFECT_SCALE);
}
- private void updateNavigationBarDimensions() {
- if (!supportsSwipeUpGesture()) {
- mNavGestureHeight = 0;
- return;
+ private boolean updateSystemGestureInsetsTop() {
+ final WindowMetrics windowMetrics = mWm.getCurrentWindowMetrics();
+ final Insets insets = windowMetrics.getWindowInsets().getInsets(systemGestures());
+ final int gestureTop =
+ insets.bottom != 0 ? windowMetrics.getBounds().bottom - insets.bottom : -1;
+ if (gestureTop != mSystemGestureTop) {
+ mSystemGestureTop = gestureTop;
+ return true;
}
- mNavGestureHeight = (mWindowBounds.width() > mWindowBounds.height())
- ? mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_landscape)
- : mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_gesture_height);
+ return false;
}
/**
@@ -255,6 +269,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (mMirrorWindowControl != null) {
mMirrorWindowControl.destroyControl();
}
+ updateSystemUIStateIfNeeded();
}
/**
@@ -277,6 +292,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
}
+ private void updateSystemUIStateIfNeeded() {
+ updateSysUIState(false);
+ }
+
private void updateAccessibilityWindowTitleIfNeeded() {
if (!isWindowVisible()) return;
LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams();
@@ -284,13 +303,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mWm.updateViewLayout(mMirrorView, params);
}
- /** Handles MirrorWindow position when the navigation bar mode changed. */
- public void onNavigationModeChanged(int mode) {
- mNavBarMode = mode;
- updateNavigationBarDimensions();
- updateMirrorViewLayout();
- }
-
/** Handles MirrorWindow position when the device rotation changed. */
private void onRotate() {
final Display display = mContext.getDisplay();
@@ -299,7 +311,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
setMagnificationFrameBoundary();
mRotation = display.getRotation();
- updateNavigationBarDimensions();
if (!isWindowVisible()) {
return;
@@ -351,6 +362,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ params.receiveInsetsIgnoringZOrder = true;
params.setTitle(mContext.getString(R.string.magnification_window_title));
params.accessibilityTitle = getAccessibilityWindowTitle();
@@ -368,6 +380,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
mMirrorView.addOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
mMirrorView.setAccessibilityDelegate(new MirrorWindowA11yDelegate());
+ mMirrorView.setOnApplyWindowInsetsListener((v, insets) -> {
+ if (!mHandler.hasCallbacks(mWindowInsetChangeRunnable)) {
+ mHandler.post(mWindowInsetChangeRunnable);
+ }
+ return v.onApplyWindowInsets(insets);
+ });
mWm.addView(mMirrorView, params);
@@ -377,6 +395,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
addDragTouchListeners();
}
+ private void onWindowInsetChanged() {
+ if (updateSystemGestureInsetsTop()) {
+ updateSystemUIStateIfNeeded();
+ }
+ }
+
private void applyTapExcludeRegion() {
final Region tapExcludeRegion = calculateTapExclude();
final IWindow window = IWindow.Stub.asInterface(mMirrorView.getWindowToken());
@@ -464,17 +488,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
return;
}
final int maxMirrorViewX = mWindowBounds.width() - mMirrorView.getWidth();
- final int maxMirrorViewY =
- mWindowBounds.height() - mMirrorView.getHeight() - mNavGestureHeight;
+ final int maxMirrorViewY = mWindowBounds.height() - mMirrorView.getHeight();
+
LayoutParams params =
(LayoutParams) mMirrorView.getLayoutParams();
params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
- // If nav bar mode supports swipe-up gesture, the Y position of mirror view should not
- // overlap nav bar window to prevent window-dragging obscured.
- if (supportsSwipeUpGesture()) {
- params.y = Math.min(params.y, maxMirrorViewY);
- }
// Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
// able to move close to the screen edges.
@@ -508,6 +527,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
return false;
}
+ public void updateSysUIStateFlag() {
+ updateSysUIState(true);
+ }
+
/**
* Calculates the desired source bounds. This will be the area under from the center of the
* displayFrame, factoring in scale.
@@ -569,6 +592,16 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
return false;
}
+ private void updateSysUIState(boolean force) {
+ final boolean overlap = isWindowVisible() && mSystemGestureTop > 0
+ && mMirrorViewBounds.bottom > mSystemGestureTop;
+ if (force || overlap != mOverlapWithGestureInsets) {
+ mOverlapWithGestureInsets = overlap;
+ mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, mOverlapWithGestureInsets)
+ .commitUpdate(mDisplayId);
+ }
+ }
+
@Override
public void surfaceCreated(SurfaceHolder holder) {
createMirror();
@@ -676,10 +709,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
return mMirrorView != null;
}
- private boolean supportsSwipeUpGesture() {
- return mNavBarMode == NAV_BAR_MODE_2BUTTON || mNavBarMode == NAV_BAR_MODE_GESTURAL;
- }
-
private CharSequence formatStateDescription(float scale) {
// Cache the locale-appropriate NumberFormat. Configuration locale is guaranteed
// non-null, so the first time this is called we will always get the appropriate
@@ -722,6 +751,14 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
scaleAnimator.start();
}
+ public void dump(PrintWriter pw) {
+ pw.println("WindowMagnificationController (displayId=" + mDisplayId + "):");
+ pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
+ pw.println(" mScale:" + mScale);
+ pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+ pw.println(" mSystemGestureTop:" + mSystemGestureTop);
+ }
+
private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 3b3bad3b255f..ee6276813512 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -18,11 +18,13 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
+import static com.android.systemui.Prefs.Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP;
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.ShapeType;
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.SizeType;
@@ -34,15 +36,19 @@ import android.os.UserHandle;
import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Prefs;
/**
* Contains logic for an accessibility floating menu view.
*/
public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
- private static final int DEFAULT_FADE_EFFECT_ENABLED = 1;
+ private static final int DEFAULT_FADE_EFFECT_IS_ENABLED = 1;
+ private static final int DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED = 0;
private static final float DEFAULT_OPACITY_VALUE = 0.55f;
private final Context mContext;
private final AccessibilityFloatingMenuView mMenuView;
+ private final MigrationTooltipView mMigrationTooltipView;
+ private final DockTooltipView mDockTooltipView;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ContentObserver mContentObserver =
@@ -86,6 +92,8 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
AccessibilityFloatingMenu(Context context, AccessibilityFloatingMenuView menuView) {
mContext = context;
mMenuView = menuView;
+ mMigrationTooltipView = new MigrationTooltipView(mContext, mMenuView);
+ mDockTooltipView = new DockTooltipView(mContext, mMenuView);
}
@Override
@@ -105,6 +113,9 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
getOpacityValue(mContext));
mMenuView.setSizeType(getSizeType(mContext));
mMenuView.setShapeType(getShapeType(mContext));
+ mMenuView.setOnDragEndListener(this::showDockTooltipIfNecessary);
+
+ showMigrationTooltipIfNecessary();
registerContentObservers();
}
@@ -116,14 +127,48 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
}
mMenuView.hide();
+ mMigrationTooltipView.hide();
+ mDockTooltipView.hide();
unregisterContentObservers();
}
+ // Migration tooltip was the android S feature. It's just used on the Android version from R
+ // to S. In addition, it only shows once.
+ private void showMigrationTooltipIfNecessary() {
+ if (isMigrationTooltipPromptEnabled(mContext)) {
+ mMigrationTooltipView.show();
+
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT, /* disabled */ 0);
+ }
+ }
+
+ private static boolean isMigrationTooltipPromptEnabled(Context context) {
+ return Settings.Secure.getInt(
+ context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+ DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED) == /* enabled */ 1;
+ }
+
+ /**
+ * Shows tooltip when user drags accessibility floating menu for the first time.
+ */
+ private void showDockTooltipIfNecessary() {
+ if (!Prefs.get(mContext).getBoolean(
+ HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, false)) {
+ // if the menu is an oval, the user has already dragged it out, so show the tooltip.
+ if (mMenuView.isOvalShape()) {
+ mDockTooltipView.show();
+ }
+
+ Prefs.putBoolean(mContext, HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, true);
+ }
+ }
+
private static boolean isFadeEffectEnabled(Context context) {
return Settings.Secure.getInt(
context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
- DEFAULT_FADE_EFFECT_ENABLED) == /* enable */ 1;
+ DEFAULT_FADE_EFFECT_IS_ENABLED) == /* enabled */ 1;
}
private static float getOpacityValue(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 934e20dfbd05..49e6b47b3b79 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -19,6 +19,8 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.util.MathUtils.constrain;
import static android.util.MathUtils.sq;
+import static java.util.Objects.requireNonNull;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -43,7 +45,9 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.animation.Animation;
import android.view.animation.OvershootInterpolator;
+import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import androidx.annotation.DimenRes;
@@ -60,6 +64,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
/**
* Accessibility floating menu is used for the actions of accessibility features, it's also the
@@ -78,6 +83,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout
private static final int MIN_WINDOW_Y = 0;
private static final float LOCATION_Y_PERCENTAGE = 0.8f;
+ private static final int ANIMATION_START_OFFSET = 600;
+ private static final int ANIMATION_DURATION_MS = 600;
+ private static final float ANIMATION_TO_X_VALUE = 0.5f;
+
private boolean mIsFadeEffectEnabled;
private boolean mIsShowing;
private boolean mIsDownInEnlargedTouchArea;
@@ -107,6 +116,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
private float mPercentageY = LOCATION_Y_PERCENTAGE;
private float mSquareScaledTouchSlop;
private final Configuration mLastConfiguration;
+ private Optional<OnDragEndListener> mOnDragEndListener = Optional.empty();
private final RecyclerView mListView;
private final AccessibilityTargetAdapter mAdapter;
private float mFadeOutValue;
@@ -161,6 +171,17 @@ public class AccessibilityFloatingMenuView extends FrameLayout
int RIGHT = 1;
}
+ /**
+ * Interface for a callback to be invoked when the floating menu was dragging.
+ */
+ interface OnDragEndListener {
+
+ /**
+ * Invoked when the floating menu has dragged end.
+ */
+ void onDragEnd();
+ }
+
public AccessibilityFloatingMenuView(Context context) {
this(context, new RecyclerView(context));
}
@@ -201,6 +222,8 @@ public class AccessibilityFloatingMenuView extends FrameLayout
updateRadiusWith(mSizeType, mRadiusType, mTargets.size());
fadeOut();
+
+ mOnDragEndListener.ifPresent(OnDragEndListener::onDragEnd);
}
});
@@ -266,7 +289,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
// Must switch the oval shape type before tapping the corresponding item in the
// list view, otherwise it can't work on it.
- if (mShapeType == ShapeType.HALF_OVAL) {
+ if (!isOvalShape()) {
setShapeType(ShapeType.OVAL);
return true;
@@ -299,10 +322,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
- if (super.performAccessibilityAction(action, arguments)) {
- return true;
- }
-
fadeIn();
final Rect bounds = getAvailableBounds();
@@ -340,7 +359,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
return true;
}
- return false;
+ return super.performAccessibilityAction(action, arguments);
}
void show() {
@@ -367,6 +386,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout
return mIsShowing;
}
+ boolean isOvalShape() {
+ return mShapeType == ShapeType.OVAL;
+ }
+
void onTargetsChanged(List<AccessibilityTarget> newTargets) {
fadeIn();
@@ -411,6 +434,41 @@ public class AccessibilityFloatingMenuView extends FrameLayout
fadeOut();
}
+ public void setOnDragEndListener(OnDragEndListener onDragListener) {
+ mOnDragEndListener = Optional.ofNullable(onDragListener);
+ }
+
+ void startTranslateXAnimation() {
+ fadeIn();
+
+ final float toXValue = mAlignment == Alignment.RIGHT
+ ? ANIMATION_TO_X_VALUE
+ : -ANIMATION_TO_X_VALUE;
+ final TranslateAnimation animation =
+ new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
+ Animation.RELATIVE_TO_SELF, toXValue,
+ Animation.RELATIVE_TO_SELF, 0,
+ Animation.RELATIVE_TO_SELF, 0);
+ animation.setDuration(ANIMATION_DURATION_MS);
+ animation.setRepeatMode(Animation.REVERSE);
+ animation.setInterpolator(new OvershootInterpolator());
+ animation.setRepeatCount(Animation.INFINITE);
+ animation.setStartOffset(ANIMATION_START_OFFSET);
+ mListView.startAnimation(animation);
+ }
+
+ void stopTranslateXAnimation() {
+ mListView.clearAnimation();
+
+ fadeOut();
+ }
+
+ Rect getWindowLocationOnScreen() {
+ final int left = mCurrentLayoutParams.x;
+ final int top = mCurrentLayoutParams.y;
+ return new Rect(left, top, left + getWindowWidth(), top + getWindowHeight());
+ }
+
void updateOpacityWith(boolean isFadeEffectEnabled, float newOpacityValue) {
mIsFadeEffectEnabled = isFadeEffectEnabled;
mFadeOutValue = newOpacityValue;
@@ -534,11 +592,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
}
private Handler createUiHandler() {
- final Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException("looper must not be null");
- }
- return new Handler(looper);
+ return new Handler(requireNonNull(Looper.myLooper(), "looper must not be null"));
}
private void updateDimensions() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
new file mode 100644
index 000000000000..d8e80fe99331
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import android.text.Annotation;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.style.ClickableSpan;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * A span that turns the text wrapped by annotation tag into the clickable link text.
+ */
+class AnnotationLinkSpan extends ClickableSpan {
+ private final Optional<View.OnClickListener> mClickListener;
+
+ private AnnotationLinkSpan(View.OnClickListener listener) {
+ mClickListener = Optional.ofNullable(listener);
+ }
+
+ @Override
+ public void onClick(View view) {
+ mClickListener.ifPresent(listener -> listener.onClick(view));
+ }
+
+ /**
+ * Makes the text has the link with the click action. In addition, the span will match first
+ * LinkInfo and attach into the text.
+ *
+ * @param text the text wrapped by annotation tag
+ * @param linkInfos used to attach the click action into the corresponding span
+ * @return the text attached with the span
+ */
+ static CharSequence linkify(CharSequence text, LinkInfo... linkInfos) {
+ final SpannableString msg = new SpannableString(text);
+ final Annotation[] spans =
+ msg.getSpans(/* queryStart= */ 0, msg.length(), Annotation.class);
+ final SpannableStringBuilder builder = new SpannableStringBuilder(msg);
+
+ Arrays.asList(spans).forEach(annotationTag -> {
+ final String key = annotationTag.getValue();
+ final Optional<LinkInfo> linkInfo =
+ Arrays.asList(linkInfos).stream().filter(
+ info -> info.mAnnotation.isPresent()
+ && info.mAnnotation.get().equals(key)).findFirst();
+
+ linkInfo.flatMap(info -> info.mListener).ifPresent(listener -> {
+ final AnnotationLinkSpan span = new AnnotationLinkSpan(listener);
+ builder.setSpan(span,
+ msg.getSpanStart(annotationTag),
+ msg.getSpanEnd(annotationTag),
+ msg.getSpanFlags(span));
+ });
+ });
+
+ return builder;
+ }
+
+ /**
+ * Data class to store the annotation and the click action.
+ */
+ static class LinkInfo {
+ static final String DEFAULT_ANNOTATION = "link";
+ private final Optional<String> mAnnotation;
+ private final Optional<View.OnClickListener> mListener;
+
+ LinkInfo(@NonNull String annotation, View.OnClickListener listener) {
+ mAnnotation = Optional.of(annotation);
+ mListener = Optional.ofNullable(listener);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java
new file mode 100644
index 000000000000..1abf559d6846
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static android.util.TypedValue.COMPLEX_UNIT_PX;
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import android.annotation.UiContext;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Bundle;
+import android.text.method.MovementMethod;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.recents.TriangleShape;
+
+/**
+ * Base tooltip view that shows the information about the operation of the
+ * Accessibility floating menu. In addition, the anchor view is only for {@link
+ * AccessibilityFloatingMenuView}, it should be more suited for displaying one-off menus to avoid
+ * the performance hit for the extra window.
+ */
+class BaseTooltipView extends FrameLayout {
+ private int mFontSize;
+ private int mTextViewMargin;
+ private int mTextViewPadding;
+ private int mTextViewCornerRadius;
+ private int mArrowMargin;
+ private int mArrowWidth;
+ private int mArrowHeight;
+ private int mArrowCornerRadius;
+ private int mScreenWidth;
+ private boolean mIsShowing;
+ private TextView mTextView;
+ private final WindowManager.LayoutParams mCurrentLayoutParams;
+ private final WindowManager mWindowManager;
+ private final AccessibilityFloatingMenuView mAnchorView;
+
+ BaseTooltipView(@UiContext Context context, AccessibilityFloatingMenuView anchorView) {
+ super(context);
+ mWindowManager = context.getSystemService(WindowManager.class);
+ mAnchorView = anchorView;
+ mCurrentLayoutParams = createDefaultLayoutParams();
+
+ updateDimensions();
+ initViews();
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ updateDimensions();
+ updateTextView();
+
+ mAnchorView.onConfigurationChanged(newConfig);
+ final Rect anchorViewLocation = mAnchorView.getWindowLocationOnScreen();
+ updateArrowWith(anchorViewLocation);
+ updateWidthWith(anchorViewLocation);
+ updateLocationWith(anchorViewLocation);
+
+ mWindowManager.updateViewLayout(this, mCurrentLayoutParams);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ hide();
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+
+ info.addAction(AccessibilityAction.ACTION_DISMISS);
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (action == AccessibilityAction.ACTION_DISMISS.getId()) {
+ hide();
+ return true;
+ }
+
+ return super.performAccessibilityAction(action, arguments);
+ }
+
+ void show() {
+ if (isShowing()) {
+ return;
+ }
+
+ mIsShowing = true;
+ final Rect anchorViewLocation = mAnchorView.getWindowLocationOnScreen();
+ updateArrowWith(anchorViewLocation);
+ updateWidthWith(anchorViewLocation);
+ updateLocationWith(anchorViewLocation);
+
+ mWindowManager.addView(this, mCurrentLayoutParams);
+ }
+
+ void hide() {
+ if (!isShowing()) {
+ return;
+ }
+
+ mIsShowing = false;
+ mWindowManager.removeView(this);
+ }
+
+ void setDescription(CharSequence text) {
+ mTextView.setText(text);
+ }
+
+ void setMovementMethod(MovementMethod movement) {
+ mTextView.setMovementMethod(movement);
+ }
+
+ private boolean isShowing() {
+ return mIsShowing;
+ }
+
+ private void initViews() {
+ final View contentView =
+ LayoutInflater.from(getContext()).inflate(
+ R.layout.accessibility_floating_menu_tooltip, this, false);
+
+ mTextView = contentView.findViewById(R.id.text);
+
+ addView(contentView);
+ }
+
+ private static WindowManager.LayoutParams createDefaultLayoutParams() {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ params.windowAnimations = android.R.style.Animation_Translucent;
+ params.gravity = Gravity.START | Gravity.TOP;
+
+ return params;
+ }
+
+ private void updateDimensions() {
+ final Resources res = getResources();
+ final DisplayMetrics dm = res.getDisplayMetrics();
+ mScreenWidth = dm.widthPixels;
+ mArrowWidth =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_width);
+ mArrowHeight =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_height);
+ mArrowMargin =
+ res.getDimensionPixelSize(
+ R.dimen.accessibility_floating_tooltip_arrow_margin);
+ mArrowCornerRadius =
+ res.getDimensionPixelSize(
+ R.dimen.accessibility_floating_tooltip_arrow_corner_radius);
+ mFontSize =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_font_size);
+ mTextViewMargin =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_margin);
+ mTextViewPadding =
+ res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_padding);
+ mTextViewCornerRadius =
+ res.getDimensionPixelSize(
+ R.dimen.accessibility_floating_tooltip_text_corner_radius);
+ }
+
+ private void updateTextView() {
+ mTextView.setTextSize(COMPLEX_UNIT_PX, mFontSize);
+ mTextView.setPadding(mTextViewPadding, mTextViewPadding, mTextViewPadding,
+ mTextViewPadding);
+
+ final GradientDrawable gradientDrawable = (GradientDrawable) mTextView.getBackground();
+ gradientDrawable.setCornerRadius(mTextViewCornerRadius);
+ }
+
+ private void updateArrowWith(Rect anchorViewLocation) {
+ final boolean isAnchorViewOnLeft = isAnchorViewOnLeft(anchorViewLocation);
+ final View arrowView = findViewById(isAnchorViewOnLeft
+ ? R.id.arrow_left
+ : R.id.arrow_right);
+ arrowView.setVisibility(VISIBLE);
+ drawArrow(arrowView, isAnchorViewOnLeft);
+
+ final LinearLayout.LayoutParams layoutParams =
+ (LinearLayout.LayoutParams) arrowView.getLayoutParams();
+ layoutParams.width = mArrowWidth;
+ layoutParams.height = mArrowHeight;
+
+ final int leftMargin = isAnchorViewOnLeft ? 0 : mArrowMargin;
+ final int rightMargin = isAnchorViewOnLeft ? mArrowMargin : 0;
+ layoutParams.setMargins(leftMargin, 0, rightMargin, 0);
+ arrowView.setLayoutParams(layoutParams);
+ }
+
+ private void updateWidthWith(Rect anchorViewLocation) {
+ final ViewGroup.LayoutParams layoutParams = mTextView.getLayoutParams();
+ layoutParams.width = getTextWidthWith(anchorViewLocation);
+ mTextView.setLayoutParams(layoutParams);
+ }
+
+ private void updateLocationWith(Rect anchorViewLocation) {
+ mCurrentLayoutParams.x = isAnchorViewOnLeft(anchorViewLocation)
+ ? anchorViewLocation.width()
+ : mScreenWidth - getWindowWidthWith(anchorViewLocation)
+ - anchorViewLocation.width();
+ mCurrentLayoutParams.y =
+ anchorViewLocation.centerY() - (getTextHeightWith(anchorViewLocation) / 2);
+ }
+
+ private void drawArrow(View view, boolean isPointingLeft) {
+ final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ final TriangleShape triangleShape =
+ TriangleShape.createHorizontal(layoutParams.width, layoutParams.height,
+ isPointingLeft);
+ final ShapeDrawable arrowDrawable = new ShapeDrawable(triangleShape);
+ final Paint arrowPaint = arrowDrawable.getPaint();
+ arrowPaint.setColor(Utils.getColorAttrDefaultColor(getContext(),
+ com.android.internal.R.attr.colorAccentPrimary));
+ final CornerPathEffect effect = new CornerPathEffect(mArrowCornerRadius);
+ arrowPaint.setPathEffect(effect);
+ view.setBackground(arrowDrawable);
+ }
+
+ private boolean isAnchorViewOnLeft(Rect anchorViewLocation) {
+ return anchorViewLocation.left < (mScreenWidth / 2);
+ }
+
+ private int getTextWidthWith(Rect anchorViewLocation) {
+ final int widthSpec =
+ MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
+ final int heightSpec =
+ MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
+ mTextView.measure(widthSpec, heightSpec);
+ return mTextView.getMeasuredWidth();
+ }
+
+ private int getTextHeightWith(Rect anchorViewLocation) {
+ final int widthSpec =
+ MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
+ final int heightSpec =
+ MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
+ mTextView.measure(widthSpec, heightSpec);
+ return mTextView.getMeasuredHeight();
+ }
+
+ private int getAvailableTextWidthWith(Rect anchorViewLocation) {
+ return mScreenWidth - anchorViewLocation.width() - mArrowWidth - mArrowMargin
+ - mTextViewMargin;
+ }
+
+ private int getWindowWidthWith(Rect anchorViewLocation) {
+ return getTextWidthWith(anchorViewLocation) + mArrowWidth + mArrowMargin;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java
new file mode 100644
index 000000000000..49056a6039ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import android.content.Context;
+
+import com.android.systemui.R;
+
+/**
+ * Dock tooltip view that shows the info about moving the Accessibility button to the edge to hide.
+ */
+class DockTooltipView extends BaseTooltipView {
+ private final AccessibilityFloatingMenuView mAnchorView;
+
+ DockTooltipView(Context context, AccessibilityFloatingMenuView anchorView) {
+ super(context, anchorView);
+ mAnchorView = anchorView;
+
+ setDescription(
+ getContext().getText(R.string.accessibility_floating_button_docking_tooltip));
+ }
+
+ @Override
+ void hide() {
+ super.hide();
+
+ mAnchorView.stopTranslateXAnimation();
+ }
+
+ @Override
+ void show() {
+ super.show();
+
+ mAnchorView.startTranslateXAnimation();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java
new file mode 100644
index 000000000000..e4f3e3145998
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_BUTTON_COMPONENT_NAME;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.text.method.LinkMovementMethod;
+
+import com.android.systemui.R;
+
+/**
+ * Migration tooltip view that shows the information about the Accessibility button was replaced
+ * with the floating menu.
+ */
+class MigrationTooltipView extends BaseTooltipView {
+ MigrationTooltipView(Context context, AccessibilityFloatingMenuView anchorView) {
+ super(context, anchorView);
+
+ final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
+ ACCESSIBILITY_BUTTON_COMPONENT_NAME.flattenToShortString());
+
+ final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo(
+ AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
+ v -> {
+ getContext().startActivity(intent);
+ hide();
+ });
+
+ final int textResId = R.string.accessibility_floating_button_migration_tooltip;
+ setDescription(AnnotationLinkSpan.linkify(getContext().getText(textResId), linkInfo));
+ setMovementMethod(LinkMovementMethod.getInstance());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index e243e1e76c54..875bfdbffff3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -59,7 +59,6 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
@@ -72,6 +71,8 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import java.util.Optional;
+
import javax.inject.Inject;
/**
@@ -87,7 +88,7 @@ import javax.inject.Inject;
*/
@SuppressWarnings("deprecation")
@SysUISingleton
-public class UdfpsController implements DozeReceiver, HbmCallback {
+public class UdfpsController implements DozeReceiver {
private static final String TAG = "UdfpsController";
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
@@ -105,11 +106,12 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@NonNull private final DumpManager mDumpManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
- @NonNull private final Vibrator mVibrator;
+ @Nullable private final Vibrator mVibrator;
@NonNull private final Handler mMainHandler;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
+ @Nullable private final UdfpsHbmCallback mHbmCallback;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -135,7 +137,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
private boolean mScreenOn;
private Runnable mAodInterruptRunnable;
- private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
+ @VisibleForTesting
+ static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
@@ -144,7 +147,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK);
private final VibrationEffect mEffectTextureTick =
VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
- private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ @VisibleForTesting
+ final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
private final VibrationEffect mEffectHeavy =
VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
private final VibrationEffect mDoubleClick =
@@ -152,6 +156,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
private final Runnable mAcquiredVibration = new Runnable() {
@Override
public void run() {
+ if (mVibrator == null) {
+ return;
+ }
String effect = Settings.Global.getString(mContext.getContentResolver(),
"udfps_acquired_type");
mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES);
@@ -345,7 +352,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
}
if (isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView)) {
Trace.beginAsyncSection(
- "UdfpsController.mOnTouchListener#isWithinSensorArea", 1);
+ "UdfpsController#ACTION_DOWN", 1);
// The pointer that causes ACTION_DOWN is always at index 0.
// We need to persist its ID to track it during ACTION_MOVE that could include
// data for many other pointers because of multi-touch support.
@@ -382,8 +389,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
minor, major, v, exceedsVelocityThreshold);
final long sinceLastLog = SystemClock.elapsedRealtime() - mTouchLogTime;
if (!isFingerDown && !exceedsVelocityThreshold) {
- Trace.endAsyncSection(
- "UdfpsController.mOnTouchListener#isWithinSensorArea", 1);
onFingerDown((int) x, (int) y, minor, major);
Log.v(TAG, "onTouch | finger down: " + touchInfo);
mTouchLogTime = SystemClock.elapsedRealtime();
@@ -391,24 +396,28 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
// TODO: this should eventually be removed after ux testing
- final ContentResolver contentResolver = mContext.getContentResolver();
- int startEnabled = Settings.Global.getInt(contentResolver,
- "udfps_start", 0);
- if (startEnabled > 0) {
- String startEffectSetting = Settings.Global.getString(
- contentResolver, "udfps_start_type");
- mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick),
- VIBRATION_SONIFICATION_ATTRIBUTES);
+ if (mVibrator != null) {
+ final ContentResolver contentResolver =
+ mContext.getContentResolver();
+ int startEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_start", 1);
+ if (startEnabled > 0) {
+ String startEffectSetting = Settings.Global.getString(
+ contentResolver, "udfps_start_type");
+ mVibrator.vibrate(getVibration(startEffectSetting,
+ mEffectClick), VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
+
+ int acquiredEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_acquired", 0);
+ if (acquiredEnabled > 0) {
+ int delay = Settings.Global.getInt(contentResolver,
+ "udfps_acquired_delay", 500);
+ mMainHandler.removeCallbacks(mAcquiredVibration);
+ mMainHandler.postDelayed(mAcquiredVibration, delay);
+ }
}
- int acquiredEnabled = Settings.Global.getInt(contentResolver,
- "udfps_acquired", 0);
- if (acquiredEnabled > 0) {
- int delay = Settings.Global.getInt(contentResolver,
- "udfps_acquired_delay", 500);
- mMainHandler.removeCallbacks(mAcquiredVibration);
- mMainHandler.postDelayed(mAcquiredVibration, delay);
- }
handled = true;
} else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
Log.v(TAG, "onTouch | finger move: " + touchInfo);
@@ -458,11 +467,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
- @NonNull ScreenLifecycle screenLifecycle) {
+ @NonNull ScreenLifecycle screenLifecycle,
+ @Nullable Vibrator vibrator,
+ @NonNull Optional<UdfpsHbmCallback> hbmCallback) {
mContext = context;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
mMainHandler = new Handler(Looper.getMainLooper());
- mVibrator = context.getSystemService(Vibrator.class);
+ mVibrator = vibrator;
mInflater = inflater;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
// fingerprint manager should never be null.
@@ -478,6 +489,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
+ mHbmCallback = hbmCallback.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
@@ -611,7 +623,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);
mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
mView.setSensorProperties(mSensorProps);
- mView.setHbmCallback(this);
+ mView.setHbmCallback(mHbmCallback);
UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
animation.init();
mView.setAnimationViewController(animation);
@@ -761,10 +773,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
Log.w(TAG, "Null view in onFingerDown");
return;
}
+ mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major);
+ Trace.endAsyncSection(
+ "UdfpsController#ACTION_DOWN", 1);
Trace.beginAsyncSection("UdfpsController#startIllumination", 1);
mView.startIllumination(() -> {
+ mFingerprintManager.onUiReady(mSensorProps.sensorId);
Trace.endAsyncSection("UdfpsController#startIllumination", 1);
- mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major);
});
}
@@ -780,17 +795,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
mView.stopIllumination();
}
- @Override
- public void enableHbm(@HbmType int hbmType, @Nullable Surface surface) {
- // Do nothing. This method can be implemented for devices that require the high-brightness
- // mode for fingerprint illumination.
- }
-
- @Override
- public void disableHbm(@HbmType int hbmType, @Nullable Surface surface) {
- // Do nothing. This method can be implemented for devices that require the high-brightness
- // mode for fingerprint illumination.
- }
private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) {
if (TextUtils.isEmpty(effect)) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index cd5abd74c260..2c48d0ad8b18 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -36,7 +36,7 @@ import com.android.systemui.R;
public class UdfpsEnrollDrawable extends UdfpsDrawable {
private static final String TAG = "UdfpsAnimationEnroll";
- static final float PROGRESS_BAR_RADIUS = 180.f;
+ static final float PROGRESS_BAR_RADIUS = 360.f;
@NonNull private final Drawable mMovingTargetFpIcon;
@NonNull private final Paint mSensorOutlinePaint;
@@ -96,11 +96,6 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
return;
}
- if (mSensorRect != null) {
- canvas.drawOval(mSensorRect, mSensorOutlinePaint);
- }
- mFingerprintDrawable.draw(canvas);
-
// Draw moving target
if (mEnrollHelper.isCenterEnrollmentComplete()) {
mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha);
@@ -117,6 +112,10 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
mMovingTargetFpIcon.draw(canvas);
canvas.restore();
} else {
+ if (mSensorRect != null) {
+ canvas.drawOval(mSensorRect, mSensorOutlinePaint);
+ }
+ mFingerprintDrawable.draw(canvas);
mFingerprintDrawable.setAlpha(mAlpha);
mSensorOutlinePaint.setAlpha(mAlpha);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
index d90d0f8d9f67..85f0d278e3f1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
@@ -19,18 +19,18 @@ package com.android.systemui.biometrics;
import android.annotation.Nullable;
import android.view.Surface;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
+import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
/**
* Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
* enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
* illumination is no longer necessary.
*/
-public interface HbmCallback {
+public interface UdfpsHbmCallback {
/**
* UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
*
- * @param hbmType The type of HBM that should be enabled. See {@link HbmTypes}.
+ * @param hbmType The type of HBM that should be enabled. See {@link UdfpsHbmTypes}.
* @param surface The surface for which the HBM is requested, in case the HBM implementation
* needs to set special surface flags to enable the HBM. Can be null.
*/
@@ -39,7 +39,7 @@ public interface HbmCallback {
/**
* UdfpsView will call this to disable the HBM when the illumination is not longer needed.
*
- * @param hbmType The type of HBM that should be disabled. See {@link HbmTypes}.
+ * @param hbmType The type of HBM that should be disabled. See {@link UdfpsHbmTypes}.
* @param surface The surface for which the HBM is requested, in case the HBM implementation
* needs to unset special surface flags to disable the HBM. Can be null.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
index f798005daabb..3ab0bd62eec8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
@@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy;
/**
* Different high-brightness mode (HBM) types that are relevant to this package.
*/
-public final class HbmTypes {
+public final class UdfpsHbmTypes {
/** HBM that applies to the whole screen. */
public static final int GLOBAL_HBM = IUdfpsHbmListener.GLOBAL_HBM;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
index 8bea05b2b54f..1676bcd57afc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
@@ -26,7 +26,7 @@ interface UdfpsIlluminator {
/**
* @param callback Invoked when HBM should be enabled or disabled.
*/
- void setHbmCallback(@Nullable HbmCallback callback);
+ void setHbmCallback(@Nullable UdfpsHbmCallback callback);
/**
* Invoked when illumination should start.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
index 4d441bd288fe..aa5f0f6cf7f4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -31,7 +31,7 @@ import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
+import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
/**
* Under-display fingerprint sensor Surface View. The surface should be used for HBM-specific things
@@ -41,7 +41,7 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
private static final String TAG = "UdfpsSurfaceView";
private static final String SETTING_HBM_TYPE =
"com.android.systemui.biometrics.UdfpsSurfaceView.hbmType";
- private static final @HbmType int DEFAULT_HBM_TYPE = HbmTypes.GLOBAL_HBM;
+ private static final @HbmType int DEFAULT_HBM_TYPE = UdfpsHbmTypes.GLOBAL_HBM;
/**
* This is used instead of {@link android.graphics.drawable.Drawable}, because the latter has
@@ -57,7 +57,7 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
private final @HbmType int mHbmType;
@NonNull private RectF mSensorRect;
- @Nullable private HbmCallback mHbmCallback;
+ @Nullable private UdfpsHbmCallback mHbmCallback;
public UdfpsSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -90,7 +90,7 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
}
@Override
- public void setHbmCallback(@Nullable HbmCallback callback) {
+ public void setHbmCallback(@Nullable UdfpsHbmCallback callback) {
mHbmCallback = callback;
}
@@ -102,7 +102,7 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
Log.e(TAG, "startIllumination | mHbmCallback is null");
}
- if (mHbmType == HbmTypes.GLOBAL_HBM) {
+ if (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) {
drawImmediately(mIlluminationDotDrawable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index f10d5f3bca1f..a1d30403e0b8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -101,7 +101,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
}
@Override
- public void setHbmCallback(@Nullable HbmCallback callback) {
+ public void setHbmCallback(@Nullable UdfpsHbmCallback callback) {
mHbmSurfaceView.setHbmCallback(callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 9d47bbb03380..f01ac68ed5a2 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -34,7 +34,7 @@ import android.view.WindowManager;
*/
public class WirelessChargingAnimation {
- public static final long DURATION = 1133;
+ public static final long DURATION = 1500;
private static final String TAG = "WirelessChargingView";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 38ffec115fba..0d3e2ae9a776 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -20,6 +20,7 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -42,7 +43,9 @@ import java.text.NumberFormat;
*/
public class WirelessChargingLayout extends FrameLayout {
public static final int UNKNOWN_BATTERY_LEVEL = -1;
- private static final long RIPPLE_ANIMATION_DURATION = 1133;
+ private static final long RIPPLE_ANIMATION_DURATION = 1500;
+ private static final int SCRIM_COLOR = 0x4C000000;
+ private static final int SCRIM_FADE_DURATION = 300;
private ChargingRippleView mRippleView;
public WirelessChargingLayout(Context context) {
@@ -121,6 +124,19 @@ public class WirelessChargingLayout extends FrameLayout {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
+ ValueAnimator scrimFadeInAnimator = ObjectAnimator.ofArgb(this,
+ "backgroundColor", Color.TRANSPARENT, SCRIM_COLOR);
+ scrimFadeInAnimator.setDuration(SCRIM_FADE_DURATION);
+ scrimFadeInAnimator.setInterpolator(Interpolators.LINEAR);
+ ValueAnimator scrimFadeOutAnimator = ObjectAnimator.ofArgb(this,
+ "backgroundColor", SCRIM_COLOR, Color.TRANSPARENT);
+ scrimFadeOutAnimator.setDuration(SCRIM_FADE_DURATION);
+ scrimFadeOutAnimator.setInterpolator(Interpolators.LINEAR);
+ scrimFadeOutAnimator.setStartDelay(RIPPLE_ANIMATION_DURATION - SCRIM_FADE_DURATION);
+ AnimatorSet animatorSetScrim = new AnimatorSet();
+ animatorSetScrim.playTogether(scrimFadeInAnimator, scrimFadeOutAnimator);
+ animatorSetScrim.start();
+
mRippleView = findViewById(R.id.wireless_charging_ripple);
OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index fd80d50c2894..26db33d6dea8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -29,6 +29,7 @@ import android.app.StatsManager;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.app.role.RoleManager;
+import android.app.smartspace.SmartspaceManager;
import android.app.trust.TrustManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -400,4 +401,10 @@ public class FrameworkServicesModule {
static PermissionManager providePermissionManager(Context context) {
return context.getSystemService(PermissionManager.class);
}
+
+ @Provides
+ @Singleton
+ static SmartspaceManager provideSmartspaceManager(Context context) {
+ return context.getSystemService(SmartspaceManager.class);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7fa48d405643..1396099db948 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -29,6 +29,7 @@ import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
+import com.android.systemui.biometrics.UdfpsHbmCallback;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
@@ -160,6 +161,9 @@ public abstract class SystemUIModule {
@BindsOptionalOf
abstract StatusBar optionalStatusBar();
+ @BindsOptionalOf
+ abstract UdfpsHbmCallback optionalUdfpsHbmCallback();
+
@SysUISingleton
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index a83b13c50671..3873c25f32c6 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -226,7 +226,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
this::getWalletViewController, mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter);
+ mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger());
if (shouldShowLockMessage(dialog)) {
dialog.showLockMessage();
@@ -294,11 +294,11 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
adapter, overflowAdapter, depthController, sysuiColorExtractor,
statusBarService, notificationShadeWindowController, sysuiState,
- onRotateCallback, keyguardShowing, powerAdapter);
+ onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger);
mWalletFactory = walletFactory;
// Update window attributes
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 2eb37624f598..f9bb35fcb723 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -249,7 +249,43 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
GA_SCREENSHOT_PRESS(347),
@UiEvent(doc = "The global actions screenshot button was long pressed.")
- GA_SCREENSHOT_LONG_PRESS(348);
+ GA_SCREENSHOT_LONG_PRESS(348),
+
+ @UiEvent(doc = "The global actions power off button was pressed.")
+ GA_SHUTDOWN_PRESS(802),
+
+ @UiEvent(doc = "The global actions power off button was long pressed.")
+ GA_SHUTDOWN_LONG_PRESS(803),
+
+ @UiEvent(doc = "The global actions reboot button was pressed.")
+ GA_REBOOT_PRESS(349),
+
+ @UiEvent(doc = "The global actions reboot button was long pressed.")
+ GA_REBOOT_LONG_PRESS(804),
+
+ @UiEvent(doc = "The global actions lockdown button was pressed.")
+ GA_LOCKDOWN_PRESS(354), // already created by cwren apparently
+
+ @UiEvent(doc = "Power menu was opened via quick settings button.")
+ GA_OPEN_QS(805),
+
+ @UiEvent(doc = "Power menu was opened via power + volume up.")
+ GA_OPEN_POWER_VOLUP(806),
+
+ @UiEvent(doc = "Power menu was opened via long press on power.")
+ GA_OPEN_LONG_PRESS_POWER(807),
+
+ @UiEvent(doc = "Power menu was closed via long press on power.")
+ GA_CLOSE_LONG_PRESS_POWER(808),
+
+ @UiEvent(doc = "Power menu was dismissed by back gesture.")
+ GA_CLOSE_BACK(809),
+
+ @UiEvent(doc = "Power menu was dismissed by tapping outside dialog.")
+ GA_CLOSE_TAP_OUTSIDE(810),
+
+ @UiEvent(doc = "Power menu was closed via power + volume up.")
+ GA_CLOSE_POWER_VOLUP(811);
private final int mId;
@@ -349,6 +385,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
return mContext;
}
+ protected UiEventLogger getEventLogger() {
+ return mUiEventLogger;
+ }
+
/**
* Show the global actions dialog (creating if necessary)
*
@@ -581,7 +621,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mAdapter, mOverflowAdapter,
mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter);
+ mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -679,13 +719,14 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@VisibleForTesting
final class ShutDownAction extends SinglePressAction implements LongPressAction {
- private ShutDownAction() {
+ ShutDownAction() {
super(R.drawable.ic_lock_power_off,
R.string.global_action_power_off);
}
@Override
public boolean onLongPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.reboot(true);
return true;
@@ -705,6 +746,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Override
public void onPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_PRESS);
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown();
}
@@ -807,12 +849,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@VisibleForTesting
final class RestartAction extends SinglePressAction implements LongPressAction {
- private RestartAction() {
+ RestartAction() {
super(R.drawable.ic_restart, R.string.global_action_restart);
}
@Override
public boolean onLongPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.reboot(true);
return true;
@@ -832,6 +875,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Override
public void onPress() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_PRESS);
mWindowManagerFuncs.reboot(false);
}
}
@@ -1062,6 +1106,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public void onPress() {
mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
UserHandle.USER_ALL);
+ mUiEventLogger.log(GlobalActionsEvent.GA_LOCKDOWN_PRESS);
try {
mIWindowManager.lockNow(null);
// Lock profiles (if any) on the background thread.
@@ -2049,6 +2094,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback;
+ private UiEventLogger mUiEventLogger;
protected ViewGroup mContainer;
@@ -2058,7 +2104,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
super(context, themeRes);
mContext = context;
mAdapter = adapter;
@@ -2071,6 +2117,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mSysUiState = sysuiState;
mOnRotateCallback = onRotateCallback;
mKeyguardShowing = keyguardShowing;
+ mUiEventLogger = uiEventLogger;
// Window initialization
Window window = getWindow();
@@ -2141,7 +2188,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mGlobalActionsLayout.setAdapter(mAdapter);
mContainer = findViewById(com.android.systemui.R.id.global_actions_container);
mContainer.setOnClickListener(v -> {
- // TODO: add logging (b/182830510)
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
cancel();
});
@@ -2218,6 +2265,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
@Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_BACK);
+ }
+
+ @Override
public void show() {
super.show();
// split this up so we can override but still call Dialog.show
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c8028d3e4b74..a17a4e5f0be0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -105,6 +105,7 @@ import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -230,6 +231,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
private AlarmManager mAlarmManager;
private AudioManager mAudioManager;
private StatusBarManager mStatusBarManager;
+ private final SysuiStatusBarStateController mStatusBarStateController;
private final Executor mUiBgExecutor;
private boolean mSystemReady;
@@ -794,7 +796,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
NavigationModeController navigationModeController,
KeyguardDisplayManager keyguardDisplayManager,
DozeParameters dozeParameters,
- StatusBarStateController statusBarStateController,
+ SysuiStatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationControllerLazy) {
super(context);
@@ -823,6 +825,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
}));
mDozeParameters = dozeParameters;
+ mStatusBarStateController = statusBarStateController;
statusBarStateController.addCallback(this);
mKeyguardStateController = keyguardStateController;
@@ -2119,19 +2122,17 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
playSounds(false);
}
- if (KeyguardService.sEnableRemoteKeyguardAnimation) {
+ // When remaining on the shade, there's no need to do a fancy remote animation,
+ // it will dismiss the panel in that case.
+ if (KeyguardService.sEnableRemoteKeyguardAnimation
+ && !mStatusBarStateController.leaveOpenOnKeyguardHide()
+ && apps != null && apps.length > 0) {
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
mSurfaceBehindRemoteAnimationRunning = true;
- if (apps != null && apps.length > 0) {
- // Pass the surface and metadata to the unlock animation controller.
- mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation(
- apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
- } else {
- // We weren't given any surfaces to animate, so just finish.
- onKeyguardExitRemoteAnimationFinished();
- return;
- }
+ // Pass the surface and metadata to the unlock animation controller.
+ mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation(
+ apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
} else {
setShowingLocked(false);
mWakeAndUnlocking = false;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index ecee1b508ce0..119e9c433f67 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -47,6 +47,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -94,7 +95,7 @@ public class KeyguardModule {
NavigationModeController navigationModeController,
KeyguardDisplayManager keyguardDisplayManager,
DozeParameters dozeParameters,
- StatusBarStateController statusBarStateController,
+ SysuiStatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController) {
return new KeyguardViewMediator(
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 644876c6b1af..ef53233c22d4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -115,7 +115,7 @@ class MediaCarouselController @Inject constructor(
private var needsReordering: Boolean = false
private var keysNeedRemoval = mutableSetOf<String>()
private var bgColor = getBackgroundColor()
- private var shouldScrollToActivePlayer: Boolean = false
+ protected var shouldScrollToActivePlayer: Boolean = false
private var isRtl: Boolean = false
set(value) {
if (value != field) {
@@ -184,7 +184,14 @@ class MediaCarouselController @Inject constructor(
true /* persistent */)
mediaManager.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- addOrUpdatePlayer(key, oldKey, data)
+ if (addOrUpdatePlayer(key, oldKey, data)) {
+ MediaPlayerData.getMediaPlayer(key, null)?.let {
+ logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
+ it.mInstanceId,
+ /* isRecommendationCard */ false,
+ it.surfaceForSmartspaceLogging)
+ }
+ }
val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
if (canRemove && !Utils.useMediaResumption(context)) {
// This view isn't playing, let's remove this! This happens e.g when
@@ -203,6 +210,12 @@ class MediaCarouselController @Inject constructor(
override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
Log.d(TAG, "My Smartspace media update is here")
addSmartspaceMediaRecommendations(key, data)
+ MediaPlayerData.getMediaPlayer(key, null)?.let {
+ logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
+ it.mInstanceId,
+ /* isRecommendationCard */ true,
+ it.surfaceForSmartspaceLogging)
+ }
if (mediaCarouselScrollHandler.visibleToUser) {
logSmartspaceImpression()
}
@@ -276,7 +289,8 @@ class MediaCarouselController @Inject constructor(
}
}
- private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) {
+ // Returns true if new player is added
+ private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData): Boolean {
val dataCopy = data.copy(backgroundColor = bgColor)
val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey)
if (existingPlayer == null) {
@@ -295,7 +309,7 @@ class MediaCarouselController @Inject constructor(
} else {
existingPlayer.bindPlayer(dataCopy, key)
MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer)
- if (visualStabilityManager.isReorderingAllowed) {
+ if (visualStabilityManager.isReorderingAllowed || shouldScrollToActivePlayer) {
reorderAllPlayers()
} else {
needsReordering = true
@@ -309,6 +323,7 @@ class MediaCarouselController @Inject constructor(
if (MediaPlayerData.players().size != mediaContent.childCount) {
Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
}
+ return existingPlayer == null
}
private fun addSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) {
@@ -325,7 +340,7 @@ class MediaCarouselController @Inject constructor(
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp)
- newRecs.bindRecommendation(data, bgColor, { v -> shouldScrollToActivePlayer = true })
+ newRecs.bindRecommendation(data, bgColor)
MediaPlayerData.addMediaRecommendation(key, newRecs)
updatePlayerToState(newRecs, noAnimation = true)
reorderAllPlayers()
@@ -520,12 +535,20 @@ class MediaCarouselController @Inject constructor(
this.desiredLocation = desiredLocation
this.desiredHostState = it
currentlyExpanded = it.expansion > 0
+
+ val shouldCloseGuts = !currentlyExpanded && !mediaManager.hasActiveMedia() &&
+ desiredHostState.showsOnlyActiveMedia
+
for (mediaPlayer in MediaPlayerData.players()) {
if (animate) {
mediaPlayer.mediaViewController.animatePendingStateChange(
duration = duration,
delay = startDelay)
}
+ if (shouldCloseGuts && mediaPlayer.mediaViewController.isGutsVisible) {
+ mediaPlayer.closeGuts(!animate)
+ }
+
mediaPlayer.mediaViewController.onLocationPreChange(desiredLocation)
}
mediaCarouselScrollHandler.showsSettingsButton = !it.showsOnlyActiveMedia
@@ -541,9 +564,9 @@ class MediaCarouselController @Inject constructor(
}
}
- fun closeGuts() {
+ fun closeGuts(immediate: Boolean = true) {
MediaPlayerData.players().forEach {
- it.closeGuts(true)
+ it.closeGuts(immediate)
}
}
@@ -641,7 +664,7 @@ class MediaCarouselController @Inject constructor(
@VisibleForTesting
internal object MediaPlayerData {
private val EMPTY = MediaData(-1, false, 0, null, null, null, null, null,
- emptyList(), emptyList(), "INVALID", null, null, null, false, null)
+ emptyList(), emptyList(), "INVALID", null, null, null, true, null)
data class MediaSortKey(
// Is Smartspace media recommendation. When the Smartspace media is present, it should
@@ -694,7 +717,7 @@ internal object MediaPlayerData {
/** Returns the index of the first non-timeout media. */
fun getActiveMediaIndex(): Int {
mediaPlayers.entries.forEachIndexed { index, e ->
- if (e.key.data.active) {
+ if (!e.key.isSsMediaRec && e.key.data.active) {
return index
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index e5a62711a8d0..c806bcfed345 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -59,7 +59,7 @@ class MediaCarouselScrollHandler(
private val mainExecutor: DelayableExecutor,
private val dismissCallback: () -> Unit,
private var translationChangedListener: () -> Unit,
- private val closeGuts: () -> Unit,
+ private val closeGuts: (immediate: Boolean) -> Unit,
private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
private val logSmartspaceImpression: () -> Unit
@@ -473,7 +473,7 @@ class MediaCarouselScrollHandler(
if (oldIndex != visibleMediaIndex && visibleToUser) {
logSmartspaceImpression()
}
- closeGuts()
+ closeGuts(false)
updatePlayerVisibilities()
}
val relativeLocation = visibleMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index fe3463f853f0..3e9559b1a9ec 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -25,7 +25,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -212,7 +213,8 @@ public class MediaControlPanel {
mMediaViewController.openGuts();
return true;
} else {
- return false;
+ closeGuts();
+ return true;
}
});
mPlayerViewHolder.getCancel().setOnClickListener(v -> {
@@ -276,7 +278,7 @@ public class MediaControlPanel {
if (mMediaViewController.isGutsVisible()) return;
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- false);
+ /* isRecommendationCard */ false);
mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
buildLaunchAnimatorController(mPlayerViewHolder.getPlayer()));
});
@@ -384,7 +386,7 @@ public class MediaControlPanel {
button.setEnabled(true);
button.setOnClickListener(v -> {
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- false);
+ /* isRecommendationCard */ false);
action.run();
});
}
@@ -418,7 +420,7 @@ public class MediaControlPanel {
mPlayerViewHolder.getDismiss().setEnabled(isDismissible);
mPlayerViewHolder.getDismiss().setOnClickListener(v -> {
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
- false);
+ /* isRecommendationCard */ false);
if (mKey != null) {
closeGuts();
@@ -472,10 +474,7 @@ public class MediaControlPanel {
}
/** Bind this recommendation view based on the data given. */
- public void bindRecommendation(
- @NonNull SmartspaceTarget target,
- @NonNull int backgroundColor,
- @Nullable View.OnClickListener callback) {
+ public void bindRecommendation(@NonNull SmartspaceTarget target, @NonNull int backgroundColor) {
if (mRecommendationViewHolder == null) {
return;
}
@@ -515,8 +514,9 @@ public class MediaControlPanel {
// Get the logo from app's package name when applicable.
String packageName = extras.getString(EXTRAS_MEDIA_SOURCE_PACKAGE_NAME);
try {
- icon = mContext.getPackageManager().getApplicationIcon(
+ Drawable drawable = mContext.getPackageManager().getApplicationIcon(
packageName);
+ icon = convertToGrayscale(drawable);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "No media source icon can be fetched via package name", e);
}
@@ -528,18 +528,13 @@ public class MediaControlPanel {
// Set up media source app's logo.
ImageView mediaSourceLogoImageView = mediaLogoItems.get(uiComponentIndex);
mediaSourceLogoImageView.setImageDrawable(icon);
- // TODO(b/186699032): Tint the app logo using the accent color.
- mediaSourceLogoImageView.setColorFilter(backgroundColor, PorterDuff.Mode.XOR);
// Set up media item cover.
ImageView mediaCoverImageView = mediaCoverItems.get(uiComponentIndex);
mediaCoverImageView.setImageIcon(recommendation.getIcon());
// Set up the click listener if applicable.
- setSmartspaceRecItemOnClickListener(
- mediaCoverImageView,
- recommendation,
- callback);
+ setSmartspaceRecItemOnClickListener(mediaCoverImageView, recommendation);
if (uiComponentIndex < MEDIA_RECOMMENDATION_ITEMS_PER_ROW) {
setVisibleAndAlpha(collapsedSet,
@@ -563,7 +558,7 @@ public class MediaControlPanel {
// Set up long press to show guts setting panel.
mRecommendationViewHolder.getDismiss().setOnClickListener(v -> {
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
- true);
+ /* isRecommendationCard */ true);
closeGuts();
mKeyguardDismissUtil.executeWhenUnlocked(() -> {
mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
@@ -651,6 +646,15 @@ public class MediaControlPanel {
return (state.getState() == PlaybackState.STATE_PLAYING);
}
+ /** Convert the pass-in source drawable to a grayscale one. */
+ private Drawable convertToGrayscale(Drawable drawable) {
+ ColorMatrix matrix = new ColorMatrix();
+ matrix.setSaturation(0);
+ ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
+ drawable.setColorFilter(filter);
+ return drawable;
+ }
+
private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible) {
set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : ConstraintSet.GONE);
set.setAlpha(actionId, visible ? 1.0f : 0.0f);
@@ -658,8 +662,7 @@ public class MediaControlPanel {
private void setSmartspaceRecItemOnClickListener(
@NonNull View view,
- @NonNull SmartspaceAction action,
- @Nullable View.OnClickListener callback) {
+ @NonNull SmartspaceAction action) {
if (view == null || action == null || action.getIntent() == null) {
Log.e(TAG, "No tap action can be set up");
return;
@@ -668,7 +671,7 @@ public class MediaControlPanel {
view.setOnClickListener(v -> {
// When media recommendation card is shown, it will always be the top card.
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- true);
+ /* isRecommendationCard */ true);
if (shouldSmartspaceRecItemOpenInForeground(action)) {
// Request to unlock the device if the activity needs to be opened in foreground.
@@ -682,9 +685,8 @@ public class MediaControlPanel {
view.getContext().startActivity(action.getIntent());
}
- if (callback != null) {
- callback.onClick(v);
- }
+ // Automatically scroll to the active player once the media is loaded.
+ mMediaCarouselController.setShouldScrollToActivePlayer(true);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 3c28f6e1651a..60e832ace7b0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -273,6 +273,7 @@ class MediaHierarchyManager @Inject constructor(
} else {
updateDesiredLocation()
qsExpanded = false
+ closeGuts()
}
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
index 2d0d5cd73aac..40f908b0c62f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
@@ -16,7 +16,6 @@
package com.android.systemui.navigationbar;
-import android.annotation.ColorInt;
import android.content.Context;
import android.view.View;
@@ -46,10 +45,9 @@ public class NavigationBarOverlayController {
}
/**
- * Initialize the controller with visibility change callback and light/dark icon color.
+ * Initialize the controller with visibility change callback.
*/
- public void init(Consumer<Boolean> visibilityChangeCallback, @ColorInt int lightIconColor,
- @ColorInt int darkIconColor) {}
+ public void init(Consumer<Boolean> visibilityChangeCallback) {}
/**
* Set whether the view can be shown.
@@ -72,11 +70,6 @@ public class NavigationBarOverlayController {
public void unregisterListeners() {}
/**
- * Set the dark intensity for all drawables.
- */
- public void setDarkIntensity(float darkIntensity) {}
-
- /**
* Return the current view.
*/
public View getCurrentView() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index fbc7c92a4a64..b4f8c10f3fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -198,7 +198,6 @@ public final class NavigationBarTransitions extends BarTransitions implements
}
mView.getRotationButtonController().setDarkIntensity(darkIntensity);
- Dependency.get(NavigationBarOverlayController.class).setDarkIntensity(darkIntensity);
for (DarkIntensityListener listener : mDarkIntensityListeners) {
listener.onDarkIntensity(darkIntensity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index f82d265d12d6..fcbd59659ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -61,7 +61,6 @@ import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
@@ -326,8 +325,7 @@ public class NavigationBarView extends FrameLayout implements
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
- mNavBarOverlayController.init(
- mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor);
+ mNavBarOverlayController.init(mNavbarOverlayVisibilityChangeCallback);
}
mConfiguration = new Configuration();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
index 0f6645641dda..b55d86e8fb1c 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
@@ -18,7 +18,9 @@ package com.android.systemui.people;
import android.content.ContentProvider;
import android.content.ContentValues;
+import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
@@ -27,16 +29,19 @@ import android.os.UserHandle;
import android.util.Log;
import android.widget.RemoteViews;
+import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.shared.system.PeopleProviderUtils;
import javax.inject.Inject;
/** API that returns a People Tile preview. */
-public class PeopleProvider extends ContentProvider {
+public class PeopleProvider extends ContentProvider implements
+ SystemUIAppComponentFactory.ContextInitializer {
private static final String TAG = "PeopleProvider";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
private static final String EMPTY_STRING = "";
+ private SystemUIAppComponentFactory.ContextAvailableCallback mCallback;
@Inject
PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
@@ -82,8 +87,8 @@ public class PeopleProvider extends ContentProvider {
Log.e(TAG, "Could not initialize people widget manager");
return null;
}
- RemoteViews view =
- mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName, extras);
+ RemoteViews view = mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName,
+ extras);
if (view == null) {
if (DEBUG) Log.d(TAG, "No preview available for shortcutId: " + shortcutId);
return null;
@@ -130,5 +135,17 @@ public class PeopleProvider extends ContentProvider {
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new IllegalArgumentException("Invalid method");
}
+
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ mCallback.onContextAvailable(context);
+ super.attachInfo(context, info);
+ }
+
+ @Override
+ public void setContextAvailableCallback(
+ SystemUIAppComponentFactory.ContextAvailableCallback callback) {
+ mCallback = callback;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 29685a4c628b..7b5ab0df1e5e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -416,9 +416,8 @@ public class PeopleSpaceUtils {
&& birthdayString == null;
boolean addBirthdayStatus = !hasBirthdayStatus(storedTile, context)
&& birthdayString != null;
- boolean shouldUpdate =
- storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
- || addBirthdayStatus;
+ boolean shouldUpdate = storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
+ || addBirthdayStatus;
if (shouldUpdate) {
if (DEBUG) Log.d(TAG, "Update " + storedTile.getUserName() + " from contacts");
manager.updateAppWidgetOptionsAndView(appWidgetId,
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 06f8a60dd015..9fc9cad0a690 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -41,8 +41,6 @@ import android.app.people.ConversationStatus;
import android.app.people.PeopleSpaceTile;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
@@ -57,13 +55,13 @@ import android.text.TextUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Pair;
+import android.view.Gravity;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -195,16 +193,10 @@ public class PeopleTileViewHelper {
*/
private RemoteViews getViewForTile() {
if (DEBUG) Log.d(TAG, "Creating view for tile key: " + mKey.toString());
- if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()) {
- if (DEBUG) Log.d(TAG, "Create empty view: " + mTile);
- return createEmptyView();
- }
-
- boolean dndBlockingTileData = isDndBlockingTileData(mTile);
- if (dndBlockingTileData) {
- if (DEBUG) Log.d(TAG, "Create DND view: " + mTile.getNotificationPolicyState());
- // TODO: Create DND view.
- return createEmptyView();
+ if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()
+ || isDndBlockingTileData(mTile)) {
+ if (DEBUG) Log.d(TAG, "Create suppressed view: " + mTile);
+ return createSuppressedView();
}
if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
@@ -265,34 +257,27 @@ public class PeopleTileViewHelper {
return !tile.canBypassDnd();
}
- private RemoteViews createEmptyView() {
- RemoteViews views = new RemoteViews(mContext.getPackageName(),
- R.layout.people_tile_empty_layout);
- Drawable appIcon = getAppBadge(mKey.getPackageName(), mKey.getUserId());
+ private RemoteViews createSuppressedView() {
+ RemoteViews views;
+ if (mTile.isUserQuieted()) {
+ views = new RemoteViews(mContext.getPackageName(),
+ R.layout.people_tile_work_profile_quiet_layout);
+ } else {
+ views = new RemoteViews(mContext.getPackageName(),
+ R.layout.people_tile_suppressed_layout);
+ }
+ Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon);
Bitmap appIconAsBitmap = convertDrawableToBitmap(appIcon);
- FastBitmapDrawable drawable = new FastBitmapDrawable(
- appIconAsBitmap);
+ FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap);
drawable.setIsDisabled(true);
Bitmap convertedBitmap = convertDrawableToBitmap(drawable);
- views.setImageViewBitmap(R.id.item, convertedBitmap);
+ views.setImageViewBitmap(R.id.icon, convertedBitmap);
return views;
}
- private Drawable getAppBadge(String packageName, int userId) {
- Drawable badge = null;
- try {
- final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
- packageName, PackageManager.GET_META_DATA, userId);
- badge = Utils.getBadgedIcon(mContext, appInfo);
- } catch (PackageManager.NameNotFoundException e) {
- badge = mContext.getPackageManager().getDefaultActivityIcon();
- }
- return badge;
- }
-
private void setMaxLines(RemoteViews views, boolean showSender) {
int textSize = mLayoutSize == LAYOUT_LARGE ? getSizeInDp(
- R.dimen.content_text_size_for_medium)
+ R.dimen.content_text_size_for_large)
: getSizeInDp(R.dimen.content_text_size_for_medium);
int lineHeight = getLineHeight(textSize);
int notificationContentHeight = getContentHeightForLayout(lineHeight);
@@ -422,9 +407,6 @@ public class PeopleTileViewHelper {
views.setViewVisibility(R.id.availability, View.GONE);
}
- if (mTile.getUserName() != null) {
- views.setTextViewText(R.id.name, mTile.getUserName().toString());
- }
views.setBoolean(R.id.image, "setClipToOutline", true);
views.setImageViewBitmap(R.id.person_icon,
getPersonIconBitmap(mContext, mTile, maxAvatarSize));
@@ -537,25 +519,31 @@ public class PeopleTileViewHelper {
statusText = getStatusTextByType(status.getActivity());
}
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
- views.setViewVisibility(R.id.messages_count, View.GONE);
- setMaxLines(views, false);
- // Secondary text color for statuses.
- views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorSecondary);
views.setTextViewText(R.id.text_content, statusText);
+ if (mLayoutSize == LAYOUT_LARGE) {
+ views.setInt(R.id.content, "setGravity", Gravity.BOTTOM);
+ }
Icon statusIcon = status.getIcon();
if (statusIcon != null) {
- // No multi-line text with status images on medium layout.
- views.setViewVisibility(R.id.text_content, View.GONE);
+ // No text content styled text on medium or large.
+ views.setViewVisibility(R.id.scrim_layout, View.VISIBLE);
+ views.setImageViewIcon(R.id.status_icon, statusIcon);
// Show 1-line subtext on large layout with status images.
if (mLayoutSize == LAYOUT_LARGE) {
- views.setViewVisibility(R.id.subtext, View.VISIBLE);
- views.setTextViewText(R.id.subtext, statusText);
+ if (DEBUG) Log.d(TAG, "Remove name for large");
+ views.setViewVisibility(R.id.name, View.GONE);
+ views.setColorAttr(R.id.text_content, "setTextColor",
+ android.R.attr.textColorPrimary);
+ } else if (mLayoutSize == LAYOUT_MEDIUM) {
+ views.setViewVisibility(R.id.text_content, View.GONE);
+ views.setTextViewText(R.id.name, statusText);
}
- views.setViewVisibility(R.id.image, View.VISIBLE);
- views.setImageViewIcon(R.id.image, statusIcon);
} else {
- views.setViewVisibility(R.id.image, View.GONE);
+ // Secondary text color for statuses without icons.
+ views.setColorAttr(R.id.text_content, "setTextColor",
+ android.R.attr.textColorSecondary);
+ setMaxLines(views, false);
}
// TODO: Set status pre-defined icons
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
@@ -741,16 +729,23 @@ public class PeopleTileViewHelper {
views.setViewVisibility(R.id.name, View.VISIBLE);
views.setViewVisibility(R.id.text_content, View.VISIBLE);
views.setViewVisibility(R.id.subtext, View.GONE);
+ views.setViewVisibility(R.id.image, View.GONE);
+ views.setViewVisibility(R.id.scrim_layout, View.GONE);
}
if (mLayoutSize == LAYOUT_MEDIUM) {
if (DEBUG) Log.d(TAG, "Set vertical padding: " + mMediumVerticalPadding);
int horizontalPadding = (int) Math.floor(MAX_MEDIUM_PADDING * mDensity);
int verticalPadding = (int) Math.floor(mMediumVerticalPadding * mDensity);
- views.setViewPadding(R.id.item, horizontalPadding, verticalPadding, horizontalPadding,
+ views.setViewPadding(R.id.content, horizontalPadding, verticalPadding,
+ horizontalPadding,
verticalPadding);
}
views.setViewVisibility(R.id.messages_count, View.GONE);
+ if (mTile.getUserName() != null) {
+ views.setTextViewText(R.id.name, mTile.getUserName());
+ }
+
return views;
}
@@ -761,6 +756,9 @@ public class PeopleTileViewHelper {
views.setViewVisibility(R.id.predefined_icon, View.GONE);
views.setViewVisibility(R.id.messages_count, View.GONE);
}
+ if (mTile.getUserName() != null) {
+ views.setTextViewText(R.id.name, mTile.getUserName());
+ }
String status = getLastInteractionString(mContext,
mTile.getLastInteractionTimestamp());
if (status != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index eec69f98b9be..1d2e74703b42 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -28,7 +28,7 @@ class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyIt
appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
.toList()
.sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
- { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest)
+ { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
types = itemsList.map { it.privacyType }.distinct().sorted()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index dd1a4af18f7f..3a3f3f1ca7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -129,6 +129,12 @@ public class QSContainerImpl extends FrameLayout {
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mNavBarInset = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
+ mQSPanelContainer.setPaddingRelative(
+ mQSPanelContainer.getPaddingStart(),
+ mQSPanelContainer.getPaddingTop(),
+ mQSPanelContainer.getPaddingEnd(),
+ mNavBarInset
+ );
return super.onApplyWindowInsets(insets);
}
@@ -138,8 +144,7 @@ public class QSContainerImpl extends FrameLayout {
// bottom and footer are inside the screen.
MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
- int availableScreenHeight = getDisplayHeight() - mNavBarInset;
- int maxQs = availableScreenHeight - layoutParams.topMargin - layoutParams.bottomMargin
+ int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
- getPaddingBottom();
int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
+ layoutParams.rightMargin;
@@ -148,10 +153,8 @@ public class QSContainerImpl extends FrameLayout {
mQSPanelContainer.measure(qsPanelWidthSpec,
MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanelContainer.getMeasuredWidth() + padding;
- int height = layoutParams.topMargin + layoutParams.bottomMargin
- + mQSPanelContainer.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(availableScreenHeight, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
// QSCustomizer will always be the height of the screen, but do this after
// other measuring to avoid changing the height of the QS.
mQSCustomizer.measure(widthMeasureSpec,
@@ -200,12 +203,6 @@ public class QSContainerImpl extends FrameLayout {
layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
mQSPanelContainer.setLayoutParams(layoutParams);
- mQSPanelContainer.setPaddingRelative(
- mQSPanelContainer.getPaddingStart(),
- mQSPanelContainer.getPaddingTop(),
- mQSPanelContainer.getPaddingEnd(),
- mContext.getResources().getDimensionPixelSize(R.dimen.qs_container_bottom_padding)
- );
int sideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
int padding = getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 05197e46fb25..0335319f5b49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -30,6 +30,7 @@ import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -153,11 +154,18 @@ public class QSDetail extends LinearLayout {
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
lp.topMargin = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- lp.bottomMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_container_bottom_padding);
setLayoutParams(lp);
}
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ int bottomNavBar = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
+ MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+ lp.bottomMargin = bottomNavBar;
+ setLayoutParams(lp);
+ return super.onApplyWindowInsets(insets);
+ }
+
public boolean isClosingDetail() {
return mClosingDetail;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 1fa926009861..f6d93895ce05 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -29,6 +29,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
@@ -73,6 +74,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
private final View mPowerMenuLite;
private final boolean mShowPMLiteButton;
private GlobalActionsDialogLite mGlobalActionsDialog;
+ private final UiEventLogger mUiEventLogger;
private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
new UserInfoController.OnUserInfoChangedListener() {
@@ -122,6 +124,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
startSettingsActivity();
}
} else if (v == mPowerMenuLite) {
+ mUiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
mGlobalActionsDialog.showOrHideDialog(false, true);
}
}
@@ -139,7 +142,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
QuickQSPanelController quickQSPanelController,
TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
@Named(PM_LITE_ENABLED) boolean showPMLiteButton,
- GlobalActionsDialogLite globalActionsDialog) {
+ GlobalActionsDialogLite globalActionsDialog, UiEventLogger uiEventLogger) {
super(view);
mUserManager = userManager;
mUserInfoController = userInfoController;
@@ -161,6 +164,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
mPowerMenuLite = mView.findViewById(R.id.pm_lite);
mShowPMLiteButton = showPMLiteButton;
mGlobalActionsDialog = globalActionsDialog;
+ mUiEventLogger = uiEventLogger;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 04e32a10db17..3a6f1d5a02ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -190,6 +190,16 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
|| vpnName != null || vpnNameWorkProfile != null
|| isProfileOwnerOfOrganizationOwnedDevice || isParentalControlsEnabled
|| (hasWorkProfile && isNetworkLoggingEnabled);
+ // Update the view to be untappable if the device is an organization-owned device with a
+ // managed profile and there is no policy set which requires a privacy disclosure.
+ if (mIsVisible && isProfileOwnerOfOrganizationOwnedDevice && !isNetworkLoggingEnabled
+ && !hasCACertsInWorkProfile && vpnNameWorkProfile == null) {
+ mRootView.setClickable(false);
+ mRootView.findViewById(R.id.footer_icon).setVisibility(View.GONE);
+ } else {
+ mRootView.setClickable(true);
+ mRootView.findViewById(R.id.footer_icon).setVisibility(View.VISIBLE);
+ }
// Update the string
mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
@@ -345,20 +355,15 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
private View createOrganizationDialogView() {
final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
- boolean isProfileOwnerOfOrganizationOwnedDevice =
- mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
final CharSequence deviceOwnerOrganization =
mSecurityController.getDeviceOwnerOrganizationName();
- final CharSequence workProfileOrganizationName =
- mSecurityController.getWorkProfileOrganizationName();
final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
final String vpnName = mSecurityController.getPrimaryVpnName();
final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
-
View dialogView = LayoutInflater.from(mContext)
.inflate(R.layout.quick_settings_footer_dialog, null, false);
@@ -368,8 +373,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
deviceManagementSubtitle.setText(getManagementTitle(deviceOwnerOrganization));
CharSequence managementMessage = getManagementMessage(isDeviceManaged,
- deviceOwnerOrganization, isProfileOwnerOfOrganizationOwnedDevice,
- workProfileOrganizationName);
+ deviceOwnerOrganization);
if (managementMessage == null) {
dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.GONE);
} else {
@@ -377,11 +381,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
TextView deviceManagementWarning =
(TextView) dialogView.findViewById(R.id.device_management_warning);
deviceManagementWarning.setText(managementMessage);
- // Don't show the policies button for profile owner of org owned device, because there
- // is no policies settings screen for it
- if (!isProfileOwnerOfOrganizationOwnedDevice) {
- mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
- }
+ mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
}
// ca certificate section
@@ -496,12 +496,11 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
}
protected CharSequence getManagementMessage(boolean isDeviceManaged,
- CharSequence organizationName, boolean isProfileOwnerOfOrganizationOwnedDevice,
- CharSequence workProfileOrganizationName) {
- if (!isDeviceManaged && !isProfileOwnerOfOrganizationOwnedDevice) {
+ CharSequence organizationName) {
+ if (!isDeviceManaged) {
return null;
}
- if (isDeviceManaged && organizationName != null) {
+ if (organizationName != null) {
if (isFinancedDevice()) {
return mContext.getString(R.string.monitoring_financed_description_named_management,
organizationName, organizationName);
@@ -509,9 +508,6 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return mContext.getString(
R.string.monitoring_description_named_management, organizationName);
}
- } else if (isProfileOwnerOfOrganizationOwnedDevice && workProfileOrganizationName != null) {
- return mContext.getString(
- R.string.monitoring_description_named_management, workProfileOrganizationName);
}
return mContext.getString(R.string.monitoring_description_management);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 73a6b34c612e..81b5318d8089 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -23,7 +23,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.MathUtils;
+import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.View;
@@ -36,9 +36,7 @@ import android.widget.Space;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.qs.QSDetail.Callback;
-import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.StatusIconContainer;
@@ -67,7 +65,11 @@ public class QuickStatusBarHeader extends FrameLayout {
private View mQSCarriers;
private Clock mClockView;
- private Space mSpace;
+ private Space mDatePrivacySeparator;
+ private View mClockIconsSeparator;
+ private boolean mShowClockIconsSeparator;
+ private ViewGroup mRightLayout;
+
private BatteryMeterView mBatteryRemainingIcon;
private StatusIconContainer mIconContainer;
private View mPrivacyChip;
@@ -80,19 +82,27 @@ public class QuickStatusBarHeader extends FrameLayout {
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
- private float mClockIconsAlpha = 1.0f;
+ private float mViewAlpha = 1.0f;
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
private int mTopViewMeasureHeight;
private final String mMobileSlotName;
+ private final String mNoCallingSlotName;
private final String mCallStrengthSlotName;
+ private final boolean mProviderModel;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
- mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling);
+ mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile);
+ mNoCallingSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling);
mCallStrengthSlotName =
context.getString(com.android.internal.R.string.status_bar_call_strength);
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ mProviderModel = true;
+ } else {
+ mProviderModel = false;
+ }
}
/**
@@ -117,9 +127,11 @@ public class QuickStatusBarHeader extends FrameLayout {
mPrivacyChip = findViewById(R.id.privacy_chip);
mDateView = findViewById(R.id.date);
mSecurityHeaderView = findViewById(R.id.header_text_container);
+ mClockIconsSeparator = findViewById(R.id.separator);
+ mRightLayout = findViewById(R.id.rightLayout);
mClockView = findViewById(R.id.clock);
- mSpace = findViewById(R.id.space);
+ mDatePrivacySeparator = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -230,36 +242,50 @@ public class QuickStatusBarHeader extends FrameLayout {
}
private void updateAlphaAnimator() {
- StatusBarIconView noCallingIcon =
- ((StatusBarIconView) mIconContainer.getViewForSlot(mMobileSlotName));
- StatusBarIconView callStrengthIcon =
- ((StatusBarIconView) mIconContainer.getViewForSlot(mCallStrengthSlotName));
TouchAnimator.Builder builder = new TouchAnimator.Builder()
// The following two views have to be hidden manually, so as not to hide the
// Privacy chip in QQS
.addFloat(mDateView, "alpha", 0, 1)
.addFloat(mSecurityHeaderView, "alpha", 0, 1)
- .addFloat(mQSCarriers, "alpha", 0, 1);
- builder.setListener(new TouchAnimator.ListenerAdapter() {
- @Override
- public void onAnimationAtEnd() {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
- }
-
- @Override
- public void onAnimationStarted() {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
- }
-
- @Override
- public void onAnimationAtStart() {
- super.onAnimationAtStart();
- mIconContainer.removeIgnoredSlot(mMobileSlotName);
- mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
- }
- });
+ .addFloat(mQSCarriers, "alpha", 0, 1)
+ .setListener(new TouchAnimator.ListenerAdapter() {
+ @Override
+ public void onAnimationAtEnd() {
+ // TODO(b/185580157): Remove the mProviderModel if the mobile slot can be
+ // hidden in Provider model.
+ if (mProviderModel) {
+ mIconContainer.addIgnoredSlot(mNoCallingSlotName);
+ mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+ } else {
+ mIconContainer.addIgnoredSlot(mMobileSlotName);
+ }
+ }
+
+ @Override
+ public void onAnimationStarted() {
+ if (mProviderModel) {
+ mIconContainer.addIgnoredSlot(mNoCallingSlotName);
+ mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+ } else {
+ mIconContainer.addIgnoredSlot(mMobileSlotName);
+ }
+
+ setSeparatorVisibility(false);
+ }
+
+ @Override
+ public void onAnimationAtStart() {
+ super.onAnimationAtStart();
+ if (mProviderModel) {
+ mIconContainer.removeIgnoredSlot(mNoCallingSlotName);
+ mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
+ } else {
+ mIconContainer.removeIgnoredSlot(mMobileSlotName);
+ }
+
+ setSeparatorVisibility(mShowClockIconsSeparator);
+ }
+ });
mAlphaAnimator = builder.build();
}
@@ -337,20 +363,30 @@ public class QuickStatusBarHeader extends FrameLayout {
cutout, cornerCutoutPadding, -1);
mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
+ LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
+ (LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
+ LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
+ (LinearLayout.LayoutParams) mClockIconsSeparator.getLayoutParams();
boolean cornerCutout = cornerCutoutPadding != null
&& (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
if (cutout != null) {
Rect topCutout = cutout.getBoundingRectTop();
if (topCutout.isEmpty() || cornerCutout) {
- lp.width = 0;
- mSpace.setVisibility(View.GONE);
+ datePrivacySeparatorLayoutParams.width = 0;
+ mDatePrivacySeparator.setVisibility(View.GONE);
+ mClockIconsSeparatorLayoutParams.width = 0;
+ setSeparatorVisibility(false);
+ mShowClockIconsSeparator = false;
} else {
- lp.width = topCutout.width();
- mSpace.setVisibility(View.VISIBLE);
+ datePrivacySeparatorLayoutParams.width = topCutout.width();
+ mDatePrivacySeparator.setVisibility(View.VISIBLE);
+ mClockIconsSeparatorLayoutParams.width = topCutout.width();
+ mShowClockIconsSeparator = true;
+ setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
}
}
- mSpace.setLayoutParams(lp);
+ mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
+ mClockIconsSeparator.setLayoutParams(mClockIconsSeparatorLayoutParams);
mCutOutPaddingLeft = padding.first;
mCutOutPaddingRight = padding.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
@@ -358,6 +394,32 @@ public class QuickStatusBarHeader extends FrameLayout {
return super.onApplyWindowInsets(insets);
}
+ /**
+ * Sets the visibility of the separator between clock and icons.
+ *
+ * This separator is "visible" when there is a center cutout, to block that space. In that
+ * case, the clock and the layout on the right (containing the icons and the battery meter) are
+ * set to weight 1 to take the available space.
+ * @param visible whether the separator between clock and icons should be visible.
+ */
+ private void setSeparatorVisibility(boolean visible) {
+ int newVisibility = visible ? View.VISIBLE : View.GONE;
+ if (mClockIconsSeparator.getVisibility() == newVisibility) return;
+
+ mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE);
+
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mClockView.getLayoutParams();
+ lp.width = visible ? 0 : WRAP_CONTENT;
+ lp.weight = visible ? 1f : 0f;
+ mClockView.setLayoutParams(lp);
+
+ lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams();
+ lp.width = visible ? 0 : WRAP_CONTENT;
+ lp.weight = visible ? 1f : 0f;
+ mRightLayout.setLayoutParams(lp);
+ }
+
private void updateHeadersPadding() {
setContentMargins(mDatePrivacyView, 0, 0);
setContentMargins(mClockIconsView, 0, 0);
@@ -408,35 +470,12 @@ public class QuickStatusBarHeader extends FrameLayout {
}
/**
- * When QS is scrolling, mClockIconsAlpha should scroll away and fade out.
- *
- * For a given scroll level, this method does the following:
- * <ol>
- * <li>Determine the alpha that {@code mClockIconsView} should have when the panel is fully
- * expanded.</li>
- * <li>Set the scroll of {@code mClockIconsView} to the same of {@code QSPanel}.</li>
- * <li>Set the alpha of {@code mClockIconsView} to that determined by the expansion of
- * the panel, interpolated between 1 (no expansion) and {@code mClockIconsAlpha} (fully
- * expanded), matching the animator.</li>
- * </ol>
+ * Scroll the headers away.
*
* @param scrollY the scroll of the QSPanel container
*/
public void setExpandedScrollAmount(int scrollY) {
- // The scrolling of the expanded qs has changed. Since the header text isn't part of it,
- // but would overlap content, we're fading it out.
- float newAlpha = 1.0f;
- if (mClockIconsView.getHeight() > 0) {
- newAlpha = MathUtils.map(0, mClockIconsView.getHeight() / 2.0f, 1.0f, 0.0f,
- scrollY);
- newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
- }
mClockIconsView.setScrollY(scrollY);
- if (newAlpha != mClockIconsAlpha) {
- mClockIconsAlpha = newAlpha;
- mClockIconsView.setAlpha(MathUtils.lerp(1.0f, mClockIconsAlpha,
- mKeyguardExpansionFraction));
- updateAlphaAnimator();
- }
+ mDatePrivacyView.setScrollY(scrollY);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 2d777a53de51..b3ec39f4f40a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tileimpl
+import android.animation.ArgbEvaluator
+import android.animation.PropertyValuesHolder
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.ColorStateList
@@ -43,6 +45,7 @@ import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.BooleanState
import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
import java.util.Objects
private const val TAG = "QSTileViewImpl"
@@ -54,6 +57,10 @@ open class QSTileViewImpl @JvmOverloads constructor(
companion object {
private const val INVALID = -1
+ private const val BACKGROUND_NAME = "background"
+ private const val LABEL_NAME = "label"
+ private const val SECONDARY_LABEL_NAME = "secondaryLabel"
+ private const val CHEVRON_NAME = "chevron"
}
override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
@@ -83,9 +90,19 @@ open class QSTileViewImpl @JvmOverloads constructor(
private lateinit var ripple: RippleDrawable
private lateinit var colorBackgroundDrawable: Drawable
private var paintColor: Int = 0
- private var paintAnimator: ValueAnimator? = null
- private var labelAnimator: ValueAnimator? = null
- private var secondaryLabelAnimator: ValueAnimator? = null
+ private val singleAnimator: ValueAnimator = ValueAnimator().apply {
+ setDuration(QS_ANIM_LENGTH)
+ addUpdateListener { animation ->
+ setAllColors(
+ // These casts will throw an exception if some property is missing. We should
+ // always have all properties.
+ animation.getAnimatedValue(BACKGROUND_NAME) as Int,
+ animation.getAnimatedValue(LABEL_NAME) as Int,
+ animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
+ animation.getAnimatedValue(CHEVRON_NAME) as Int
+ )
+ }
+ }
private var accessibilityClass: String? = null
private var stateDescriptionDeltas: CharSequence? = null
@@ -104,8 +121,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
clipToPadding = false
isFocusable = true
background = createTileBackground()
- paintColor = getCircleColor(QSTile.State.DEFAULT_STATE)
- colorBackgroundDrawable.setTint(paintColor)
+ setColor(getBackgroundColorForState(QSTile.State.DEFAULT_STATE))
val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
@@ -166,8 +182,8 @@ open class QSTileViewImpl @JvmOverloads constructor(
labelContainer.ignoreLastView = true
secondaryLabel.alpha = 0f
}
- label.setTextColor(getLabelColor(QSTile.State.DEFAULT_STATE))
- secondaryLabel.setTextColor(getSecondaryLabelColor(QSTile.State.DEFAULT_STATE))
+ setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE))
+ setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE))
addView(labelContainer)
}
@@ -176,6 +192,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
.inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
customDrawableView = sideView.requireViewById(R.id.customDrawable)
chevronView = sideView.requireViewById(R.id.chevron)
+ setChevronColor(getChevronColorForState(QSTile.State.DEFAULT_STATE))
addView(sideView)
}
@@ -322,19 +339,6 @@ open class QSTileViewImpl @JvmOverloads constructor(
icon.setIcon(state, allowAnimations)
contentDescription = state.contentDescription
- // Background color animation
- val newColor = getCircleColor(state.state)
- if (allowAnimations) {
- animateBackground(newColor)
- } else {
- clearBackgroundAnimator()
- colorBackgroundDrawable.setTintList(ColorStateList.valueOf(newColor)).also {
- paintColor = newColor
- }
- paintColor = newColor
- }
- //
-
// State handling and description
val stateDescription = StringBuilder()
val stateText = getStateText(state)
@@ -383,23 +387,80 @@ open class QSTileViewImpl @JvmOverloads constructor(
}
}
- if (allowAnimations) {
- animateLabelColor(getLabelColor(state.state))
- animateSecondaryLabelColor(getSecondaryLabelColor(state.state))
- } else {
- label.setTextColor(getLabelColor(state.state))
- secondaryLabel.setTextColor(getSecondaryLabelColor(state.state))
+ // Colors
+ if (state.state != lastState) {
+ singleAnimator.cancel()
+ if (allowAnimations) {
+ singleAnimator.setValues(
+ colorValuesHolder(
+ BACKGROUND_NAME,
+ paintColor,
+ getBackgroundColorForState(state.state)
+ ),
+ colorValuesHolder(
+ LABEL_NAME,
+ label.currentTextColor,
+ getLabelColorForState(state.state)
+ ),
+ colorValuesHolder(
+ SECONDARY_LABEL_NAME,
+ label.currentTextColor,
+ getSecondaryLabelColorForState(state.state)
+ ),
+ colorValuesHolder(
+ CHEVRON_NAME,
+ chevronView.imageTintList?.defaultColor ?: 0,
+ getChevronColorForState(state.state)
+ )
+ )
+ singleAnimator.start()
+ } else {
+ setAllColors(
+ getBackgroundColorForState(state.state),
+ getLabelColorForState(state.state),
+ getLabelColorForState(state.state),
+ getChevronColorForState(state.state)
+ )
+ }
}
// Right side icon
loadSideViewDrawableIfNecessary(state)
- chevronView.imageTintList = ColorStateList.valueOf(getSecondaryLabelColor(state.state))
label.isEnabled = !state.disabledByPolicy
lastState = state.state
}
+ private fun setAllColors(
+ backgroundColor: Int,
+ labelColor: Int,
+ secondaryLabelColor: Int,
+ chevronColor: Int
+ ) {
+ setColor(backgroundColor)
+ setLabelColor(labelColor)
+ setSecondaryLabelColor(secondaryLabelColor)
+ setChevronColor(chevronColor)
+ }
+
+ private fun setColor(color: Int) {
+ colorBackgroundDrawable.setTint(color)
+ paintColor = color
+ }
+
+ private fun setLabelColor(color: Int) {
+ label.setTextColor(color)
+ }
+
+ private fun setSecondaryLabelColor(color: Int) {
+ secondaryLabel.setTextColor(color)
+ }
+
+ private fun setChevronColor(color: Int) {
+ chevronView.imageTintList = ColorStateList.valueOf(color)
+ }
+
private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
if (state.sideViewCustomDrawable != null) {
customDrawableView.setImageDrawable(state.sideViewCustomDrawable)
@@ -446,63 +507,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
return locInScreen.get(1) >= -height
}
- private fun animateBackground(newBackgroundColor: Int) {
- if (newBackgroundColor != paintColor) {
- clearBackgroundAnimator()
- paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener { animation: ValueAnimator ->
- val c = animation.animatedValue as Int
- colorBackgroundDrawable.setTintList(ColorStateList.valueOf(c)).also {
- paintColor = c
- }
- }
- start()
- }
- }
- }
-
- private fun animateLabelColor(color: Int) {
- val currentColor = label.textColors.defaultColor
- if (currentColor != color) {
- clearLabelAnimator()
- labelAnimator = ValueAnimator.ofArgb(currentColor, color)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener {
- label.setTextColor(it.animatedValue as Int)
- }
- start()
- }
- }
- }
-
- private fun animateSecondaryLabelColor(color: Int) {
- val currentColor = secondaryLabel.textColors.defaultColor
- if (currentColor != color) {
- clearSecondaryLabelAnimator()
- secondaryLabelAnimator = ValueAnimator.ofArgb(currentColor, color)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener {
- secondaryLabel.setTextColor(it.animatedValue as Int)
- }
- start()
- }
- }
- }
-
- private fun clearBackgroundAnimator() {
- paintAnimator?.cancel()?.also { paintAnimator = null }
- }
-
- private fun clearLabelAnimator() {
- labelAnimator?.cancel()?.also { labelAnimator = null }
- }
-
- private fun clearSecondaryLabelAnimator() {
- secondaryLabelAnimator?.cancel()?.also { secondaryLabelAnimator = null }
- }
-
- private fun getCircleColor(state: Int): Int {
+ private fun getBackgroundColorForState(state: Int): Int {
return when (state) {
Tile.STATE_ACTIVE -> colorActive
Tile.STATE_INACTIVE -> colorInactive
@@ -514,7 +519,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
}
}
- private fun getLabelColor(state: Int): Int {
+ private fun getLabelColorForState(state: Int): Int {
return when (state) {
Tile.STATE_ACTIVE -> colorLabelActive
Tile.STATE_INACTIVE -> colorLabelInactive
@@ -526,7 +531,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
}
}
- private fun getSecondaryLabelColor(state: Int): Int {
+ private fun getSecondaryLabelColorForState(state: Int): Int {
return when (state) {
Tile.STATE_ACTIVE -> colorLabelActive
Tile.STATE_INACTIVE, Tile.STATE_UNAVAILABLE -> colorLabelUnavailable
@@ -536,4 +541,12 @@ open class QSTileViewImpl @JvmOverloads constructor(
}
}
}
+
+ private fun getChevronColorForState(state: Int): Int = getSecondaryLabelColorForState(state)
+}
+
+private fun colorValuesHolder(name: String, vararg values: Int): PropertyValuesHolder {
+ return PropertyValuesHolder.ofInt(name, *values).apply {
+ setEvaluator(ArgbEvaluator.getInstance())
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 577c0d8455eb..b7f2cd0da642 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -319,10 +319,18 @@ public class InternetTile extends QSTileImpl<SignalState> {
Log.d(TAG, "setIsAirplaneMode: "
+ "icon = " + (icon == null ? "" : icon.toString()));
}
+ if (mCellularInfo.mAirplaneModeEnabled == icon.visible) {
+ return;
+ }
mCellularInfo.mAirplaneModeEnabled = icon.visible;
mWifiInfo.mAirplaneModeEnabled = icon.visible;
if (!mSignalCallback.mEthernetInfo.mConnected) {
- refreshState(mCellularInfo);
+ if (mWifiInfo.mEnabled && (mWifiInfo.mWifiSignalIconId > 0)
+ && (mWifiInfo.mSsid != null)) {
+ refreshState(mWifiInfo);
+ } else {
+ refreshState(mCellularInfo);
+ }
}
}
@@ -456,6 +464,9 @@ public class InternetTile extends QSTileImpl<SignalState> {
state.dualLabelContentDescription = r.getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
state.expandedAccessibilityClassName = Switch.class.getName();
+ if (DEBUG) {
+ Log.d(TAG, "handleUpdateWifiState: " + "SignalState = " + state.toString());
+ }
}
private void handleUpdateCellularState(SignalState state, Object arg) {
@@ -496,6 +507,9 @@ public class InternetTile extends QSTileImpl<SignalState> {
} else {
state.stateDescription = state.secondaryLabel;
}
+ if (DEBUG) {
+ Log.d(TAG, "handleUpdateCellularState: " + "SignalState = " + state.toString());
+ }
}
private void handleUpdateEthernetState(SignalState state, Object arg) {
@@ -508,6 +522,9 @@ public class InternetTile extends QSTileImpl<SignalState> {
state.state = Tile.STATE_ACTIVE;
state.icon = ResourceIcon.get(cb.mEthernetSignalIconId);
state.secondaryLabel = cb.mEthernetContentDescription;
+ if (DEBUG) {
+ Log.d(TAG, "handleUpdateEthernetState: " + "SignalState = " + state.toString());
+ }
}
private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index fa28754e41a7..30c9b44536e1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -22,7 +22,6 @@ import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
-import android.graphics.Matrix;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
@@ -35,7 +34,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.ScrollCaptureResponse;
import android.view.View;
-import android.view.Window;
import android.widget.ImageView;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -68,8 +66,6 @@ public class LongScreenshotActivity extends Activity {
public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
- private static final boolean USE_SHARED_ELEMENT = false;
-
private final UiEventLogger mUiEventLogger;
private final Executor mUiExecutor;
private final Executor mBackgroundExecutor;
@@ -89,6 +85,7 @@ public class LongScreenshotActivity extends Activity {
private ListenableFuture<File> mCacheSaveFuture;
private ListenableFuture<ImageLoader.Result> mCacheLoadFuture;
+ private Bitmap mOutputBitmap;
private LongScreenshot mLongScreenshot;
private boolean mTransitionStarted;
@@ -114,7 +111,7 @@ public class LongScreenshotActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
super.onCreate(savedInstanceState);
- getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
+
setContentView(R.layout.long_screenshot);
mPreview = requireViewById(R.id.preview);
@@ -173,7 +170,6 @@ public class LongScreenshotActivity extends Activity {
}
}, mUiExecutor);
mCacheLoadFuture = null;
- return;
} else {
LongScreenshot longScreenshot = mLongScreenshotHolder.takeLongScreenshot();
if (longScreenshot != null) {
@@ -189,7 +185,6 @@ public class LongScreenshotActivity extends Activity {
Log.d(TAG, "onLongScreenshotReceived(longScreenshot=" + longScreenshot + ")");
mLongScreenshot = longScreenshot;
mPreview.setImageDrawable(mLongScreenshot.getDrawable());
- mTransitionView.setImageDrawable(mLongScreenshot.getDrawable());
updateImageDimensions();
mCropView.setVisibility(View.VISIBLE);
mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
@@ -310,19 +305,15 @@ public class LongScreenshotActivity extends Activity {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- if (USE_SHARED_ELEMENT) {
- updateImageDimensions();
- mTransitionView.setVisibility(View.VISIBLE);
- // TODO: listen for transition completing instead of finishing onStop
- mTransitionStarted = true;
- startActivity(intent,
- ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
- } else {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- startActivityAsUser(intent, UserHandle.CURRENT);
- finishAndRemoveTask();
- }
+ mTransitionView.setImageBitmap(mOutputBitmap);
+ mTransitionView.setVisibility(View.VISIBLE);
+ mTransitionView.setTransitionName(
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
+ // TODO: listen for transition completing instead of finishing onStop
+ mTransitionStarted = true;
+ startActivity(intent,
+ ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
}
private void doShare(Uri uri) {
@@ -368,9 +359,11 @@ public class LongScreenshotActivity extends Activity {
return;
}
- Bitmap output = renderBitmap(mPreview.getDrawable(), bounds);
+ updateImageDimensions();
+
+ mOutputBitmap = renderBitmap(drawable, bounds);
ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
- mBackgroundExecutor, UUID.randomUUID(), output, ZonedDateTime.now());
+ mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now());
exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor);
}
@@ -419,7 +412,6 @@ public class LongScreenshotActivity extends Activity {
// The image width and height on screen
int imageHeight = previewHeight;
int imageWidth = previewWidth;
- float scale;
if (imageRatio > viewRatio) {
// Image is full width and height is constrained, compute extra padding to inform
// CropView
@@ -428,15 +420,13 @@ public class LongScreenshotActivity extends Activity {
mCropView.setExtraPadding(extraPadding + mPreview.getPaddingTop(),
extraPadding + mPreview.getPaddingBottom());
imageTop += (previewHeight - imageHeight) / 2;
- scale = imageHeight / bounds.height();
mCropView.setExtraPadding(extraPadding, extraPadding);
mCropView.setImageWidth(previewWidth);
} else {
imageWidth = (int) (previewWidth * imageRatio / viewRatio);
imageLeft += (previewWidth - imageWidth) / 2;
- scale = imageWidth / (float) bounds.width();
// Image is full height
- mCropView.setExtraPadding(mPreview.getPaddingTop(), mPreview.getPaddingBottom());
+ mCropView.setExtraPadding(mPreview.getPaddingTop(), mPreview.getPaddingBottom());
mCropView.setImageWidth((int) (previewHeight * imageRatio));
}
@@ -449,10 +439,5 @@ public class LongScreenshotActivity extends Activity {
params.width = boundaries.width();
params.height = boundaries.height();
mTransitionView.setLayoutParams(params);
-
- Matrix matrix = new Matrix();
- matrix.postScale(scale, scale, 0, 0);
- matrix.postTranslate(-boundaries.left, -boundaries.top);
- mTransitionView.setImageMatrix(matrix);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 1ff30a32c4ef..6baacb931a68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -57,6 +57,7 @@ import androidx.annotation.NonNull;
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.GcUtils;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.commandline.CommandRegistry;
@@ -1086,6 +1087,12 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
thr.start();
}
+ @Override
+ public void runGcForTest() {
+ // Gc sysui
+ GcUtils.runGcAndFinalizersSync();
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 7f31fddbfb6c..5437ce63475e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar;
import static com.android.systemui.statusbar.RemoteInputController.processForRemoteInput;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -35,6 +34,7 @@ import android.util.Log;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
import java.util.List;
@@ -46,6 +46,7 @@ import java.util.List;
@SuppressLint("OverrideAbstract")
public class NotificationListener extends NotificationListenerWithPlugins {
private static final String TAG = "NotificationListener";
+ private static final boolean DEBUG = StatusBar.DEBUG;
private final Context mContext;
private final NotificationManager mNotificationManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 70b3a7be02fb..ca81a7b43df6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -169,7 +169,7 @@ public class NotificationRemoteInputManager implements Dumpable {
Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
boolean started = RemoteViews.startPendingIntent(view, pendingIntent, options);
- if (started) releaseNotificationIfKeptForRemoteInputHistory(entry.getKey());
+ if (started) releaseNotificationIfKeptForRemoteInputHistory(entry);
return started;
});
}
@@ -608,7 +608,11 @@ public class NotificationRemoteInputManager implements Dumpable {
* (after unlock, if applicable), and will then wait a short time to allow the app to update the
* notification in response to the action.
*/
- private void releaseNotificationIfKeptForRemoteInputHistory(String key) {
+ private void releaseNotificationIfKeptForRemoteInputHistory(NotificationEntry entry) {
+ if (entry == null) {
+ return;
+ }
+ final String key = entry.getKey();
if (isNotificationKeptForRemoteInputHistory(key)) {
mMainHandler.postDelayed(() -> {
if (isNotificationKeptForRemoteInputHistory(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index eb7854e63a85..491959320ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -61,6 +61,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.DeviceConfigProxy;
@@ -243,11 +244,12 @@ public interface StatusBarDependenciesModule {
SystemClock systemClock,
ActivityStarter activityStarter,
@Main Executor mainExecutor,
- IActivityManager iActivityManager) {
+ IActivityManager iActivityManager,
+ OngoingCallLogger logger) {
OngoingCallController ongoingCallController =
new OngoingCallController(
notifCollection, featureFlags, systemClock, activityStarter, mainExecutor,
- iActivityManager);
+ iActivityManager, logger);
ongoingCallController.init();
return ongoingCallController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 2481ed482872..5f10e557faed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -29,13 +29,11 @@ import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
import com.android.systemui.statusbar.phone.StatusBarMarginUpdatedListener
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
-import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
-import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
-import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import java.lang.IllegalStateException
import java.util.concurrent.Executor
@@ -50,7 +48,7 @@ import javax.inject.Inject
* will have its gravity set towards the corner (i.e., top-right corner gets top|right gravity), and
* the contained ImageView will be set to center_vertical and away from the corner horizontally. The
* Views will match the status bar top padding and status bar height so that the dot can appear to
- * reside directly after the status bar system contents (basically to the right of the battery).
+ * reside directly after the status bar system contents (basically after the battery).
*
* NOTE: any operation that modifies views directly must run on the provided executor, because
* these views are owned by ScreenDecorations and it runs in its own thread
@@ -85,21 +83,27 @@ class PrivacyDotViewController @Inject constructor(
// Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
private var uiExecutor: DelayableExecutor? = null
- private var e: DelayableExecutor? = null
+
+ private val marginListener: StatusBarMarginUpdatedListener =
+ object : StatusBarMarginUpdatedListener {
+ override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) {
+ setStatusBarMargins(marginLeft, marginRight)
+ }
+ }
private val views: Sequence<View>
get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
init {
- locationPublisher.addCallback(object : StatusBarMarginUpdatedListener {
- override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) {
- setStatusBarMargins(marginLeft, marginRight)
- }
- })
+ locationPublisher.addCallback(marginListener)
stateController.addCallback(object : StatusBarStateController.StateListener {
override fun onExpandedChanged(isExpanded: Boolean) {
- setStatusBarExpanded(isExpanded)
+ updateStatusBarState()
+ }
+
+ override fun onStateChanged(newState: Int) {
+ updateStatusBarState()
}
})
}
@@ -108,6 +112,13 @@ class PrivacyDotViewController @Inject constructor(
uiExecutor = e
}
+ fun setQsExpanded(expanded: Boolean) {
+ dlog("setQsExpanded $expanded")
+ synchronized(lock) {
+ nextViewState = nextViewState.copy(qsExpanded = expanded)
+ }
+ }
+
@UiThread
fun setNewRotation(rot: Int) {
dlog("updateRotation: $rot")
@@ -125,8 +136,8 @@ class PrivacyDotViewController @Inject constructor(
val index = newCorner.cornerIndex()
val h = when (rot) {
- ROTATION_NONE, ROTATION_UPSIDE_DOWN -> sbHeightPortrait
- ROTATION_LANDSCAPE, ROTATION_SEASCAPE -> sbHeightLandscape
+ 0, 2 -> sbHeightPortrait
+ 1, 3 -> sbHeightLandscape
else -> 0
}
synchronized(lock) {
@@ -326,15 +337,22 @@ class PrivacyDotViewController @Inject constructor(
}
}
- /**
- * We won't show the dot when quick settings is showing
- */
- private fun setStatusBarExpanded(expanded: Boolean) {
+ private fun updateStatusBarState() {
synchronized(lock) {
- nextViewState = nextViewState.copy(hideDotForQuickSettings = expanded)
+ nextViewState = nextViewState.copy(shadeExpanded = isShadeInQs())
}
}
+ /**
+ * If we are unlocked with an expanded shade, QS is showing. On keyguard, the shade is always
+ * expanded so we use other signals from the panel view controller to know if QS is expanded
+ */
+ @GuardedBy("lock")
+ private fun isShadeInQs(): Boolean {
+ return (stateController.isExpanded && stateController.state == SHADE) ||
+ (stateController.state == SHADE_LOCKED)
+ }
+
private fun scheduleUpdate() {
dlog("scheduleUpdate: ")
@@ -431,13 +449,20 @@ private fun dlog(s: String) {
}
}
+private fun vlog(s: String) {
+ if (DEBUG_VERBOSE) {
+ Log.d(TAG, s)
+ }
+}
+
const val TOP_LEFT = 0
const val TOP_RIGHT = 1
const val BOTTOM_RIGHT = 2
const val BOTTOM_LEFT = 3
private const val DURATION = 160L
private const val TAG = "PrivacyDotViewController"
-private const val DEBUG = false
+private const val DEBUG = true
+private const val DEBUG_VERBOSE = false
private fun Int.toGravity(): Int {
return when (this) {
@@ -460,10 +485,10 @@ private fun Int.innerGravity(): Int {
}
private data class ViewState(
- // don't @ me with names
val systemPrivacyEventIsActive: Boolean = false,
- val hideDotForQuickSettings: Boolean = false,
- val statusBarExpanded: Boolean = false,
+ val shadeExpanded: Boolean = false,
+ val qsExpanded: Boolean = false,
+
val rotation: Int = 0,
val height: Int = 0,
val marginLeft: Int = 0,
@@ -472,7 +497,7 @@ private data class ViewState(
val designatedCorner: View? = null
) {
fun shouldShowDot(): Boolean {
- return systemPrivacyEventIsActive && !hideDotForQuickSettings
+ return systemPrivacyEventIsActive && !shadeExpanded && !qsExpanded
}
fun needsLayout(other: ViewState): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
new file mode 100644
index 000000000000..ce60c859e9bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.lockscreen
+
+import android.app.PendingIntent
+import android.app.smartspace.SmartspaceConfig
+import android.app.smartspace.SmartspaceManager
+import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceTarget
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.view.View
+import android.view.ViewGroup
+import com.android.settingslib.Utils
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.Execution
+import com.android.systemui.util.settings.SecureSettings
+import java.lang.RuntimeException
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Controller for managing the smartspace view on the lockscreen
+ */
+@SysUISingleton
+class LockscreenSmartspaceController @Inject constructor(
+ private val context: Context,
+ private val featureFlags: FeatureFlags,
+ private val smartspaceManager: SmartspaceManager,
+ private val activityStarter: ActivityStarter,
+ private val falsingManager: FalsingManager,
+ private val secureSettings: SecureSettings,
+ private val userTracker: UserTracker,
+ private val contentResolver: ContentResolver,
+ private val configurationController: ConfigurationController,
+ private val statusBarStateController: StatusBarStateController,
+ private val execution: Execution,
+ @Main private val uiExecutor: Executor,
+ @Main private val handler: Handler,
+ optionalPlugin: Optional<BcSmartspaceDataPlugin>
+) {
+ private var session: SmartspaceSession? = null
+ private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
+ private lateinit var smartspaceView: SmartspaceView
+
+ lateinit var view: View
+ private set
+
+ private var showSensitiveContentForCurrentUser = false
+ private var showSensitiveContentForManagedUser = false
+ private var managedUserHandle: UserHandle? = null
+
+ fun isEnabled(): Boolean {
+ execution.assertIsMainThread()
+
+ return featureFlags.isSmartspaceEnabled && plugin != null
+ }
+
+ /**
+ * Constructs the smartspace view and connects it to the smartspace service. Subsequent calls
+ * are idempotent until [disconnect] is called.
+ */
+ fun buildAndConnectView(parent: ViewGroup): View {
+ execution.assertIsMainThread()
+
+ if (!isEnabled()) {
+ throw RuntimeException("Cannot build view when not enabled")
+ }
+
+ buildView(parent)
+ connectSession()
+
+ return view
+ }
+
+ private fun buildView(parent: ViewGroup) {
+ if (plugin == null || this::view.isInitialized) {
+ return
+ }
+
+ val ssView = plugin.getView(parent)
+ ssView.registerDataProvider(plugin)
+ ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
+ override fun startIntent(v: View?, i: Intent?) {
+ activityStarter.startActivity(i, true /* dismissShade */)
+ }
+
+ override fun startPendingIntent(pi: PendingIntent?) {
+ activityStarter.startPendingIntentDismissingKeyguard(pi)
+ }
+ })
+ ssView.setFalsingManager(falsingManager)
+
+ this.smartspaceView = ssView
+ this.view = ssView as View
+
+ updateTextColorFromWallpaper()
+ statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount)
+ }
+
+ private fun connectSession() {
+ if (plugin == null || session != null) {
+ return
+ }
+ val session = smartspaceManager.createSmartspaceSession(
+ SmartspaceConfig.Builder(context, "lockscreen").build())
+ session.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+
+ userTracker.addCallback(userTrackerCallback, uiExecutor)
+ contentResolver.registerContentObserver(
+ secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
+ configurationController.addCallback(configChangeListener)
+ statusBarStateController.addCallback(statusBarStateListener)
+
+ this.session = session
+
+ reloadSmartspace()
+ }
+
+ /**
+ * Disconnects the smartspace view from the smartspace service and cleans up any resources.
+ * Calling [buildAndConnectView] again will cause the same view to be reconnected to the
+ * service.
+ */
+ fun disconnect() {
+ execution.assertIsMainThread()
+
+ if (session == null) {
+ return
+ }
+
+ session?.let {
+ it.removeOnTargetsAvailableListener(sessionListener)
+ it.close()
+ }
+ userTracker.removeCallback(userTrackerCallback)
+ contentResolver.unregisterContentObserver(settingsObserver)
+ configurationController.removeCallback(configChangeListener)
+ statusBarStateController.removeCallback(statusBarStateListener)
+ session = null
+
+ plugin?.onTargetsAvailable(emptyList())
+ }
+
+ fun addListener(listener: SmartspaceTargetListener) {
+ execution.assertIsMainThread()
+ plugin?.registerListener(listener)
+ }
+
+ fun removeListener(listener: SmartspaceTargetListener) {
+ execution.assertIsMainThread()
+ plugin?.unregisterListener(listener)
+ }
+
+ private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
+ execution.assertIsMainThread()
+ val filteredTargets = targets.filter(::filterSmartspaceTarget)
+ plugin?.onTargetsAvailable(filteredTargets)
+ }
+
+ private val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ execution.assertIsMainThread()
+ reloadSmartspace()
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ }
+ }
+
+ private val settingsObserver = object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ execution.assertIsMainThread()
+ reloadSmartspace()
+ }
+ }
+
+ private val configChangeListener = object : ConfigurationController.ConfigurationListener {
+ override fun onThemeChanged() {
+ execution.assertIsMainThread()
+ updateTextColorFromWallpaper()
+ }
+ }
+
+ private val statusBarStateListener = object : StatusBarStateController.StateListener {
+ override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ execution.assertIsMainThread()
+ smartspaceView.setDozeAmount(eased)
+ }
+ }
+
+ private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
+ return when (t.userHandle) {
+ userTracker.userHandle -> {
+ !t.isSensitive || showSensitiveContentForCurrentUser
+ }
+ managedUserHandle -> {
+ // Really, this should be "if this managed profile is associated with the current
+ // active user", but we don't have a good way to check that, so instead we cheat:
+ // Only the primary user can have an associated managed profile, so only show
+ // content for the managed profile if the primary user is active
+ userTracker.userHandle.identifier == UserHandle.USER_SYSTEM &&
+ (!t.isSensitive || showSensitiveContentForManagedUser)
+ }
+ else -> {
+ false
+ }
+ }
+ }
+
+ private fun updateTextColorFromWallpaper() {
+ val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)
+ smartspaceView.setPrimaryTextColor(wallpaperTextColor)
+ }
+
+ private fun reloadSmartspace() {
+ val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
+
+ showSensitiveContentForCurrentUser =
+ secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1
+
+ managedUserHandle = getWorkProfileUser()
+ val managedId = managedUserHandle?.identifier
+ if (managedId != null) {
+ showSensitiveContentForManagedUser =
+ secureSettings.getIntForUser(setting, 0, managedId) == 1
+ }
+
+ session?.requestSmartspaceUpdate()
+ }
+
+ private fun getWorkProfileUser(): UserHandle? {
+ for (userInfo in userTracker.userProfiles) {
+ if (userInfo.isManagedProfile) {
+ return userInfo.userHandle
+ }
+ }
+ return null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index c8c0755344a4..13a8661f94dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -140,7 +140,7 @@ public class NotificationEntryManager implements
private final KeyguardEnvironment mKeyguardEnvironment;
private final NotificationGroupManagerLegacy mGroupManager;
- private final NotificationRankingManager mRankingManager;
+ private final Lazy<NotificationRankingManager> mRankingManager;
private final FeatureFlags mFeatureFlags;
private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
@@ -200,7 +200,7 @@ public class NotificationEntryManager implements
public NotificationEntryManager(
NotificationEntryManagerLogger logger,
NotificationGroupManagerLegacy groupManager,
- NotificationRankingManager rankingManager,
+ Lazy<NotificationRankingManager> rankingManager,
KeyguardEnvironment keyguardEnvironment,
FeatureFlags featureFlags,
Lazy<NotificationRowBinder> notificationRowBinderLazy,
@@ -419,7 +419,7 @@ public class NotificationEntryManager implements
mActiveNotifications.put(entry.getKey(), entry);
mGroupManager.onEntryAdded(entry);
- updateRankingAndSort(mRankingManager.getRankingMap(), "addEntryInternalInternal");
+ updateRankingAndSort(mRankingManager.get().getRankingMap(), "addEntryInternalInternal");
}
/**
@@ -886,13 +886,13 @@ public class NotificationEntryManager implements
/** Resorts / filters the current notification set with the current RankingMap */
public void reapplyFilterAndSort(String reason) {
- updateRankingAndSort(mRankingManager.getRankingMap(), reason);
+ updateRankingAndSort(mRankingManager.get().getRankingMap(), reason);
}
/** Calls to NotificationRankingManager and updates mSortedAndFiltered */
private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
mSortedAndFiltered.clear();
- mSortedAndFiltered.addAll(mRankingManager.updateRanking(
+ mSortedAndFiltered.addAll(mRankingManager.get().updateRanking(
rankingMap, mActiveNotifications.values(), reason));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index d6356de5ea51..f40f24a935c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar.notification.collection.legacy;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
@@ -31,6 +33,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.wm.shell.bubbles.Bubbles;
@@ -39,10 +42,12 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.TreeSet;
import javax.inject.Inject;
@@ -58,13 +63,21 @@ import dagger.Lazy;
public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, StateListener,
GroupMembershipManager, GroupExpansionManager, Dumpable {
- private static final String TAG = "NotificationGroupManager";
+ private static final String TAG = "NotifGroupManager";
+ private static final boolean DEBUG = StatusBar.DEBUG;
+ private static final boolean SPEW = StatusBar.SPEW;
+ /**
+ * The maximum amount of time (in ms) between the posting of notifications that can be
+ * considered part of the same update batch.
+ */
+ private static final long POST_BATCH_MAX_AGE = 5000;
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners =
new ArraySet<>();
private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
private final Optional<Bubbles> mBubblesOptional;
+ private final EventBuffer mEventBuffer = new EventBuffer();
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
@@ -134,8 +147,14 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
* When we want to remove an entry from being tracked for grouping
*/
public void onEntryRemoved(NotificationEntry removed) {
+ if (SPEW) {
+ Log.d(TAG, "onEntryRemoved: entry=" + removed);
+ }
onEntryRemovedInternal(removed, removed.getSbn());
- mIsolatedEntries.remove(removed.getKey());
+ StatusBarNotification oldSbn = mIsolatedEntries.remove(removed.getKey());
+ if (oldSbn != null) {
+ updateSuppression(mGroupMap.get(oldSbn.getGroupKey()));
+ }
}
/**
@@ -162,6 +181,9 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
// the close future. See b/23676310 for reference.
return;
}
+ if (SPEW) {
+ Log.d(TAG, "onEntryRemovedInternal: entry=" + removed + " group=" + group.groupKey);
+ }
if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
group.children.remove(removed.getKey());
} else {
@@ -182,6 +204,9 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
* Notify the group manager that a new entry was added
*/
public void onEntryAdded(final NotificationEntry added) {
+ if (SPEW) {
+ Log.d(TAG, "onEntryAdded: entry=" + added);
+ }
updateIsolation(added);
onEntryAddedInternal(added);
}
@@ -195,13 +220,16 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
String groupKey = getGroupKey(sbn);
NotificationGroup group = mGroupMap.get(groupKey);
if (group == null) {
- group = new NotificationGroup();
+ group = new NotificationGroup(groupKey);
mGroupMap.put(groupKey, group);
for (OnGroupChangeListener listener : mGroupChangeListeners) {
listener.onGroupCreated(group, groupKey);
}
}
+ if (SPEW) {
+ Log.d(TAG, "onEntryAddedInternal: entry=" + added + " group=" + group.groupKey);
+ }
if (isGroupChild) {
NotificationEntry existing = group.children.get(added.getKey());
if (existing != null && existing != added) {
@@ -213,9 +241,11 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
+ " added removed" + added.isRowRemoved(), new Throwable());
}
group.children.put(added.getKey(), added);
+ addToPostBatchHistory(group, added);
updateSuppression(group);
} else {
group.summary = added;
+ addToPostBatchHistory(group, added);
group.expanded = added.areChildrenExpanded();
updateSuppression(group);
if (!group.children.isEmpty()) {
@@ -231,6 +261,27 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
}
}
+ private void addToPostBatchHistory(NotificationGroup group, @Nullable NotificationEntry entry) {
+ if (entry == null) {
+ return;
+ }
+ boolean didAdd = group.postBatchHistory.add(new PostRecord(entry));
+ if (didAdd) {
+ trimPostBatchHistory(group.postBatchHistory);
+ }
+ }
+
+ /** remove all history that's too old to be in the batch. */
+ private void trimPostBatchHistory(@NonNull TreeSet<PostRecord> postBatchHistory) {
+ if (postBatchHistory.size() <= 1) {
+ return;
+ }
+ long batchStartTime = postBatchHistory.last().postTime - POST_BATCH_MAX_AGE;
+ while (!postBatchHistory.isEmpty() && postBatchHistory.first().postTime < batchStartTime) {
+ postBatchHistory.pollFirst();
+ }
+ }
+
private void onEntryBecomingChild(NotificationEntry entry) {
updateIsolation(entry);
}
@@ -239,6 +290,9 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
if (group == null) {
return;
}
+ NotificationEntry prevAlertOverride = group.alertOverride;
+ group.alertOverride = getPriorityConversationAlertOverride(group);
+
int childCount = 0;
boolean hasBubbles = false;
for (NotificationEntry entry : group.children.values()) {
@@ -255,18 +309,150 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
group.suppressed = group.summary != null && !group.expanded
&& (childCount == 1
|| (childCount == 0
- && group.summary.getSbn().getNotification().isGroupSummary()
- && (hasIsolatedChildren(group) || hasBubbles)));
- if (prevSuppressed != group.suppressed) {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- if (!mIsUpdatingUnchangedGroup) {
- listener.onGroupSuppressionChanged(group, group.suppressed);
- listener.onGroupsChanged();
+ && group.summary.getSbn().getNotification().isGroupSummary()
+ && (hasIsolatedChildren(group) || hasBubbles)));
+
+ boolean alertOverrideChanged = prevAlertOverride != group.alertOverride;
+ boolean suppressionChanged = prevSuppressed != group.suppressed;
+ if (alertOverrideChanged || suppressionChanged) {
+ if (DEBUG && alertOverrideChanged) {
+ Log.d(TAG, "updateSuppression: alertOverride was=" + prevAlertOverride
+ + " now=" + group.alertOverride + " group:\n" + group);
+ }
+ if (DEBUG && suppressionChanged) {
+ Log.d(TAG,
+ "updateSuppression: suppressed changed to " + group.suppressed
+ + " group:\n" + group);
+ }
+ if (!mIsUpdatingUnchangedGroup) {
+ if (alertOverrideChanged) {
+ mEventBuffer.notifyAlertOverrideChanged(group, prevAlertOverride);
+ }
+ if (suppressionChanged) {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupSuppressionChanged(group, group.suppressed);
+ }
+ }
+ mEventBuffer.notifyGroupsChanged();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, group + " did not notify listeners of above change(s)");
}
}
}
}
+ /**
+ * Finds the isolated logical child of this group which is should be alerted instead.
+ *
+ * Notifications from priority conversations are isolated from their groups to make them more
+ * prominent, however apps may post these with a GroupAlertBehavior that has the group receiving
+ * the alert. This would lead to the group alerting even though the conversation that was
+ * updated was not actually a part of that group. This method finds the best priority
+ * conversation in this situation, if there is one, so they can be set as the alertOverride of
+ * the group.
+ *
+ * @param group the group to check
+ * @return the entry which should receive the alert instead of the group, if any.
+ */
+ @Nullable
+ private NotificationEntry getPriorityConversationAlertOverride(NotificationGroup group) {
+ // GOAL: if there is a priority child which wouldn't alert based on its groupAlertBehavior,
+ // but which should be alerting (because priority conversations are isolated), find it.
+ if (group == null || group.summary == null) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary");
+ }
+ return null;
+ }
+ if (isIsolated(group.summary.getKey())) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: isolated group");
+ }
+ return null;
+ }
+
+ // Precondiions:
+ // * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
+ // * Only necessary when at least one notification in the group is on a priority channel
+ if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
+ != Notification.GROUP_ALERT_SUMMARY) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: summary != GROUP_ALERT_SUMMARY");
+ }
+ return null;
+ }
+
+ // Get the important children first, copy the keys for the final importance check,
+ // then add the non-isolated children to the map for unified lookup.
+ HashMap<String, NotificationEntry> children = getImportantConversations(group);
+ if (children == null || children.isEmpty()) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations");
+ }
+ return null;
+ }
+ HashSet<String> importantChildKeys = new HashSet<>(children.keySet());
+ children.putAll(group.children);
+
+ // Ensure all children have GROUP_ALERT_SUMMARY
+ for (NotificationEntry child : children.values()) {
+ if (child.getSbn().getNotification().getGroupAlertBehavior()
+ != Notification.GROUP_ALERT_SUMMARY) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: "
+ + "child != GROUP_ALERT_SUMMARY");
+ }
+ return null;
+ }
+ }
+
+ // Create a merged post history from all the children
+ TreeSet<PostRecord> combinedHistory = new TreeSet<>(group.postBatchHistory);
+ for (String importantChildKey : importantChildKeys) {
+ NotificationGroup importantChildGroup = mGroupMap.get(importantChildKey);
+ combinedHistory.addAll(importantChildGroup.postBatchHistory);
+ }
+ trimPostBatchHistory(combinedHistory);
+
+ // This is a streamlined implementation of the following idea:
+ // * From the subset of notifications in the latest 'batch' of updates. A batch is:
+ // * Notifs posted less than POST_BATCH_MAX_AGE before the most recently posted.
+ // * Only including notifs newer than the second-to-last post of any notification.
+ // * Find the newest child in the batch -- the with the largest 'when' value.
+ // * If the newest child is a priority conversation, set that as the override.
+ HashSet<String> batchKeys = new HashSet<>();
+ long newestChildWhen = -1;
+ NotificationEntry newestChild = null;
+ // Iterate backwards through the post history, tracking the child with the smallest sort key
+ for (PostRecord record : combinedHistory.descendingSet()) {
+ if (batchKeys.contains(record.key)) {
+ // Once you see a notification again, the batch has ended
+ break;
+ }
+ batchKeys.add(record.key);
+ NotificationEntry child = children.get(record.key);
+ if (child != null) {
+ long childWhen = child.getSbn().getNotification().when;
+ if (newestChild == null || childWhen > newestChildWhen) {
+ newestChildWhen = childWhen;
+ newestChild = child;
+ }
+ }
+ }
+ if (newestChild != null && importantChildKeys.contains(newestChild.getKey())) {
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: result=" + newestChild);
+ }
+ return newestChild;
+ }
+ if (SPEW) {
+ Log.d(TAG, "getPriorityConversationAlertOverride: result=null, newestChild="
+ + newestChild);
+ }
+ return null;
+ }
+
private boolean hasIsolatedChildren(NotificationGroup group) {
return getNumberOfIsolatedChildren(group.summary.getSbn().getGroupKey()) != 0;
}
@@ -281,12 +467,33 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
return count;
}
+ @Nullable
+ private HashMap<String, NotificationEntry> getImportantConversations(NotificationGroup group) {
+ String groupKey = group.summary.getSbn().getGroupKey();
+ HashMap<String, NotificationEntry> result = null;
+ for (StatusBarNotification sbn : mIsolatedEntries.values()) {
+ if (sbn.getGroupKey().equals(groupKey)) {
+ NotificationEntry entry = mGroupMap.get(sbn.getKey()).summary;
+ if (isImportantConversation(entry)) {
+ if (result == null) {
+ result = new HashMap<>();
+ }
+ result.put(sbn.getKey(), entry);
+ }
+ }
+ }
+ return result;
+ }
+
/**
* Update an entry's group information
* @param entry notification entry to update
* @param oldNotification previous notification info before this update
*/
public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
+ if (SPEW) {
+ Log.d(TAG, "onEntryUpdated: entry=" + entry);
+ }
onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
oldNotification.getNotification().isGroupSummary());
}
@@ -325,7 +532,17 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
* Whether the given notification is the summary of a group that is being suppressed
*/
public boolean isSummaryOfSuppressedGroup(StatusBarNotification sbn) {
- return isGroupSuppressed(getGroupKey(sbn)) && sbn.getNotification().isGroupSummary();
+ return sbn.getNotification().isGroupSummary() && isGroupSuppressed(getGroupKey(sbn));
+ }
+
+ /**
+ * If the given notification is a summary, get the group for it.
+ */
+ public NotificationGroup getGroupForSummary(StatusBarNotification sbn) {
+ if (sbn.getNotification().isGroupSummary()) {
+ return mGroupMap.get(getGroupKey(sbn));
+ }
+ return null;
}
private boolean isOnlyChild(StatusBarNotification sbn) {
@@ -545,9 +762,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
return false;
}
- int peopleNotificationType =
- mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
- if (peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON) {
+ if (isImportantConversation(entry)) {
return true;
}
if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) {
@@ -560,18 +775,25 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
|| isGroupNotFullyVisible(notificationGroup));
}
+ private boolean isImportantConversation(NotificationEntry entry) {
+ int peopleNotificationType =
+ mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
+ return peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON;
+ }
+
/**
* Isolate a notification from its group so that it visually shows as its own group.
*
* @param entry the notification to isolate
*/
private void isolateNotification(NotificationEntry entry) {
- StatusBarNotification sbn = entry.getSbn();
-
+ if (SPEW) {
+ Log.d(TAG, "isolateNotification: entry=" + entry);
+ }
// We will be isolated now, so lets update the groups
onEntryRemovedInternal(entry, entry.getSbn());
- mIsolatedEntries.put(sbn.getKey(), sbn);
+ mIsolatedEntries.put(entry.getKey(), entry.getSbn());
onEntryAddedInternal(entry);
// We also need to update the suppression of the old group, because this call comes
@@ -588,6 +810,14 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
* Update the isolation of an entry, splitting it from the group.
*/
public void updateIsolation(NotificationEntry entry) {
+ // We need to buffer a few events because we do isolation changes in 3 steps:
+ // removeInternal, update mIsolatedEntries, addInternal. This means that often the
+ // alertOverride will update on the removal, however processing the event in that case can
+ // cause problems because the mIsolatedEntries map is not in its final state, so the event
+ // listener may be unable to correctly determine the true state of the group. By delaying
+ // the alertOverride change until after the add phase, we can ensure that listeners only
+ // have to handle a consistent state.
+ mEventBuffer.startBuffering();
boolean isIsolated = isIsolated(entry.getSbn().getKey());
if (shouldIsolate(entry)) {
if (!isIsolated) {
@@ -596,6 +826,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
} else if (isIsolated) {
stopIsolatingNotification(entry);
}
+ mEventBuffer.flushAndStopBuffering();
}
/**
@@ -604,15 +835,15 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
* @param entry the notification to un-isolate
*/
private void stopIsolatingNotification(NotificationEntry entry) {
- StatusBarNotification sbn = entry.getSbn();
- if (isIsolated(sbn.getKey())) {
- // not isolated anymore, we need to update the groups
- onEntryRemovedInternal(entry, entry.getSbn());
- mIsolatedEntries.remove(sbn.getKey());
- onEntryAddedInternal(entry);
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupsChanged();
- }
+ if (SPEW) {
+ Log.d(TAG, "stopIsolatingNotification: entry=" + entry);
+ }
+ // not isolated anymore, we need to update the groups
+ onEntryRemovedInternal(entry, entry.getSbn());
+ mIsolatedEntries.remove(entry.getKey());
+ onEntryAddedInternal(entry);
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupsChanged();
}
}
@@ -648,33 +879,154 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
}
/**
+ * A record of a notification being posted, containing the time of the post and the key of the
+ * notification entry. These are stored in a TreeSet by the NotificationGroup and used to
+ * calculate a batch of notifications.
+ */
+ public static class PostRecord implements Comparable<PostRecord> {
+ public final long postTime;
+ public final String key;
+
+ /** constructs a record containing the post time and key from the notification entry */
+ public PostRecord(@NonNull NotificationEntry entry) {
+ this.postTime = entry.getSbn().getPostTime();
+ this.key = entry.getKey();
+ }
+
+ @Override
+ public int compareTo(PostRecord o) {
+ int postTimeComparison = Long.compare(this.postTime, o.postTime);
+ return postTimeComparison == 0
+ ? String.CASE_INSENSITIVE_ORDER.compare(this.key, o.key)
+ : postTimeComparison;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PostRecord that = (PostRecord) o;
+ return postTime == that.postTime && key.equals(that.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(postTime, key);
+ }
+ }
+
+ /**
* Represents a notification group in the notification shade.
*/
public static class NotificationGroup {
+ public final String groupKey;
public final HashMap<String, NotificationEntry> children = new HashMap<>();
+ public final TreeSet<PostRecord> postBatchHistory = new TreeSet<>();
public NotificationEntry summary;
public boolean expanded;
/**
* Is this notification group suppressed, i.e its summary is hidden
*/
public boolean suppressed;
+ /**
+ * The child (which is isolated from this group) to which the alert should be transferred,
+ * due to priority conversations.
+ */
+ public NotificationEntry alertOverride;
+
+ NotificationGroup(String groupKey) {
+ this.groupKey = groupKey;
+ }
@Override
public String toString() {
- String result = " summary:\n "
- + (summary != null ? summary.getSbn() : "null")
- + (summary != null && summary.getDebugThrowable() != null
- ? Log.getStackTraceString(summary.getDebugThrowable())
- : "");
- result += "\n children size: " + children.size();
+ StringBuilder sb = new StringBuilder();
+ sb.append(" groupKey: ").append(groupKey);
+ sb.append("\n summary:");
+ appendEntry(sb, summary);
+ sb.append("\n children size: ").append(children.size());
for (NotificationEntry child : children.values()) {
- result += "\n " + child.getSbn()
- + (child.getDebugThrowable() != null
- ? Log.getStackTraceString(child.getDebugThrowable())
- : "");
+ appendEntry(sb, child);
+ }
+ sb.append("\n alertOverride:");
+ appendEntry(sb, alertOverride);
+ sb.append("\n summary suppressed: ").append(suppressed);
+ return sb.toString();
+ }
+
+ private void appendEntry(StringBuilder sb, NotificationEntry entry) {
+ sb.append("\n ").append(entry != null ? entry.getSbn() : "null");
+ if (entry != null && entry.getDebugThrowable() != null) {
+ sb.append(Log.getStackTraceString(entry.getDebugThrowable()));
+ }
+ }
+ }
+
+ /**
+ * This class is a toggleable buffer for a subset of events of {@link OnGroupChangeListener}.
+ * When buffering, instead of notifying the listeners it will set internal state that will allow
+ * it to notify listeners of those events later
+ */
+ private class EventBuffer {
+ private final HashMap<String, NotificationEntry> mOldAlertOverrideByGroup = new HashMap<>();
+ private boolean mIsBuffering = false;
+ private boolean mDidGroupsChange = false;
+
+ void notifyAlertOverrideChanged(NotificationGroup group,
+ NotificationEntry oldAlertOverride) {
+ if (mIsBuffering) {
+ // The value in this map is the override before the event. If there is an entry
+ // already in the map, then we are effectively coalescing two events, which means
+ // we need to preserve the original initial value.
+ mOldAlertOverrideByGroup.putIfAbsent(group.groupKey, oldAlertOverride);
+ } else {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupAlertOverrideChanged(group, oldAlertOverride,
+ group.alertOverride);
+ }
+ }
+ }
+
+ void notifyGroupsChanged() {
+ if (mIsBuffering) {
+ mDidGroupsChange = true;
+ } else {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupsChanged();
+ }
+ }
+ }
+
+ void startBuffering() {
+ mIsBuffering = true;
+ }
+
+ void flushAndStopBuffering() {
+ // stop buffering so that we can call our own helpers
+ mIsBuffering = false;
+ // alert all group alert override changes for groups that were not removed
+ for (Map.Entry<String, NotificationEntry> entry : mOldAlertOverrideByGroup.entrySet()) {
+ NotificationGroup group = mGroupMap.get(entry.getKey());
+ if (group == null) {
+ // The group can be null if this alertOverride changed before the group was
+ // permanently removed, meaning that there's no guarantee that listeners will
+ // that field clear.
+ continue;
+ }
+ NotificationEntry oldAlertOverride = entry.getValue();
+ if (group.alertOverride == oldAlertOverride) {
+ // If the final alertOverride equals the initial, it means we coalesced two
+ // events which undid the change, so we can drop it entirely.
+ continue;
+ }
+ notifyAlertOverrideChanged(group, oldAlertOverride);
+ }
+ mOldAlertOverrideByGroup.clear();
+ // alert that groups changed
+ if (mDidGroupsChange) {
+ notifyGroupsChanged();
+ mDidGroupsChange = false;
}
- result += "\n summary suppressed: " + suppressed;
- return result;
}
}
@@ -714,6 +1066,18 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
boolean suppressed) {}
/**
+ * The alert override of a group has changed.
+ *
+ * @param group the group that has changed
+ * @param oldAlertOverride the previous notification to which the group's alerts were sent
+ * @param newAlertOverride the notification to which the group's alerts should now be sent
+ */
+ default void onGroupAlertOverrideChanged(
+ NotificationGroup group,
+ @Nullable NotificationEntry oldAlertOverride,
+ @Nullable NotificationEntry newAlertOverride) {}
+
+ /**
* A group of children just received a summary notification and should therefore become
* children of it.
*
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
index e2a37f647bf5..89bb65278dce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -80,8 +80,6 @@ import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional;
import java.util.concurrent.Executor;
-import javax.inject.Provider;
-
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
@@ -102,7 +100,7 @@ public interface NotificationsModule {
static NotificationEntryManager provideNotificationEntryManager(
NotificationEntryManagerLogger logger,
NotificationGroupManagerLegacy groupManager,
- NotificationRankingManager rankingManager,
+ Lazy<NotificationRankingManager> rankingManager,
NotificationEntryManager.KeyguardEnvironment keyguardEnvironment,
FeatureFlags featureFlags,
Lazy<NotificationRowBinder> notificationRowBinderLazy,
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 120f9732f555..3b64d48f4df9 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
@@ -664,7 +664,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mDebugPaint.setColor(Color.CYAN);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
- y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
+ y = (int) (mAmbientState.getStackY() + mSidePaddings + mAmbientState.getStackHeight());
mDebugPaint.setColor(Color.BLUE);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
}
@@ -1148,12 +1148,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mOnStackYChanged != null) {
mOnStackYChanged.run();
}
-
- final float stackEndHeight = getHeight() - getEmptyBottomMargin() - mTopPadding;
- mAmbientState.setStackEndHeight(stackEndHeight);
- mAmbientState.setStackHeight(
- MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION,
- stackEndHeight, fraction));
+ if (mQsExpansionFraction <= 0) {
+ final float stackEndHeight = Math.max(0f,
+ getHeight() - getEmptyBottomMargin() - stackY - mSidePaddings);
+ mAmbientState.setStackEndHeight(stackEndHeight);
+ mAmbientState.setStackHeight(
+ MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION,
+ stackEndHeight, fraction));
+ }
}
void setOnStackYChanged(Runnable onStackYChanged) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index d94d030f326e..b2d39a952fe2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -58,6 +58,7 @@ public class StackScrollAlgorithm {
private int mStatusBarHeight;
private float mHeadsUpInset;
private int mPinnedZTranslationExtra;
+ private float mNotificationScrimPadding;
public StackScrollAlgorithm(
Context context,
@@ -82,6 +83,7 @@ public class StackScrollAlgorithm {
mPinnedZTranslationExtra = res.getDimensionPixelSize(
R.dimen.heads_up_pinned_elevation);
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
+ mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
}
/**
@@ -258,6 +260,9 @@ public class StackScrollAlgorithm {
// expanded. Consider updating these states in updateContentView instead so that we don't
// have to recalculate in every frame.
float currentY = -scrollY;
+ if (!ambientState.isOnKeyguard()) {
+ currentY += mNotificationScrimPadding;
+ }
float previousY = 0;
state.firstViewInShelf = null;
state.viewHeightBeforeShelf = -1;
@@ -318,6 +323,9 @@ public class StackScrollAlgorithm {
AmbientState ambientState) {
// The y coordinate of the current child.
float currentYPosition = -algorithmState.scrollY;
+ if (!ambientState.isOnKeyguard()) {
+ currentYPosition += mNotificationScrimPadding;
+ }
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 2b51b56062bb..c5a155efdc86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -23,6 +23,7 @@ import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
@@ -325,11 +326,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
// Show the ongoing call chip only if there is an ongoing call *and* notification icons
// are allowed. (The ongoing call chip occupies the same area as the notification icons,
// so if the icons are disabled then the call chip should be, too.)
- if (hasOngoingCall && !disableNotifications) {
+ boolean showOngoingCallChip = hasOngoingCall && !disableNotifications;
+ if (showOngoingCallChip) {
showOngoingCallChip(animate);
} else {
hideOngoingCallChip(animate);
}
+ mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip);
}
private boolean shouldHideNotificationIcons() {
@@ -348,7 +351,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private void showSystemIconArea(boolean animate) {
// Only show the system icon area if we are not currently animating
- if (mAnimationScheduler.getAnimationState() == IDLE) {
+ int state = mAnimationScheduler.getAnimationState();
+ if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
animateShow(mSystemIconArea, animate);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 0bcdcb96e39a..95098bd1151b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -936,7 +936,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
GetWalletCardsRequest request =
new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */,
- 1 /* iconSizePx */, 2 /* maxCards */);
+ 1 /* iconSizePx */, 1 /* maxCards */);
mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 3181f520dca2..9787a9446019 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -22,12 +22,12 @@ import android.app.Notification;
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.Log;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -41,17 +41,21 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
* A helper class dealing with the alert interactions between {@link NotificationGroupManagerLegacy}
* and {@link HeadsUpManager}. In particular, this class deals with keeping
- * the correct notification in a group alerting based off the group suppression.
+ * the correct notification in a group alerting based off the group suppression and alertOverride.
*/
public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
StateListener {
private static final long ALERT_TRANSFER_TIMEOUT = 300;
+ private static final String TAG = "NotifGroupAlertTransfer";
+ private static final boolean DEBUG = StatusBar.DEBUG;
+ private static final boolean SPEW = StatusBar.SPEW;
/**
* The list of entries containing group alert metadata for each group. Keyed by group key.
@@ -142,41 +146,98 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
@Override
public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
- if (suppressed) {
- if (mHeadsUpManager.isAlerting(group.summary.getKey())) {
- handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
+ if (DEBUG) {
+ Log.d(TAG, "!! onGroupSuppressionChanged: group.summary=" + group.summary
+ + " suppressed=" + suppressed);
+ }
+ NotificationEntry oldAlertOverride = group.alertOverride;
+ onGroupChanged(group, oldAlertOverride);
+ }
+
+ @Override
+ public void onGroupAlertOverrideChanged(NotificationGroup group,
+ @Nullable NotificationEntry oldAlertOverride,
+ @Nullable NotificationEntry newAlertOverride) {
+ if (DEBUG) {
+ Log.d(TAG, "!! onGroupAlertOverrideChanged: group.summary=" + group.summary
+ + " oldAlertOverride=" + oldAlertOverride
+ + " newAlertOverride=" + newAlertOverride);
+ }
+ onGroupChanged(group, oldAlertOverride);
+ }
+ };
+
+ /**
+ * Called when either the suppressed or alertOverride fields of the group changed
+ *
+ * @param group the group which changed
+ * @param oldAlertOverride the previous value of group.alertOverride
+ */
+ private void onGroupChanged(NotificationGroup group,
+ NotificationEntry oldAlertOverride) {
+ // Group summary can be null if we are no longer suppressed because the summary was
+ // removed. In that case, we don't need to alert the summary.
+ if (group.summary == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onGroupChanged: summary is null");
+ }
+ return;
+ }
+ if (group.suppressed || group.alertOverride != null) {
+ checkForForwardAlertTransfer(group.summary, oldAlertOverride);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "onGroupChanged: maybe transfer back");
+ }
+ GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
+ group.summary.getSbn()));
+ // Group is no longer suppressed or overridden.
+ // We should check if we need to transfer the alert back to the summary.
+ if (groupAlertEntry.mAlertSummaryOnNextAddition) {
+ if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
+ alertNotificationWhenPossible(group.summary);
}
+ groupAlertEntry.mAlertSummaryOnNextAddition = false;
} else {
- // Group summary can be null if we are no longer suppressed because the summary was
- // removed. In that case, we don't need to alert the summary.
- if (group.summary == null) {
- return;
- }
- GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
- group.summary.getSbn()));
- // Group is no longer suppressed. We should check if we need to transfer the alert
- // back to the summary now that it's no longer suppressed.
- if (groupAlertEntry.mAlertSummaryOnNextAddition) {
- if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
- alertNotificationWhenPossible(group.summary, mHeadsUpManager);
- }
- groupAlertEntry.mAlertSummaryOnNextAddition = false;
- } else {
- checkShouldTransferBack(groupAlertEntry);
- }
+ checkShouldTransferBack(groupAlertEntry);
}
}
- };
+ }
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+ if (DEBUG) {
+ Log.d(TAG, "!! onHeadsUpStateChanged: entry=" + entry + " isHeadsUp=" + isHeadsUp);
+ }
+ if (isHeadsUp && entry.getSbn().getNotification().isGroupSummary()) {
+ // a group summary is alerting; trigger the forward transfer checks
+ checkForForwardAlertTransfer(entry, /* oldAlertOverride */ null);
+ }
}
- private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
- AlertingNotificationManager alertManager) {
- if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.getSbn())) {
- handleSuppressedSummaryAlerted(entry, alertManager);
+ /**
+ * Handles changes in a group's suppression or alertOverride, but where at least one of those
+ * conditions is still true (either the group is suppressed, the group has an alertOverride,
+ * or both). The method determined which kind of child needs to receive the alert, finds the
+ * entry currently alerting, and makes the transfer.
+ *
+ * Internally, this is handled with two main cases: the override needs the alert, or there is
+ * no override but the summary is suppressed (so an isolated child needs the alert).
+ *
+ * @param summary the notification entry of the summary of the logical group.
+ * @param oldAlertOverride the former value of group.alertOverride, before whatever event
+ * required us to check for for a transfer condition.
+ */
+ private void checkForForwardAlertTransfer(NotificationEntry summary,
+ NotificationEntry oldAlertOverride) {
+ if (DEBUG) {
+ Log.d(TAG, "checkForForwardAlertTransfer: enter");
+ }
+ NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
+ if (group != null && group.alertOverride != null) {
+ handleOverriddenSummaryAlerted(summary);
+ } else if (mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())) {
+ handleSuppressedSummaryAlerted(summary, oldAlertOverride);
}
}
@@ -186,9 +247,16 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
// see as early as we can if we need to abort a transfer.
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
+ if (DEBUG) {
+ Log.d(TAG, "!! onPendingEntryAdded: entry=" + entry);
+ }
String groupKey = mGroupManager.getGroupKey(entry.getSbn());
GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
- if (groupAlertEntry != null) {
+ if (groupAlertEntry != null && groupAlertEntry.mGroup.alertOverride == null) {
+ // new pending group entries require us to transfer back from the child to the
+ // group, but alertOverrides are only present in very limited circumstances, so
+ // while it's possible the group should ALSO alert, the previous detection which set
+ // this alertOverride won't be invalidated by this notification added to this group.
checkShouldTransferBack(groupAlertEntry);
}
}
@@ -262,43 +330,128 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
}
/**
- * Handles the scenario where a summary that has been suppressed is alerted. A suppressed
+ * Handles the scenario where a summary that has been suppressed is itself, or has a former
+ * alertOverride (in the form of an isolated logical child) which was alerted. A suppressed
* summary should for all intents and purposes be invisible to the user and as a result should
* not alert. When this is the case, it is our responsibility to pass the alert to the
* appropriate child which will be the representative notification alerting for the group.
*
- * @param summary the summary that is suppressed and alerting
- * @param alertManager the alert manager that manages the alerting summary
+ * @param summary the summary that is suppressed and (potentially) alerting
+ * @param oldAlertOverride the alertOverride before whatever event triggered this method. If
+ * the alert override was removed, this will be the entry that should
+ * be transferred back from.
*/
private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
- @NonNull AlertingNotificationManager alertManager) {
- StatusBarNotification sbn = summary.getSbn();
+ NotificationEntry oldAlertOverride) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + summary);
+ }
GroupAlertEntry groupAlertEntry =
- mGroupAlertEntries.get(mGroupManager.getGroupKey(sbn));
+ mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
+
if (!mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())
- || !alertManager.isAlerting(sbn.getKey())
|| groupAlertEntry == null) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: invalid state");
+ }
+ return;
+ }
+ boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
+ boolean priorityIsAlerting = oldAlertOverride != null
+ && mHeadsUpManager.isAlerting(oldAlertOverride.getKey());
+ if (!summaryIsAlerting && !priorityIsAlerting) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: no summary or override alerting");
+ }
return;
}
if (pendingInflationsWillAddChildren(groupAlertEntry.mGroup)) {
// New children will actually be added to this group, let's not transfer the alert.
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: pending inflations");
+ }
return;
}
NotificationEntry child =
mGroupManager.getLogicalChildren(summary.getSbn()).iterator().next();
- if (child != null) {
- if (child.getRow().keepInParent()
- || child.isRowRemoved()
- || child.isRowDismissed()) {
- // The notification is actually already removed. No need to alert it.
- return;
+ if (summaryIsAlerting) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: transfer summary -> child");
}
- if (!alertManager.isAlerting(child.getKey()) && onlySummaryAlerts(summary)) {
- groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
+ tryTransferAlertState(summary, /*from*/ summary, /*to*/ child, groupAlertEntry);
+ return;
+ }
+ // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
+ // it's not too late to transfer back, then transfer the alert from the oldAlertOverride to
+ // the isolated child which should receive the alert.
+ if (!canStillTransferBack(groupAlertEntry)) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: transfer from override: too late");
+ }
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "handleSuppressedSummaryAlerted: transfer override -> child");
+ }
+ tryTransferAlertState(summary, /*from*/ oldAlertOverride, /*to*/ child, groupAlertEntry);
+ }
+
+ /**
+ * Checks for and handles the scenario where the given entry is the summary of a group which
+ * has an alertOverride, and either the summary itself or one of its logical isolated children
+ * is currently alerting (which happens if the summary is suppressed).
+ */
+ private void handleOverriddenSummaryAlerted(NotificationEntry summary) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + summary);
+ }
+ GroupAlertEntry groupAlertEntry =
+ mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
+ NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
+ if (group == null || group.alertOverride == null || groupAlertEntry == null) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: invalid state");
+ }
+ return;
+ }
+ boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
+ if (summaryIsAlerting) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: transfer summary -> override");
+ }
+ tryTransferAlertState(summary, /*from*/ summary, group.alertOverride, groupAlertEntry);
+ return;
+ }
+ // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
+ // it's not too late to transfer back, then remove the alert from any of the logical
+ // children, and if one of them was alerting, we can alert the override.
+ if (!canStillTransferBack(groupAlertEntry)) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: transfer from child: too late");
+ }
+ return;
+ }
+ List<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.getSbn());
+ if (children == null) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: no children");
+ }
+ return;
+ }
+ children.remove(group.alertOverride); // do not release the alert on our desired destination
+ boolean releasedChild = releaseChildAlerts(children);
+ if (releasedChild) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: transfer child -> override");
+ }
+ tryTransferAlertState(summary, /*from*/ null, group.alertOverride, groupAlertEntry);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "handleOverriddenSummaryAlerted: no child alert released");
}
- transferAlertState(summary, child, alertManager);
}
}
@@ -307,14 +460,37 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
* immediately to have the incorrect one up as short as possible. The second should alert
* when possible.
*
+ * @param summary entry of the summary
* @param fromEntry entry to transfer alert from
* @param toEntry entry to transfer to
- * @param alertManager alert manager for the alert type
*/
- private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
- @NonNull AlertingNotificationManager alertManager) {
- alertManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
- alertNotificationWhenPossible(toEntry, alertManager);
+ private void tryTransferAlertState(
+ NotificationEntry summary,
+ NotificationEntry fromEntry,
+ NotificationEntry toEntry,
+ GroupAlertEntry groupAlertEntry) {
+ if (toEntry != null) {
+ if (toEntry.getRow().keepInParent()
+ || toEntry.isRowRemoved()
+ || toEntry.isRowDismissed()) {
+ // The notification is actually already removed. No need to alert it.
+ return;
+ }
+ if (!mHeadsUpManager.isAlerting(toEntry.getKey()) && onlySummaryAlerts(summary)) {
+ groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
+ }
+ if (DEBUG) {
+ Log.d(TAG, "transferAlertState: fromEntry=" + fromEntry + " toEntry=" + toEntry);
+ }
+ transferAlertState(fromEntry, toEntry);
+ }
+ }
+ private void transferAlertState(@Nullable NotificationEntry fromEntry,
+ @NonNull NotificationEntry toEntry) {
+ if (fromEntry != null) {
+ mHeadsUpManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
+ }
+ alertNotificationWhenPossible(toEntry);
}
/**
@@ -326,11 +502,13 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
* more children are coming. Thus, if a child is added within a certain timeframe after we
* transfer, we back out and alert the summary again.
*
+ * An alert can only transfer back within a small window of time after a transfer away from the
+ * summary to a child happened.
+ *
* @param groupAlertEntry group alert entry to check
*/
private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
- if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
- < ALERT_TRANSFER_TIMEOUT) {
+ if (canStillTransferBack(groupAlertEntry)) {
NotificationEntry summary = groupAlertEntry.mGroup.summary;
if (!onlySummaryAlerts(summary)) {
@@ -338,30 +516,17 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
}
ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(
summary.getSbn());
- int numChildren = children.size();
+ int numActiveChildren = children.size();
int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
- numChildren += numPendingChildren;
+ int numChildren = numActiveChildren + numPendingChildren;
if (numChildren <= 1) {
return;
}
- boolean releasedChild = false;
- for (int i = 0; i < children.size(); i++) {
- NotificationEntry entry = children.get(i);
- if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
- releasedChild = true;
- mHeadsUpManager.removeNotification(
- entry.getKey(), true /* releaseImmediately */);
- }
- if (mPendingAlerts.containsKey(entry.getKey())) {
- // This is the child that would've been removed if it was inflated.
- releasedChild = true;
- mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
- }
- }
+ boolean releasedChild = releaseChildAlerts(children);
if (releasedChild && !mHeadsUpManager.isAlerting(summary.getKey())) {
- boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
+ boolean notifyImmediately = numActiveChildren > 1;
if (notifyImmediately) {
- alertNotificationWhenPossible(summary, mHeadsUpManager);
+ alertNotificationWhenPossible(summary);
} else {
// Should wait until the pending child inflates before alerting.
groupAlertEntry.mAlertSummaryOnNextAddition = true;
@@ -371,25 +536,61 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
}
}
+ private boolean canStillTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
+ return SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
+ < ALERT_TRANSFER_TIMEOUT;
+ }
+
+ private boolean releaseChildAlerts(List<NotificationEntry> children) {
+ boolean releasedChild = false;
+ if (SPEW) {
+ Log.d(TAG, "releaseChildAlerts: numChildren=" + children.size());
+ }
+ for (int i = 0; i < children.size(); i++) {
+ NotificationEntry entry = children.get(i);
+ if (SPEW) {
+ Log.d(TAG, "releaseChildAlerts: checking i=" + i + " entry=" + entry
+ + " onlySummaryAlerts=" + onlySummaryAlerts(entry)
+ + " isAlerting=" + mHeadsUpManager.isAlerting(entry.getKey())
+ + " isPendingAlert=" + mPendingAlerts.containsKey(entry.getKey()));
+ }
+ if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
+ releasedChild = true;
+ mHeadsUpManager.removeNotification(
+ entry.getKey(), true /* releaseImmediately */);
+ }
+ if (mPendingAlerts.containsKey(entry.getKey())) {
+ // This is the child that would've been removed if it was inflated.
+ releasedChild = true;
+ mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
+ }
+ }
+ if (SPEW) {
+ Log.d(TAG, "releaseChildAlerts: didRelease=" + releasedChild);
+ }
+ return releasedChild;
+ }
+
/**
* Tries to alert the notification. If its content view is not inflated, we inflate and continue
* when the entry finishes inflating the view.
*
* @param entry entry to show
- * @param alertManager alert manager for the alert type
*/
- private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
- @NonNull AlertingNotificationManager alertManager) {
- @InflationFlag int contentFlag = alertManager.getContentFlag();
+ private void alertNotificationWhenPossible(@NonNull NotificationEntry entry) {
+ @InflationFlag int contentFlag = mHeadsUpManager.getContentFlag();
final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
if ((params.getContentViews() & contentFlag) == 0) {
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible: async requestRebind entry=" + entry);
+ }
mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
params.requireContentViews(contentFlag);
mRowContentBindStage.requestRebind(entry, en -> {
PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey());
if (alertInfo != null) {
if (alertInfo.isStillValid()) {
- alertNotificationWhenPossible(entry, mHeadsUpManager);
+ alertNotificationWhenPossible(entry);
} else {
// The transfer is no longer valid. Free the content.
mRowContentBindStage.getStageParams(entry).markContentViewsFreeable(
@@ -400,10 +601,16 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
});
return;
}
- if (alertManager.isAlerting(entry.getKey())) {
- alertManager.updateNotification(entry.getKey(), true /* alert */);
+ if (mHeadsUpManager.isAlerting(entry.getKey())) {
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible: continue alerting entry=" + entry);
+ }
+ mHeadsUpManager.updateNotification(entry.getKey(), true /* alert */);
} else {
- alertManager.showNotification(entry);
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible: start alerting entry=" + entry);
+ }
+ mHeadsUpManager.showNotification(entry);
}
}
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 82bff2c8bf82..632a01b5b33f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -124,6 +124,7 @@ import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -308,6 +309,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final QSDetailDisplayer mQSDetailDisplayer;
private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
+ private final PrivacyDotViewController mPrivacyDotViewController;
// Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
// If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
@@ -607,6 +609,7 @@ public class NotificationPanelViewController extends PanelViewController {
FeatureFlags featureFlags,
QuickAccessWalletClient quickAccessWalletClient,
KeyguardMediaController keyguardMediaController,
+ PrivacyDotViewController privacyDotViewController,
@Main Executor uiExecutor,
EmergencyButtonController.Factory emergencyButtonControllerFactory) {
super(view, falsingManager, dozeLog, keyguardStateController,
@@ -616,6 +619,7 @@ public class NotificationPanelViewController extends PanelViewController {
mView = view;
mVibratorHelper = vibratorHelper;
mKeyguardMediaController = keyguardMediaController;
+ mPrivacyDotViewController = privacyDotViewController;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
mConfigurationController = configurationController;
@@ -1906,6 +1910,7 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardBypassController.setQSExpanded(expanded);
mStatusBarKeyguardViewManager.setQsExpanded(expanded);
mLockIconViewController.setQsExpanded(expanded);
+ mPrivacyDotViewController.setQsExpanded(expanded);
}
}
@@ -2078,14 +2083,13 @@ public class NotificationPanelViewController extends PanelViewController {
final int qsPanelBottomY = calculateQsBottomPosition(getQsExpansionFraction());
final boolean visible = (getQsExpansionFraction() > 0 || qsPanelBottomY > 0)
&& !mShouldUseSplitNotificationShade;
- final float notificationTop = mAmbientState.getStackY()
- - mNotificationScrimPadding
- - mAmbientState.getScrollY();
+ final float notificationTop = mAmbientState.getStackY() - mAmbientState.getScrollY();
setQsExpansionEnabled(mAmbientState.getScrollY() == 0);
int radius = mScrimCornerRadius;
if (!mShouldUseSplitNotificationShade) {
- top = (int) Math.min(qsPanelBottomY, notificationTop);
+ top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
+ : notificationTop);
bottom = getView().getBottom();
left = getView().getLeft();
right = getView().getRight();
@@ -2585,16 +2589,19 @@ public class NotificationPanelViewController extends PanelViewController {
// Small parallax as we pull down and clip QS
startHeight = -mQsExpansionHeight * 0.2f;
}
- if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
- && mNotificationStackScrollLayoutController.isPulseExpanding()) {
- if (!mPulseExpansionHandler.isExpanding()
- && !mPulseExpansionHandler.getLeavingLockscreen()) {
- // If we aborted the expansion we need to make sure the header doesn't reappear
- // again after the header has animated away
- appearAmount = 0;
+ if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
+ if (mNotificationStackScrollLayoutController.isPulseExpanding()) {
+ if (!mPulseExpansionHandler.isExpanding()
+ && !mPulseExpansionHandler.getLeavingLockscreen()) {
+ // If we aborted the expansion we need to make sure the header doesn't reappear
+ // again after the header has animated away
+ appearAmount = 0;
+ } else {
+ appearAmount = mNotificationStackScrollLayoutController
+ .calculateAppearFractionBypass();
+ }
} else {
- appearAmount = mNotificationStackScrollLayoutController
- .calculateAppearFractionBypass();
+ appearAmount = 0.0f;
}
startHeight = -mQs.getQsMinExpansionHeight();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index df5bdb83b4da..4f67a391d83e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -97,13 +97,13 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
mIconController = iconController;
+ mCarrierConfigTracker = carrierConfigTracker;
mNetworkController = Dependency.get(NetworkController.class);
mSecurityController = Dependency.get(SecurityController.class);
Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mNetworkController.addCallback(this);
mSecurityController.addCallback(this);
- mCarrierConfigTracker = carrierConfigTracker;
}
public void destroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
index 1fe77fd441bc..6e27caec9365 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
@@ -56,6 +56,7 @@ class OngoingCallChronometer @JvmOverloads constructor(
// call starts.
minimumTextWidth = 0
shouldHideText = false
+ visibility = VISIBLE
super.setBase(base)
}
@@ -76,6 +77,9 @@ class OngoingCallChronometer @JvmOverloads constructor(
if (desiredTextWidth > enforcedTextWidth) {
shouldHideText = true
+ // Changing visibility ensures that the content description is not read aloud when the
+ // time isn't displayed.
+ visibility = GONE
setMeasuredDimension(0, 0)
} else {
// It's possible that the current text could fit in a smaller width, but we don't want
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 6d1df5b5fa19..e9d256cc5ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -49,7 +49,8 @@ class OngoingCallController @Inject constructor(
private val systemClock: SystemClock,
private val activityStarter: ActivityStarter,
@Main private val mainExecutor: Executor,
- private val iActivityManager: IActivityManager
+ private val iActivityManager: IActivityManager,
+ private val logger: OngoingCallLogger
) : CallbackController<OngoingCallListener> {
/** Null if there's no ongoing call. */
@@ -104,7 +105,7 @@ class OngoingCallController @Inject constructor(
/**
* Sets the chip view that will contain ongoing call information.
*
- * Should only be called from [CollapedStatusBarFragment].
+ * Should only be called from [CollapsedStatusBarFragment].
*/
fun setChipView(chipView: ViewGroup) {
this.chipView = chipView
@@ -113,6 +114,16 @@ class OngoingCallController @Inject constructor(
}
}
+
+ /**
+ * Called when the chip's visibility may have changed.
+ *
+ * Should only be called from [CollapsedStatusBarFragment].
+ */
+ fun notifyChipVisibilityChanged(chipIsVisible: Boolean) {
+ logger.logChipVisibilityChanged(chipIsVisible)
+ }
+
/**
* Returns true if there's an active ongoing call that should be displayed in a status bar chip.
*/
@@ -150,6 +161,7 @@ class OngoingCallController @Inject constructor(
timeView.start()
currentChipView.setOnClickListener {
+ logger.logChipClicked()
activityStarter.postStartActivityDismissingKeyguard(
currentOngoingCallInfo.intent, 0,
ActivityLaunchAnimator.Controller.fromView(it))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
new file mode 100644
index 000000000000..177f21537ec9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class to log events for the ongoing call chip. */
+@SysUISingleton
+class OngoingCallLogger @Inject constructor(private val logger: UiEventLogger) {
+
+ private var chipIsVisible: Boolean = false
+
+ /** Logs that the ongoing call chip was clicked. */
+ fun logChipClicked() {
+ logger.log(OngoingCallEvents.ONGOING_CALL_CLICKED)
+ }
+
+ /**
+ * If needed, logs that the ongoing call chip's visibility has changed.
+ *
+ * For now, only logs when the chip changes from not visible to visible.
+ */
+ fun logChipVisibilityChanged(chipIsVisible: Boolean) {
+ if (chipIsVisible && chipIsVisible != this.chipIsVisible) {
+ logger.log(OngoingCallEvents.ONGOING_CALL_VISIBLE)
+ }
+ this.chipIsVisible = chipIsVisible
+ }
+
+ @VisibleForTesting
+ enum class OngoingCallEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The ongoing call chip became visible")
+ ONGOING_CALL_VISIBLE(813),
+
+ @UiEvent(doc = "The ongoing call chip was clicked")
+ ONGOING_CALL_CLICKED(814);
+
+ override fun getId() = metricId
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 82ad00ad7c6d..2e75395cb5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -30,6 +30,8 @@ import android.util.Log;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
@@ -60,9 +62,28 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
private final ArrayMap<String, Long> mSnoozedPackages;
private final AccessibilityManagerWrapper mAccessibilityMgr;
+ private final UiEventLogger mUiEventLogger;
+
+ /**
+ * Enum entry for notification peek logged from this class.
+ */
+ enum NotificationPeekEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Heads-up notification peeked on screen.")
+ NOTIFICATION_PEEK(801);
+
+ private final int mId;
+ NotificationPeekEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+ }
+
public HeadsUpManager(@NonNull final Context context) {
mContext = context;
mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
+ mUiEventLogger = Dependency.get(UiEventLogger.class);
Resources resources = context.getResources();
mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
mAutoDismissNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
@@ -130,6 +151,11 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
if (entry.isRowPinned() != isPinned) {
entry.setRowPinned(isPinned);
updatePinnedMode();
+ if (isPinned && entry.getSbn() != null) {
+ mUiEventLogger.logWithInstanceId(
+ NotificationPeekEvent.NOTIFICATION_PEEK, entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(), entry.getSbn().getInstanceId());
+ }
for (OnHeadsUpChangedListener listener : mListeners) {
if (isPinned) {
listener.onHeadsUpPinned(entry);
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 6a26e020a563..a6bec8c3713d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -371,11 +371,11 @@ public class NetworkControllerImpl extends BroadcastReceiver
if (network.equals(mLastNetwork) && validated == lastValidated) {
// Should not rely on getTransportTypes() returning the same order of transport
// types. So sort the array before comparing.
- int[] newTypes = networkCapabilities.getTransportTypes();
+ int[] newTypes = getProcessedTransportTypes(networkCapabilities);
Arrays.sort(newTypes);
int[] lastTypes = (mLastNetworkCapabilities != null)
- ? mLastNetworkCapabilities.getTransportTypes() : null;
+ ? getProcessedTransportTypes(mLastNetworkCapabilities) : null;
if (lastTypes != null) Arrays.sort(lastTypes);
if (Arrays.equals(newTypes, lastTypes)) {
@@ -543,6 +543,21 @@ public class NetworkControllerImpl extends BroadcastReceiver
return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
}
+ private int[] getProcessedTransportTypes(NetworkCapabilities networkCapabilities) {
+ int[] transportTypes = networkCapabilities.getTransportTypes();
+ for (int i = 0; i < transportTypes.length; i++) {
+ // For VCN over WiFi, the transportType is set to be TRANSPORT_CELLULAR in the
+ // NetworkCapabilities, but we need to convert it into TRANSPORT_WIFI in order to
+ // distinguish it from VCN over Cellular.
+ if (transportTypes[i] == NetworkCapabilities.TRANSPORT_CELLULAR
+ && Utils.tryGetWifiInfoForVcn(networkCapabilities) != null) {
+ transportTypes[i] = NetworkCapabilities.TRANSPORT_WIFI;
+ break;
+ }
+ }
+ return transportTypes;
+ }
+
private MobileSignalController getDataController() {
int dataSubId = mSubDefaults.getActiveDataSubId();
return getControllerWithSubId(dataSubId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index edec61832c8e..41b1dd12639a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -23,6 +23,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ContrastColorUtil;
@@ -58,15 +59,6 @@ public class SmartReplyView extends ViewGroup {
/** Spacing to be applied between views. */
private final int mSpacing;
- /** Horizontal padding of smart reply buttons if all of them use only one line of text. */
- private final int mSingleLineButtonPaddingHorizontal;
-
- /** Horizontal padding of smart reply buttons if at least one of them uses two lines of text. */
- private final int mDoubleLineButtonPaddingHorizontal;
-
- /** Increase in width of a smart reply button as a result of using two lines instead of one. */
- private final int mSingleToDoubleLineButtonWidthIncrease;
-
private final BreakIterator mBreakIterator;
private PriorityQueue<Button> mCandidateButtonQueueForSqueezing;
@@ -114,8 +106,6 @@ public class SmartReplyView extends ViewGroup {
mDefaultBackgroundColor);
int spacing = 0;
- int singleLineButtonPaddingHorizontal = 0;
- int doubleLineButtonPaddingHorizontal = 0;
int strokeWidth = 0;
final TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.SmartReplyView,
@@ -125,10 +115,6 @@ public class SmartReplyView extends ViewGroup {
int attr = arr.getIndex(i);
if (attr == R.styleable.SmartReplyView_spacing) {
spacing = arr.getDimensionPixelSize(i, 0);
- } else if (attr == R.styleable.SmartReplyView_singleLineButtonPaddingHorizontal) {
- singleLineButtonPaddingHorizontal = arr.getDimensionPixelSize(i, 0);
- } else if (attr == R.styleable.SmartReplyView_doubleLineButtonPaddingHorizontal) {
- doubleLineButtonPaddingHorizontal = arr.getDimensionPixelSize(i, 0);
} else if (attr == R.styleable.SmartReplyView_buttonStrokeWidth) {
strokeWidth = arr.getDimensionPixelSize(i, 0);
}
@@ -137,10 +123,6 @@ public class SmartReplyView extends ViewGroup {
mStrokeWidth = strokeWidth;
mSpacing = spacing;
- mSingleLineButtonPaddingHorizontal = singleLineButtonPaddingHorizontal;
- mDoubleLineButtonPaddingHorizontal = doubleLineButtonPaddingHorizontal;
- mSingleToDoubleLineButtonWidthIncrease =
- 2 * (doubleLineButtonPaddingHorizontal - singleLineButtonPaddingHorizontal);
mBreakIterator = BreakIterator.getLineInstance();
@@ -222,6 +204,12 @@ public class SmartReplyView extends ViewGroup {
return new LayoutParams(params.width, params.height);
}
+ private void clearLayoutLineCount(View view) {
+ if (view instanceof TextView) {
+ ((TextView) view).nullLayouts();
+ }
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int targetWidth = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED
@@ -237,8 +225,7 @@ public class SmartReplyView extends ViewGroup {
SmartSuggestionMeasures accumulatedMeasures = new SmartSuggestionMeasures(
mPaddingLeft + mPaddingRight,
- 0 /* maxChildHeight */,
- mSingleLineButtonPaddingHorizontal);
+ 0 /* maxChildHeight */);
int displayedChildCount = 0;
// Set up a list of suggestions where actions come before replies. Note that the Buttons
@@ -268,8 +255,7 @@ public class SmartReplyView extends ViewGroup {
continue;
}
- child.setPadding(accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingTop(),
- accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingBottom());
+ clearLayoutLineCount(child);
child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
coveredSuggestions.add(child);
@@ -299,18 +285,6 @@ public class SmartReplyView extends ViewGroup {
accumulatedMeasures.mMaxChildHeight =
Math.max(accumulatedMeasures.mMaxChildHeight, childHeight);
- // Do we need to increase the number of lines in smart reply buttons to two?
- final boolean increaseToTwoLines =
- (accumulatedMeasures.mButtonPaddingHorizontal
- == mSingleLineButtonPaddingHorizontal)
- && (lineCount == 2 || accumulatedMeasures.mMeasuredWidth > targetWidth);
- if (increaseToTwoLines) {
- accumulatedMeasures.mMeasuredWidth +=
- (displayedChildCount + 1) * mSingleToDoubleLineButtonWidthIncrease;
- accumulatedMeasures.mButtonPaddingHorizontal =
- mDoubleLineButtonPaddingHorizontal;
- }
-
// If the last button doesn't fit into the remaining width, try squeezing preceding
// smart reply buttons.
if (accumulatedMeasures.mMeasuredWidth > targetWidth) {
@@ -372,18 +346,11 @@ public class SmartReplyView extends ViewGroup {
mCandidateButtonQueueForSqueezing.clear();
// Finally, we need to re-measure some buttons.
- remeasureButtonsIfNecessary(accumulatedMeasures.mButtonPaddingHorizontal,
- accumulatedMeasures.mMaxChildHeight);
+ remeasureButtonsIfNecessary(accumulatedMeasures.mMaxChildHeight);
int buttonHeight = Math.max(getSuggestedMinimumHeight(), mPaddingTop
+ accumulatedMeasures.mMaxChildHeight + mPaddingBottom);
- // Set the corner radius to half the button height to make the side of the buttons look like
- // a semicircle.
- for (View smartSuggestionButton : smartSuggestions) {
- setCornerRadius((Button) smartSuggestionButton, ((float) buttonHeight) / 2);
- }
-
setMeasuredDimension(
resolveSize(Math.max(getSuggestedMinimumWidth(),
accumulatedMeasures.mMeasuredWidth),
@@ -411,18 +378,14 @@ public class SmartReplyView extends ViewGroup {
private static class SmartSuggestionMeasures {
int mMeasuredWidth = -1;
int mMaxChildHeight = -1;
- int mButtonPaddingHorizontal = -1;
- SmartSuggestionMeasures(int measuredWidth, int maxChildHeight,
- int buttonPaddingHorizontal) {
+ SmartSuggestionMeasures(int measuredWidth, int maxChildHeight) {
this.mMeasuredWidth = measuredWidth;
this.mMaxChildHeight = maxChildHeight;
- this.mButtonPaddingHorizontal = buttonPaddingHorizontal;
}
public SmartSuggestionMeasures clone() {
- return new SmartSuggestionMeasures(
- mMeasuredWidth, mMaxChildHeight, mButtonPaddingHorizontal);
+ return new SmartSuggestionMeasures(mMeasuredWidth, mMaxChildHeight);
}
}
@@ -553,17 +516,11 @@ public class SmartReplyView extends ViewGroup {
private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) {
int oldWidth = button.getMeasuredWidth();
- if (button.getPaddingLeft() != mDoubleLineButtonPaddingHorizontal) {
- // Correct for the fact that the button was laid out with single-line horizontal
- // padding.
- oldWidth += mSingleToDoubleLineButtonWidthIncrease;
- }
// Re-measure the squeezed smart reply button.
- button.setPadding(mDoubleLineButtonPaddingHorizontal, button.getPaddingTop(),
- mDoubleLineButtonPaddingHorizontal, button.getPaddingBottom());
+ clearLayoutLineCount(button);
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
- 2 * mDoubleLineButtonPaddingHorizontal + textWidth
+ button.getPaddingLeft() + button.getPaddingRight() + textWidth
+ getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
button.measure(widthMeasureSpec, heightMeasureSpec);
@@ -579,8 +536,7 @@ public class SmartReplyView extends ViewGroup {
}
}
- private void remeasureButtonsIfNecessary(
- int buttonPaddingHorizontal, int maxChildHeight) {
+ private void remeasureButtonsIfNecessary(int maxChildHeight) {
final int maxChildHeightMeasure =
MeasureSpec.makeMeasureSpec(maxChildHeight, MeasureSpec.EXACTLY);
@@ -602,24 +558,7 @@ public class SmartReplyView extends ViewGroup {
newWidth = Integer.MAX_VALUE;
}
- // Re-measure reason 2: The button's horizontal padding is incorrect (because it was
- // measured with the wrong number of lines).
- if (child.getPaddingLeft() != buttonPaddingHorizontal) {
- requiresNewMeasure = true;
- if (newWidth != Integer.MAX_VALUE) {
- if (buttonPaddingHorizontal == mSingleLineButtonPaddingHorizontal) {
- // Change padding (2->1 line).
- newWidth -= mSingleToDoubleLineButtonWidthIncrease;
- } else {
- // Change padding (1->2 lines).
- newWidth += mSingleToDoubleLineButtonWidthIncrease;
- }
- }
- child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
- buttonPaddingHorizontal, child.getPaddingBottom());
- }
-
- // Re-measure reason 3: The button's height is less than the max height of all buttons
+ // Re-measure reason 2: The button's height is less than the max height of all buttons
// (all should have the same height).
if (child.getMeasuredHeight() != maxChildHeight) {
requiresNewMeasure = true;
@@ -725,23 +664,6 @@ public class SmartReplyView extends ViewGroup {
button.setTextColor(mCurrentTextColor);
}
- private void setCornerRadius(Button button, float radius) {
- Drawable drawable = button.getBackground();
- if (drawable instanceof RippleDrawable) {
- // Mutate in case other notifications are using this drawable.
- drawable = drawable.mutate();
- RippleDrawable ripple = (RippleDrawable) drawable;
- Drawable inset = ripple.getDrawable(0);
- if (inset instanceof InsetDrawable) {
- Drawable background = ((InsetDrawable) inset).getDrawable();
- if (background instanceof GradientDrawable) {
- GradientDrawable gradientDrawable = (GradientDrawable) background;
- gradientDrawable.setCornerRadius(radius);
- }
- }
- }
- }
-
enum SmartButtonType {
REPLY,
ACTION
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index 3c1e12327d8f..865aa23f69e6 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -66,6 +66,13 @@ public class ThemeOverlayApplier implements Dumpable {
"android.theme.customization.accent_color";
static final String OVERLAY_CATEGORY_SYSTEM_PALETTE =
"android.theme.customization.system_palette";
+
+ static final String OVERLAY_COLOR_SOURCE = "android.theme.customization.color_source";
+
+ static final String COLOR_SOURCE_PRESET = "preset";
+
+ static final String TIMESTAMP_FIELD = "_applied_timestamp";
+
@VisibleForTesting
static final String OVERLAY_CATEGORY_FONT = "android.theme.customization.font";
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 9bdd8c009531..195114f0f19f 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -15,8 +15,11 @@
*/
package com.android.systemui.theme;
+import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE;
+import static com.android.systemui.theme.ThemeOverlayApplier.TIMESTAMP_FIELD;
import android.annotation.Nullable;
import android.app.WallpaperColors;
@@ -90,12 +93,12 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
private final UserManager mUserManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final Executor mBgExecutor;
- private final SecureSettings mSecureSettings;
+ private SecureSettings mSecureSettings;
private final Executor mMainExecutor;
private final Handler mBgHandler;
private final WallpaperManager mWallpaperManager;
private final boolean mIsMonetEnabled;
- private final UserTracker mUserTracker;
+ private UserTracker mUserTracker;
private DeviceProvisionedController mDeviceProvisionedController;
private WallpaperColors mSystemColors;
// If fabricated overlays were already created for the current theme.
@@ -112,6 +115,8 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
private boolean mAcceptColorEvents = true;
// Defers changing themes until Setup Wizard is done.
private boolean mDeferredThemeEvaluation;
+ // Determines if we should ignore THEME_CUSTOMIZATION_OVERLAY_PACKAGES setting changes.
+ private boolean mSkipSettingChange;
private final DeviceProvisionedListener mDeviceProvisionedListener =
new DeviceProvisionedListener() {
@@ -162,6 +167,35 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
}
}
}
+ // Check if we need to reset to default colors (if a color override was set that is sourced
+ // from the wallpaper)
+ int currentUser = mUserTracker.getUserId();
+ String overlayPackageJson = mSecureSettings.getStringForUser(
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ currentUser);
+ if (!TextUtils.isEmpty(overlayPackageJson)) {
+ try {
+ JSONObject jsonObject = new JSONObject(overlayPackageJson);
+ if ((jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR)
+ || jsonObject.has(OVERLAY_CATEGORY_SYSTEM_PALETTE))
+ && !COLOR_SOURCE_PRESET.equals(
+ jsonObject.optString(OVERLAY_COLOR_SOURCE))) {
+ mSkipSettingChange = true;
+ jsonObject.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
+ jsonObject.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ jsonObject.remove(OVERLAY_COLOR_SOURCE);
+ jsonObject.put(TIMESTAMP_FIELD, System.currentTimeMillis());
+ if (DEBUG) {
+ Log.d(TAG, "Updating theme setting from "
+ + overlayPackageJson + " to " + jsonObject.toString());
+ }
+ mSecureSettings.putString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+ jsonObject.toString());
+ }
+ } catch (JSONException e) {
+ Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
+ }
+ }
reevaluateSystemTheme(false /* forceReload */);
};
@@ -232,6 +266,11 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
mDeferredThemeEvaluation = true;
return;
}
+ if (mSkipSettingChange) {
+ if (DEBUG) Log.d(TAG, "Skipping setting change");
+ mSkipSettingChange = false;
+ return;
+ }
reevaluateSystemTheme(true /* forceReload */);
}
},
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/Execution.kt b/packages/SystemUI/src/com/android/systemui/util/concurrency/Execution.kt
new file mode 100644
index 000000000000..647faeba1e77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/Execution.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency
+
+import android.os.Looper
+import javax.inject.Inject
+
+/**
+ * Methods to check or assert that we're on the main thread
+ */
+interface Execution {
+ fun assertIsMainThread()
+ fun isMainThread(): Boolean
+}
+
+class ExecutionImpl @Inject constructor() : Execution {
+ private val mainLooper = Looper.getMainLooper()
+
+ override fun assertIsMainThread() {
+ if (!mainLooper.isCurrentThread) {
+ throw IllegalStateException("should be called from the main thread." +
+ " Main thread name=" + mainLooper.thread.name +
+ " Thread.currentThread()=" + Thread.currentThread().name)
+ }
+ }
+
+ override fun isMainThread(): Boolean {
+ return mainLooper.isCurrentThread
+ }
+}
+
+class FakeExecution : Execution {
+ var simulateMainThread = true
+
+ override fun assertIsMainThread() {
+ if (!simulateMainThread) {
+ throw IllegalStateException("should be called from the main thread")
+ }
+ }
+
+ override fun isMainThread(): Boolean {
+ return simulateMainThread
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
index 5946af383b0f..1c504961e715 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
@@ -24,6 +24,8 @@ import com.android.systemui.dagger.qualifiers.Main;
import java.util.concurrent.Executor;
+import javax.inject.Singleton;
+
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -40,7 +42,7 @@ public abstract class GlobalConcurrencyModule {
@Binds
public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl);
- /** Main Looper */
+ /** Main Looper */
@Provides
@Main
public static Looper provideMainLooper() {
@@ -67,4 +69,8 @@ public abstract class GlobalConcurrencyModule {
return context.getMainExecutor();
}
+ /** */
+ @Binds
+ @Singleton
+ public abstract Execution provideExecution(ExecutionImpl execution);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
index 2270f9631094..7a5ceb547d9e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.concurrency;
+import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
@@ -29,6 +30,14 @@ import java.util.concurrent.Executor;
*/
public interface ThreadFactory {
/**
+ * Returns a {@link Handler} running on a named thread.
+ *
+ * The thread is implicitly started and may be left running indefinitely, depending on the
+ * implementation. Assume this is the case and use responsibly.
+ */
+ Handler builderHandlerOnNewThread(String threadName);
+
+ /**
* Return an {@link java.util.concurrent.Executor} running on a named thread.
*
* The thread is implicitly started and may be left running indefinitely, depending on the
@@ -45,6 +54,11 @@ public interface ThreadFactory {
DelayableExecutor buildDelayableExecutorOnNewThread(String threadName);
/**
+ * Return an {@link DelayableExecutor} running on the given HandlerThread.
+ **/
+ DelayableExecutor buildDelayableExecutorOnHandler(Handler handler);
+
+ /**
* Return an {@link DelayableExecutor} running the given Looper
**/
DelayableExecutor buildDelayableExecutorOnLooper(Looper looper);
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
index 2d9f2b424aae..184b83113d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.concurrency;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -27,16 +28,31 @@ class ThreadFactoryImpl implements ThreadFactory {
@Inject
ThreadFactoryImpl() {}
+ @Override
+ public Handler builderHandlerOnNewThread(String threadName) {
+ HandlerThread handlerThread = new HandlerThread(threadName);
+ handlerThread.start();
+ return new Handler(handlerThread.getLooper());
+ }
+
+ @Override
public Executor buildExecutorOnNewThread(String threadName) {
return buildDelayableExecutorOnNewThread(threadName);
}
+ @Override
public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
HandlerThread handlerThread = new HandlerThread(threadName);
handlerThread.start();
- return new ExecutorImpl(handlerThread.getLooper());
+ return buildDelayableExecutorOnLooper(handlerThread.getLooper());
+ }
+
+ @Override
+ public DelayableExecutor buildDelayableExecutorOnHandler(Handler handler) {
+ return buildDelayableExecutorOnLooper(handler.getLooper());
}
+ @Override
public DelayableExecutor buildDelayableExecutorOnLooper(Looper looper) {
return new ExecutorImpl(looper);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
index 644addfef1d1..5e9bae98a17e 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
@@ -20,13 +20,10 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.text.TextPaint;
import android.util.MathUtils;
import android.view.View;
-import android.widget.TextView;
import androidx.annotation.ColorInt;
-import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
@@ -39,12 +36,10 @@ final class DotIndicatorDecoration extends RecyclerView.ItemDecoration {
@ColorInt private final int mUnselectedColor;
@ColorInt private final int mSelectedColor;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
private WalletCardCarousel mCardCarousel;
DotIndicatorDecoration(Context context) {
super();
-
mUnselectedRadius =
context.getResources().getDimensionPixelSize(
R.dimen.card_carousel_dot_unselected_radius);
@@ -53,13 +48,8 @@ final class DotIndicatorDecoration extends RecyclerView.ItemDecoration {
R.dimen.card_carousel_dot_selected_radius);
mDotMargin = context.getResources().getDimensionPixelSize(R.dimen.card_carousel_dot_margin);
- TextView textView = new TextView(context);
- mTextPaint.set(textView.getPaint());
- // Text color is not copied from text appearance.
- mTextPaint.setColor(ContextCompat.getColor(context, R.color.GM2_blue_600));
-
- mUnselectedColor = ContextCompat.getColor(context, R.color.GM2_grey_300);
- mSelectedColor = ContextCompat.getColor(context, R.color.GM2_blue_600);
+ mUnselectedColor = context.getColor(com.android.internal.R.color.system_neutral1_300);
+ mSelectedColor = context.getColor(com.android.internal.R.color.system_neutral1_0);
}
@Override
@@ -107,9 +97,9 @@ final class DotIndicatorDecoration extends RecyclerView.ItemDecoration {
int i = isLayoutLtr() ? itemsDrawn : itemCount - itemsDrawn - 1;
if (isSelectedItem(i)) {
- drawSelectedDot(canvas, interpolatedProgress, i);
+ drawSelectedDot(canvas, interpolatedProgress);
} else if (isNextItemInScrollingDirection(i)) {
- drawFadingUnselectedDot(canvas, interpolatedProgress, i);
+ drawFadingUnselectedDot(canvas, interpolatedProgress);
} else {
drawUnselectedDot(canvas);
}
@@ -121,7 +111,7 @@ final class DotIndicatorDecoration extends RecyclerView.ItemDecoration {
this.mCardCarousel = null; // No need to hold a reference.
}
- private void drawSelectedDot(Canvas canvas, float progress, int position) {
+ private void drawSelectedDot(Canvas canvas, float progress) {
// Divide progress by 2 because the other half of the animation is done by
// drawFadingUnselectedDot.
mPaint.setColor(
@@ -132,13 +122,13 @@ final class DotIndicatorDecoration extends RecyclerView.ItemDecoration {
canvas.translate(radius * 2, 0);
}
- private void drawFadingUnselectedDot(Canvas canvas, float progress, int position) {
+ private void drawFadingUnselectedDot(Canvas canvas, float progress) {
// Divide progress by 2 because the first half of the animation is done by drawSelectedDot.
int blendedColor =
ColorUtils.blendARGB(
mUnselectedColor, mSelectedColor, progress / 2);
mPaint.setColor(getTransitionAdjustedColor(blendedColor));
- float radius = MathUtils.lerp(mSelectedRadius, mUnselectedRadius, progress / 2);
+ float radius = MathUtils.lerp(mUnselectedRadius, mSelectedColor, progress / 2);
canvas.drawCircle(radius, 0, radius, mPaint);
canvas.translate(radius * 2, 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index ac8b16a2206d..66bd48b9d188 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -17,6 +17,7 @@
package com.android.systemui.wallet.ui;
import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.service.quickaccesswallet.QuickAccessWalletClient;
@@ -95,7 +96,7 @@ public class WalletActivity extends LifecycleActivity {
}
setTitle("");
getActionBar().setDisplayHomeAsUpEnabled(true);
- getActionBar().setHomeAsUpIndicator(R.drawable.ic_close);
+ getActionBar().setHomeAsUpIndicator(getHomeIndicatorDrawable());
getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
WalletView walletView = requireViewById(R.id.wallet_view);
mWalletScreenController = new WalletScreenController(
@@ -175,4 +176,10 @@ public class WalletActivity extends LifecycleActivity {
mWalletScreenController.onDismissed();
super.onDestroy();
}
+
+ private Drawable getHomeIndicatorDrawable() {
+ Drawable drawable = getDrawable(R.drawable.ic_close);
+ drawable.setTint(getColor(com.android.internal.R.color.system_neutral1_300));
+ return drawable;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index ec62981cae73..b57d93762381 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -234,7 +234,9 @@ public class WalletScreenController implements
mWalletView.show();
mWalletView.hideErrorMessage();
int iconSizePx =
- mContext.getResources().getDimensionPixelSize(R.dimen.wallet_view_header_icon_size);
+ mContext
+ .getResources()
+ .getDimensionPixelSize(R.dimen.wallet_screen_header_icon_size);
GetWalletCardsRequest request =
new GetWalletCardsRequest(cardWidthPx, cardHeightPx, iconSizePx, MAX_CARDS);
mWalletClient.getWalletCards(mExecutor, request, this);
@@ -340,7 +342,11 @@ public class WalletScreenController implements
@Override
public CharSequence getLabel() {
- return mWalletCard.getCardLabel();
+ CharSequence label = mWalletCard.getCardLabel();
+ if (label == null) {
+ return "";
+ }
+ return label;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index c547bb346617..e42ce6ac50f3 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -37,6 +37,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import java.util.List;
@@ -100,7 +101,7 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
public void onCardScroll(WalletCardViewInfo centerCard, WalletCardViewInfo nextCard,
float percentDistanceFromCenter) {
CharSequence centerCardText = getLabelText(centerCard);
- Drawable centerCardIcon = centerCard.getIcon();
+ Drawable centerCardIcon = getHeaderIcon(mContext, centerCard);
if (!TextUtils.equals(mCenterCardText, centerCardText)) {
mCenterCardText = centerCardText;
mCardLabel.setText(centerCardText);
@@ -133,7 +134,8 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
mCardCarouselContainer.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
mEmptyStateView.setVisibility(GONE);
- renderHeaderIconAndActionButton(data.get(selectedIndex), isDeviceLocked);
+ mIcon.setImageDrawable(getHeaderIcon(mContext, data.get(selectedIndex)));
+ renderActionButton(data.get(selectedIndex), isDeviceLocked);
if (shouldAnimate) {
animateViewsShown(mIcon, mCardLabel, mActionButton);
}
@@ -220,10 +222,15 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
return mCardLabel;
}
- private void renderHeaderIconAndActionButton(WalletCardViewInfo walletCard, boolean isLocked) {
- mIcon.setImageDrawable(walletCard.getIcon());
- mIcon.setVisibility(VISIBLE);
- renderActionButton(walletCard, isLocked);
+ @Nullable
+ private static Drawable getHeaderIcon(Context context, WalletCardViewInfo walletCard) {
+ Drawable icon = walletCard.getIcon();
+ if (icon != null) {
+ icon.setTint(
+ Utils.getColorAttrDefaultColor(
+ context, com.android.internal.R.attr.colorAccentPrimary));
+ }
+ return icon;
}
private void renderActionButton(WalletCardViewInfo walletCard, boolean isDeviceLocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 26b68afed494..6c306743e4ce 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -280,8 +280,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
- return new PipSurfaceTransactionHelper(context);
+ static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper() {
+ return new PipSurfaceTransactionHelper();
}
@WMSingleton