Merge "Controls on lockscreen" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-02-06 05:00:40 +00:00
committed by Android (Google) Code Review
10 changed files with 287 additions and 3 deletions

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorBackgroundFloating" />
<corners android:radius="@dimen/notification_corner_radius" />
</shape>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/control_detail_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginVertical="@dimen/controls_activity_view_top_offset"
android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset"
android:padding="8dp"
android:orientation="vertical"
android:background="@drawable/controls_dialog_bg">
<com.android.systemui.globalactions.MinHeightScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:scrollbars="none">
<LinearLayout
android:id="@+id/global_actions_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:orientation="vertical"
android:clipToPadding="false" />
</com.android.systemui.globalactions.MinHeightScrollView>
</LinearLayout>

View File

@@ -81,6 +81,17 @@
android:contentDescription="@string/accessibility_phone_button"
android:tint="?attr/wallpaperTextColor" />
<ImageView
android:id="@+id/alt_left_button"
android:layout_height="@dimen/keyguard_affordance_height"
android:layout_width="@dimen/keyguard_affordance_width"
android:layout_gravity="bottom|start"
android:scaleType="center"
android:tint="?attr/wallpaperTextColor"
android:layout_marginStart="24dp"
android:layout_marginBottom="48dp"
android:visibility="gone" />
<FrameLayout
android:id="@+id/overlay_container"
android:layout_width="match_parent"

View File

@@ -264,6 +264,7 @@
<color name="control_enabled_cool_foreground">@color/GM2_blue_300</color>
<color name="control_thumbnail_tint">#33000000</color>
<color name="control_thumbnail_shadow_color">@*android:color/black</color>
<color name="controls_lockscreen_scrim">#AA000000</color>
<!-- Docked misalignment message -->
<color name="misalignment_text_color">#F28B82</color>

View File

@@ -1260,6 +1260,7 @@
<!-- Home Controls activity view detail panel-->
<dimen name="controls_activity_view_top_offset">100dp</dimen>
<dimen name="controls_activity_view_side_offset">12dp</dimen>
<dimen name="controls_activity_view_text_size">17sp</dimen>
<dimen name="controls_activity_view_corner_radius">@*android:dimen/config_bottomDialogCornerRadius</dimen>

View File

@@ -662,6 +662,19 @@
<item name="android:windowExitAnimation">@anim/bottomsheet_out</item>
</style>
<style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
<item name="android:windowAnimationStyle">@style/Animation.Fade</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowBackground">@color/controls_lockscreen_scrim</item>
<item name="android:backgroundDimEnabled">true</item>
</style>
<style name="Animation.Fade">
<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
</style>
<style name="Control" />
<style name="Control.MenuItem">

View File

@@ -0,0 +1,90 @@
/*
* 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.controls.ui
import android.app.Dialog
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import com.android.systemui.Interpolators
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
/**
* Show the controls space inside a dialog, as from the lock screen.
*/
class ControlsDialog(
thisContext: Context,
val broadcastDispatcher: BroadcastDispatcher
) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.getAction()
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
dismiss()
}
}
}
init {
window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
setContentView(R.layout.controls_in_dialog)
requireViewById<ViewGroup>(R.id.control_detail_root).apply {
setOnClickListener { dismiss() }
(getParent() as View).setOnClickListener { dismiss() }
}
}
fun show(
controller: ControlsUiController
): ControlsDialog {
super.show()
val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
vg.alpha = 0f
controller.show(vg, { /* do nothing */ })
vg.animate()
.alpha(1f)
.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
.setDuration(300)
val filter = IntentFilter()
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
broadcastDispatcher.registerReceiver(receiver, filter)
return this
}
override fun dismiss() {
broadcastDispatcher.unregisterReceiver(receiver)
if (!isShowing()) return
super.dismiss()
}
}

View File

@@ -48,6 +48,7 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
import android.provider.Settings;
import android.service.media.CameraPrewarmService;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -60,6 +61,7 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,6 +73,10 @@ import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.controls.ui.ControlsDialog;
import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.IntentButtonProvider;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
@@ -117,11 +123,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
// TODO(b/179494051): May no longer be needed
private final boolean mShowLeftAffordance;
private final boolean mShowCameraAffordance;
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
private ImageView mAltLeftButton;
private ViewGroup mIndicationArea;
private TextView mEnterpriseDisclosure;
private TextView mIndicationText;
@@ -171,6 +179,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private int mBurnInYOffset;
private ActivityIntentHelper mActivityIntentHelper;
private ControlsDialog mControlsDialog;
private ControlsComponent mControlsComponent;
private int mLockScreenMode;
private BroadcastDispatcher mBroadcastDispatcher;
public KeyguardBottomAreaView(Context context) {
this(context, null);
}
@@ -236,6 +249,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mOverlayContainer = findViewById(R.id.overlay_container);
mRightAffordanceView = findViewById(R.id.camera_button);
mLeftAffordanceView = findViewById(R.id.left_button);
mAltLeftButton = findViewById(R.id.alt_left_button);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
mEnterpriseDisclosure = findViewById(
R.id.keyguard_indication_enterprise_disclosure);
@@ -334,6 +348,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
mLeftAffordanceView.setLayoutParams(lp);
updateLeftAffordanceIcon();
lp = mAltLeftButton.getLayoutParams();
lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
mAltLeftButton.setLayoutParams(lp);
}
private void updateRightAffordanceIcon() {
@@ -392,10 +411,17 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
private void updateLeftAffordanceIcon() {
if (mDozing) {
mAltLeftButton.setVisibility(GONE);
} else if (mAltLeftButton.getDrawable() != null) {
mAltLeftButton.setVisibility(VISIBLE);
}
if (!mShowLeftAffordance || mDozing) {
mLeftAffordanceView.setVisibility(GONE);
return;
}
IconState state = mLeftButton.getIcon();
mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
if (state.drawable != mLeftAffordanceView.getDrawable()
@@ -669,6 +695,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public void startFinishDozeAnimation() {
long delay = 0;
if (mAltLeftButton.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mAltLeftButton, delay);
}
if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mLeftAffordanceView, delay);
delay += DOZE_ANIMATION_STAGGER_DELAY;
@@ -744,6 +773,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
if (dozing) {
mOverlayContainer.setVisibility(INVISIBLE);
if (mControlsDialog != null) {
mControlsDialog.dismiss();
mControlsDialog = null;
}
} else {
mOverlayContainer.setVisibility(VISIBLE);
if (animate) {
@@ -773,6 +806,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mLeftAffordanceView.setAlpha(alpha);
mRightAffordanceView.setAlpha(alpha);
mIndicationArea.setAlpha(alpha);
mAltLeftButton.setAlpha(alpha);
}
private class DefaultLeftButton implements IntentButton {
@@ -844,4 +878,54 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
return insets;
}
/**
* Show or hide controls, depending on the lock screen mode and controls
* availability.
*/
public void setupControls(ControlsComponent component, BroadcastDispatcher dispatcher) {
mControlsComponent = component;
mBroadcastDispatcher = dispatcher;
setupControls();
}
private void setupControls() {
if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
mAltLeftButton.setVisibility(View.GONE);
mAltLeftButton.setOnClickListener(null);
return;
}
if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) {
return;
}
if (mControlsComponent.getControlsListingController().isPresent()) {
mControlsComponent.getControlsListingController().get()
.addCallback(list -> {
if (!list.isEmpty()) {
mAltLeftButton.setImageDrawable(list.get(0).loadIcon());
mAltLeftButton.setVisibility(View.VISIBLE);
mAltLeftButton.setOnClickListener((v) -> {
ControlsUiController ui = mControlsComponent
.getControlsUiController().get();
mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher)
.show(ui);
});
} else {
mAltLeftButton.setVisibility(View.GONE);
mAltLeftButton.setOnClickListener(null);
}
});
}
}
/**
* Optionally add controls when in the new lockscreen mode
*/
public void onLockScreenModeChanged(int mode) {
mLockScreenMode = mode;
setupControls();
}
}

View File

@@ -80,8 +80,10 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
@@ -229,6 +231,7 @@ public class NotificationPanelViewController extends PanelViewController {
public void onLockScreenModeChanged(int mode) {
mLockScreenMode = mode;
mClockPositionAlgorithm.onLockScreenModeChanged(mode);
mKeyguardBottomArea.onLockScreenModeChanged(mode);
}
@Override
@@ -295,6 +298,8 @@ public class NotificationPanelViewController extends PanelViewController {
private final QSDetailDisplayer mQSDetailDisplayer;
private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
private final ControlsComponent mControlsComponent;
// 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
private final int mMaxKeyguardNotifications;
@@ -503,6 +508,7 @@ public class NotificationPanelViewController extends PanelViewController {
private NotificationShelfController mNotificationShelfController;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
private BroadcastDispatcher mBroadcastDispatcher;
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
@@ -557,8 +563,9 @@ public class NotificationPanelViewController extends PanelViewController {
ScrimController scrimController,
MediaDataManager mediaDataManager,
AmbientState ambientState,
FeatureFlags featureFlags
) {
FeatureFlags featureFlags,
ControlsComponent controlsComponent,
BroadcastDispatcher broadcastDispatcher) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
@@ -592,6 +599,7 @@ public class NotificationPanelViewController extends PanelViewController {
mBiometricUnlockController = biometricUnlockController;
mScrimController = scrimController;
mMediaDataManager = mediaDataManager;
mControlsComponent = controlsComponent;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) {
mQs.animateHeaderSlidingOut();
@@ -630,6 +638,7 @@ public class NotificationPanelViewController extends PanelViewController {
mEntryManager = notificationEntryManager;
mConversationNotificationManager = conversationNotificationManager;
mAuthController = authController;
mBroadcastDispatcher = broadcastDispatcher;
mView.setBackgroundColor(Color.TRANSPARENT);
OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
@@ -838,6 +847,7 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
mKeyguardBottomArea.setStatusBar(mStatusBar);
mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
mKeyguardBottomArea.setupControls(mControlsComponent, mBroadcastDispatcher);
}
private void updateMaxDisplayedNotifications(boolean recompute) {

View File

@@ -61,8 +61,10 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
@@ -207,6 +209,10 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private FeatureFlags mFeatureFlags;
@Mock
private ControlsComponent mControlsComponent;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
private NotificationsQuickSettingsContainer mNotificationContainerParent;
@Mock
private AmbientState mAmbientState;
@@ -300,7 +306,9 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mScrimController,
mMediaDataManager,
mAmbientState,
mFeatureFlags);
mFeatureFlags,
mControlsComponent,
mBroadcastDispatcher);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);