Preview layouts for phone/camera affordance.
Bug: 15126905
Change-Id: I72cbb90718e6add22a7c579a647f9f405793961a
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 42fb740..f831570 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -22,6 +22,23 @@
android:layout_height="match_parent"
android:layout_width="match_parent"
>
+
+ <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+ android:id="@+id/keyguard_indication_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="70dp"
+ android:layout_gravity="bottom|center_horizontal"
+ android:textStyle="italic"
+ android:textColor="#ffffff"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+ <FrameLayout
+ android:id="@+id/preview_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ </FrameLayout>
+
<com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/camera_button"
android:layout_height="64dp"
@@ -42,16 +59,6 @@
android:scaleType="center"
android:contentDescription="@string/accessibility_phone_button" />
- <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="70dp"
- android:layout_gravity="bottom|center_horizontal"
- android:textStyle="italic"
- android:textColor="#ffffff"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
-
<com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/lock_icon"
android:layout_width="64dp"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 9968bbc..9f0f84e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.view.ViewPropertyAnimator;
@@ -72,7 +73,7 @@
* @param endValue the end value of the animator
* @param velocity the current velocity of the motion
*/
- public void apply(ValueAnimator animator, float currValue, float endValue, float velocity) {
+ public void apply(Animator animator, float currValue, float endValue, float velocity) {
apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
}
@@ -101,7 +102,7 @@
* @param maxDistance the maximum distance for this interaction; the maximum animation length
* gets multiplied by the ratio between the actual distance and this value
*/
- public void apply(ValueAnimator animator, float currValue, float endValue, float velocity,
+ public void apply(Animator animator, float currValue, float endValue, float velocity,
float maxDistance) {
AnimatorProperties properties = getProperties(currValue, endValue, velocity,
maxDistance);
@@ -168,7 +169,7 @@
* @param maxDistance the maximum distance for this interaction; the maximum animation length
* gets multiplied by the ratio between the actual distance and this value
*/
- public void applyDismissing(ValueAnimator animator, float currValue, float endValue,
+ public void applyDismissing(Animator animator, float currValue, float endValue,
float velocity, float maxDistance) {
AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
maxDistance);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 845e0ae..19bf121 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -28,6 +28,8 @@
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewAnimationUtils;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
@@ -69,6 +71,16 @@
private int mCircleColor;
private boolean mIsLeft;
private float mArrowAlpha = 0.0f;
+ private View mPreviewView;
+ private float mCircleStartRadius;
+ private float mMaxCircleSize;
+ private Animator mPreviewClipper;
+ private AnimatorListenerAdapter mClipEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPreviewClipper = null;
+ }
+ };
private AnimatorListenerAdapter mCircleEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -136,6 +148,7 @@
super.onLayout(changed, left, top, right, bottom);
mCenterX = getWidth() / 2;
mCenterY = getHeight() / 2;
+ mMaxCircleSize = getMaxCircleSize();
}
@Override
@@ -149,6 +162,10 @@
canvas.restore();
}
+ public void setPreviewView(View v) {
+ mPreviewView = v;
+ }
+
private void drawArrow(Canvas canvas) {
if (mArrowAlpha > 0) {
canvas.save();
@@ -183,6 +200,11 @@
private void updateCircleColor() {
float fraction = 0.5f + 0.5f * Math.max(0.0f, Math.min(1.0f,
(mCircleRadius - mMinBackgroundRadius) / (0.5f * mMinBackgroundRadius)));
+ if (mPreviewView != null) {
+ float finishingFraction = 1 - Math.max(0, mCircleRadius - mCircleStartRadius)
+ / (mMaxCircleSize - mCircleStartRadius);
+ fraction *= finishingFraction;
+ }
int color = Color.argb((int) (Color.alpha(mCircleColor) * fraction),
Color.red(mCircleColor),
Color.green(mCircleColor), Color.blue(mCircleColor));
@@ -191,6 +213,8 @@
public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) {
cancelAnimator(mCircleAnimator);
+ cancelAnimator(mPreviewClipper);
+ mCircleStartRadius = mCircleRadius;
float maxCircleSize = getMaxCircleSize();
ValueAnimator animatorToRadius = getAnimatorToRadius(maxCircleSize);
mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize,
@@ -203,6 +227,16 @@
});
animatorToRadius.start();
setImageAlpha(0, true);
+ if (mPreviewView != null) {
+ mPreviewView.setVisibility(View.VISIBLE);
+ mPreviewClipper = ViewAnimationUtils.createCircularReveal(
+ mPreviewView, getLeft() + mCenterX, getTop() + mCenterY, mCircleRadius,
+ maxCircleSize);
+ mFlingAnimationUtils.applyDismissing(mPreviewClipper, mCircleRadius, maxCircleSize,
+ velocity, maxCircleSize);
+ mPreviewClipper.addListener(mClipEndListener);
+ mPreviewClipper.start();
+ }
}
private float getMaxCircleSize() {
@@ -234,6 +268,11 @@
if (mCircleAnimator == null) {
mCircleRadius = circleRadius;
invalidate();
+ if (nowHidden) {
+ if (mPreviewView != null) {
+ mPreviewView.setVisibility(View.INVISIBLE);
+ }
+ }
} else if (!mCircleWillBeHidden) {
// We just update the end value
@@ -244,6 +283,7 @@
}
} else {
cancelAnimator(mCircleAnimator);
+ cancelAnimator(mPreviewClipper);
ValueAnimator animator = getAnimatorToRadius(circleRadius);
Interpolator interpolator = circleRadius == 0.0f
? mDisappearInterpolator
@@ -255,6 +295,22 @@
duration = Math.min(duration, CIRCLE_DISAPPEAR_MAX_DURATION);
animator.setDuration(duration);
animator.start();
+ if (mPreviewView != null) {
+ mPreviewView.setVisibility(View.VISIBLE);
+ mPreviewClipper = ViewAnimationUtils.createCircularReveal(
+ mPreviewView, getLeft() + mCenterX, getTop() + mCenterY, mCircleRadius,
+ circleRadius);
+ mPreviewClipper.setInterpolator(interpolator);
+ mPreviewClipper.setDuration(duration);
+ mPreviewClipper.addListener(mClipEndListener);
+ mPreviewClipper.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPreviewView.setVisibility(View.INVISIBLE);
+ }
+ });
+ mPreviewClipper.start();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index a8a0cb1..9cc559f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -67,7 +67,6 @@
private Animator mSwipeAnimator;
private int mMinBackgroundRadius;
private boolean mMotionPerformedByUser;
- private PowerManager mPM;
private AnimatorListenerAdapter mFlingEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -89,11 +88,12 @@
mLeftIcon.setIsLeft(true);
mCenterIcon = mCallback.getCenterIcon();
mRightIcon = mCallback.getRightIcon();
+ mLeftIcon.setPreviewView(mCallback.getLeftPreview());
+ mRightIcon.setPreviewView(mCallback.getRightPreview());
updateIcon(mLeftIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
updateIcon(mCenterIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
updateIcon(mRightIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
initDimens();
- mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
private void initDimens() {
@@ -334,7 +334,6 @@
float absTranslation = Math.abs(translation);
if (absTranslation > Math.abs(mTranslationOnDown) + mMinTranslationAmount ||
mMotionPerformedByUser) {
- userActivity();
mMotionPerformedByUser = true;
}
if (translation != mTranslation || isReset) {
@@ -387,11 +386,6 @@
return translation * BACKGROUND_RADIUS_SCALE_FACTOR + mMinBackgroundRadius;
}
-
- private void userActivity() {
- mPM.userActivity(SystemClock.uptimeMillis(), false);
- }
-
public void animateHideLeftRightIcon() {
updateIcon(mRightIcon, 0f, 0f, true);
updateIcon(mLeftIcon, 0f, 0f, true);
@@ -475,5 +469,9 @@
KeyguardAffordanceView getCenterIcon();
KeyguardAffordanceView getRightIcon();
+
+ View getLeftPreview();
+
+ View getRightPreview();
}
}
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 b9f012c..b5f517d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -30,6 +30,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -40,6 +41,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.policy.PreviewInflater;
/**
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
@@ -61,6 +63,10 @@
private KeyguardAffordanceView mPhoneImageView;
private KeyguardAffordanceView mLockIcon;
private View mIndicationText;
+ private ViewGroup mPreviewContainer;
+
+ private View mPhonePreview;
+ private View mCameraPreview;
private ActivityStarter mActivityStarter;
private UnlockMethodCache mUnlockMethodCache;
@@ -88,6 +94,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mLockPatternUtils = new LockPatternUtils(mContext);
+ mPreviewContainer = (ViewGroup) findViewById(R.id.preview_container);
mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button);
mPhoneImageView = (KeyguardAffordanceView) findViewById(R.id.phone_button);
mLockIcon = (KeyguardAffordanceView) findViewById(R.id.lock_icon);
@@ -101,6 +108,7 @@
updateTrust();
setClipChildren(false);
setClipToPadding(false);
+ inflatePreviews();
}
public void setActivityStarter(ActivityStarter activityStarter) {
@@ -239,6 +247,14 @@
return mCameraImageView;
}
+ public View getPhonePreview() {
+ return mPhonePreview;
+ }
+
+ public View getCameraPreview() {
+ return mCameraPreview;
+ }
+
public KeyguardAffordanceView getLockIcon() {
return mLockIcon;
}
@@ -258,6 +274,20 @@
updateCameraVisibility();
}
+ private void inflatePreviews() {
+ PreviewInflater inflater = new PreviewInflater(mContext, new LockPatternUtils(mContext));
+ mPhonePreview = inflater.inflatePreview(PHONE_INTENT);
+ mCameraPreview = inflater.inflatePreview(getCameraIntent());
+ if (mPhonePreview != null) {
+ mPreviewContainer.addView(mPhonePreview);
+ mPhonePreview.setVisibility(View.INVISIBLE);
+ }
+ if (mCameraPreview != null) {
+ mPreviewContainer.addView(mCameraPreview);
+ mCameraPreview.setVisibility(View.INVISIBLE);
+ }
+ }
+
private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
post(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPreviewContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPreviewContainer.java
new file mode 100644
index 0000000..7579039
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPreviewContainer.java
@@ -0,0 +1,70 @@
+/*
+ * 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 android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.WindowInsets;
+import android.widget.FrameLayout;
+
+/**
+ * A view group which contains the preview of phone/camera and draws a black bar at the bottom as
+ * the fake navigation bar.
+ */
+public class KeyguardPreviewContainer extends FrameLayout {
+
+ private Drawable mBlackBarDrawable = new Drawable() {
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.save();
+ canvas.clipRect(0, getHeight() - getPaddingBottom(), getWidth(), getHeight());
+ canvas.drawColor(Color.BLACK);
+ canvas.restore();
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // noop
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ // noop
+ }
+
+ @Override
+ public int getOpacity() {
+ return android.graphics.PixelFormat.OPAQUE;
+ }
+ };
+
+ public KeyguardPreviewContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setBackground(mBlackBarDrawable);
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ setPadding(0, 0, 0, insets.getStableInsetBottom());
+ return super.onApplyWindowInsets(insets);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 11a38b5..291e692 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1257,6 +1257,20 @@
}
@Override
+ public View getLeftPreview() {
+ return getLayoutDirection() == LAYOUT_DIRECTION_RTL
+ ? mKeyguardBottomArea.getCameraPreview()
+ : mKeyguardBottomArea.getPhonePreview();
+ }
+
+ @Override
+ public View getRightPreview() {
+ return getLayoutDirection() == LAYOUT_DIRECTION_RTL
+ ? mKeyguardBottomArea.getPhonePreview()
+ : mKeyguardBottomArea.getCameraPreview();
+ }
+
+ @Override
protected float getPeekHeight() {
if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
return mNotificationStackScroller.getPeekHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
new file mode 100644
index 0000000..d405172
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
@@ -0,0 +1,127 @@
+/*
+ * 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.policy;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardActivityLauncher;
+import com.android.systemui.statusbar.phone.KeyguardPreviewContainer;
+
+import java.util.List;
+
+/**
+ * Utility class to inflate previews for phone and camera affordance.
+ */
+public class PreviewInflater {
+
+ private static final String TAG = "PreviewInflater";
+
+ private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
+
+ private Context mContext;
+ private LockPatternUtils mLockPatternUtils;
+
+ public PreviewInflater(Context context, LockPatternUtils lockPatternUtils) {
+ mContext = context;
+ mLockPatternUtils = lockPatternUtils;
+ }
+
+ public View inflatePreview(Intent intent) {
+ WidgetInfo info = getWidgetInfo(intent);
+ if (info == null) {
+ return null;
+ }
+ View v = inflateWidgetView(info);
+ if (v == null) {
+ return null;
+ }
+ KeyguardPreviewContainer container = new KeyguardPreviewContainer(mContext, null);
+ container.addView(v);
+ return container;
+ }
+
+ private View inflateWidgetView(WidgetInfo widgetInfo) {
+ View widgetView = null;
+ try {
+ Context appContext = mContext.createPackageContext(
+ widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
+ LayoutInflater appInflater = (LayoutInflater)
+ appContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ appInflater = appInflater.cloneInContext(appContext);
+ widgetView = appInflater.inflate(widgetInfo.layoutId, null, false);
+ } catch (PackageManager.NameNotFoundException|RuntimeException e) {
+ Log.w(TAG, "Error creating widget view", e);
+ }
+ return widgetView;
+ }
+
+ private WidgetInfo getWidgetInfo(Intent intent) {
+ WidgetInfo info = new WidgetInfo();
+ PackageManager packageManager = mContext.getPackageManager();
+ final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
+ intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
+ if (appList.size() == 0) {
+ return null;
+ }
+ ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
+ PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
+ mLockPatternUtils.getCurrentUser());
+ if (wouldLaunchResolverActivity(resolved, appList)) {
+ return null;
+ }
+ if (resolved == null || resolved.activityInfo == null) {
+ return null;
+ }
+ if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
+ return null;
+ }
+ int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
+ if (layoutId == 0) {
+ return null;
+ }
+ info.contextPackage = resolved.activityInfo.packageName;
+ info.layoutId = layoutId;
+ return info;
+ }
+
+ private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
+ // If the list contains the above resolved activity, then it can't be
+ // ResolverActivity itself.
+ for (int i = 0; i < appList.size(); i++) {
+ ResolveInfo tmp = appList.get(i);
+ if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
+ && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static class WidgetInfo {
+ String contextPackage;
+ int layoutId;
+ }
+}