Driven by a new framework config bool. Tested on all layouts, but disabled by default. Can be enabled with a config overlay. (Sliding/MultiPane)ChallengeLayout have custom measure/layout logic that peeks at the root view's padding (!). So we must keep the root view's padding = system window insets. However, we need the scrims + the fullscreen camera widget preview to use the entire display size if the bars are transparent. So the approach is to maintain the root view's (KeyguardViewManager$ViewManagerHost) padding as before via setFitsSystemWindows(true) but allow non KeyguardHostViews to extend into the padding if bars are transparent via a custom measure/draw on the root view. KeyguardHostView background + challege scrims moved up to the root view. Also: - Remove logspam line for fixed bug. - Fix status bar staying opaque on interaction on secure keyguard. Known issues: - Nav bar blip dismissing global actions, keyguard app widget picker bars are opaque. Both will use the new inherit flag once it exists. - Emergency dialer layout dance, will be fixed in the phone project. Bug:10606085 Change-Id: I2648b4ee9a54a67324ce9dcbab976ad4da4bbcfa
510 lines
20 KiB
Java
510 lines
20 KiB
Java
/*
|
|
* Copyright (C) 2007 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.keyguard;
|
|
|
|
import com.android.internal.policy.IKeyguardShowCallback;
|
|
import com.android.internal.widget.LockPatternUtils;
|
|
|
|
import android.app.Activity;
|
|
import android.app.ActivityManager;
|
|
import android.appwidget.AppWidgetManager;
|
|
import android.content.Context;
|
|
import android.content.pm.ActivityInfo;
|
|
import android.content.res.Configuration;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.ColorFilter;
|
|
import android.graphics.PixelFormat;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Bundle;
|
|
import android.os.IBinder;
|
|
import android.os.Parcelable;
|
|
import android.os.RemoteException;
|
|
import android.os.SystemProperties;
|
|
import android.util.Log;
|
|
import android.util.Slog;
|
|
import android.util.SparseArray;
|
|
import android.view.KeyEvent;
|
|
import android.view.LayoutInflater;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewManager;
|
|
import android.view.WindowManager;
|
|
import android.widget.FrameLayout;
|
|
|
|
/**
|
|
* Manages creating, showing, hiding and resetting the keyguard. Calls back
|
|
* via {@link KeyguardViewMediator.ViewMediatorCallback} to poke
|
|
* the wake lock and report that the keyguard is done, which is in turn,
|
|
* reported to this class by the current {@link KeyguardViewBase}.
|
|
*/
|
|
public class KeyguardViewManager {
|
|
private final static boolean DEBUG = KeyguardViewMediator.DEBUG;
|
|
private static String TAG = "KeyguardViewManager";
|
|
public static boolean USE_UPPER_CASE = true;
|
|
public final static String IS_SWITCHING_USER = "is_switching_user";
|
|
|
|
// Timeout used for keypresses
|
|
static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
|
|
|
|
private final Context mContext;
|
|
private final ViewManager mViewManager;
|
|
private final KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
|
|
|
|
private WindowManager.LayoutParams mWindowLayoutParams;
|
|
private boolean mNeedsInput = false;
|
|
|
|
private ViewManagerHost mKeyguardHost;
|
|
private KeyguardHostView mKeyguardView;
|
|
|
|
private boolean mScreenOn = false;
|
|
private LockPatternUtils mLockPatternUtils;
|
|
|
|
public interface ShowListener {
|
|
void onShown(IBinder windowToken);
|
|
};
|
|
|
|
/**
|
|
* @param context Used to create views.
|
|
* @param viewManager Keyguard will be attached to this.
|
|
* @param callback Used to notify of changes.
|
|
* @param lockPatternUtils
|
|
*/
|
|
public KeyguardViewManager(Context context, ViewManager viewManager,
|
|
KeyguardViewMediator.ViewMediatorCallback callback,
|
|
LockPatternUtils lockPatternUtils) {
|
|
mContext = context;
|
|
mViewManager = viewManager;
|
|
mViewMediatorCallback = callback;
|
|
mLockPatternUtils = lockPatternUtils;
|
|
}
|
|
|
|
/**
|
|
* Show the keyguard. Will handle creating and attaching to the view manager
|
|
* lazily.
|
|
*/
|
|
public synchronized void show(Bundle options) {
|
|
if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
|
|
|
|
boolean enableScreenRotation = shouldEnableScreenRotation();
|
|
|
|
maybeCreateKeyguardLocked(enableScreenRotation, false, options);
|
|
maybeEnableScreenRotation(enableScreenRotation);
|
|
|
|
// Disable common aspects of the system/status/navigation bars that are not appropriate or
|
|
// useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED
|
|
// activities. Other disabled bits are handled by the KeyguardViewMediator talking
|
|
// directly to the status bar service.
|
|
int visFlags = View.STATUS_BAR_DISABLE_HOME;
|
|
if (shouldEnableTransparentBars()) {
|
|
visFlags |= View.SYSTEM_UI_FLAG_TRANSPARENT_STATUS
|
|
| View.SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION;
|
|
}
|
|
if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");
|
|
mKeyguardHost.setSystemUiVisibility(visFlags);
|
|
|
|
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
|
|
mKeyguardHost.setVisibility(View.VISIBLE);
|
|
mKeyguardView.show();
|
|
mKeyguardView.requestFocus();
|
|
}
|
|
|
|
private boolean shouldEnableScreenRotation() {
|
|
Resources res = mContext.getResources();
|
|
return SystemProperties.getBoolean("lockscreen.rot_override",false)
|
|
|| res.getBoolean(R.bool.config_enableLockScreenRotation);
|
|
}
|
|
|
|
private boolean shouldEnableTransparentBars() {
|
|
Resources res = mContext.getResources();
|
|
return res.getBoolean(R.bool.config_enableLockScreenTransparentBars);
|
|
}
|
|
|
|
class ViewManagerHost extends FrameLayout {
|
|
private static final int BACKGROUND_COLOR = 0x70000000;
|
|
// This is a faster way to draw the background on devices without hardware acceleration
|
|
private final Drawable mBackgroundDrawable = new Drawable() {
|
|
@Override
|
|
public void draw(Canvas canvas) {
|
|
canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC);
|
|
}
|
|
|
|
@Override
|
|
public void setAlpha(int alpha) {
|
|
}
|
|
|
|
@Override
|
|
public void setColorFilter(ColorFilter cf) {
|
|
}
|
|
|
|
@Override
|
|
public int getOpacity() {
|
|
return PixelFormat.TRANSLUCENT;
|
|
}
|
|
};
|
|
private final View mScrimView;
|
|
private boolean mExtendIntoPadding;
|
|
public ViewManagerHost(Context context, boolean extendIntoPadding) {
|
|
super(context);
|
|
mExtendIntoPadding = extendIntoPadding;
|
|
setFitsSystemWindows(true);
|
|
setClipToPadding(!mExtendIntoPadding);
|
|
setBackground(mBackgroundDrawable);
|
|
|
|
mScrimView = new View(context);
|
|
mScrimView.setVisibility(View.GONE);
|
|
mScrimView.setBackgroundColor(0x99000000);
|
|
addView(mScrimView);
|
|
}
|
|
|
|
private boolean considerPadding(View child) {
|
|
return !mExtendIntoPadding || child instanceof KeyguardHostView;
|
|
}
|
|
|
|
@Override
|
|
protected void measureChildWithMargins(View child,
|
|
int parentWidthMeasureSpec, int widthUsed,
|
|
int parentHeightMeasureSpec, int heightUsed) {
|
|
if (considerPadding(child)) {
|
|
// don't extend into padding (default behavior)
|
|
super.measureChildWithMargins(child,
|
|
parentWidthMeasureSpec, widthUsed,
|
|
parentHeightMeasureSpec, heightUsed);
|
|
} else {
|
|
// allowed to extend into padding (scrim / camera preview)
|
|
child.measure(parentWidthMeasureSpec, parentHeightMeasureSpec);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
|
final int count = getChildCount();
|
|
for (int i = 0; i < count; i++) {
|
|
final View child = getChildAt(i);
|
|
int cl = l, ct = t, cr = r, cb = b;
|
|
if (considerPadding(child)) {
|
|
cl += mPaddingLeft;
|
|
ct += mPaddingTop;
|
|
cr -= mPaddingRight;
|
|
cb -= mPaddingBottom;
|
|
}
|
|
child.layout(cl, ct, cr, cb);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onConfigurationChanged(Configuration newConfig) {
|
|
super.onConfigurationChanged(newConfig);
|
|
if (mKeyguardHost.getVisibility() == View.VISIBLE) {
|
|
// only propagate configuration messages if we're currently showing
|
|
maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
|
|
} else {
|
|
if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
|
if (mKeyguardView != null) {
|
|
// Always process back and menu keys, regardless of focus
|
|
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
|
int keyCode = event.getKeyCode();
|
|
if (keyCode == KeyEvent.KEYCODE_BACK && mKeyguardView.handleBackKey()) {
|
|
return true;
|
|
} else if (keyCode == KeyEvent.KEYCODE_MENU && mKeyguardView.handleMenuKey()) {
|
|
return true;
|
|
}
|
|
}
|
|
// Always process media keys, regardless of focus
|
|
if (mKeyguardView.dispatchKeyEvent(event)) {
|
|
return true;
|
|
}
|
|
}
|
|
return super.dispatchKeyEvent(event);
|
|
}
|
|
}
|
|
|
|
SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>();
|
|
|
|
private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
|
|
Bundle options) {
|
|
if (mKeyguardHost != null) {
|
|
mKeyguardHost.saveHierarchyState(mStateContainer);
|
|
}
|
|
|
|
if (mKeyguardHost == null) {
|
|
if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
|
|
|
|
mKeyguardHost = new ViewManagerHost(mContext, shouldEnableTransparentBars());
|
|
|
|
int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
|
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
|
|
| WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
|
|
| WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
|
|
|
|
if (!mNeedsInput) {
|
|
flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
|
}
|
|
|
|
final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
|
|
final int type = WindowManager.LayoutParams.TYPE_KEYGUARD;
|
|
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
|
stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);
|
|
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
|
|
lp.windowAnimations = R.style.Animation_LockScreen;
|
|
lp.screenOrientation = enableScreenRotation ?
|
|
ActivityInfo.SCREEN_ORIENTATION_USER : ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
|
|
|
|
if (ActivityManager.isHighEndGfx()) {
|
|
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
|
|
lp.privateFlags |=
|
|
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
|
|
}
|
|
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;
|
|
lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
|
|
lp.setTitle("Keyguard");
|
|
mWindowLayoutParams = lp;
|
|
mViewManager.addView(mKeyguardHost, lp);
|
|
}
|
|
|
|
if (force || mKeyguardView == null) {
|
|
inflateKeyguardView(options);
|
|
mKeyguardView.requestFocus();
|
|
}
|
|
updateUserActivityTimeoutInWindowLayoutParams();
|
|
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
|
|
|
|
mKeyguardHost.restoreHierarchyState(mStateContainer);
|
|
}
|
|
|
|
private void inflateKeyguardView(Bundle options) {
|
|
View v = mKeyguardHost.findViewById(R.id.keyguard_host_view);
|
|
if (v != null) {
|
|
mKeyguardHost.removeView(v);
|
|
}
|
|
final LayoutInflater inflater = LayoutInflater.from(mContext);
|
|
View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true);
|
|
mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view);
|
|
mKeyguardView.setLockPatternUtils(mLockPatternUtils);
|
|
mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);
|
|
mKeyguardView.initializeSwitchingUserState(options != null &&
|
|
options.getBoolean(IS_SWITCHING_USER));
|
|
mKeyguardView.setScrimView(mKeyguardHost.mScrimView);
|
|
|
|
// HACK
|
|
// The keyguard view will have set up window flags in onFinishInflate before we set
|
|
// the view mediator callback. Make sure it knows the correct IME state.
|
|
if (mViewMediatorCallback != null) {
|
|
KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(
|
|
R.id.keyguard_password_view);
|
|
|
|
if (kpv != null) {
|
|
mViewMediatorCallback.setNeedsInput(kpv.needsInput());
|
|
}
|
|
}
|
|
|
|
if (options != null) {
|
|
int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET,
|
|
AppWidgetManager.INVALID_APPWIDGET_ID);
|
|
if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
|
|
mKeyguardView.goToWidget(widgetToShow);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void updateUserActivityTimeout() {
|
|
updateUserActivityTimeoutInWindowLayoutParams();
|
|
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
|
|
}
|
|
|
|
private void updateUserActivityTimeoutInWindowLayoutParams() {
|
|
// Use the user activity timeout requested by the keyguard view, if any.
|
|
if (mKeyguardView != null) {
|
|
long timeout = mKeyguardView.getUserActivityTimeout();
|
|
if (timeout >= 0) {
|
|
mWindowLayoutParams.userActivityTimeout = timeout;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise, use the default timeout.
|
|
mWindowLayoutParams.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS;
|
|
}
|
|
|
|
private void maybeEnableScreenRotation(boolean enableScreenRotation) {
|
|
// TODO: move this outside
|
|
if (enableScreenRotation) {
|
|
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!");
|
|
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
|
|
} else {
|
|
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
|
|
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
|
|
}
|
|
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
|
|
}
|
|
|
|
public void setNeedsInput(boolean needsInput) {
|
|
mNeedsInput = needsInput;
|
|
if (mWindowLayoutParams != null) {
|
|
if (needsInput) {
|
|
mWindowLayoutParams.flags &=
|
|
~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
|
} else {
|
|
mWindowLayoutParams.flags |=
|
|
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
|
}
|
|
|
|
try {
|
|
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
|
|
} catch (java.lang.IllegalArgumentException e) {
|
|
// TODO: Ensure this method isn't called on views that are changing...
|
|
Log.w(TAG,"Can't update input method on " + mKeyguardHost + " window not attached");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset the state of the view.
|
|
*/
|
|
public synchronized void reset(Bundle options) {
|
|
if (DEBUG) Log.d(TAG, "reset()");
|
|
// User might have switched, check if we need to go back to keyguard
|
|
// TODO: It's preferable to stay and show the correct lockscreen or unlock if none
|
|
maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options);
|
|
}
|
|
|
|
public synchronized void onScreenTurnedOff() {
|
|
if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
|
|
mScreenOn = false;
|
|
if (mKeyguardView != null) {
|
|
mKeyguardView.onScreenTurnedOff();
|
|
}
|
|
}
|
|
|
|
public synchronized void onScreenTurnedOn(final IKeyguardShowCallback callback) {
|
|
if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
|
|
mScreenOn = true;
|
|
if (mKeyguardView != null) {
|
|
mKeyguardView.onScreenTurnedOn();
|
|
|
|
// Caller should wait for this window to be shown before turning
|
|
// on the screen.
|
|
if (callback != null) {
|
|
if (mKeyguardHost.getVisibility() == View.VISIBLE) {
|
|
// Keyguard may be in the process of being shown, but not yet
|
|
// updated with the window manager... give it a chance to do so.
|
|
mKeyguardHost.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
IBinder token = null;
|
|
if (mKeyguardHost.getVisibility() == View.VISIBLE) {
|
|
token = mKeyguardHost.getWindowToken();
|
|
}
|
|
try {
|
|
callback.onShown(token);
|
|
} catch (RemoteException e) {
|
|
Slog.w(TAG, "Exception calling onShown():", e);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
try {
|
|
callback.onShown(null);
|
|
} catch (RemoteException e) {
|
|
Slog.w(TAG, "Exception calling onShown():", e);
|
|
}
|
|
}
|
|
}
|
|
} else if (callback != null) {
|
|
try {
|
|
callback.onShown(null);
|
|
} catch (RemoteException e) {
|
|
Slog.w(TAG, "Exception calling onShown():", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public synchronized void verifyUnlock() {
|
|
if (DEBUG) Log.d(TAG, "verifyUnlock()");
|
|
show(null);
|
|
mKeyguardView.verifyUnlock();
|
|
}
|
|
|
|
/**
|
|
* Hides the keyguard view
|
|
*/
|
|
public synchronized void hide() {
|
|
if (DEBUG) Log.d(TAG, "hide()");
|
|
|
|
if (mKeyguardHost != null) {
|
|
mKeyguardHost.setVisibility(View.GONE);
|
|
|
|
// We really only want to preserve keyguard state for configuration changes. Hence
|
|
// we should clear state of widgets (e.g. Music) when we hide keyguard so it can
|
|
// start with a fresh state when we return.
|
|
mStateContainer.clear();
|
|
|
|
// Don't do this right away, so we can let the view continue to animate
|
|
// as it goes away.
|
|
if (mKeyguardView != null) {
|
|
final KeyguardViewBase lastView = mKeyguardView;
|
|
mKeyguardView = null;
|
|
mKeyguardHost.postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
synchronized (KeyguardViewManager.this) {
|
|
lastView.cleanUp();
|
|
mKeyguardHost.removeView(lastView);
|
|
}
|
|
}
|
|
}, 500);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dismisses the keyguard by going to the next screen or making it gone.
|
|
*/
|
|
public synchronized void dismiss() {
|
|
if (mScreenOn) {
|
|
mKeyguardView.dismiss();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return Whether the keyguard is showing
|
|
*/
|
|
public synchronized boolean isShowing() {
|
|
return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
|
|
}
|
|
|
|
public void showAssistant() {
|
|
if (mKeyguardView != null) {
|
|
mKeyguardView.showAssistant();
|
|
}
|
|
}
|
|
|
|
public void dispatch(MotionEvent event) {
|
|
if (mKeyguardView != null) {
|
|
mKeyguardView.dispatch(event);
|
|
}
|
|
}
|
|
}
|