blob: fe991e8094fcd98df2d16e9d59271834b8decec1 [file] [log] [blame]
Pankaj Garg1c13cab2015-05-12 11:52:17 -07001/*
2 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30package com.android.browser;
31
32import android.graphics.Bitmap;
33import android.graphics.Canvas;
34import android.graphics.ColorMatrix;
35import android.graphics.ColorMatrixColorFilter;
36import android.graphics.Paint;
37import android.os.CountDownTimer;
38import android.support.v4.widget.ViewDragHelper;
39import android.view.View;
40
41import org.codeaurora.swe.WebHistoryItem;
42import org.codeaurora.swe.util.Activator;
43import org.codeaurora.swe.util.Observable;
44
45public class EdgeSwipeController extends ViewDragHelper.Callback {
46 private ViewDragHelper mDragHelper;
47 private int mState = ViewDragHelper.STATE_IDLE;
48 private int mFromEdge = ViewDragHelper.EDGE_LEFT;
49 private boolean mbNavigated = false;
50 private int mOldX = 0;
51 private int mOldDx = 0;
52 private Observable mPageLoadTarget;
53 private Observable mPageLoadObservable;
54
55 private boolean mbCurrBMSynced = false;
56
57 private Tab mActiveTab;
58 private TitleBar mTitleBar;
59
60 private static final float mMinAlpha = 0.5f;
61 private static final int mMinProgress = 85;
62 private static final int mProgressWaitMS = 1000;
63 private static final int EDGE_SWIPE_INVALID_INDEX = -2;
64
65 private CountDownTimer mLoadTimer, mCommitTimer;
66
67 private int mCurrIndex = EDGE_SWIPE_INVALID_INDEX;
68 private int mPrevIndex;
69 private int mNextIndex;
70 private int mMaxIndex;
71
72 private EdgeSwipeModel mModel;
73 private EdgeSwipeView mView;
74
75 public EdgeSwipeController(View container,
76 int stationaryViewId,
77 int slidingViewId,
78 int slidingViewShadowId,
79 int opacityViewId,
80 int liveViewId,
81 int viewGroupId,
82 BaseUi ui) {
83 DraggableFrameLayout viewGroup = (DraggableFrameLayout)
84 container.findViewById(viewGroupId);
85
86 mActiveTab = ui.getActiveTab();
87 mTitleBar = ui.getTitleBar();
88
89 mModel = new EdgeSwipeModel(mActiveTab, mTitleBar);
90 mView = new EdgeSwipeView(
91 container,
92 stationaryViewId,
93 slidingViewId,
94 slidingViewShadowId,
95 opacityViewId,
96 liveViewId,
97 viewGroupId,
98 mTitleBar);
99
100 mPageLoadTarget = mActiveTab.getTabHistoryUpdateObservable();
101 mPageLoadObservable = Activator.activate(
102 new Observable.Observer() {
103 @Override
104 public void onChange(Object... params) {
105 if (mDragHelper == null ||
106 mPageLoadTarget == null) {
107 return;
108 }
109
110 synchronized (this) {
111 int index = (int) params[0];
112 if (mState == ViewDragHelper.STATE_IDLE && index == mCurrIndex) {
113 monitorProgressAtHistoryUpdate(index);
114 }
115 }
116 }
117 },
118 mPageLoadTarget
119 );
120
121 mDragHelper = ViewDragHelper.create(viewGroup, 0.5f, this);
122 mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT | ViewDragHelper.EDGE_RIGHT);
123 viewGroup.setDragHelper(mDragHelper);
124 }
125
126 private void swipeSessionCleanup() {
127 mView.goLive();
128 mModel.cleanup();
129 mCurrIndex = EDGE_SWIPE_INVALID_INDEX;
130 mState = ViewDragHelper.STATE_IDLE;
131 }
132
133 private boolean setState(int curState, int newState) {
134 if (mState == curState) {
135 mState = newState;
136 return true;
137 }
138 return false;
139 }
140
141 public void cleanup() {
142 if (mPageLoadObservable != null) {
143 mPageLoadObservable.onOff(false);
144 synchronized (this) {
145 mDragHelper.cancel();
146 swipeSessionCleanup();
147 }
148 }
149 }
150
151 public void onConfigurationChanged() {
152 synchronized (this) {
153 swipeSessionCleanup();
154 }
155 }
156
157 private void showCurrBMInStationaryView() {
158 if (!mbCurrBMSynced) {
159 Bitmap currBM = mModel.readSnapshot(mCurrIndex);
160 if (currBM != null) {
161 mView.setStationaryViewBitmap(currBM);
162 mbCurrBMSynced = true;
163 }
164 }
165 }
166
167 private void showCurrBMInSlidingView() {
168 if (!mbCurrBMSynced) {
169 Bitmap currBM = mModel.readSnapshot(mCurrIndex);
170 mView.setSlidingViewBitmap(currBM);
171 if (currBM != null) {
172 mbCurrBMSynced = true;
173 }
174 }
175 }
176
177 private Bitmap getGrayscale(Bitmap bitmap)
178 {
179 if (bitmap == null)
180 return null;
181
182 int height = bitmap.getHeight();
183 int width = bitmap.getWidth();
184
185 Bitmap gray = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
186 Canvas c = new Canvas(gray);
187 Paint paint = new Paint();
188 ColorMatrix cm = new ColorMatrix();
189
190 cm.setSaturation(0);
191
192 ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
193
194 paint.setColorFilter(f);
195
196 c.drawBitmap(bitmap, 0, 0, paint);
197 return gray;
198 }
199
200 private void monitorProgressAtLoad(final int pageIndex) {
201 if (mLoadTimer != null) {
202 mLoadTimer.cancel();
203 }
204
205 mLoadTimer = new CountDownTimer(mProgressWaitMS * 5, mProgressWaitMS) {
206 boolean mGrayBM = false;
207
208 public void onTick(long msRemain) {
209 if (msRemain > mProgressWaitMS * 4) {
210 return;
211 }
212 synchronized (this) {
213 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
214 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
215 swipeSessionCleanup();
216
217 }
218 cancel();
219 } else {
220 if (mGrayBM) {
221 return;
222 }
223 mView.setStationaryViewBitmap(
224 getGrayscale(getSnapshotOrFavicon(pageIndex)));
225 mGrayBM = true;
226 }
227 }
228 }
229
230 public void onFinish() {
231 mGrayBM = false;
232 synchronized (this) {
233 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
234 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
235 swipeSessionCleanup();
236 }
237 cancel();
238 }
239 }
240 }
241 }.start();
242 }
243
244 private void monitorProgressAtHistoryUpdate(final int pageIndex) {
245 if (mCommitTimer != null) {
246 mCommitTimer.cancel();
247 }
248
249 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress
250 && mActiveTab.getWebView().getLastCommittedHistoryIndex() == pageIndex) {
251 swipeSessionCleanup();
252 return;
253 }
254
255 mCommitTimer = new CountDownTimer(mProgressWaitMS * 5, mProgressWaitMS) {
256 public void onTick(long msRemain) {
257 synchronized (this) {
258 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
259 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
260 swipeSessionCleanup();
261
262 }
263 cancel();
264 }
265 }
266 }
267
268 public void onFinish() {
269 synchronized (this) {
270 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
271 swipeSessionCleanup();
272 }
273 }
274 }
275 }.start();
276 }
277
278 private boolean isPortrait(Bitmap bitmap) {
279 return (bitmap.getHeight() < bitmap.getWidth());
280 }
281
282 private Bitmap getSnapshotOrFavicon(int index) {
283 Bitmap bm = mModel.readSnapshot(index);
284 if (bm == null || mView.isPortrait() != isPortrait(bm)) {
285 WebHistoryItem item = mActiveTab.getWebView()
286 .copyBackForwardList().getItemAtIndex(index);
287 if (item != null) {
288 bm = item.getFavicon();
289 }
290 }
291 return bm;
292 }
293
294 public void onViewDragStateChanged(int state) {
295 synchronized (this) {
296 if (mState != ViewDragHelper.STATE_SETTLING || state != ViewDragHelper.STATE_IDLE) {
297 return;
298 }
299
300 mView.hideSlidingViews();
301
302 if (mView.isLive()) {
303 return;
304 }
305
306 if (mbNavigated) {
307 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mCurrIndex));
308 mView.setStationaryViewAlpha(1.0f);
309 } else {
310 swipeSessionCleanup();
311 }
312
313 mView.invalidate();
314
315 setState(ViewDragHelper.STATE_SETTLING, ViewDragHelper.STATE_IDLE);
316 }
317 }
318
319 public void onViewReleased(View releasedChild, float xvel, float yvel) {
320 synchronized (this) {
321 if (!setState(ViewDragHelper.STATE_DRAGGING, ViewDragHelper.STATE_SETTLING)) {
322 mOldX = 0;
323 mOldDx = 0;
324 return;
325 }
326
327 mbNavigated = true;
328
329 boolean bCrossedEventHorizon = Math.abs(mOldX) > mView.getWidth() / 2;
330
331 if (mCurrIndex >= 0) {
332 if ((xvel > 0 || (xvel == 0 && mOldX > 0 && bCrossedEventHorizon))
333 && mFromEdge == ViewDragHelper.EDGE_LEFT
334 && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex - 1)) {
335 mCurrIndex -= 1;
336 mActiveTab.getWebView().stopLoading();
337 mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
338 monitorProgressAtLoad(mCurrIndex);
339 mDragHelper.settleCapturedViewAt(
340 releasedChild.getMeasuredWidth(),
341 releasedChild.getTop());
342 } else if ((xvel < 0 || (xvel == 0 && mOldX < 0 && bCrossedEventHorizon))
343 && mFromEdge == ViewDragHelper.EDGE_RIGHT
344 && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex + 1)) {
345 mCurrIndex += 1;
346 mActiveTab.getWebView().stopLoading();
347 mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
348 monitorProgressAtLoad(mCurrIndex);
349 mDragHelper.settleCapturedViewAt(
350 -releasedChild.getMeasuredWidth(),
351 releasedChild.getTop());
352 mView.goDormant();
353 } else {
354 mbNavigated = false;
355 mDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
356 }
357 }
358 mOldX = 0;
359 mOldDx = 0;
360
361 mView.invalidate();
362 }
363 }
364
365 public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
366 float alpha = ((float) Math.abs(left)) / mView.getMeasuredWidth();
367
368 synchronized (this) {
369 switch (mFromEdge) {
370 case ViewDragHelper.EDGE_LEFT:
371 if (mView.isLive()) {
372 return;
373 }
374
375 if (mState != ViewDragHelper.STATE_IDLE) {
376 mView.moveShadowView(left);
377 }
378
379 showCurrBMInSlidingView();
380
381 if (mPrevIndex >= 0) {
382 if (!mView.stationaryViewHasImage()) {
383 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex));
384 }
385
386 if (mActiveTab.getWebView().canGoToHistoryIndex(mPrevIndex)) {
387 mView.setStationaryViewAlpha(mMinAlpha + alpha * (1 - mMinAlpha));
388 }
389 }
390 break;
391 case ViewDragHelper.EDGE_RIGHT:
392 if (mState != ViewDragHelper.STATE_IDLE) {
393 mView.moveShadowView(mView.getMeasuredWidth() + left);
394
395 if (!mView.slidingViewHasImage() && mNextIndex < mMaxIndex) {
396 mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex));
397 }
398
399 showCurrBMInStationaryView();
400 if (mbCurrBMSynced) {
401 mView.goDormant();
402 }
403 }
404 if (mNextIndex < mMaxIndex &&
405 mActiveTab.getWebView().canGoToHistoryIndex(mNextIndex)) {
406 mView.setStationaryViewAlpha(mMinAlpha + (1 - alpha) * (1 - mMinAlpha));
407 }
408 break;
409 default:
410 break;
411 }
412 }
413 }
414
415 public void onEdgeDragStarted(int edgeFlags, int pointerId) {
416 synchronized (this) {
417 if (mActiveTab.isPrivateBrowsingEnabled()) {
418 mDragHelper.abort();
419 return;
420 }
421
422 if (mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE ||
423 !setState(ViewDragHelper.STATE_IDLE, ViewDragHelper.STATE_DRAGGING)) {
424 mDragHelper.abort();
425 return;
426 }
427
428 if ((edgeFlags & mFromEdge) != mFromEdge || mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
429 onEdgeTouched(edgeFlags, pointerId);
430 }
431
432 mbCurrBMSynced = false;
433
434 switch (mFromEdge) {
435 case ViewDragHelper.EDGE_LEFT:
436 mView.showSlidingViews();
437 mView.goDormant();
438 mPrevIndex = mCurrIndex - 1;
439 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex));
440 showCurrBMInSlidingView();
441 break;
442 case ViewDragHelper.EDGE_RIGHT:
443 mView.showSlidingViews();
444 mNextIndex = mCurrIndex + 1;
445 mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex));
446 showCurrBMInStationaryView();
447 if (mbCurrBMSynced)
448 mView.goDormant();
449 break;
450 default:
451 break;
452 }
453 }
454 }
455
456 public int getOrderedChildIndex(int index) {
457 return mView.slidingViewIndex();
458 }
459
460 public void onEdgeTouched (int edgeFlags, int pointerId) {
461 synchronized (this) {
Sagar Dhawan33551ff2015-07-08 17:24:44 -0700462 if (mActiveTab.isPrivateBrowsingEnabled() || mActiveTab.isKeyboardShowing()) {
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700463 mDragHelper.abort();
464 return;
465 }
466
467 if (mState != ViewDragHelper.STATE_IDLE && mCurrIndex != EDGE_SWIPE_INVALID_INDEX) {
468 mDragHelper.abort();
469 return;
470 }
471
472 mView.init();
473
474 if (mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
475 mCurrIndex = mActiveTab.getWebView().getLastCommittedHistoryIndex();
476 }
477
478 mMaxIndex = mActiveTab.getWebView().copyBackForwardList().getSize() - 1;
479 mModel.updateSnapshot(mCurrIndex);
480
481 if (ViewDragHelper.EDGE_LEFT == (edgeFlags & ViewDragHelper.EDGE_LEFT)) {
482 mFromEdge = ViewDragHelper.EDGE_LEFT;
483 mView.slidingViewTouched(mFromEdge);
484 if (mCurrIndex > 0) {
485 mModel.fetchSnapshot(mCurrIndex - 1);
486 }
487 } else if (ViewDragHelper.EDGE_RIGHT == (edgeFlags & ViewDragHelper.EDGE_RIGHT)) {
488 mFromEdge = ViewDragHelper.EDGE_RIGHT;
489 mView.slidingViewTouched(mFromEdge);
490 if (mCurrIndex < mMaxIndex) {
491 mModel.fetchSnapshot(mCurrIndex + 1);
492 }
493 }
494 }
495 }
496
497 public int getViewHorizontalDragRange(View child) {
498 return child.getMeasuredWidth();
499 }
500
501 public boolean tryCaptureView(View child, int pointerId) {
502 return (mState == ViewDragHelper.STATE_DRAGGING && mView.allowCapture(child));
503 }
504
505 public int clampViewPositionHorizontal(View child, int left, int dx) {
506 if (mOldX != 0 && Math.signum(dx) != Math.signum(mOldDx)) {
507 mOldDx = dx;
508 return mOldX;
509 }
510
511 switch (mFromEdge) {
512 case ViewDragHelper.EDGE_LEFT:
513 if (left < 0) {
514 mOldDx = dx;
515 return mOldX;
516 }
517 if (!mActiveTab.getWebView().canGoToHistoryIndex(mPrevIndex)) {
518 if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
519 return child.getMeasuredWidth() / 3;
520 }
521 }
522 break;
523 case ViewDragHelper.EDGE_RIGHT:
524 if (left > 0) {
525 mOldDx = dx;
526 return mOldX;
527 }
528 if (!mActiveTab.getWebView().canGoToHistoryIndex(mNextIndex)) {
529 if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
530 return -child.getMeasuredWidth() / 3;
531 }
532 }
533 break;
534 default:
535 break;
536 }
537
538 mOldX = left;
539 mOldDx = dx;
540 return left;
541 }
542}
543