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