diff options
author | Beverly <beverlyt@google.com> | 2021-02-03 09:58:26 -0500 |
---|---|---|
committer | Beverly <beverlyt@google.com> | 2021-02-09 08:35:31 -0500 |
commit | aac8e536ab2411a0febacbcfaae1fe79d85f556c (patch) | |
tree | f819c4d36572c77eec9006217356eedd173f6959 /packages/SystemUI/src | |
parent | 5e8177bf787c384f914fa9bb388a871afd6e25fa (diff) |
Rotate keyguard indication messages
In the new LS layout, we show the following messages in the keyguard
bottom area instead of in the KeyguardStatusView:
- Logout button
- Owner information
We also now show now playing as part of the rotating text on the lock
screen.
Bug: 178794517
Test: atest SystemUITest, manual
Change-Id: I1a0a47e300d1f9e5fe11c17d143e5a8f0ad8af60
Diffstat (limited to 'packages/SystemUI/src')
8 files changed, 981 insertions, 166 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 97aa26fb7f68..fea152abe36a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -56,8 +56,10 @@ public class KeyguardStatusView extends GridLayout { private final IActivityManager mIActivityManager; private TextView mLogoutView; + private boolean mCanShowLogout = true; // by default, try to show the logout button here private KeyguardClockSwitch mClockView; private TextView mOwnerInfo; + private boolean mCanShowOwnerInfo = true; // by default, try to show the owner information here private KeyguardSliceView mKeyguardSlice; private View mNotificationIcons; private Runnable mPendingMarqueeStart; @@ -114,6 +116,25 @@ public class KeyguardStatusView extends GridLayout { if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled); } + void setCanShowOwnerInfo(boolean canShowOwnerInfo) { + mCanShowOwnerInfo = canShowOwnerInfo; + mOwnerInfo = findViewById(R.id.owner_info); + if (mOwnerInfo != null) { + if (mCanShowOwnerInfo) { + mOwnerInfo.setVisibility(VISIBLE); + updateOwnerInfo(); + } else { + mOwnerInfo.setVisibility(GONE); + mOwnerInfo = null; + } + } + } + + void setCanShowLogout(boolean canShowLogout) { + mCanShowLogout = canShowLogout; + updateLogoutView(); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -128,7 +149,10 @@ public class KeyguardStatusView extends GridLayout { if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) { mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext)); } - mOwnerInfo = findViewById(R.id.owner_info); + if (mCanShowOwnerInfo) { + mOwnerInfo = findViewById(R.id.owner_info); + } + mKeyguardSlice = findViewById(R.id.keyguard_status_area); mTextColor = mClockView.getCurrentTextColor(); @@ -189,7 +213,7 @@ public class KeyguardStatusView extends GridLayout { if (mLogoutView == null) { return; } - mLogoutView.setVisibility(shouldShowLogout() ? VISIBLE : GONE); + mLogoutView.setVisibility(mCanShowLogout && shouldShowLogout() ? VISIBLE : GONE); // Logout button will stay in language of user 0 if we don't set that manually. mLogoutView.setText(mContext.getResources().getString( com.android.internal.R.string.global_action_logout)); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 973b49384c09..a5f364d30d7d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -134,7 +134,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** - * Get the height of the logout button. + * Get the height of the owner information view. */ public int getOwnerInfoHeight() { return mView.getOwnerInfoHeight(); @@ -335,9 +335,13 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV // of the top of the view mKeyguardSliceViewController.updateTopMargin( mKeyguardClockSwitchController.getClockTextTopPadding()); + mView.setCanShowOwnerInfo(false); + mView.setCanShowLogout(false); } else { // reset margin mKeyguardSliceViewController.updateTopMargin(0); + mView.setCanShowOwnerInfo(true); + mView.setCanShowLogout(false); } updateAodIcons(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java new file mode 100644 index 000000000000..3a06f7aeb6bf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java @@ -0,0 +1,155 @@ +/* + * 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.keyguard; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; +import android.view.View; + +/** + * Data class containing display information (message, icon, styling) for indication to show at + * the bottom of the keyguard. + * + * See {@link com.android.systemui.statusbar.phone.KeyguardBottomAreaView}. + */ +public class KeyguardIndication { + @NonNull + private final CharSequence mMessage; + @NonNull + private final ColorStateList mTextColor; + @Nullable + private final Drawable mIcon; + @Nullable + private final View.OnClickListener mOnClickListener; + @Nullable + private final Drawable mBackground; + + private KeyguardIndication( + CharSequence message, + ColorStateList textColor, + Drawable icon, + View.OnClickListener onClickListener, + Drawable background) { + mMessage = message; + mTextColor = textColor; + mIcon = icon; + mOnClickListener = onClickListener; + mBackground = background; + } + + /** + * Message to display + */ + public @NonNull CharSequence getMessage() { + return mMessage; + } + + /** + * TextColor to display the message. + */ + public @NonNull ColorStateList getTextColor() { + return mTextColor; + } + + /** + * Icon to display. + */ + public @Nullable Drawable getIcon() { + return mIcon; + } + + /** + * Click listener for messsage. + */ + public @Nullable View.OnClickListener getClickListener() { + return mOnClickListener; + } + + /** + * Background for textView. + */ + public @Nullable Drawable getBackground() { + return mBackground; + } + + /** + * KeyguardIndication Builder + */ + public static class Builder { + private CharSequence mMessage; + private Drawable mIcon; + private View.OnClickListener mOnClickListener; + private ColorStateList mTextColor; + private Drawable mBackground; + + public Builder() { } + + /** + * Required field. Message to display. + */ + public Builder setMessage(@NonNull CharSequence message) { + this.mMessage = message; + return this; + } + + /** + * Required field. Text color to use to display the message. + */ + public Builder setTextColor(@NonNull ColorStateList textColor) { + this.mTextColor = textColor; + return this; + } + + /** + * Optional. Icon to show next to the text. Icon location changes based on language + * display direction. For LTR, icon shows to the left of the message. For RTL, icon shows + * to the right of the message. + */ + public Builder setIcon(Drawable icon) { + this.mIcon = icon; + return this; + } + + /** + * Optional. Set a click listener on the message. + */ + public Builder setClickListener(View.OnClickListener onClickListener) { + this.mOnClickListener = onClickListener; + return this; + } + + /** + * Optional. Set a custom background on the TextView. + */ + public Builder setBackground(Drawable background) { + this.mBackground = background; + return this; + } + + /** + * Build the KeyguardIndication. + */ + public KeyguardIndication build() { + if (mMessage == null) throw new IllegalStateException("message must be set"); + if (mTextColor == null) throw new IllegalStateException("text color must be set"); + return new KeyguardIndication( + mMessage, mTextColor, mIcon, mOnClickListener, mBackground); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java new file mode 100644 index 000000000000..d0070d8d9ae9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java @@ -0,0 +1,326 @@ +/* + * 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.keyguard; + +import android.annotation.Nullable; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.text.TextUtils; +import android.view.View; + +import androidx.annotation.IntDef; + +import com.android.settingslib.Utils; +import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; +import com.android.systemui.util.ViewController; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Rotates through messages to show on the keyguard bottom area on the lock screen + * NOTE: This controller should not be used on AoD to avoid waking up the AP too often. + */ +public class KeyguardIndicationRotateTextViewController extends + ViewController<KeyguardIndicationTextView> implements Dumpable { + public static String TAG = "KgIndicationRotatingCtrl"; + private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds + + private final StatusBarStateController mStatusBarStateController; + private final float mMaxAlpha; + private final ColorStateList mInitialTextColorState; + + // Stores @IndicationType => KeyguardIndication messages + private final Map<Integer, KeyguardIndication> mIndicationMessages = new HashMap<>(); + + // Executor that will show the next message after a delay + private final DelayableExecutor mExecutor; + @Nullable private ShowNextIndication mShowNextIndicationRunnable; + + // List of indication types to show. The next indication to show is always at index 0 + private final List<Integer> mIndicationQueue = new LinkedList<>(); + private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE; + + private boolean mIsDozing; + + public KeyguardIndicationRotateTextViewController( + KeyguardIndicationTextView view, + @Main DelayableExecutor executor, + StatusBarStateController statusBarStateController, + int lockScreenMode + ) { + super(view); + mMaxAlpha = view.getAlpha(); + mExecutor = executor; + mInitialTextColorState = mView != null + ? mView.getTextColors() : ColorStateList.valueOf(Color.WHITE); + mStatusBarStateController = statusBarStateController; + mView.setLockScreenMode(lockScreenMode); + init(); + } + + @Override + protected void onViewAttached() { + mStatusBarStateController.addCallback(mStatusBarStateListener); + } + + @Override + protected void onViewDetached() { + mStatusBarStateController.removeCallback(mStatusBarStateListener); + cancelScheduledIndication(); + } + + /** + * Update the indication type with the given String. + * @param type of indication + * @param newIndication message to associate with this indication type + * @param showImmediately if true: shows this indication message immediately. Else, the text + * associated with this type is updated and will show when its turn in + * the IndicationQueue comes around. + */ + public void updateIndication(@IndicationType int type, KeyguardIndication newIndication, + boolean showImmediately) { + final boolean hasPreviousIndication = mIndicationMessages.get(type) != null; + final boolean hasNewIndication = newIndication != null + && !TextUtils.isEmpty(newIndication.getMessage()); + if (!hasNewIndication) { + mIndicationMessages.remove(type); + mIndicationQueue.removeIf(x -> x == type); + } else { + if (!hasPreviousIndication) { + mIndicationQueue.add(type); + } + + mIndicationMessages.put(type, newIndication); + } + + if (mIsDozing) { + return; + } + + final boolean showNow = showImmediately + || mCurrIndicationType == INDICATION_TYPE_NONE + || mCurrIndicationType == type; + if (hasNewIndication) { + if (showNow) { + showIndication(type); + } else if (!isNextIndicationScheduled()) { + scheduleShowNextIndication(); + } + return; + } + + if (mCurrIndicationType == type + && !hasNewIndication + && showImmediately) { + if (mShowNextIndicationRunnable != null) { + mShowNextIndicationRunnable.runImmediately(); + } else { + showIndication(INDICATION_TYPE_NONE); + } + } + } + + /** + * Stop showing the following indication type. + * + * If the current indication is of this type, immediately stops showing the message. + */ + public void hideIndication(@IndicationType int type) { + updateIndication(type, null, true); + } + + /** + * Show a transient message. + * Transient messages: + * - show immediately + * - will continue to be in the rotation of messages shown until hideTransient is called. + * - can be presented with an "error" color if isError is true + */ + public void showTransient(CharSequence newIndication, boolean isError) { + updateIndication(INDICATION_TYPE_TRANSIENT, + new KeyguardIndication.Builder() + .setMessage(newIndication) + .setTextColor(isError + ? Utils.getColorError(getContext()) + : mInitialTextColorState) + .build(), + /* showImmediately */true); + } + + /** + * Hide a transient message immediately. + */ + public void hideTransient() { + hideIndication(INDICATION_TYPE_TRANSIENT); + } + + /** + * @return true if there are available indications to show + */ + public boolean hasIndications() { + return mIndicationMessages.keySet().size() > 0; + } + + /** + * Immediately show the passed indication type and schedule the next indication to show. + * Will re-add this indication to be re-shown after all other indications have been + * rotated through. + */ + private void showIndication(@IndicationType int type) { + cancelScheduledIndication(); + + mCurrIndicationType = type; + mIndicationQueue.removeIf(x -> x == type); + if (mCurrIndicationType == INDICATION_TYPE_NONE) { + mView.setVisibility(View.GONE); + } else { + mView.setVisibility(View.VISIBLE); + mIndicationQueue.add(type); // re-add to show later + } + + // pass the style update to be run right before our new indication is shown: + mView.switchIndication(mIndicationMessages.get(type)); + + // only schedule next indication if there's more than just this indication in the queue + if (mCurrIndicationType != INDICATION_TYPE_NONE && mIndicationQueue.size() > 1) { + scheduleShowNextIndication(); + } + } + + protected boolean isNextIndicationScheduled() { + return mShowNextIndicationRunnable != null; + } + + private void scheduleShowNextIndication() { + cancelScheduledIndication(); + mShowNextIndicationRunnable = new ShowNextIndication(DEFAULT_INDICATION_SHOW_LENGTH); + } + + private void cancelScheduledIndication() { + if (mShowNextIndicationRunnable != null) { + mShowNextIndicationRunnable.cancelDelayedExecution(); + mShowNextIndicationRunnable = null; + } + } + + private StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onDozeAmountChanged(float linear, float eased) { + mView.setAlpha((1 - linear) * mMaxAlpha); + } + + @Override + public void onDozingChanged(boolean isDozing) { + if (isDozing == mIsDozing) return; + mIsDozing = isDozing; + if (mIsDozing) { + showIndication(INDICATION_TYPE_NONE); + } else if (mIndicationQueue.size() > 0) { + showIndication(mIndicationQueue.remove(0)); + } + } + }; + + /** + * Shows the next indication in the IndicationQueue after an optional delay. + * This wrapper has the ability to cancel itself (remove runnable from DelayableExecutor) or + * immediately run itself (which also removes itself from the DelayableExecutor). + */ + class ShowNextIndication { + private final Runnable mShowIndicationRunnable; + private Runnable mCancelDelayedRunnable; + + ShowNextIndication(long delay) { + mShowIndicationRunnable = () -> { + int type = mIndicationQueue.size() == 0 + ? INDICATION_TYPE_NONE : mIndicationQueue.remove(0); + showIndication(type); + }; + mCancelDelayedRunnable = mExecutor.executeDelayed(mShowIndicationRunnable, delay); + } + + public void runImmediately() { + cancelDelayedExecution(); + mShowIndicationRunnable.run(); + } + + public void cancelDelayedExecution() { + if (mCancelDelayedRunnable != null) { + mCancelDelayedRunnable.run(); + mCancelDelayedRunnable = null; + } + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("KeyguardIndicationRotatingTextViewController:"); + pw.println(" currentMessage=" + mView.getText()); + pw.println(" dozing:" + mIsDozing); + pw.println(" queue:" + mIndicationQueue.toString()); + pw.println(" showNextIndicationRunnable:" + mShowNextIndicationRunnable); + + if (hasIndications()) { + pw.println(" All messages:"); + for (int type : mIndicationMessages.keySet()) { + pw.println(" type=" + type + " message=" + mIndicationMessages.get(type)); + } + } + } + + private static final int INDICATION_TYPE_NONE = -1; + public static final int INDICATION_TYPE_OWNER_INFO = 0; + public static final int INDICATION_TYPE_DISCLOSURE = 1; + public static final int INDICATION_TYPE_LOGOUT = 2; + public static final int INDICATION_TYPE_BATTERY = 3; + public static final int INDICATION_TYPE_ALIGNMENT = 4; + public static final int INDICATION_TYPE_TRANSIENT = 5; + public static final int INDICATION_TYPE_TRUST = 6; + public static final int INDICATION_TYPE_RESTING = 7; + public static final int INDICATION_TYPE_USER_LOCKED = 8; + public static final int INDICATION_TYPE_NOW_PLAYING = 9; + public static final int INDICATION_TYPE_REVERSE_CHARGING = 10; + + @IntDef({ + INDICATION_TYPE_NONE, + INDICATION_TYPE_DISCLOSURE, + INDICATION_TYPE_OWNER_INFO, + INDICATION_TYPE_LOGOUT, + INDICATION_TYPE_BATTERY, + INDICATION_TYPE_ALIGNMENT, + INDICATION_TYPE_TRANSIENT, + INDICATION_TYPE_TRUST, + INDICATION_TYPE_RESTING, + INDICATION_TYPE_USER_LOCKED, + INDICATION_TYPE_NOW_PLAYING, + INDICATION_TYPE_REVERSE_CHARGING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface IndicationType{} +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index c816784a22f2..c70a93b5c894 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -16,10 +16,24 @@ package com.android.systemui.statusbar; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1; import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.app.ActivityManager; +import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -47,6 +61,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; +import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.ViewClippingUtil; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; @@ -56,13 +71,16 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; +import com.android.systemui.keyguard.KeyguardIndication; +import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -76,8 +94,7 @@ import javax.inject.Inject; * Controls the indications and error messages shown on the Keyguard */ @SysUISingleton -public class KeyguardIndicationController implements StateListener, - KeyguardStateController.Callback { +public class KeyguardIndicationController implements KeyguardStateController.Callback { private static final String TAG = "KeyguardIndication"; private static final boolean DEBUG_CHARGING_SPEED = false; @@ -94,14 +111,17 @@ public class KeyguardIndicationController implements StateListener, private final StatusBarStateController mStatusBarStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private ViewGroup mIndicationArea; - private KeyguardIndicationTextView mTextView; - private KeyguardIndicationTextView mDisclosure; + private KeyguardIndicationTextView mTopIndicationView; private final IBatteryStats mBatteryInfo; private final SettableWakeLock mWakeLock; private final DockManager mDockManager; private final DevicePolicyManager mDevicePolicyManager; private final UserManager mUserManager; + private final @Main DelayableExecutor mExecutor; + private final LockPatternUtils mLockPatternUtils; + private final IActivityManager mIActivityManager; + protected KeyguardIndicationRotateTextViewController mRotateTextViewController; private BroadcastReceiver mBroadcastReceiver; private LockscreenLockIconController mLockIconController; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -110,7 +130,7 @@ public class KeyguardIndicationController implements StateListener, private String mAlignmentIndication; private CharSequence mTransientIndication; private boolean mTransientTextIsError; - private ColorStateList mInitialTextColorState; + protected ColorStateList mInitialTextColorState; private boolean mVisible; private boolean mHideTransientMessageOnScreenOff; @@ -124,8 +144,8 @@ public class KeyguardIndicationController implements StateListener, private int mBatteryLevel; private boolean mBatteryPresent = true; private long mChargingTimeRemaining; - private float mDisclosureMaxAlpha; private String mMessageToShowOnScreenOn; + protected int mLockScreenMode; private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; @@ -151,7 +171,8 @@ public class KeyguardIndicationController implements StateListener, BroadcastDispatcher broadcastDispatcher, DevicePolicyManager devicePolicyManager, IBatteryStats iBatteryStats, - UserManager userManager) { + UserManager userManager, + @Main DelayableExecutor executor) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mDevicePolicyManager = devicePolicyManager; @@ -165,23 +186,29 @@ public class KeyguardIndicationController implements StateListener, wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG); mBatteryInfo = iBatteryStats; mUserManager = userManager; + mExecutor = executor; + mLockPatternUtils = new LockPatternUtils(context); + mIActivityManager = ActivityManager.getService(); mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); mKeyguardUpdateMonitor.registerCallback(mTickReceiver); - mStatusBarStateController.addCallback(this); + mStatusBarStateController.addCallback(mStatusBarStateListener); mKeyguardStateController.addCallback(this); } public void setIndicationArea(ViewGroup indicationArea) { mIndicationArea = indicationArea; - mTextView = indicationArea.findViewById(R.id.keyguard_indication_text); - mInitialTextColorState = mTextView != null ? - mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE); - mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure); - mDisclosureMaxAlpha = mDisclosure.getAlpha(); + mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text); + mInitialTextColorState = mTopIndicationView != null + ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE); + mRotateTextViewController = new KeyguardIndicationRotateTextViewController( + indicationArea.findViewById(R.id.keyguard_indication_text_bottom), + mExecutor, + mStatusBarStateController, + mLockScreenMode); updateIndication(false /* animate */); updateDisclosure(); - + updateOwnerInfo(); if (mBroadcastReceiver == null) { // Update the disclosure proactively to avoid IPC on the critical path. mBroadcastReceiver = new BroadcastReceiver() { @@ -233,19 +260,196 @@ public class KeyguardIndicationController implements StateListener, return mUpdateMonitorCallback; } + /** + * Doesn't include owner information or disclosure which get triggered separately. + */ + private void updateIndications(boolean animate, int userId) { + updateBattery(animate); + updateUserLocked(userId); + updateTransient(); + updateTrust(userId, getTrustGrantedIndication(), getTrustManagedIndication()); + updateAlignment(); + updateResting(); + } + private void updateDisclosure() { - // NOTE: Because this uses IPC, avoid calling updateDisclosure() on a critical path. if (whitelistIpcs(this::isOrganizationOwnedDevice)) { - CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName(); - if (organizationName != null) { - mDisclosure.switchIndication(mContext.getResources().getString( - R.string.do_disclosure_with_name, organizationName)); - } else { - mDisclosure.switchIndication(R.string.do_disclosure_generic); + final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName(); + final CharSequence disclosure = organizationName != null + ? mContext.getResources().getString(R.string.do_disclosure_with_name, + organizationName) + : mContext.getResources().getText(R.string.do_disclosure_generic); + mRotateTextViewController.updateIndication( + INDICATION_TYPE_DISCLOSURE, + new KeyguardIndication.Builder() + .setMessage(disclosure) + .setTextColor(mInitialTextColorState) + .build(), + /* updateImmediately */ false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_DISCLOSURE); + } + + if (isKeyguardLayoutEnabled()) { + updateIndication(false); // resting indication may need to update + } + } + + private void updateBattery(boolean animate) { + if (mPowerPluggedIn || mEnableBatteryDefender) { + String powerIndication = computePowerIndication(); + if (DEBUG_CHARGING_SPEED) { + powerIndication += ", " + (mChargingWattage / 1000) + " mW"; } - mDisclosure.setVisibility(View.VISIBLE); + + mRotateTextViewController.updateIndication( + INDICATION_TYPE_BATTERY, + new KeyguardIndication.Builder() + .setMessage(powerIndication) + .setTextColor(mInitialTextColorState) + .build(), + animate); } else { - mDisclosure.setVisibility(View.GONE); + // don't show the charging information if device isn't plugged in + mRotateTextViewController.hideIndication(INDICATION_TYPE_BATTERY); + } + } + + private void updateUserLocked(int userId) { + if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_USER_LOCKED, + new KeyguardIndication.Builder() + .setMessage(mContext.getResources().getText( + com.android.internal.R.string.lockscreen_storage_locked)) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_USER_LOCKED); + } + } + + private void updateTransient() { + if (!TextUtils.isEmpty(mTransientIndication)) { + mRotateTextViewController.showTransient(mTransientIndication, + mTransientTextIsError); + } else { + mRotateTextViewController.hideTransient(); + } + } + + private void updateTrust(int userId, CharSequence trustGrantedIndication, + CharSequence trustManagedIndication) { + if (!TextUtils.isEmpty(trustGrantedIndication) + && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_TRUST, + new KeyguardIndication.Builder() + .setMessage(trustGrantedIndication) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else if (!TextUtils.isEmpty(trustManagedIndication) + && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) + && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_TRUST, + new KeyguardIndication.Builder() + .setMessage(trustManagedIndication) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_TRUST); + } + } + + private void updateAlignment() { + if (!TextUtils.isEmpty(mAlignmentIndication)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_ALIGNMENT, + new KeyguardIndication.Builder() + .setMessage(mAlignmentIndication) + .setTextColor(Utils.getColorError(mContext)) + .build(), + true); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_ALIGNMENT); + } + } + + private void updateResting() { + if (mRestingIndication != null + && !mRotateTextViewController.hasIndications()) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_RESTING, + new KeyguardIndication.Builder() + .setMessage(mRestingIndication) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + mRotateTextViewController.hideIndication(INDICATION_TYPE_RESTING); + } + } + + protected boolean isKeyguardLayoutEnabled() { + return mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1; + } + + private void updateLogoutView() { + if (!isKeyguardLayoutEnabled()) { + return; + } + final boolean shouldShowLogout = mKeyguardUpdateMonitor.isLogoutEnabled() + && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM; + String logoutString = shouldShowLogout ? mContext.getResources().getString( + com.android.internal.R.string.global_action_logout) : null; + mRotateTextViewController.updateIndication( + INDICATION_TYPE_LOGOUT, + new KeyguardIndication.Builder() + .setMessage(logoutString) + .setTextColor(mInitialTextColorState) + .setBackground(mContext.getDrawable( + com.android.systemui.R.drawable.logout_button_background)) + .setClickListener((view) -> { + int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); + try { + mIActivityManager.switchUser(UserHandle.USER_SYSTEM); + mIActivityManager.stopUser(currentUserId, true /* force */, null); + } catch (RemoteException re) { + Log.e(TAG, "Failed to logout user", re); + } + }) + .build(), + false); + updateIndication(false); // resting indication may need to update + } + + private void updateOwnerInfo() { + if (!isKeyguardLayoutEnabled()) { + return; + } + String info = mLockPatternUtils.getDeviceOwnerInfo(); + if (info == null) { + // Use the current user owner information if enabled. + final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled( + KeyguardUpdateMonitor.getCurrentUser()); + if (ownerInfoEnabled) { + info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser()); + } + } + if (info != null) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_OWNER_INFO, + new KeyguardIndication.Builder() + .setMessage(info) + .setTextColor(mInitialTextColorState) + .build(), + false); + } else { + updateIndication(false); // resting indication may need to update } } @@ -281,9 +485,10 @@ public class KeyguardIndicationController implements StateListener, return UserHandle.USER_NULL; } - public void setVisible(boolean visible) { + @VisibleForTesting + protected void setVisible(boolean visible) { mVisible = visible; - mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE); + mIndicationArea.setVisibility(visible ? VISIBLE : GONE); if (visible) { // If this is called after an error message was already shown, we should not clear it. // Otherwise the error message won't be shown @@ -382,6 +587,7 @@ public class KeyguardIndicationController implements StateListener, mTransientIndication = null; mHideTransientMessageOnScreenOff = false; mHandler.removeMessages(MSG_HIDE_TRANSIENT); + mRotateTextViewController.hideTransient(); updateIndication(false); } } @@ -396,98 +602,109 @@ public class KeyguardIndicationController implements StateListener, } // A few places might need to hide the indication, so always start by making it visible - mIndicationArea.setVisibility(View.VISIBLE); + mIndicationArea.setVisibility(VISIBLE); // Walk down a precedence-ordered list of what indication // should be shown based on user or device state + // AoD if (mDozing) { + mTopIndicationView.setVisibility(VISIBLE); // When dozing we ignore any text color and use white instead, because // colors can be hard to read in low brightness. - mTextView.setTextColor(Color.WHITE); + mTopIndicationView.setTextColor(Color.WHITE); if (!TextUtils.isEmpty(mTransientIndication)) { - mTextView.switchIndication(mTransientIndication); + mTopIndicationView.switchIndication(mTransientIndication, null); } else if (!mBatteryPresent) { // If there is no battery detected, hide the indication and bail - mIndicationArea.setVisibility(View.GONE); + mIndicationArea.setVisibility(GONE); } else if (!TextUtils.isEmpty(mAlignmentIndication)) { - mTextView.switchIndication(mAlignmentIndication); - mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); + mTopIndicationView.switchIndication(mAlignmentIndication, null); + mTopIndicationView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); } else if (mPowerPluggedIn || mEnableBatteryDefender) { String indication = computePowerIndication(); if (animate) { - animateText(mTextView, indication); + animateText(mTopIndicationView, indication); } else { - mTextView.switchIndication(indication); + mTopIndicationView.switchIndication(indication, null); } } else { String percentage = NumberFormat.getPercentInstance() .format(mBatteryLevel / 100f); - mTextView.switchIndication(percentage); + mTopIndicationView.switchIndication(percentage, null); } return; } + // LOCK SCREEN + // Some cases here might need to hide the indication (if the battery is not present) int userId = KeyguardUpdateMonitor.getCurrentUser(); - String trustGrantedIndication = getTrustGrantedIndication(); - String trustManagedIndication = getTrustManagedIndication(); - - String powerIndication = null; - if (mPowerPluggedIn || mEnableBatteryDefender) { - powerIndication = computePowerIndication(); - } - // Some cases here might need to hide the indication (if the battery is not present) - boolean hideIndication = false; - boolean isError = false; - if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { - mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); - } else if (!TextUtils.isEmpty(mTransientIndication)) { - if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - mTransientIndication, powerIndication); - mTextView.switchIndication(indication); - hideIndication = !mBatteryPresent; - } else { - mTextView.switchIndication(mTransientIndication); + if (mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1) { + mTopIndicationView.setVisibility(GONE); + updateIndications(animate, userId); + } else { + boolean hideIndication = false; + boolean isError = false; + String trustGrantedIndication = getTrustGrantedIndication(); + String trustManagedIndication = getTrustManagedIndication(); + String powerIndication = null; + + if (mPowerPluggedIn || mEnableBatteryDefender) { + powerIndication = computePowerIndication(); } - isError = mTransientTextIsError; - } else if (!TextUtils.isEmpty(trustGrantedIndication) - && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - if (powerIndication != null) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - trustGrantedIndication, powerIndication); - mTextView.switchIndication(indication); + if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { + mTopIndicationView.switchIndication( + com.android.internal.R.string.lockscreen_storage_locked); + } else if (!TextUtils.isEmpty(mTransientIndication)) { + if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + mTransientIndication, powerIndication); + mTopIndicationView.switchIndication(indication, null); + hideIndication = !mBatteryPresent; + } else { + mTopIndicationView.switchIndication(mTransientIndication, null); + } + isError = mTransientTextIsError; + } else if (!TextUtils.isEmpty(trustGrantedIndication) + && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + if (powerIndication != null) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + trustGrantedIndication, powerIndication); + mTopIndicationView.switchIndication(indication, null); + hideIndication = !mBatteryPresent; + } else { + mTopIndicationView.switchIndication(trustGrantedIndication, null); + } + } else if (!TextUtils.isEmpty(mAlignmentIndication)) { + mTopIndicationView.switchIndication(mAlignmentIndication, null); + isError = true; + hideIndication = !mBatteryPresent; + } else if (mPowerPluggedIn || mEnableBatteryDefender) { + if (DEBUG_CHARGING_SPEED) { + powerIndication += ", " + (mChargingWattage / 1000) + " mW"; + } + if (animate) { + animateText(mTopIndicationView, powerIndication); + } else { + mTopIndicationView.switchIndication(powerIndication, null); + } hideIndication = !mBatteryPresent; + } else if (!TextUtils.isEmpty(trustManagedIndication) + && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) + && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mTopIndicationView.switchIndication(trustManagedIndication, null); } else { - mTextView.switchIndication(trustGrantedIndication); - } - } else if (!TextUtils.isEmpty(mAlignmentIndication)) { - mTextView.switchIndication(mAlignmentIndication); - isError = true; - hideIndication = !mBatteryPresent; - } else if (mPowerPluggedIn || mEnableBatteryDefender) { - if (DEBUG_CHARGING_SPEED) { - powerIndication += ", " + (mChargingWattage / 1000) + " mW"; + mTopIndicationView.switchIndication(mRestingIndication, null); } - if (animate) { - animateText(mTextView, powerIndication); - } else { - mTextView.switchIndication(powerIndication); + + mTopIndicationView.setTextColor( + isError ? Utils.getColorError(mContext) : mInitialTextColorState); + + if (hideIndication) { + mIndicationArea.setVisibility(GONE); } - hideIndication = !mBatteryPresent; - } else if (!TextUtils.isEmpty(trustManagedIndication) - && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) - && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - mTextView.switchIndication(trustManagedIndication); - } else { - mTextView.switchIndication(mRestingIndication); - } - mTextView.setTextColor(isError ? Utils.getColorError(mContext) - : mInitialTextColorState); - if (hideIndication) { - mIndicationArea.setVisibility(View.GONE); } } @@ -510,7 +727,7 @@ public class KeyguardIndicationController implements StateListener, @Override public void onAnimationStart(Animator animation) { - textView.switchIndication(indication); + textView.switchIndication(indication, null); } @Override @@ -634,18 +851,6 @@ public class KeyguardIndicationController implements StateListener, } } - public void setDozing(boolean dozing) { - if (mDozing == dozing) { - return; - } - mDozing = dozing; - if (mHideTransientMessageOnScreenOff && mDozing) { - hideTransientIndication(); - } else { - updateIndication(false); - } - } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("KeyguardIndicationController:"); pw.println(" mTransientTextIsError: " + mTransientTextIsError); @@ -659,23 +864,10 @@ public class KeyguardIndicationController implements StateListener, pw.println(" mDozing: " + mDozing); pw.println(" mBatteryLevel: " + mBatteryLevel); pw.println(" mBatteryPresent: " + mBatteryPresent); - pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText())); + pw.println(" mTextView.getText(): " + ( + mTopIndicationView == null ? null : mTopIndicationView.getText())); pw.println(" computePowerIndication(): " + computePowerIndication()); - } - - @Override - public void onStateChanged(int newState) { - // don't care - } - - @Override - public void onDozingChanged(boolean isDozing) { - setDozing(isDozing); - } - - @Override - public void onDozeAmountChanged(float linear, float eased) { - mDisclosure.setAlpha((1 - linear) * mDisclosureMaxAlpha); + mRotateTextViewController.dump(fd, pw, args); } @Override @@ -687,6 +879,11 @@ public class KeyguardIndicationController implements StateListener, public static final int HIDE_DELAY_MS = 5000; @Override + public void onLockScreenModeChanged(int mode) { + mLockScreenMode = mode; + } + + @Override public void onRefreshBatteryInfo(BatteryStatus status) { boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING || status.status == BatteryManager.BATTERY_STATUS_FULL; @@ -845,6 +1042,7 @@ public class KeyguardIndicationController implements StateListener, @Override public void onUserSwitchComplete(int userId) { if (mVisible) { + updateOwnerInfo(); updateIndication(false); } } @@ -857,11 +1055,39 @@ public class KeyguardIndicationController implements StateListener, } @Override + public void onLogoutEnabledChanged() { + if (mVisible) { + updateLogoutView(); + } + } + + @Override public void onRequireUnlockForNfc() { showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc), false /* isError */, false /* hideOnScreenOff */); hideTransientIndicationDelayed(HIDE_DELAY_MS); } - } + + private StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + setVisible(newState == StatusBarState.KEYGUARD); + } + + @Override + public void onDozingChanged(boolean dozing) { + if (mDozing == dozing) { + return; + } + mDozing = dozing; + + if (mHideTransientMessageOnScreenOff && mDozing) { + hideTransientIndication(); + } else { + updateIndication(false); + } + } + }; } 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 0a366c9bb380..3ff04d2d83a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -123,8 +123,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private KeyguardAffordanceView mRightAffordanceView; private KeyguardAffordanceView mLeftAffordanceView; private ViewGroup mIndicationArea; - private TextView mEnterpriseDisclosure; private TextView mIndicationText; + private TextView mIndicationTextBottom; private ViewGroup mPreviewContainer; private ViewGroup mOverlayContainer; @@ -237,9 +237,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mRightAffordanceView = findViewById(R.id.camera_button); mLeftAffordanceView = findViewById(R.id.left_button); mIndicationArea = findViewById(R.id.keyguard_indication_area); - mEnterpriseDisclosure = findViewById( - R.id.keyguard_indication_enterprise_disclosure); mIndicationText = findViewById(R.id.keyguard_indication_text); + mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom); mIndicationBottomMargin = getResources().getDimensionPixelSize( R.dimen.keyguard_indication_margin_bottom); mBurnInYOffset = getResources().getDimensionPixelSize( @@ -316,7 +315,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } // Respect font size setting. - mEnterpriseDisclosure.setTextSize(TypedValue.COMPLEX_UNIT_PX, + mIndicationTextBottom.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize( com.android.internal.R.dimen.text_size_small_material)); mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index 30c951ac518c..8970a9a20931 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -16,11 +16,15 @@ package com.android.systemui.statusbar.phone; +import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; @@ -28,6 +32,7 @@ import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Interpolators; +import com.android.systemui.keyguard.KeyguardIndication; import java.util.LinkedList; @@ -35,13 +40,15 @@ import java.util.LinkedList; * A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open"). */ public class KeyguardIndicationTextView extends TextView { - private static final int FADE_OUT_MILLIS = 200; private static final int FADE_IN_MILLIS = 250; private static final long MSG_DURATION_MILLIS = 600; private long mNextAnimationTime = 0; private boolean mAnimationsEnabled = true; private LinkedList<CharSequence> mMessages = new LinkedList<>(); + private LinkedList<KeyguardIndication> mKeyguardIndicationInfo = new LinkedList<>(); + + private boolean mUseNewAnimations = false; public KeyguardIndicationTextView(Context context) { super(context); @@ -60,12 +67,33 @@ public class KeyguardIndicationTextView extends TextView { super(context, attrs, defStyleAttr, defStyleRes); } + public void setLockScreenMode(int lockScreenMode) { + mUseNewAnimations = lockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1; + } + + /** + * Changes the text with an animation and makes sure a single indication is shown long enough. + */ + public void switchIndication(int textResId) { + switchIndication(getResources().getText(textResId), null); + } + + /** + * Changes the text with an animation and makes sure a single indication is shown long enough. + * + * @param indication The text to show. + */ + public void switchIndication(KeyguardIndication indication) { + switchIndication(indication == null ? null : indication.getMessage(), indication); + } + /** * Changes the text with an animation and makes sure a single indication is shown long enough. * * @param text The text to show. + * @param indication optional display information for the text */ - public void switchIndication(CharSequence text) { + public void switchIndication(CharSequence text, KeyguardIndication indication) { if (text == null) text = ""; CharSequence lastPendingMessage = mMessages.peekLast(); @@ -74,55 +102,119 @@ public class KeyguardIndicationTextView extends TextView { return; } mMessages.add(text); + mKeyguardIndicationInfo.add(indication); - Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f); - fadeOut.setDuration(getFadeOutMillis()); - fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); - - final CharSequence nextText = text; - fadeOut.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animator) { - setText(mMessages.poll()); - } - }); - + final boolean hasIcon = indication != null && indication.getIcon() != null; final AnimatorSet animSet = new AnimatorSet(); - final AnimatorSet.Builder animSetBuilder = animSet.play(fadeOut); + final AnimatorSet.Builder animSetBuilder = animSet.play(getOutAnimator()); // Make sure each animation is visible for a minimum amount of time, while not worrying // about fading in blank text long timeInMillis = System.currentTimeMillis(); long delay = Math.max(0, mNextAnimationTime - timeInMillis); - setNextAnimationTime(timeInMillis + delay + getFadeOutMillis()); + setNextAnimationTime(timeInMillis + delay + getFadeOutDuration()); - if (!text.equals("")) { + if (!text.equals("") || hasIcon) { setNextAnimationTime(mNextAnimationTime + MSG_DURATION_MILLIS); - - ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f); - fadeIn.setDuration(getFadeInMillis()); - fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - animSetBuilder.before(fadeIn); + animSetBuilder.before(getInAnimator()); } animSet.setStartDelay(delay); animSet.start(); } + private AnimatorSet getOutAnimator() { + AnimatorSet animatorSet = new AnimatorSet(); + Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f); + fadeOut.setDuration(getFadeOutDuration()); + fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + fadeOut.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + KeyguardIndication info = mKeyguardIndicationInfo.poll(); + if (info != null) { + setTextColor(info.getTextColor()); + setOnClickListener(info.getClickListener()); + final Drawable icon = info.getIcon(); + if (icon != null) { + icon.setTint(getCurrentTextColor()); + if (icon instanceof AnimatedVectorDrawable) { + ((AnimatedVectorDrawable) icon).start(); + } + } + setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null); + } + setText(mMessages.poll()); + } + }); + + if (mUseNewAnimations) { + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -getYTranslationPixels()); + yTranslate.setDuration(getFadeOutDuration()); + fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + animatorSet.playTogether(fadeOut, yTranslate); + } else { + animatorSet.play(fadeOut); + } + + return animatorSet; + } + + private AnimatorSet getInAnimator() { + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f); + fadeIn.setStartDelay(getFadeInDelay()); + fadeIn.setDuration(getFadeInDuration()); + fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + + if (mUseNewAnimations) { + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, getYTranslationPixels(), 0); + yTranslate.setDuration(getYInDuration()); + yTranslate.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + setTranslationY(0); + } + }); + animatorSet.playTogether(yTranslate, fadeIn); + } else { + animatorSet.play(fadeIn); + } + + return animatorSet; + } + @VisibleForTesting public void setAnimationsEnabled(boolean enabled) { mAnimationsEnabled = enabled; } - private long getFadeInMillis() { - if (mAnimationsEnabled) return FADE_IN_MILLIS; + private long getFadeInDelay() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 150L; return 0L; } - private long getFadeOutMillis() { - if (mAnimationsEnabled) return FADE_OUT_MILLIS; + private long getFadeInDuration() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 317L; + return FADE_IN_MILLIS; + } + + private long getYInDuration() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 600L; return 0L; } + private long getFadeOutDuration() { + if (!mAnimationsEnabled) return 0L; + if (mUseNewAnimations) return 167L; + return FADE_OUT_MILLIS; + } + private void setNextAnimationTime(long time) { if (mAnimationsEnabled) { mNextAnimationTime = time; @@ -131,10 +223,8 @@ public class KeyguardIndicationTextView extends TextView { } } - /** - * See {@link #switchIndication}. - */ - public void switchIndication(int textResId) { - switchIndication(getResources().getText(textResId)); + private int getYTranslationPixels() { + return mContext.getResources().getDimensionPixelSize( + com.android.systemui.R.dimen.keyguard_indication_y_translation); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 83651398be43..1425dabc6d9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1136,8 +1136,6 @@ public class StatusBar extends SystemUI implements DemoMode, mLockscreenWallpaper = mLockscreenWallpaperLazy.get(); } - mKeyguardIndicationController.setIndicationArea( - mNotificationShadeWindowView.findViewById(R.id.keyguard_indication_area)); mNotificationPanelViewController.setKeyguardIndicationController( mKeyguardIndicationController); @@ -3613,26 +3611,19 @@ public class StatusBar extends SystemUI implements DemoMode, mNavigationBarController.touchAutoDim(mDisplayId); Trace.beginSection("StatusBar#updateKeyguardState"); if (mState == StatusBarState.KEYGUARD) { - mKeyguardIndicationController.setVisible(true); if (mKeyguardUserSwitcher != null) { mKeyguardUserSwitcher.setKeyguard(true, mStatusBarStateController.fromShadeLocked()); } if (mStatusBarView != null) mStatusBarView.removePendingHideExpandedRunnables(); - if (mAmbientIndicationContainer != null) { - mAmbientIndicationContainer.setVisibility(View.VISIBLE); - } } else { - mKeyguardIndicationController.setVisible(false); if (mKeyguardUserSwitcher != null) { mKeyguardUserSwitcher.setKeyguard(false, mStatusBarStateController.goingToFullShade() || mState == StatusBarState.SHADE_LOCKED || mStatusBarStateController.fromShadeLocked()); } - if (mAmbientIndicationContainer != null) { - mAmbientIndicationContainer.setVisibility(View.INVISIBLE); - } + } updateDozingState(); checkBarModes(); |