OSDN Git Service

Preview layouts for phone/camera affordance.
authorJorim Jaggi <jjaggi@google.com>
Fri, 25 Jul 2014 19:58:29 +0000 (21:58 +0200)
committerJorim Jaggi <jjaggi@google.com>
Mon, 28 Jul 2014 12:06:24 +0000 (12:06 +0000)
Bug: 15126905
Change-Id: I72cbb90718e6add22a7c579a647f9f405793961a

packages/SystemUI/res/layout/keyguard_bottom_area.xml
packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPreviewContainer.java [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java [new file with mode: 0644]

index 42fb740..f831570 100644 (file)
     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"
         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"
index 9968bbc..9f0f84e 100644 (file)
@@ -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 @@ public class FlingAnimationUtils {
      * @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 @@ public class FlingAnimationUtils {
      * @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 @@ public class FlingAnimationUtils {
      * @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);
index 845e0ae..19bf121 100644 (file)
@@ -28,6 +28,8 @@ import android.graphics.Paint;
 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 @@ public class KeyguardAffordanceView extends ImageView {
     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 @@ public class KeyguardAffordanceView extends ImageView {
         super.onLayout(changed, left, top, right, bottom);
         mCenterX = getWidth() / 2;
         mCenterY = getHeight() / 2;
+        mMaxCircleSize = getMaxCircleSize();
     }
 
     @Override
@@ -149,6 +162,10 @@ public class KeyguardAffordanceView extends ImageView {
         canvas.restore();
     }
 
+    public void setPreviewView(View v) {
+        mPreviewView = v;
+    }
+
     private void drawArrow(Canvas canvas) {
         if (mArrowAlpha > 0) {
             canvas.save();
@@ -183,6 +200,11 @@ public class KeyguardAffordanceView extends ImageView {
     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 class KeyguardAffordanceView extends ImageView {
 
     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 @@ public class KeyguardAffordanceView extends ImageView {
         });
         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 @@ public class KeyguardAffordanceView extends ImageView {
             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 @@ public class KeyguardAffordanceView extends ImageView {
             }
         } else {
             cancelAnimator(mCircleAnimator);
+            cancelAnimator(mPreviewClipper);
             ValueAnimator animator = getAnimatorToRadius(circleRadius);
             Interpolator interpolator = circleRadius == 0.0f
                     ? mDisappearInterpolator
@@ -255,6 +295,22 @@ public class KeyguardAffordanceView extends ImageView {
             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();
+            }
         }
     }
 
index a8a0cb1..9cc559f 100644 (file)
@@ -67,7 +67,6 @@ public class KeyguardAffordanceHelper {
     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 @@ public class KeyguardAffordanceHelper {
         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 @@ public class KeyguardAffordanceHelper {
         float absTranslation = Math.abs(translation);
         if (absTranslation > Math.abs(mTranslationOnDown) + mMinTranslationAmount ||
                 mMotionPerformedByUser) {
-            userActivity();
             mMotionPerformedByUser = true;
         }
         if (translation != mTranslation || isReset) {
@@ -387,11 +386,6 @@ public class KeyguardAffordanceHelper {
         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 @@ public class KeyguardAffordanceHelper {
         KeyguardAffordanceView getCenterIcon();
 
         KeyguardAffordanceView getRightIcon();
+
+        View getLeftPreview();
+
+        View getRightPreview();
     }
 }
index b9f012c..b5f517d 100644 (file)
@@ -30,6 +30,7 @@ import android.provider.MediaStore;
 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.keyguard.KeyguardUpdateMonitorCallback;
 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 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
     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 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
     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 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
         updateTrust();
         setClipChildren(false);
         setClipToPadding(false);
+        inflatePreviews();
     }
 
     public void setActivityStarter(ActivityStarter activityStarter) {
@@ -239,6 +247,14 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
         return mCameraImageView;
     }
 
+    public View getPhonePreview() {
+        return mPhonePreview;
+    }
+
+    public View getCameraPreview() {
+        return mCameraPreview;
+    }
+
     public KeyguardAffordanceView getLockIcon() {
         return mLockIcon;
     }
@@ -258,6 +274,20 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
         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 (file)
index 0000000..7579039
--- /dev/null
@@ -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);
+    }
+}
index 11a38b5..291e692 100644 (file)
@@ -1257,6 +1257,20 @@ public class NotificationPanelView extends PanelView implements
     }
 
     @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 (file)
index 0000000..d405172
--- /dev/null
@@ -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;
+    }
+}