diff options
author | Jim Miller <jaggies@google.com> | 2013-09-10 18:37:01 -0700 |
---|---|---|
committer | Jim Miller <jaggies@google.com> | 2013-09-19 17:14:59 -0700 |
commit | caf24fc2c4bb9747eb80138bf3ce0be067851749 (patch) | |
tree | a81859dd75a9ca17e752c32f1ecf0b40a62df1bd | |
parent | f429247867ac524cf63bc01499ac47a90ebcaca9 (diff) | |
download | frameworks_base-caf24fc2c4bb9747eb80138bf3ce0be067851749.zip frameworks_base-caf24fc2c4bb9747eb80138bf3ce0be067851749.tar.gz frameworks_base-caf24fc2c4bb9747eb80138bf3ce0be067851749.tar.bz2 |
Add camera affordance to navigation bar on phones
This adds a camera button on phones that can be used to show
and launch the camera.
- Minor refactoring of touch event dispatch in PagedView.
- Disables usability hints when keyguard loads.
- Only add a touch handler for camera icon once during layout.
- Update after review.
- Updated with latest UX camera and camera background assets
Change-Id: I09cd5cb0e501fd0f4659bea96d00c92b07f805c4
23 files changed, 454 insertions, 83 deletions
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl index d1f7fa3..dd2e006 100644 --- a/core/java/com/android/internal/policy/IKeyguardService.aidl +++ b/core/java/com/android/internal/policy/IKeyguardService.aidl @@ -15,6 +15,8 @@ */ package com.android.internal.policy; +import android.view.MotionEvent; + import com.android.internal.policy.IKeyguardShowCallback; import com.android.internal.policy.IKeyguardExitCallback; @@ -39,4 +41,5 @@ interface IKeyguardService { oneway void doKeyguardTimeout(in Bundle options); oneway void setCurrentUser(int userId); oneway void showAssistant(); + oneway void dispatch(in MotionEvent event); } diff --git a/packages/Keyguard/res/drawable-hdpi/kg_widget_bg_padded.9.png b/packages/Keyguard/res/drawable-hdpi/kg_widget_bg_padded.9.png Binary files differindex dff1dfa..c697f44 100644 --- a/packages/Keyguard/res/drawable-hdpi/kg_widget_bg_padded.9.png +++ b/packages/Keyguard/res/drawable-hdpi/kg_widget_bg_padded.9.png diff --git a/packages/Keyguard/res/drawable-mdpi/kg_widget_bg_padded.9.png b/packages/Keyguard/res/drawable-mdpi/kg_widget_bg_padded.9.png Binary files differindex c313df1..562bc7e 100644 --- a/packages/Keyguard/res/drawable-mdpi/kg_widget_bg_padded.9.png +++ b/packages/Keyguard/res/drawable-mdpi/kg_widget_bg_padded.9.png diff --git a/packages/Keyguard/res/drawable-xhdpi/kg_widget_bg_padded.9.png b/packages/Keyguard/res/drawable-xhdpi/kg_widget_bg_padded.9.png Binary files differindex a84bfa3..b3edfd7 100644 --- a/packages/Keyguard/res/drawable-xhdpi/kg_widget_bg_padded.9.png +++ b/packages/Keyguard/res/drawable-xhdpi/kg_widget_bg_padded.9.png diff --git a/packages/Keyguard/res/drawable-xxhdpi/kg_widget_bg_padded.9.png b/packages/Keyguard/res/drawable-xxhdpi/kg_widget_bg_padded.9.png Binary files differnew file mode 100644 index 0000000..8d31fee --- /dev/null +++ b/packages/Keyguard/res/drawable-xxhdpi/kg_widget_bg_padded.9.png diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java index fbe3a9c..0787286 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java @@ -1650,4 +1650,9 @@ public class KeyguardHostView extends KeyguardViewBase { mActivityLauncher.launchActivityWithAnimation( intent, false, opts.toBundle(), null, null); } + + public void dispatch(MotionEvent event) { + mAppWidgetContainer.handleExternalCameraEvent(event); + } + } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java index a70e5bd..77006c5 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java @@ -28,6 +28,7 @@ import android.os.Bundle; import android.os.Debug; import android.os.IBinder; import android.util.Log; +import android.view.MotionEvent; import com.android.internal.policy.IKeyguardService; import com.android.internal.policy.IKeyguardExitCallback; @@ -132,6 +133,10 @@ public class KeyguardService extends Service { checkPermission(); mKeyguardViewMediator.showAssistant(); } + public void dispatch(MotionEvent event) { + checkPermission(); + mKeyguardViewMediator.dispatch(event); + } }; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java index 35bea26..4837458 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java @@ -38,6 +38,7 @@ 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; @@ -425,4 +426,10 @@ public class KeyguardViewManager { mKeyguardView.showAssistant(); } } + + public void dispatch(MotionEvent event) { + if (mKeyguardView != null) { + mKeyguardView.dispatch(event); + } + } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java index e746f72..478096c 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java @@ -49,6 +49,7 @@ import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.WindowManager; import android.view.WindowManagerPolicy; @@ -120,6 +121,7 @@ public class KeyguardViewMediator { private static final int SET_HIDDEN = 12; private static final int KEYGUARD_TIMEOUT = 13; private static final int SHOW_ASSISTANT = 14; + private static final int DISPATCH_EVENT = 15; /** * The default amount of time we stay awake (used for all key input) @@ -1066,6 +1068,9 @@ public class KeyguardViewMediator { case SHOW_ASSISTANT: handleShowAssistant(); break; + case DISPATCH_EVENT: + handleDispatchEvent((MotionEvent) msg.obj); + break; } } }; @@ -1102,6 +1107,10 @@ public class KeyguardViewMediator { sendUserPresentBroadcast(); } + protected void handleDispatchEvent(MotionEvent event) { + mKeyguardViewManager.dispatch(event); + } + private void sendUserPresentBroadcast() { final UserHandle currentUser = new UserHandle(mLockPatternUtils.getCurrentUser()); mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, currentUser); @@ -1327,4 +1336,9 @@ public class KeyguardViewMediator { public static MultiUserAvatarCache getAvatarCache() { return sMultiUserAvatarCache; } + + public void dispatch(MotionEvent event) { + Message msg = mHandler.obtainMessage(DISPATCH_EVENT, event); + mHandler.sendMessage(msg); + } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java index e85f6df..0539e82 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java @@ -23,6 +23,7 @@ public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChallengeScrolledListener, ChallengeLayout.OnBouncerStateChangedListener { + private static final int WARP_FADE_DURATION = 250; private KeyguardWidgetPager mKeyguardWidgetPager; private ChallengeLayout mChallengeLayout; private KeyguardHostView mKeyguardHostView; @@ -32,6 +33,7 @@ public class KeyguardViewStateManager implements private KeyguardSecurityView mKeyguardSecurityContainer; private static final int SCREEN_ON_HINT_DURATION = 1000; private static final int SCREEN_ON_RING_HINT_DELAY = 300; + private static final boolean SHOW_INITIAL_PAGE_HINTS = false; Handler mMainQueue = new Handler(Looper.myLooper()); int mLastScrollState = SlidingChallengeLayout.SCROLL_STATE_IDLE; @@ -167,6 +169,16 @@ public class KeyguardViewStateManager implements mCurrentPage = newPageIndex; } + public void onPageBeginWarp() { + // fadeOutSecurity(WARP_FADE_DURATION); + // mKeyguardWidgetPager.showNonWarpViews(WARP_FADE_DURATION, false); + } + + public void onPageEndWarp() { + // fadeInSecurity(WARP_FADE_DURATION); + // mKeyguardWidgetPager.showNonWarpViews(WARP_FADE_DURATION, true); + } + private int getChallengeTopRelativeToFrame(KeyguardWidgetFrame frame, int top) { mTmpPoint[0] = 0; mTmpPoint[1] = top; @@ -296,7 +308,9 @@ public class KeyguardViewStateManager implements mKeyguardSecurityContainer.showUsabilityHint(); } } , SCREEN_ON_RING_HINT_DELAY); - mKeyguardWidgetPager.showInitialPageHints(); + if (SHOW_INITIAL_PAGE_HINTS) { + mKeyguardWidgetPager.showInitialPageHints(); + } if (mHideHintsRunnable != null) { mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION); } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java index c566457..8d2f21b 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java @@ -186,6 +186,16 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit } @Override + public void onPageBeginWarp() { + mViewStateManager.onPageBeginWarp(); + } + + @Override + public void onPageEndWarp() { + mViewStateManager.onPageEndWarp(); + } + + @Override public void sendAccessibilityEvent(int eventType) { if (eventType != AccessibilityEvent.TYPE_VIEW_SCROLLED || isPageMoving()) { super.sendAccessibilityEvent(eventType); @@ -923,4 +933,27 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit return flags; } + + public void handleExternalCameraEvent(MotionEvent event) { + beginCameraEvent(); + int cameraPage = getPageCount() - 1; + boolean endWarp = false; + if (isCameraPage(cameraPage)) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + userActivity(); + startWarp(cameraPage); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + endWarp = true; + break; + } + dispatchTouchEvent(event); + // This has to happen after the event has been handled by the real widget pager + if (endWarp) endWarp(); + } + endCameraEvent(); + } + } diff --git a/packages/Keyguard/src/com/android/keyguard/PagedView.java b/packages/Keyguard/src/com/android/keyguard/PagedView.java index 881d14d..cbf7946 100644 --- a/packages/Keyguard/src/com/android/keyguard/PagedView.java +++ b/packages/Keyguard/src/com/android/keyguard/PagedView.java @@ -62,6 +62,7 @@ import java.util.ArrayList; public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener { private static final String TAG = "WidgetPagedView"; private static final boolean DEBUG = false; + private static final boolean DEBUG_WARP = false; protected static final int INVALID_PAGE = -1; // the min drag distance for a fling to register, to prevent random page shifts @@ -130,6 +131,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected final static int TOUCH_STATE_REORDERING = 4; protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; + protected final static float TOUCH_SLOP_SCALE = 1.0f; protected int mTouchState = TOUCH_STATE_REST; protected boolean mForceScreenScrolled = false; @@ -250,6 +252,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Bouncer private boolean mTopAlignPageWhenShrinkingForBouncer = false; + // Page warping + private int mPageSwapIndex = -1; + private boolean mIsCameraEvent; + public interface PageSwitchListener { void onPageSwitching(View newPage, int newPageIndex); void onPageSwitched(View newPage, int newPageIndex); @@ -469,15 +475,26 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } protected void pageBeginMoving() { + if (DEBUG_WARP) Log.v(TAG, "pageBeginMoving(" + mIsPageMoving + ")"); if (!mIsPageMoving) { mIsPageMoving = true; + if (mPageSwapIndex != -1) { + onPageBeginWarp(); + swapPages(mPageSwapIndex, getPageCount() - 1); + } onPageBeginMoving(); } } protected void pageEndMoving() { + if (DEBUG_WARP) Log.v(TAG, "pageEndMoving(" + mIsPageMoving + ")"); if (mIsPageMoving) { mIsPageMoving = false; + if (mPageSwapIndex != -1) { + swapPages(mPageSwapIndex, getPageCount() - 1); + onPageEndWarp(); + mPageSwapIndex = -1; + } onPageEndMoving(); } } @@ -737,6 +754,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc setHorizontalScrollBarEnabled(true); mFirstLayout = false; } + // If a page was swapped when we rebuilt the layout, swap it again now. + if (mPageSwapIndex != -1) { + if (DEBUG_WARP) Log.v(TAG, "onLayout: swapping pages"); + swapPages(mPageSwapIndex, getPageCount() - 1); + } } protected void screenScrolled(int screenCenter) { @@ -1071,7 +1093,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc * whether the user has moved far enough from his original down touch. */ if (mActivePointerId != INVALID_POINTER) { - determineScrollingStart(ev); + if (mIsCameraEvent || determineScrollingStart(ev)) { + startScrolling(ev); + } break; } // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN @@ -1082,27 +1106,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } case MotionEvent.ACTION_DOWN: { - final float x = ev.getX(); - final float y = ev.getY(); - // Remember location of down touch - mDownMotionX = x; - mDownMotionY = y; - mDownScrollX = getScrollX(); - mLastMotionX = x; - mLastMotionY = y; - float[] p = mapPointFromViewToParent(this, x, y); - mParentDownMotionX = p[0]; - mParentDownMotionY = p[1]; - mLastMotionXRemainder = 0; - mTotalMotionX = 0; - mActivePointerId = ev.getPointerId(0); - - // Determine if the down event is within the threshold to be an edge swipe - int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize; - int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize; - if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) { - mDownEventOnEdge = true; - } + // Remember where the motion event started + saveDownState(ev); /* * If being flinged and user touches the screen, initiate drag; @@ -1112,25 +1117,29 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); if (finishedScrolling) { - mTouchState = TOUCH_STATE_REST; + setTouchState(TOUCH_STATE_REST); mScroller.abortAnimation(); } else { - if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) { - mTouchState = TOUCH_STATE_SCROLLING; + if (mIsCameraEvent || isTouchPointInViewportWithBuffer( + (int) mDownMotionX, (int) mDownMotionY)) { + setTouchState(TOUCH_STATE_SCROLLING); } else { - mTouchState = TOUCH_STATE_REST; + setTouchState(TOUCH_STATE_REST); } } // check if this can be the beginning of a tap on the side of the pages // to scroll the current page if (!DISABLE_TOUCH_SIDE_PAGES) { - if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { + if (mTouchState != TOUCH_STATE_PREV_PAGE + && mTouchState != TOUCH_STATE_NEXT_PAGE) { if (getChildCount() > 0) { + float x = ev.getX(); + float y = ev.getY(); if (hitsPreviousPage(x, y)) { - mTouchState = TOUCH_STATE_PREV_PAGE; + setTouchState(TOUCH_STATE_PREV_PAGE); } else if (hitsNextPage(x, y)) { - mTouchState = TOUCH_STATE_NEXT_PAGE; + setTouchState(TOUCH_STATE_NEXT_PAGE); } } } @@ -1160,48 +1169,85 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return mTouchState != TOUCH_STATE_REST; } - protected void determineScrollingStart(MotionEvent ev) { - determineScrollingStart(ev, 1.0f); + private void setTouchState(int touchState) { + if (mTouchState != touchState) { + onTouchStateChanged(touchState); + mTouchState = touchState; + } + } + + void onTouchStateChanged(int newTouchState) { + if (DEBUG) { + Log.v(TAG, "onTouchStateChanged(old="+ mTouchState + ", new=" + newTouchState + ")"); + } + } + + /** + * Save the state when we get {@link MotionEvent#ACTION_DOWN} + * @param ev + */ + private void saveDownState(MotionEvent ev) { + // Remember where the motion event started + mDownMotionX = mLastMotionX = ev.getX(); + mDownMotionY = mLastMotionY = ev.getY(); + mDownScrollX = getScrollX(); + float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY); + mParentDownMotionX = p[0]; + mParentDownMotionY = p[1]; + mLastMotionXRemainder = 0; + mTotalMotionX = 0; + mActivePointerId = ev.getPointerId(0); + + // Determine if the down event is within the threshold to be an edge swipe + int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize; + int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize; + if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) { + mDownEventOnEdge = true; + } } /* * Determines if we should change the touch state to start scrolling after the * user moves their touch point too far. */ - protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { + protected boolean determineScrollingStart(MotionEvent ev) { // Disallow scrolling if we don't have a valid pointer index final int pointerIndex = ev.findPointerIndex(mActivePointerId); - if (pointerIndex == -1) return; + if (pointerIndex == -1) return false; // Disallow scrolling if we started the gesture from outside the viewport final float x = ev.getX(pointerIndex); final float y = ev.getY(pointerIndex); - if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return; + if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return false; // If we're only allowing edge swipes, we break out early if the down event wasn't // at the edge. - if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return; + if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return false; final int xDiff = (int) Math.abs(x - mLastMotionX); final int yDiff = (int) Math.abs(y - mLastMotionY); - final int touchSlop = Math.round(touchSlopScale * mTouchSlop); + final int touchSlop = Math.round(TOUCH_SLOP_SCALE * mTouchSlop); boolean xPaged = xDiff > mPagingTouchSlop; boolean xMoved = xDiff > touchSlop; boolean yMoved = yDiff > touchSlop; - if (xMoved || xPaged || yMoved) { - if (mUsePagingTouchSlop ? xPaged : xMoved) { - // Scroll if the user moved far enough along the X axis - mTouchState = TOUCH_STATE_SCROLLING; - mTotalMotionX += Math.abs(mLastMotionX - x); - mLastMotionX = x; - mLastMotionXRemainder = 0; - mTouchX = getViewportOffsetX() + getScrollX(); - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - pageBeginMoving(); - } - } + return (xMoved || xPaged || yMoved) && (mUsePagingTouchSlop ? xPaged : xMoved); + } + + private void startScrolling(MotionEvent ev) { + // Ignore if we don't have a valid pointer index + final int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == -1) return; + + final float x = ev.getX(pointerIndex); + setTouchState(TOUCH_STATE_SCROLLING); + mTotalMotionX += Math.abs(mLastMotionX - x); + mLastMotionX = x; + mLastMotionXRemainder = 0; + mTouchX = getViewportOffsetX() + getScrollX(); + mSmoothingTime = System.nanoTime() / NANOTIME_DIV; + pageBeginMoving(); } protected float getMaxScrollProgress() { @@ -1322,22 +1368,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } // Remember where the motion event started - mDownMotionX = mLastMotionX = ev.getX(); - mDownMotionY = mLastMotionY = ev.getY(); - mDownScrollX = getScrollX(); - float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY); - mParentDownMotionX = p[0]; - mParentDownMotionY = p[1]; - mLastMotionXRemainder = 0; - mTotalMotionX = 0; - mActivePointerId = ev.getPointerId(0); - - // Determine if the down event is within the threshold to be an edge swipe - int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize; - int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize; - if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) { - mDownEventOnEdge = true; - } + saveDownState(ev); if (mTouchState == TOUCH_STATE_SCROLLING) { pageBeginMoving(); @@ -1479,8 +1510,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc removeCallbacks(mSidePageHoverRunnable); mSidePageHoverIndex = -1; } - } else { - determineScrollingStart(ev); + } else if (mIsCameraEvent || determineScrollingStart(ev)) { + startScrolling(ev); } break; @@ -1604,7 +1635,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private void resetTouchState() { releaseVelocityTracker(); endReordering(); - mTouchState = TOUCH_STATE_REST; + setTouchState(TOUCH_STATE_REST); mActivePointerId = INVALID_POINTER; mDownEventOnEdge = false; } @@ -1821,8 +1852,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected void snapToPage(int whichPage, int delta, int duration) { snapToPage(whichPage, delta, duration, false); } + protected void snapToPage(int whichPage, int delta, int duration, boolean immediate) { - mNextPage = whichPage; + if (mPageSwapIndex != -1 && whichPage == mPageSwapIndex) { + // jump to the last page + mNextPage = getPageCount() - 1; + if (DEBUG_WARP) Log.v(TAG, "snapToPage(" + whichPage + ") : reset mPageSwapIndex"); + mPageSwapIndex = -1; + } else { + mNextPage = whichPage; + } + notifyPageSwitching(whichPage); View focusedChild = getFocusedChild(); if (focusedChild != null && whichPage != mCurrentPage && @@ -2102,7 +2142,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.) - mTouchState = TOUCH_STATE_REORDERING; + setTouchState(TOUCH_STATE_REORDERING); mIsReordering = true; // Mark all the non-widget pages as invisible @@ -2564,4 +2604,46 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public boolean onHoverEvent(android.view.MotionEvent event) { return true; } + + void beginCameraEvent() { + mIsCameraEvent = true; + } + + void endCameraEvent() { + mIsCameraEvent = false; + } + + /** + * Swaps the position of the views by setting the left and right edges appropriately. + */ + void swapPages(int indexA, int indexB) { + View viewA = getPageAt(indexA); + View viewB = getPageAt(indexB); + if (viewA != viewB && viewA != null && viewB != null) { + int deltaX = viewA.getLeft() - viewB.getLeft(); + viewA.offsetLeftAndRight(-deltaX); + viewB.offsetLeftAndRight(deltaX); + } + } + + public void startWarp(int pageIndex) { + if (DEBUG_WARP) Log.v(TAG, "START WARP"); + if (pageIndex != mCurrentPage + 1) { + mPageSwapIndex = mCurrentPage + 1; + } + } + + public void endWarp() { + if (DEBUG_WARP) Log.v(TAG, "END WARP"); + // mPageSwapIndex is reset in snapToPage() after the scroll animation completes + } + + public void onPageBeginWarp() { + + } + + public void onPageEndWarp() { + + } + } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index fa5c769..fb73d39 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -52,7 +52,7 @@ <uses-permission android:name="android.permission.START_ANY_ACTIVITY" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" /> - + <!-- WindowManager --> <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> @@ -68,6 +68,9 @@ <!-- Alarm clocks --> <uses-permission android:name="com.android.alarm.permission.SET_ALARM" /> + <!-- Keyguard --> + <uses-permission android:name="android.permission.CONTROL_KEYGUARD" /> + <application android:persistent="true" android:allowClearUserData="false" diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png Binary files differnew file mode 100644 index 0000000..0bb590e --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png Binary files differnew file mode 100644 index 0000000..b767098 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png Binary files differnew file mode 100644 index 0000000..ea93f1a --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png Binary files differnew file mode 100644 index 0000000..cda7d4b --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index 09923a7..0421910 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -145,15 +145,30 @@ /> </LinearLayout> - <com.android.systemui.statusbar.policy.KeyButtonView - android:layout_width="80dp" - android:id="@+id/search_light" - android:layout_height="match_parent" - android:layout_gravity="center_horizontal" - android:src="@drawable/search_light" - android:scaleType="center" - android:visibility="gone" - /> + <FrameLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.android.systemui.statusbar.policy.KeyButtonView + android:layout_width="80dp" + android:id="@+id/search_light" + android:layout_height="match_parent" + android:layout_gravity="center" + android:src="@drawable/search_light" + android:scaleType="center" + android:visibility="gone" + /> + + <com.android.systemui.statusbar.policy.KeyButtonView + android:id="@+id/camera_button" + android:layout_height="match_parent" + android:layout_width="80dp" + android:layout_gravity="center_vertical|right" + android:src="@drawable/ic_sysbar_camera" + android:scaleType="center" + android:visibility="gone" + /> + </FrameLayout> <com.android.systemui.statusbar.policy.DeadZone android:id="@+id/deadzone" @@ -299,6 +314,8 @@ android:visibility="gone" /> + <!-- No camera button in landscape mode --> + <com.android.systemui.statusbar.policy.DeadZone android:id="@+id/deadzone" android:layout_height="match_parent" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java new file mode 100644 index 0000000..a6e2347 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 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.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.internal.policy.IKeyguardExitCallback; +import com.android.internal.policy.IKeyguardShowCallback; +import com.android.internal.policy.IKeyguardService; + + +/** + * Facilitates event communication between navigation bar and keyguard. Currently used to + * control WidgetPager in keyguard to expose the camera widget. + * + */ +public class KeyguardTouchDelegate { + // TODO: propagate changes to these to {@link KeyguardServiceDelegate} + static final String KEYGUARD_PACKAGE = "com.android.keyguard"; + static final String KEYGUARD_CLASS = "com.android.keyguard.KeyguardService"; + + IKeyguardService mService; + + protected static final boolean DEBUG = false; + protected static final String TAG = "KeyguardTouchDelegate"; + + private final ServiceConnection mKeyguardConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + Log.v(TAG, "Connected to keyguard"); + mService = IKeyguardService.Stub.asInterface(service); + + } + + @Override + public void onServiceDisconnected(ComponentName name) { + Log.v(TAG, "Disconnected from keyguard"); + mService = null; + } + + }; + + public KeyguardTouchDelegate(Context context) { + Intent intent = new Intent(); + intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS); + if (!context.bindServiceAsUser(intent, mKeyguardConnection, + Context.BIND_AUTO_CREATE, UserHandle.OWNER)) { + if (DEBUG) Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS); + } else { + if (DEBUG) Log.v(TAG, "*** Keyguard started"); + } + } + + public boolean isSecure() { + boolean secure = false; + if (mService != null) { + try { + secure = mService.isSecure(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException calling keyguard.isSecure()!", e); + } + } else { + Log.w(TAG, "isSecure(): NO SERVICE!"); + } + return secure; + } + + public boolean dispatch(MotionEvent event) { + if (mService != null) { + try { + mService.dispatch(event); + } catch (RemoteException e) { + // What to do? + Log.e(TAG, "RemoteException sending event to keyguard!", e); + return false; + } + return true; + } else { + Log.w(TAG, "dispatch(event): NO SERVICE!"); + } + return false; + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java index 085130b..531a2fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java @@ -87,6 +87,7 @@ public final class NavigationBarTransitions extends BarTransitions { setKeyButtonViewQuiescentAlpha(mView.getHomeButton(), alpha, animate); setKeyButtonViewQuiescentAlpha(mView.getRecentsButton(), alpha, animate); setKeyButtonViewQuiescentAlpha(mView.getMenuButton(), alpha, animate); + setKeyButtonViewQuiescentAlpha(mView.getCameraButton(), alpha, animate); // apply to lights out applyLightsOut(mode == MODE_LIGHTS_OUT, animate, force); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index b56d7be..3c6faae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -17,14 +17,20 @@ package com.android.systemui.statusbar.phone; import android.animation.LayoutTransition; +import android.app.ActivityManagerNative; import android.app.StatusBarManager; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; +import android.os.RemoteException; import android.util.AttributeSet; import android.util.Log; import android.view.Display; @@ -77,6 +83,17 @@ public class NavigationBarView extends LinearLayout { final static boolean WORKAROUND_INVALID_LAYOUT = true; final static int MSG_CHECK_INVALID_LAYOUT = 8686; + // used to disable the camera icon in navbar when disabled by DPM + private boolean mCameraDisabledByDpm; + KeyguardTouchDelegate mTouchDelegate; + + private final OnTouchListener mCameraTouchListener = new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return mTouchDelegate.dispatch(event); + } + }; + private class H extends Handler { public void handleMessage(Message m) { switch (m.what) { @@ -115,6 +132,26 @@ public class NavigationBarView extends LinearLayout { getIcons(res); mBarTransitions = new NavigationBarTransitions(this); + + mTouchDelegate = new KeyguardTouchDelegate(mContext); + + mCameraDisabledByDpm = isCameraDisabledByDpm(); + watchForDevicePolicyChanges(); + } + + private void watchForDevicePolicyChanges() { + final IntentFilter filter = new IntentFilter(); + filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + mContext.registerReceiver(new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + post(new Runnable() { + @Override + public void run() { + mCameraDisabledByDpm = isCameraDisabledByDpm(); + } + }); + } + }, filter); } public BarTransitions getBarTransitions() { @@ -173,6 +210,11 @@ public class NavigationBarView extends LinearLayout { return mCurrentView.findViewById(R.id.search_light); } + // shown when keyguard is visible and camera is available + public View getCameraButton() { + return mCurrentView.findViewById(R.id.camera_button); + } + private void getIcons(Resources res) { mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back); mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land); @@ -259,7 +301,31 @@ public class NavigationBarView extends LinearLayout { getHomeButton() .setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); - getSearchLight().setVisibility((disableHome && !disableSearch) ? View.VISIBLE : View.GONE); + final boolean shouldShowSearch = disableHome && !disableSearch; + getSearchLight().setVisibility(shouldShowSearch ? View.VISIBLE : View.GONE); + final View cameraButton = getCameraButton(); + if (cameraButton != null) { + cameraButton.setVisibility( + shouldShowSearch && !mCameraDisabledByDpm ? View.VISIBLE : View.GONE); + } + } + + private boolean isCameraDisabledByDpm() { + final DevicePolicyManager dpm = + (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); + if (dpm != null) { + try { + final int userId = ActivityManagerNative.getDefault().getCurrentUser().id; + final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId); + final boolean disabledBecauseKeyguardSecure = + (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0 + && mTouchDelegate.isSecure(); + return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure; + } catch (RemoteException e) { + Log.e(TAG, "Can't get userId", e); + } + } + return false; } public void setSlippery(boolean newSlippery) { @@ -302,6 +368,14 @@ public class NavigationBarView extends LinearLayout { : findViewById(R.id.rot270); mCurrentView = mRotatedViews[Surface.ROTATION_0]; + + // Add a touch handler for camera icon for all view orientations. + for (int i = 0; i < mRotatedViews.length; i++) { + View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button); + if (cameraButton != null) { + cameraButton.setOnTouchListener(mCameraTouchListener); + } + } } public boolean isVertical() { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java index 874076a..56a282b 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java @@ -29,8 +29,10 @@ import com.android.internal.widget.LockPatternUtils; * local or remote instances of keyguard. */ public class KeyguardServiceDelegate { - private static final String KEYGUARD_PACKAGE = "com.android.keyguard"; - private static final String KEYGUARD_CLASS = "com.android.keyguard.KeyguardService"; + // TODO: propagate changes to these to {@link KeyguardTouchDelegate} + public static final String KEYGUARD_PACKAGE = "com.android.keyguard"; + public static final String KEYGUARD_CLASS = "com.android.keyguard.KeyguardService"; + private static final String TAG = "KeyguardServiceDelegate"; private static final boolean DEBUG = true; protected KeyguardServiceWrapper mKeyguardService; diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java index 6b9c7df..b27584d 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java @@ -20,6 +20,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import android.view.MotionEvent; import com.android.internal.policy.IKeyguardShowCallback; import com.android.internal.policy.IKeyguardExitCallback; @@ -187,6 +188,10 @@ public class KeyguardServiceWrapper implements IKeyguardService { } } + public void dispatch(MotionEvent event) { + // Not used by PhoneWindowManager. See code in {@link NavigationBarView} + } + @Override public IBinder asBinder() { return mService.asBinder(); |