From 86b6357e5eb91950eac7de7ffe29e5a4ad32903b Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Wed, 24 Oct 2012 11:24:25 -0400 Subject: [PATCH] Manual f/b merge of lockhotness camera integration. Change-Id: I5c7c3c49e13656e9ba4a3761be7d503642d5b76f --- .../res/res/layout/keyguard_camera_widget.xml | 33 --- core/res/res/values-sw600dp/bools.xml | 2 + core/res/res/values/bools.xml | 1 + core/res/res/values/strings.xml | 8 + core/res/res/values/symbols.xml | 6 +- .../impl/keyguard/CameraWidgetFrame.java | 249 ++++++++++++++++++ .../impl/keyguard/KeyguardHostView.java | 28 +- .../impl/keyguard/KeyguardWidgetFrame.java | 4 + .../impl/keyguard/KeyguardWidgetPager.java | 9 + .../impl/keyguard/SlidingChallengeLayout.java | 9 +- 10 files changed, 312 insertions(+), 37 deletions(-) delete mode 100644 core/res/res/layout/keyguard_camera_widget.xml create mode 100644 policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java diff --git a/core/res/res/layout/keyguard_camera_widget.xml b/core/res/res/layout/keyguard_camera_widget.xml deleted file mode 100644 index f1f5817219ec2..0000000000000 --- a/core/res/res/layout/keyguard_camera_widget.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml index 3753aba3749a6..eae4f87d7b09a 100644 --- a/core/res/res/values-sw600dp/bools.xml +++ b/core/res/res/values-sw600dp/bools.xml @@ -19,4 +19,6 @@ true false false + + false diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml index f9762b18f9ad5..d4ead012f3a0f 100644 --- a/core/res/res/values/bools.xml +++ b/core/res/res/values/bools.xml @@ -15,6 +15,7 @@ --> + true true false true diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index f8dbd84da82c8..881e847bb4f11 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3935,6 +3935,14 @@ you will be asked to unlock your phone using an email account.\n\n Try again in %d seconds. + + Camera widget not found + + com.google.android.gallery3d + + com.android.gallery3d + + keyguard_widget diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 322453b7b4a83..a6161185cd84f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1088,7 +1088,6 @@ - @@ -1192,6 +1191,7 @@ + @@ -1381,6 +1381,10 @@ + + + + diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java new file mode 100644 index 0000000000000..09e832cd4ca75 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java @@ -0,0 +1,249 @@ +/* + * 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.internal.policy.impl.keyguard; + +import java.util.List; + +import android.app.ActivityManagerNative; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.graphics.Color; +import android.os.Handler; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.MediaStore; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.TextView; + +import com.android.internal.R; +import com.android.internal.widget.LockPatternUtils; + +public class CameraWidgetFrame extends KeyguardWidgetFrame { + private static final String TAG = CameraWidgetFrame.class.getSimpleName(); + private static final boolean DEBUG = KeyguardHostView.DEBUG; + + interface Callbacks { + void onLaunchingCamera(); + void onCameraLaunched(); + } + + private final Handler mHandler = new Handler(); + private final LockPatternUtils mLockPatternUtils; + private final Callbacks mCallbacks; + + private boolean mCameraWidgetFound; + private long mLaunchCameraStart; + + private final Runnable mLaunchCameraRunnable = new Runnable() { + @Override + public void run() { + launchCamera(); + }}; + + public CameraWidgetFrame(Context context, Callbacks callbacks) { + super(context); + + mLockPatternUtils = new LockPatternUtils(context); + mCallbacks = callbacks; + + View cameraView = createCameraView(); + addView(cameraView); + } + + private View createCameraView() { + View cameraView = null; + Exception exception = null; + try { + String contextPackage = mContext.getString(R.string.kg_camera_widget_context_package); + String layoutPackage = mContext.getString(R.string.kg_camera_widget_layout_package); + String layoutName = mContext.getString(R.string.kg_camera_widget_layout_name); + + Context cameraContext = mContext.createPackageContext( + contextPackage, Context.CONTEXT_RESTRICTED); + LayoutInflater cameraInflater = (LayoutInflater) + cameraContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + cameraInflater = cameraInflater.cloneInContext(cameraContext); + + int layoutId = cameraContext.getResources() + .getIdentifier(layoutName, "layout", layoutPackage); + cameraView = cameraInflater.inflate(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); + } + if (cameraView == null) { + cameraView = createCameraErrorView(); + } else { + mCameraWidgetFound = true; + } + return cameraView; + } + + private View createCameraErrorView() { + TextView errorView = new TextView(mContext); + errorView.setGravity(Gravity.CENTER); + errorView.setText(R.string.kg_camera_widget_not_found); + errorView.setBackgroundColor(Color.argb(127, 0, 0, 0)); + return errorView; + } + + private void transitionToCamera() { + animate() + .scaleX(1.22f) // TODO compute this at runtime + .scaleY(1.22f) + .setDuration(250) + .withEndAction(mLaunchCameraRunnable) + .start(); + mCallbacks.onLaunchingCamera(); + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + if (!mCameraWidgetFound) return; + + if (!hasWindowFocus) { + if (mLaunchCameraStart > 0) { + long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart; + if (DEBUG) Log.d(TAG, String.format("Camera took %s to launch", launchTime)); + mLaunchCameraStart = 0; + } + onCameraLaunched(); + } + } + + @Override + public void onActive(boolean isActive) { + if (!mCameraWidgetFound) return; + + if (isActive) { + mHandler.post(new Runnable(){ + @Override + public void run() { + transitionToCamera(); + }}); + } else { + reset(); + } + } + + private void onCameraLaunched() { + reset(); + mCallbacks.onCameraLaunched(); + } + + private void reset() { + animate().cancel(); + setScaleX(1); + setScaleY(1); + } + + // =========== from KeyguardSelectorView =========== + protected void launchCamera() { + mLaunchCameraStart = SystemClock.uptimeMillis(); + boolean isSecure = mLockPatternUtils.isSecure(); + if (DEBUG) Log.d(TAG, "launchCamera " + (isSecure?"(secure)":"(insecure)")); + if (isSecure) { + // Launch the secure version of the camera + final Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE); + intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + + if (wouldLaunchResolverActivity(intent)) { + // TODO: Show disambiguation dialog instead. + // For now, we'll treat this like launching any other app from secure keyguard. + // When they do, user sees the system's ResolverActivity which lets them choose + // which secure camera to use. + launchActivity(intent, false); + } else { + launchActivity(intent, true); + } + } else { + // Launch the normal camera + launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA), false); + } + } + + /** + * Launches the said intent for the current foreground user. + * @param intent + * @param showsWhileLocked true if the activity can be run on top of keyguard. + * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED} + */ + private void launchActivity(final Intent intent, boolean showsWhileLocked) { + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_SINGLE_TOP + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + boolean isSecure = mLockPatternUtils.isSecure(); + if (!isSecure || showsWhileLocked) { + if (!isSecure) try { + ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); + } catch (RemoteException e) { + Log.w(TAG, "can't dismiss keyguard on launch"); + } + try { + mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "Activity not found for intent + " + intent.getAction()); + } + } else { + Log.w(TAG, "TODO: handle this case"); + onCameraLaunched(); +// // Create a runnable to start the activity and ask the user to enter their +// // credentials. +// mCallback.setOnDismissRunnable(new Runnable() { +// @Override +// public void run() { +// mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); +// } +// }); +// mCallback.dismiss(false); + } + } + + private boolean wouldLaunchResolverActivity(Intent intent) { + PackageManager packageManager = mContext.getPackageManager(); + ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, + PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser()); + final List appList = packageManager.queryIntentActivitiesAsUser( + intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser()); + // 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; + } + +} diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java index 8753c8d5f12be..118a3ce87b6d4 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -834,8 +834,32 @@ public class KeyguardHostView extends KeyguardViewBase { mAppWidgetContainer.addWidget(addWidget); View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true); mAppWidgetContainer.addWidget(statusWidget); - View cameraWidget = inflater.inflate(R.layout.keyguard_camera_widget, null, true); - mAppWidgetContainer.addWidget(cameraWidget); + if (mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) { + View cameraWidget = new CameraWidgetFrame(mContext, new CameraWidgetFrame.Callbacks() { + + private SlidingChallengeLayout locateSlider() { + return (SlidingChallengeLayout) findViewById(R.id.sliding_layout); + } + + @Override + public void onLaunchingCamera() { + SlidingChallengeLayout slider = locateSlider(); + if (slider != null) { + slider.showHandle(false); + } + } + + @Override + public void onCameraLaunched() { + SlidingChallengeLayout slider = locateSlider(); + if (slider != null) { + slider.showHandle(true); + } + mAppWidgetContainer.scrollLeft(); + } + }); + mAppWidgetContainer.addWidget(cameraWidget); + } View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view); addWidgetButton.setOnClickListener(new OnClickListener() { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java index 1026f94a14e1b..e38a445822fd8 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java @@ -265,4 +265,8 @@ public class KeyguardWidgetFrame extends FrameLayout { invalidate(); } } + + public void onActive(boolean isActive) { + // hook for subclasses + } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java index 738c8778801ec..fa8e04b70a20a 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java @@ -98,11 +98,20 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit // Extend the display timeout if the user switches pages if (mPage != newPageIndex) { + int oldPageIndex = mPage; mPage = newPageIndex; if (mCallbacks != null) { mCallbacks.onUserActivityTimeoutChanged(); mCallbacks.userActivity(); } + KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex); + if (oldWidgetPage != null) { + oldWidgetPage.onActive(false); + } + KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex); + if (newWidgetPage != null) { + newWidgetPage.onActive(true); + } } if (mViewStateManager != null) { mViewStateManager.onPageSwitch(newPage, newPageIndex); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java index fc98a044ae4be..28b86cf95a694 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java @@ -42,6 +42,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout // Drawn to show the drag handle in closed state; crossfades to the challenge view // when challenge is fully visible private Drawable mHandleDrawable; + private boolean mShowHandle = true; // Initialized during measurement from child layoutparams private View mChallengeView; @@ -556,7 +557,8 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout @Override public void draw(Canvas c) { super.draw(c); - if (mChallengeOffset < 1.f && mChallengeView != null && mHandleDrawable != null) { + if (mChallengeOffset < 1.f + && mChallengeView != null && mHandleDrawable != null && mShowHandle) { final int top = mChallengeView.getTop(); mHandleDrawable.setBounds(0, top, getWidth(), top + mDragHandleSize); final float alpha = sHandleFadeInterpolator.getInterpolation(1 - mChallengeOffset); @@ -661,6 +663,11 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout showChallenge(true); } + public void showHandle(boolean show) { + mShowHandle = show; + invalidate(); + } + @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs);