1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.keyguard;
17
18import android.os.Handler;
19import android.os.Looper;
20import android.util.Log;
21import android.view.View;
22
23public class KeyguardViewStateManager implements
24        SlidingChallengeLayout.OnChallengeScrolledListener,
25        ChallengeLayout.OnBouncerStateChangedListener {
26
27    private static final String TAG = "KeyguardViewStateManager";
28    private KeyguardWidgetPager mKeyguardWidgetPager;
29    private ChallengeLayout mChallengeLayout;
30    private KeyguardHostView mKeyguardHostView;
31    private int[] mTmpPoint = new int[2];
32    private int[] mTmpLoc = new int[2];
33
34    private KeyguardSecurityView mKeyguardSecurityContainer;
35    private static final int SCREEN_ON_HINT_DURATION = 1000;
36    private static final int SCREEN_ON_RING_HINT_DELAY = 300;
37    private static final boolean SHOW_INITIAL_PAGE_HINTS = false;
38    Handler mMainQueue = new Handler(Looper.myLooper());
39
40    int mLastScrollState = SlidingChallengeLayout.SCROLL_STATE_IDLE;
41
42    // Paged view state
43    private int mPageListeningToSlider = -1;
44    private int mCurrentPage = -1;
45    private int mPageIndexOnPageBeginMoving = -1;
46
47    int mChallengeTop = 0;
48
49    public KeyguardViewStateManager(KeyguardHostView hostView) {
50        mKeyguardHostView = hostView;
51    }
52
53    public void setPagedView(KeyguardWidgetPager pagedView) {
54        mKeyguardWidgetPager = pagedView;
55        updateEdgeSwiping();
56    }
57
58    public void setChallengeLayout(ChallengeLayout layout) {
59        mChallengeLayout = layout;
60        updateEdgeSwiping();
61    }
62
63    private void updateEdgeSwiping() {
64        if (mChallengeLayout != null && mKeyguardWidgetPager != null) {
65            if (mChallengeLayout.isChallengeOverlapping()) {
66                mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(true);
67            } else {
68                mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(false);
69            }
70        }
71    }
72
73    public boolean isChallengeShowing() {
74        if (mChallengeLayout != null) {
75            return mChallengeLayout.isChallengeShowing();
76        }
77        return false;
78    }
79
80    public boolean isChallengeOverlapping() {
81        if (mChallengeLayout != null) {
82            return mChallengeLayout.isChallengeOverlapping();
83        }
84        return false;
85    }
86
87    public void setSecurityViewContainer(KeyguardSecurityView container) {
88        mKeyguardSecurityContainer = container;
89    }
90
91    public void showBouncer(boolean show) {
92        CharSequence what = mKeyguardHostView.getContext().getResources().getText(
93                show ? R.string.keyguard_accessibility_show_bouncer
94                        : R.string.keyguard_accessibility_hide_bouncer);
95        mKeyguardHostView.announceForAccessibility(what);
96        mKeyguardHostView.announceCurrentSecurityMethod();
97        mChallengeLayout.showBouncer();
98    }
99
100    public boolean isBouncing() {
101        return mChallengeLayout.isBouncing();
102    }
103
104    public void fadeOutSecurity(int duration) {
105        ((View) mKeyguardSecurityContainer).animate().alpha(0f).setDuration(duration).start();
106    }
107
108    public void fadeInSecurity(int duration) {
109        ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration).start();
110    }
111
112    public void onPageBeginMoving() {
113        if (mChallengeLayout.isChallengeOverlapping() &&
114                mChallengeLayout instanceof SlidingChallengeLayout) {
115            SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
116            if (!mKeyguardWidgetPager.isWarping()) {
117                scl.fadeOutChallenge();
118            }
119            mPageIndexOnPageBeginMoving = mKeyguardWidgetPager.getCurrentPage();
120        }
121        // We use mAppWidgetToShow to show a particular widget after you add it--
122        // once the user swipes a page we clear that behavior
123        if (mKeyguardHostView != null) {
124            mKeyguardHostView.clearAppWidgetToShow();
125            mKeyguardHostView.setOnDismissAction(null);
126        }
127        if (mHideHintsRunnable != null) {
128            mMainQueue.removeCallbacks(mHideHintsRunnable);
129            mHideHintsRunnable = null;
130        }
131    }
132
133    public void onPageEndMoving() {
134        mPageIndexOnPageBeginMoving = -1;
135    }
136
137    public void onPageSwitching(View newPage, int newPageIndex) {
138        if (mKeyguardWidgetPager != null && mChallengeLayout instanceof SlidingChallengeLayout) {
139            boolean isCameraPage = newPage instanceof CameraWidgetFrame;
140            SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
141            scl.setChallengeInteractive(!isCameraPage);
142            if (isCameraPage) {
143                scl.fadeOutChallenge();
144            }
145            final int currentFlags = mKeyguardWidgetPager.getSystemUiVisibility();
146            final int newFlags = isCameraPage ? (currentFlags | View.STATUS_BAR_DISABLE_SEARCH)
147                    : (currentFlags & ~View.STATUS_BAR_DISABLE_SEARCH);
148            mKeyguardWidgetPager.setSystemUiVisibility(newFlags);
149        }
150
151        // If the page we're settling to is the same as we started on, and the action of
152        // moving the page hid the security, we restore it immediately.
153        if (mPageIndexOnPageBeginMoving == mKeyguardWidgetPager.getNextPage() &&
154                mChallengeLayout instanceof SlidingChallengeLayout) {
155            SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
156            scl.fadeInChallenge();
157            mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(-1);
158        }
159        mPageIndexOnPageBeginMoving = -1;
160    }
161
162    public void onPageSwitched(View newPage, int newPageIndex) {
163        // Reset the previous page size and ensure the current page is sized appropriately.
164        // We only modify the page state if it is not currently under control by the slider.
165        // This prevents conflicts.
166
167        // If the page hasn't switched, don't bother with any of this
168        if (mCurrentPage == newPageIndex) return;
169
170        if (mKeyguardWidgetPager != null && mChallengeLayout != null) {
171            KeyguardWidgetFrame prevPage = mKeyguardWidgetPager.getWidgetPageAt(mCurrentPage);
172            if (prevPage != null && mCurrentPage != mPageListeningToSlider && mCurrentPage
173                    != mKeyguardWidgetPager.getWidgetToResetOnPageFadeOut()) {
174                prevPage.resetSize();
175            }
176
177            KeyguardWidgetFrame newCurPage = mKeyguardWidgetPager.getWidgetPageAt(newPageIndex);
178            boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
179            if (challengeOverlapping && !newCurPage.isSmall()
180                    && mPageListeningToSlider != newPageIndex) {
181                newCurPage.shrinkWidget();
182            }
183        }
184
185        mCurrentPage = newPageIndex;
186    }
187
188    public void onPageBeginWarp() {
189        fadeOutSecurity(SlidingChallengeLayout.CHALLENGE_FADE_OUT_DURATION);
190        View frame = mKeyguardWidgetPager.getPageAt(mKeyguardWidgetPager.getPageWarpIndex());
191        ((KeyguardWidgetFrame)frame).showFrame(this);
192    }
193
194    public void onPageEndWarp() {
195        fadeInSecurity(SlidingChallengeLayout.CHALLENGE_FADE_IN_DURATION);
196        View frame = mKeyguardWidgetPager.getPageAt(mKeyguardWidgetPager.getPageWarpIndex());
197        ((KeyguardWidgetFrame)frame).hideFrame(this);
198    }
199
200    private int getChallengeTopRelativeToFrame(KeyguardWidgetFrame frame, int top) {
201        mTmpPoint[0] = 0;
202        mTmpPoint[1] = top;
203        mapPoint((View) mChallengeLayout, frame, mTmpPoint);
204        return mTmpPoint[1];
205    }
206
207    /**
208     * Simple method to map a point from one view's coordinates to another's. Note: this method
209     * doesn't account for transforms, so if the views will be transformed, this should not be used.
210     *
211     * @param fromView The view to which the point is relative
212     * @param toView The view into which the point should be mapped
213     * @param pt The point
214     */
215    private void mapPoint(View fromView, View toView, int pt[]) {
216        fromView.getLocationInWindow(mTmpLoc);
217
218        int x = mTmpLoc[0];
219        int y = mTmpLoc[1];
220
221        toView.getLocationInWindow(mTmpLoc);
222        int vX = mTmpLoc[0];
223        int vY = mTmpLoc[1];
224
225        pt[0] += x - vX;
226        pt[1] += y - vY;
227    }
228
229    private void userActivity() {
230        if (mKeyguardHostView != null) {
231            mKeyguardHostView.onUserActivityTimeoutChanged();
232            mKeyguardHostView.userActivity();
233        }
234    }
235
236    @Override
237    public void onScrollStateChanged(int scrollState) {
238        if (mKeyguardWidgetPager == null || mChallengeLayout == null) return;
239
240        boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
241
242        if (scrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
243            KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
244            if (frame == null) return;
245
246            if (!challengeOverlapping) {
247                if (!mKeyguardWidgetPager.isPageMoving()) {
248                    frame.resetSize();
249                    userActivity();
250                } else {
251                    mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(mPageListeningToSlider);
252                }
253            }
254            if (frame.isSmall()) {
255                // This is to make sure that if the scroller animation gets cut off midway
256                // that the frame doesn't stay in a partial down position.
257                frame.setFrameHeight(frame.getSmallFrameHeight());
258            }
259            if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
260                frame.hideFrame(this);
261            }
262            updateEdgeSwiping();
263
264            if (mChallengeLayout.isChallengeShowing()) {
265                mKeyguardSecurityContainer.onResume(KeyguardSecurityView.VIEW_REVEALED);
266            } else {
267                mKeyguardSecurityContainer.onPause();
268            }
269            mPageListeningToSlider = -1;
270        } else if (mLastScrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
271            // Whether dragging or settling, if the last state was idle, we use this signal
272            // to update the current page who will receive events from the sliding challenge.
273            // We resize the frame as appropriate.
274            mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
275            KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
276            if (frame == null) return;
277
278            // Skip showing the frame and shrinking the widget if we are
279            if (!mChallengeLayout.isBouncing()) {
280                if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
281                    frame.showFrame(this);
282                }
283
284                // As soon as the security begins sliding, the widget becomes small (if it wasn't
285                // small to begin with).
286                if (!frame.isSmall()) {
287                    // We need to fetch the final page, in case the pages are in motion.
288                    mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
289                    frame.shrinkWidget(false);
290                }
291            } else {
292                if (!frame.isSmall()) {
293                    // We need to fetch the final page, in case the pages are in motion.
294                    mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
295                }
296            }
297
298            // View is on the move.  Pause the security view until it completes.
299            mKeyguardSecurityContainer.onPause();
300        }
301        mLastScrollState = scrollState;
302    }
303
304    @Override
305    public void onScrollPositionChanged(float scrollPosition, int challengeTop) {
306        mChallengeTop = challengeTop;
307        KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
308        if (frame != null && mLastScrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
309            frame.adjustFrame(getChallengeTopRelativeToFrame(frame, mChallengeTop));
310        }
311    }
312
313    private Runnable mHideHintsRunnable = new Runnable() {
314        @Override
315        public void run() {
316            if (mKeyguardWidgetPager != null) {
317                mKeyguardWidgetPager.hideOutlinesAndSidePages();
318            }
319        }
320    };
321
322    public void showUsabilityHints() {
323        mMainQueue.postDelayed( new Runnable() {
324            @Override
325            public void run() {
326                mKeyguardSecurityContainer.showUsabilityHint();
327            }
328        } , SCREEN_ON_RING_HINT_DELAY);
329        if (SHOW_INITIAL_PAGE_HINTS) {
330            mKeyguardWidgetPager.showInitialPageHints();
331        }
332        if (mHideHintsRunnable != null) {
333            mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION);
334        }
335    }
336
337    // ChallengeLayout.OnBouncerStateChangedListener
338    @Override
339    public void onBouncerStateChanged(boolean bouncerActive) {
340        if (bouncerActive) {
341            mKeyguardWidgetPager.zoomOutToBouncer();
342        } else {
343            mKeyguardWidgetPager.zoomInFromBouncer();
344            if (mKeyguardHostView != null) {
345                mKeyguardHostView.setOnDismissAction(null);
346            }
347        }
348    }
349}
350