Improve scrim handling

Bug: 15163546
Change-Id: I0d2c05b035f832f4b4e6a2fc34113fe9d5677525
This commit is contained in:
Jorim Jaggi
2014-05-26 18:14:37 +02:00
parent a0be6d53a8
commit ecc798e666
10 changed files with 342 additions and 93 deletions

View File

@@ -18,12 +18,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<View android:id="@+id/bouncer_background"
android:background="#cc000000"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include
style="@style/BouncerSecurityContainer"
layout="@layout/keyguard_simple_host_view"

View File

@@ -26,6 +26,10 @@
android:fitsSystemWindows="true"
android:descendantFocusability="afterDescendants">
<View android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/status_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height" />
@@ -40,4 +44,8 @@
android:visibility="gone" />
</com.android.systemui.statusbar.phone.PanelHolder>
<View android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.android.systemui.statusbar.phone.StatusBarWindowView>

View File

@@ -31,8 +31,6 @@
<drawable name="recents_callout_line">#99ffffff</drawable>
<drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable>
<drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
<color name="notification_panel_scrim_color">#A0000000</color>
<color name="notification_panel_scrim_color_keyguard">#80000000</color>
<color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white -->
<color name="batterymeter_charge_color">#FFFFFFFF</color>
<color name="batterymeter_bolt_color">#FFFFFFFF</color>

View File

@@ -62,6 +62,7 @@ import com.android.keyguard.analytics.KeyguardAnalytics;
import com.android.keyguard.analytics.Session;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarWindowManager;
@@ -1316,9 +1317,10 @@ public class KeyguardViewMediator extends SystemUI {
}
public StatusBarKeyguardViewManager registerStatusBar(PhoneStatusBar phoneStatusBar,
ViewGroup container, StatusBarWindowManager statusBarWindowManager) {
ViewGroup container, StatusBarWindowManager statusBarWindowManager,
ScrimController scrimController) {
mStatusBarKeyguardViewManager.registerStatusBar(phoneStatusBar, container,
statusBarWindowManager);
statusBarWindowManager, scrimController);
return mStatusBarKeyguardViewManager;
}

View File

@@ -29,18 +29,11 @@ import android.os.UserHandle;
import android.provider.MediaStore;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
/**
@@ -48,7 +41,8 @@ import com.android.systemui.R;
* text.
*/
public class KeyguardBottomAreaView extends FrameLayout
implements SwipeAffordanceView.AffordanceListener {
implements SwipeAffordanceView.AffordanceListener,
UnlockMethodCache.OnUnlockMethodChangedListener {
final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
@@ -60,9 +54,7 @@ public class KeyguardBottomAreaView extends FrameLayout
private PowerManager mPowerManager;
private ActivityStarter mActivityStarter;
private LockPatternUtils mLockPatternUtils;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private UnlockMethodCache mUnlockMethodCache;
public KeyguardBottomAreaView(Context context) {
super(context);
@@ -89,13 +81,12 @@ public class KeyguardBottomAreaView extends FrameLayout
mLockIcon = (ImageView) findViewById(R.id.lock_icon);
mCameraButton.setAffordanceListener(this);
mPhoneButton.setAffordanceListener(this);
mLockPatternUtils = new LockPatternUtils(getContext());
mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mCallback);
watchForDevicePolicyChanges();
watchForAccessibilityChanges();
updateCameraVisibility();
updatePhoneVisibility();
mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
mUnlockMethodCache.addListener(this);
updateTrust();
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
@@ -211,28 +202,14 @@ public class KeyguardBottomAreaView extends FrameLayout
if (getVisibility() != VISIBLE) {
return;
}
int user = mLockPatternUtils.getCurrentUser();
boolean trust = !mLockPatternUtils.isSecure() ||
mKeyguardUpdateMonitor.getUserHasTrust(user);
int iconRes = trust ? R.drawable.ic_lock_open_24dp : R.drawable.ic_lock_24dp;
int iconRes = mUnlockMethodCache.isMethodInsecure()
? R.drawable.ic_lock_open_24dp
: R.drawable.ic_lock_24dp;
mLockIcon.setImageResource(iconRes);
}
final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onScreenTurnedOn() {
updateTrust();
}
@Override
public void onUserSwitchComplete(int userId) {
updateTrust();
}
@Override
public void onTrustChanged(int userId) {
updateTrust();
}
};
@Override
public void onMethodSecureChanged(boolean methodSecure) {
updateTrust();
}
}

View File

@@ -372,6 +372,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private ViewMediatorCallback mKeyguardViewMediatorCallback;
private ScrimController mScrimController;
private final Runnable mAutohide = new Runnable() {
@Override
@@ -638,9 +639,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_speed_bump, mStackScroller, false);
mStackScroller.setSpeedBumpView(speedBump);
mExpandedContents = mStackScroller;
mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind),
mStatusBarWindow.findViewById(R.id.scrim_in_front));
mStatusBarView.setScrimController(mScrimController);
mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
mHeader.setActivityStarter(this);
mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
@@ -775,7 +779,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private void startKeyguard() {
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
mStatusBarWindow, mStatusBarWindowManager);
mStatusBarWindow, mStatusBarWindowManager, mScrimController);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
}
@@ -1446,6 +1450,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
startActivityDismissingKeyguard(intent, false);
}
public ScrimController getScrimController() {
return mScrimController;
}
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -2771,10 +2779,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mKeyguardBottomArea.setVisibility(View.VISIBLE);
mHeader.setKeyguardShowing(true);
mNotificationPanel.setKeyguardShowing(true);
mScrimController.setKeyguardShowing(true);
} else {
mKeyguardBottomArea.setVisibility(View.GONE);
mHeader.setKeyguardShowing(false);
mNotificationPanel.setKeyguardShowing(false);
mScrimController.setKeyguardShowing(false);
}
updateStackScrollerState();

View File

@@ -36,21 +36,16 @@ public class PhoneStatusBarView extends PanelBar {
private static final boolean DEBUG_GESTURES = true;
PhoneStatusBar mBar;
int mScrimColor;
int mScrimColorKeyguard;
PanelView mFadingPanel = null;
PanelView mLastFullyOpenedPanel = null;
PanelView mNotificationPanel;
private boolean mShouldFade;
private final PhoneStatusBarTransitions mBarTransitions;
private ScrimController mScrimController;
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
Resources res = getContext().getResources();
mScrimColor = res.getColor(R.color.notification_panel_scrim_color);
mScrimColorKeyguard = res.getColor(R.color.notification_panel_scrim_color_keyguard);
mBarTransitions = new PhoneStatusBarTransitions(this);
}
@@ -62,6 +57,10 @@ public class PhoneStatusBarView extends PanelBar {
mBar = bar;
}
public void setScrimController(ScrimController scrimController) {
mScrimController = scrimController;
}
@Override
public void onFinishInflate() {
mBarTransitions.init();
@@ -109,28 +108,12 @@ public class PhoneStatusBarView extends PanelBar {
mBar.makeExpandedVisible(false);
}
@Override
public void startOpeningPanel(PanelView panel) {
super.startOpeningPanel(panel);
// we only want to start fading if this is the "first" or "last" panel,
// which is kind of tricky to determine
mShouldFade = (mFadingPanel == null || mFadingPanel.isFullyExpanded());
if (DEBUG) {
Log.v(TAG, "start opening: " + panel + " shouldfade=" + mShouldFade);
}
mFadingPanel = panel;
}
@Override
public void onAllPanelsCollapsed() {
super.onAllPanelsCollapsed();
// give animations time to settle
mBar.makeExpandedInvisibleSoon();
mFadingPanel = null;
mLastFullyOpenedPanel = null;
if (mScrimColor != 0 && ActivityManager.isHighEndGfx() && mBar.mStatusBarWindow != null) {
mBar.mStatusBarWindow.setBackgroundColor(0);
}
}
@Override
@@ -139,9 +122,7 @@ public class PhoneStatusBarView extends PanelBar {
if (openPanel != mLastFullyOpenedPanel) {
openPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
mFadingPanel = openPanel;
mLastFullyOpenedPanel = openPanel;
mShouldFade = true; // now you own the fade, mister
}
@Override
@@ -163,6 +144,7 @@ public class PhoneStatusBarView extends PanelBar {
public void onTrackingStarted(PanelView panel) {
super.onTrackingStarted(panel);
mBar.onTrackingStarted();
mScrimController.onTrackingStarted();
}
@Override
@@ -184,27 +166,7 @@ public class PhoneStatusBarView extends PanelBar {
Log.v(TAG, "panelExpansionChanged: f=" + frac);
}
if (panel == mFadingPanel && mScrimColor != 0 && ActivityManager.isHighEndGfx()
&& mBar.mStatusBarWindow != null) {
if (mShouldFade) {
int scrimColor = (mBar.getBarState() == StatusBarState.KEYGUARD
|| mBar.getBarState() == StatusBarState.SHADE_LOCKED)
? mScrimColorKeyguard
: mScrimColor;
frac = mPanelExpandedFractionSum; // don't judge me
// let's start this 20% of the way down the screen
frac = frac * 1.2f - 0.2f;
if (frac <= 0) {
mBar.mStatusBarWindow.setBackgroundColor(0);
} else {
// woo, special effects
final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
// attenuate background color alpha by k
final int color = (int) ((scrimColor >>> 24) * k) << 24 | (scrimColor & 0xFFFFFF);
mBar.mStatusBarWindow.setBackgroundColor(color);
}
}
}
mScrimController.setPanelExpansion(frac);
// fade out the panel as it gets buried into the status bar to avoid overdrawing the
// status bar on the last frame of a close animation

View File

@@ -0,0 +1,187 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.statusbar.phone;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
/**
* Controls both the scrim behind the notifications and in front of the notifications (when a
* security method gets shown).
*/
public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
private static final float SCRIM_BEHIND_ALPHA = 0.62f;
private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.5f;
private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
private static final long ANIMATION_DURATION = 220;
private final View mScrimBehind;
private final View mScrimInFront;
private final UnlockMethodCache mUnlockMethodCache;
private boolean mKeyguardShowing;
private float mFraction;
private boolean mDarkenWhileDragging;
private boolean mBouncerShowing;
private boolean mAnimateChange;
private boolean mUpdatePending;
private final Interpolator mInterpolator = new DecelerateInterpolator();
public ScrimController(View scrimBehind, View scrimInFront) {
mScrimBehind = scrimBehind;
mScrimInFront = scrimInFront;
mUnlockMethodCache = UnlockMethodCache.getInstance(scrimBehind.getContext());
}
public void setKeyguardShowing(boolean showing) {
mKeyguardShowing = showing;
scheduleUpdate();
}
public void onTrackingStarted() {
mDarkenWhileDragging = !mUnlockMethodCache.isMethodInsecure();
}
public void setPanelExpansion(float fraction) {
mFraction = fraction;
scheduleUpdate();
}
public void setBouncerShowing(boolean showing) {
mBouncerShowing = showing;
mAnimateChange = true;
scheduleUpdate();
}
private void scheduleUpdate() {
if (mUpdatePending) return;
mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
mUpdatePending = true;
}
private void updateScrims() {
if (!mKeyguardShowing) {
updateScrimNormal();
setScrimInFrontColor(0);
} else {
updateScrimKeyguard();
}
mAnimateChange = false;
}
private void updateScrimKeyguard() {
if (mBouncerShowing) {
setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
setScrimBehindColor(0f);
} else if (mDarkenWhileDragging) {
float behindFraction = Math.max(0, Math.min(mFraction, 1));
float fraction = 1 - behindFraction;
setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA);
setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD);
} else {
setScrimInFrontColor(0f);
setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD);
}
}
private void updateScrimNormal() {
float frac = mFraction;
// let's start this 20% of the way down the screen
frac = frac * 1.2f - 0.2f;
if (frac <= 0) {
setScrimBehindColor(0);
} else {
// woo, special effects
final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
setScrimBehindColor(k * SCRIM_BEHIND_ALPHA);
}
}
private void setScrimBehindColor(float alpha) {
setScrimColor(mScrimBehind, alpha);
}
private void setScrimInFrontColor(float alpha) {
setScrimColor(mScrimInFront, alpha);
if (alpha == 0f) {
mScrimInFront.setClickable(false);
} else {
// Eat touch events.
mScrimInFront.setClickable(true);
}
}
private void setScrimColor(View scrim, float alpha) {
int color = Color.argb((int) (alpha * 255), 0, 0, 0);
if (mAnimateChange) {
startScrimAnimation(scrim, color);
} else {
scrim.setBackgroundColor(color);
}
}
private void startScrimAnimation(final View scrim, int targetColor) {
int current = getBackgroundAlpha(scrim);
int target = Color.alpha(targetColor);
if (current == targetColor) {
return;
}
ValueAnimator anim = ValueAnimator.ofInt(current, target);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
scrim.setBackgroundColor(Color.argb(value, 0, 0, 0));
}
});
anim.setInterpolator(mInterpolator);
anim.setDuration(ANIMATION_DURATION);
anim.start();
}
private int getBackgroundAlpha(View scrim) {
if (scrim.getBackground() instanceof ColorDrawable) {
ColorDrawable drawable = (ColorDrawable) scrim.getBackground();
return Color.alpha(drawable.getColor());
} else {
return 0;
}
}
@Override
public boolean onPreDraw() {
mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
mUpdatePending = false;
updateScrims();
return true;
}
}

View File

@@ -45,6 +45,7 @@ public class StatusBarKeyguardViewManager {
private LockPatternUtils mLockPatternUtils;
private ViewMediatorCallback mViewMediatorCallback;
private PhoneStatusBar mPhoneStatusBar;
private ScrimController mScrimController;
private ViewGroup mContainer;
private StatusBarWindowManager mStatusBarWindowManager;
@@ -68,10 +69,12 @@ public class StatusBarKeyguardViewManager {
}
public void registerStatusBar(PhoneStatusBar phoneStatusBar,
ViewGroup container, StatusBarWindowManager statusBarWindowManager) {
ViewGroup container, StatusBarWindowManager statusBarWindowManager,
ScrimController scrimController) {
mPhoneStatusBar = phoneStatusBar;
mContainer = container;
mStatusBarWindowManager = statusBarWindowManager;
mScrimController = scrimController;
mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
mStatusBarWindowManager, container);
}
@@ -252,6 +255,7 @@ public class StatusBarKeyguardViewManager {
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
mPhoneStatusBar.setBouncerShowing(bouncerShowing);
mScrimController.setBouncerShowing(bouncerShowing);
}
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.statusbar.phone;
import android.content.Context;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import java.util.ArrayList;
/**
* Caches whether the current unlock method is insecure, taking trust into account. This information
* might be a little bit out of date and should not be used for actual security decisions; it should
* be only used for visual indications.
*/
public class UnlockMethodCache {
private static UnlockMethodCache sInstance;
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ArrayList<OnUnlockMethodChangedListener> mListeners = new ArrayList<>();
private boolean mMethodInsecure;
private UnlockMethodCache(Context ctx) {
mLockPatternUtils = new LockPatternUtils(ctx);
mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(ctx);
KeyguardUpdateMonitor.getInstance(ctx).registerCallback(mCallback);
updateMethodSecure(true /* updateAlways */);
}
public static UnlockMethodCache getInstance(Context context) {
if (sInstance == null) {
sInstance = new UnlockMethodCache(context);
}
return sInstance;
}
/**
* @return whether the current security method is secure, i. e. the bouncer will be shown
*/
public boolean isMethodInsecure() {
return mMethodInsecure;
}
public void addListener(OnUnlockMethodChangedListener listener) {
mListeners.add(listener);
}
public void removeListener(OnUnlockMethodChangedListener listener) {
mListeners.remove(listener);
}
private void updateMethodSecure(boolean updateAlways) {
int user = mLockPatternUtils.getCurrentUser();
boolean methodInsecure = !mLockPatternUtils.isSecure() ||
mKeyguardUpdateMonitor.getUserHasTrust(user);
boolean changed = methodInsecure != mMethodInsecure;
if (changed || updateAlways) {
mMethodInsecure = methodInsecure;
notifyListeners(mMethodInsecure);
}
}
private void notifyListeners(boolean secure) {
for (OnUnlockMethodChangedListener listener : mListeners) {
listener.onMethodSecureChanged(secure);
}
}
private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onUserSwitchComplete(int userId) {
updateMethodSecure(false /* updateAlways */);
}
@Override
public void onTrustChanged(int userId) {
updateMethodSecure(false /* updateAlways */);
}
@Override
public void onScreenTurnedOn() {
updateMethodSecure(false /* updateAlways */);
}
};
public static interface OnUnlockMethodChangedListener {
void onMethodSecureChanged(boolean methodSecure);
}
}