Files
frameworks_base/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java
Jorim Jaggi 5cf17879a3 Reuse KeyguardViewMediator for new Keyguard implementation.
This change reuses KeyguardViewMediator for the new Keyguard
implementation in status bar. KeyguardViewManager is replaced by
StatusBarKeyguardManager which handles adding the view, setting the
state etc. StatusBarWindowManager is introduced to managed the window
of the status bar, which has the logic of both the old Keyguard window
and the old status bar window. In the current implementation, Keyguard
gets displayed like it would be in the bouncer state, but that's likely
to change in the future. Also, setHidden in IKeyguardService is also
renamed to setOccluded, as the word hidden interferes with the
terminology when dismissing the Keyguard.

Bug: 13635952
Change-Id: I1c5d5a49d810d8532089f464cb2efe35e577f517
2014-03-31 20:58:31 +02:00

524 lines
19 KiB
Java

/*
* Copyright (C) 2012 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 android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import com.android.keyguard.KeyguardActivityLauncher.CameraWidgetInfo;
public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnClickListener {
private static final String TAG = CameraWidgetFrame.class.getSimpleName();
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final int WIDGET_ANIMATION_DURATION = 250; // ms
private static final int WIDGET_WAIT_DURATION = 400; // ms
private static final int RECOVERY_DELAY = 1000; // ms
interface Callbacks {
void onLaunchingCamera();
void onCameraLaunchedSuccessfully();
void onCameraLaunchedUnsuccessfully();
}
private final Handler mHandler = new Handler();
private final KeyguardActivityLauncher mActivityLauncher;
private final Callbacks mCallbacks;
private final CameraWidgetInfo mWidgetInfo;
private final WindowManager mWindowManager;
private final Point mRenderedSize = new Point();
private final int[] mTmpLoc = new int[2];
private long mLaunchCameraStart;
private boolean mActive;
private boolean mTransitioning;
private boolean mDown;
private final Rect mInsets = new Rect();
private FixedSizeFrameLayout mPreview;
private View mFullscreenPreview;
private View mFakeNavBar;
private boolean mUseFastTransition;
private final Runnable mTransitionToCameraRunnable = new Runnable() {
@Override
public void run() {
transitionToCamera();
}};
private final Runnable mTransitionToCameraEndAction = new Runnable() {
@Override
public void run() {
if (!mTransitioning)
return;
Handler worker = getWorkerHandler() != null ? getWorkerHandler() : mHandler;
mLaunchCameraStart = SystemClock.uptimeMillis();
if (DEBUG) Log.d(TAG, "Launching camera at " + mLaunchCameraStart);
mActivityLauncher.launchCamera(worker, mSecureCameraActivityStartedRunnable);
}};
private final Runnable mPostTransitionToCameraEndAction = new Runnable() {
@Override
public void run() {
mHandler.post(mTransitionToCameraEndAction);
}};
private final Runnable mRecoverRunnable = new Runnable() {
@Override
public void run() {
recover();
}};
private final Runnable mRenderRunnable = new Runnable() {
@Override
public void run() {
render();
}};
private final Runnable mSecureCameraActivityStartedRunnable = new Runnable() {
@Override
public void run() {
onSecureCameraActivityStarted();
}
};
private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
private boolean mShowing;
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (mShowing == showing)
return;
mShowing = showing;
CameraWidgetFrame.this.onKeyguardVisibilityChanged(mShowing);
}
};
private static final class FixedSizeFrameLayout extends FrameLayout {
int width;
int height;
FixedSizeFrameLayout(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
setMeasuredDimension(width, height);
}
}
private CameraWidgetFrame(Context context, Callbacks callbacks,
KeyguardActivityLauncher activityLauncher,
CameraWidgetInfo widgetInfo, View previewWidget) {
super(context);
mCallbacks = callbacks;
mActivityLauncher = activityLauncher;
mWidgetInfo = widgetInfo;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
KeyguardUpdateMonitor.getInstance(context).registerCallback(mCallback);
mPreview = new FixedSizeFrameLayout(context);
mPreview.addView(previewWidget);
addView(mPreview);
View clickBlocker = new View(context);
clickBlocker.setBackgroundColor(Color.TRANSPARENT);
clickBlocker.setOnClickListener(this);
addView(clickBlocker);
setContentDescription(context.getString(R.string.keyguard_accessibility_camera));
if (DEBUG) Log.d(TAG, "new CameraWidgetFrame instance " + instanceId());
}
public static CameraWidgetFrame create(Context context, Callbacks callbacks,
KeyguardActivityLauncher launcher) {
if (context == null || callbacks == null || launcher == null)
return null;
CameraWidgetInfo widgetInfo = launcher.getCameraWidgetInfo();
if (widgetInfo == null)
return null;
View previewWidget = getPreviewWidget(context, widgetInfo);
if (previewWidget == null)
return null;
return new CameraWidgetFrame(context, callbacks, launcher, widgetInfo, previewWidget);
}
private static View getPreviewWidget(Context context, CameraWidgetInfo widgetInfo) {
return widgetInfo.layoutId > 0 ?
inflateWidgetView(context, widgetInfo) :
inflateGenericWidgetView(context);
}
private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) {
if (DEBUG) Log.d(TAG, "inflateWidgetView: " + widgetInfo.contextPackage);
View widgetView = null;
Exception exception = null;
try {
Context cameraContext = context.createPackageContext(
widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
LayoutInflater cameraInflater = (LayoutInflater)
cameraContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
cameraInflater = cameraInflater.cloneInContext(cameraContext);
widgetView = cameraInflater.inflate(widgetInfo.layoutId, null, false);
} catch (NameNotFoundException e) {
exception = e;
} catch (RuntimeException e) {
exception = e;
}
if (exception != null) {
Log.w(TAG, "Error creating camera widget view", exception);
}
return widgetView;
}
private static View inflateGenericWidgetView(Context context) {
if (DEBUG) Log.d(TAG, "inflateGenericWidgetView");
ImageView iv = new ImageView(context);
iv.setImageResource(R.drawable.ic_lockscreen_camera);
iv.setScaleType(ScaleType.CENTER);
iv.setBackgroundColor(Color.argb(127, 0, 0, 0));
return iv;
}
private void render() {
final View root = getRootView();
final int width = root.getWidth() - mInsets.right; // leave room
final int height = root.getHeight() - mInsets.bottom; // for bars
if (mRenderedSize.x == width && mRenderedSize.y == height) {
if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s %d%%",
width, height, (int)(100*mPreview.getScaleX())));
return;
}
if (width == 0 || height == 0) {
return;
}
mPreview.width = width;
mPreview.height = height;
mPreview.requestLayout();
final int thisWidth = getWidth() - getPaddingLeft() - getPaddingRight();
final int thisHeight = getHeight() - getPaddingTop() - getPaddingBottom();
final float pvScaleX = (float) thisWidth / width;
final float pvScaleY = (float) thisHeight / height;
final float pvScale = Math.min(pvScaleX, pvScaleY);
final int pvWidth = (int) (pvScale * width);
final int pvHeight = (int) (pvScale * height);
final float pvTransX = pvWidth < thisWidth ? (thisWidth - pvWidth) / 2 : 0;
final float pvTransY = pvHeight < thisHeight ? (thisHeight - pvHeight) / 2 : 0;
final boolean isRtl = mPreview.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
mPreview.setPivotX(isRtl ? mPreview.width : 0);
mPreview.setPivotY(0);
mPreview.setScaleX(pvScale);
mPreview.setScaleY(pvScale);
mPreview.setTranslationX((isRtl ? -1 : 1) * pvTransX);
mPreview.setTranslationY(pvTransY);
mRenderedSize.set(width, height);
if (DEBUG) Log.d(TAG, String.format("Rendered camera widget size=%sx%s %d%% instance=%s",
width, height, (int)(100*mPreview.getScaleX()), instanceId()));
}
private void transitionToCamera() {
if (mTransitioning || mDown) return;
mTransitioning = true;
enableWindowExitAnimation(false);
final int navHeight = mInsets.bottom;
final int navWidth = mInsets.right;
mPreview.getLocationInWindow(mTmpLoc);
final float pvHeight = mPreview.getHeight() * mPreview.getScaleY();
final float pvCenter = mTmpLoc[1] + pvHeight / 2f;
final ViewGroup root = (ViewGroup) getRootView();
if (DEBUG) {
Log.d(TAG, "root = " + root.getLeft() + "," + root.getTop() + " "
+ root.getWidth() + "x" + root.getHeight());
}
if (mFullscreenPreview == null) {
mFullscreenPreview = getPreviewWidget(mContext, mWidgetInfo);
mFullscreenPreview.setClickable(false);
root.addView(mFullscreenPreview, new FrameLayout.LayoutParams(
root.getWidth() - navWidth,
root.getHeight() - navHeight));
}
final float fsHeight = root.getHeight() - navHeight;
final float fsCenter = root.getTop() + fsHeight / 2;
final float fsScaleY = mPreview.getScaleY();
final float fsTransY = pvCenter - fsCenter;
final float fsScaleX = fsScaleY;
mPreview.setVisibility(View.GONE);
mFullscreenPreview.setVisibility(View.VISIBLE);
mFullscreenPreview.setTranslationY(fsTransY);
mFullscreenPreview.setScaleX(fsScaleX);
mFullscreenPreview.setScaleY(fsScaleY);
mFullscreenPreview
.animate()
.scaleX(1)
.scaleY(1)
.translationX(0)
.translationY(0)
.setDuration(WIDGET_ANIMATION_DURATION)
.withEndAction(mPostTransitionToCameraEndAction)
.start();
if (navHeight > 0 || navWidth > 0) {
final boolean atBottom = navHeight > 0;
if (mFakeNavBar == null) {
mFakeNavBar = new View(mContext);
mFakeNavBar.setBackgroundColor(Color.BLACK);
root.addView(mFakeNavBar, new FrameLayout.LayoutParams(
atBottom ? FrameLayout.LayoutParams.MATCH_PARENT
: navWidth,
atBottom ? navHeight
: FrameLayout.LayoutParams.MATCH_PARENT,
atBottom ? Gravity.BOTTOM|Gravity.FILL_HORIZONTAL
: Gravity.RIGHT|Gravity.FILL_VERTICAL));
mFakeNavBar.setPivotY(navHeight);
mFakeNavBar.setPivotX(navWidth);
}
mFakeNavBar.setAlpha(0f);
if (atBottom) {
mFakeNavBar.setScaleY(0.5f);
} else {
mFakeNavBar.setScaleX(0.5f);
}
mFakeNavBar.setVisibility(View.VISIBLE);
mFakeNavBar.animate()
.alpha(1f)
.scaleY(1f)
.scaleY(1f)
.setDuration(WIDGET_ANIMATION_DURATION)
.start();
}
mCallbacks.onLaunchingCamera();
}
private void recover() {
if (DEBUG) Log.d(TAG, "recovering at " + SystemClock.uptimeMillis());
mCallbacks.onCameraLaunchedUnsuccessfully();
reset();
}
@Override
public void setOnLongClickListener(OnLongClickListener l) {
// ignore
}
@Override
public void onClick(View v) {
if (DEBUG) Log.d(TAG, "clicked");
if (mTransitioning) return;
if (mActive) {
cancelTransitionToCamera();
transitionToCamera();
}
}
@Override
protected void onDetachedFromWindow() {
if (DEBUG) Log.d(TAG, "onDetachedFromWindow: instance " + instanceId()
+ " at " + SystemClock.uptimeMillis());
super.onDetachedFromWindow();
KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mCallback);
cancelTransitionToCamera();
mHandler.removeCallbacks(mRecoverRunnable);
}
@Override
public void onActive(boolean isActive) {
mActive = isActive;
if (mActive) {
rescheduleTransitionToCamera();
} else {
reset();
}
}
@Override
public boolean onUserInteraction(MotionEvent event) {
if (mTransitioning) {
if (DEBUG) Log.d(TAG, "onUserInteraction eaten: mTransitioning");
return true;
}
getLocationOnScreen(mTmpLoc);
int rawBottom = mTmpLoc[1] + getHeight();
if (event.getRawY() > rawBottom) {
if (DEBUG) Log.d(TAG, "onUserInteraction eaten: below widget");
return true;
}
int action = event.getAction();
mDown = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE;
if (mActive) {
rescheduleTransitionToCamera();
}
if (DEBUG) Log.d(TAG, "onUserInteraction observed, not eaten");
return false;
}
@Override
protected void onFocusLost() {
if (DEBUG) Log.d(TAG, "onFocusLost at " + SystemClock.uptimeMillis());
cancelTransitionToCamera();
super.onFocusLost();
}
public void onScreenTurnedOff() {
if (DEBUG) Log.d(TAG, "onScreenTurnedOff");
reset();
}
private void rescheduleTransitionToCamera() {
if (DEBUG) Log.d(TAG, "rescheduleTransitionToCamera at " + SystemClock.uptimeMillis());
mHandler.removeCallbacks(mTransitionToCameraRunnable);
final long duration = mUseFastTransition ? 0 : WIDGET_WAIT_DURATION;
mHandler.postDelayed(mTransitionToCameraRunnable, duration);
}
private void cancelTransitionToCamera() {
if (DEBUG) Log.d(TAG, "cancelTransitionToCamera at " + SystemClock.uptimeMillis());
mHandler.removeCallbacks(mTransitionToCameraRunnable);
}
private void onCameraLaunched() {
mCallbacks.onCameraLaunchedSuccessfully();
reset();
}
private void reset() {
if (DEBUG) Log.d(TAG, "reset at " + SystemClock.uptimeMillis());
mLaunchCameraStart = 0;
mTransitioning = false;
mDown = false;
cancelTransitionToCamera();
mHandler.removeCallbacks(mRecoverRunnable);
mPreview.setVisibility(View.VISIBLE);
if (mFullscreenPreview != null) {
mFullscreenPreview.animate().cancel();
mFullscreenPreview.setVisibility(View.GONE);
}
if (mFakeNavBar != null) {
mFakeNavBar.animate().cancel();
mFakeNavBar.setVisibility(View.GONE);
}
enableWindowExitAnimation(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (DEBUG) Log.d(TAG, String.format("onSizeChanged new=%sx%s old=%sx%s at %s",
w, h, oldw, oldh, SystemClock.uptimeMillis()));
if ((w != oldw && oldw > 0) || (h != oldh && oldh > 0)) {
// we can't trust the old geometry anymore; force a re-render
mRenderedSize.x = mRenderedSize.y = -1;
}
mHandler.post(mRenderRunnable);
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
public void onBouncerShowing(boolean showing) {
if (showing) {
mTransitioning = false;
mHandler.post(mRecoverRunnable);
}
}
private void enableWindowExitAnimation(boolean isEnabled) {
View root = getRootView();
ViewGroup.LayoutParams lp = root.getLayoutParams();
if (!(lp instanceof WindowManager.LayoutParams))
return;
WindowManager.LayoutParams wlp = (WindowManager.LayoutParams) lp;
int newWindowAnimations = isEnabled ? R.style.Animation_LockScreen : 0;
if (newWindowAnimations != wlp.windowAnimations) {
if (DEBUG) Log.d(TAG, "setting windowAnimations to: " + newWindowAnimations
+ " at " + SystemClock.uptimeMillis());
wlp.windowAnimations = newWindowAnimations;
mWindowManager.updateViewLayout(root, wlp);
}
}
private void onKeyguardVisibilityChanged(boolean showing) {
if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged " + showing
+ " at " + SystemClock.uptimeMillis());
if (mTransitioning && !showing) {
mTransitioning = false;
mHandler.removeCallbacks(mRecoverRunnable);
if (mLaunchCameraStart > 0) {
long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
mLaunchCameraStart = 0;
onCameraLaunched();
}
}
}
private void onSecureCameraActivityStarted() {
if (DEBUG) Log.d(TAG, "onSecureCameraActivityStarted at " + SystemClock.uptimeMillis());
mHandler.postDelayed(mRecoverRunnable, RECOVERY_DELAY);
}
private String instanceId() {
return Integer.toHexString(hashCode());
}
public void setInsets(Rect insets) {
if (DEBUG) Log.d(TAG, "setInsets: " + insets);
mInsets.set(insets);
}
public void setUseFastTransition(boolean useFastTransition) {
mUseFastTransition = useFastTransition;
}
}