blob: b9c25680fb20bac6013d0cf86de40faa1d6aadc7 [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;
Sagar Dhawan97ed6c92015-07-31 15:36:07 -070042import org.codeaurora.swe.WebView;
Pankaj Garg1c13cab2015-05-12 11:52:17 -070043import org.codeaurora.swe.util.Activator;
44import org.codeaurora.swe.util.Observable;
45
46public class EdgeSwipeController extends ViewDragHelper.Callback {
47 private ViewDragHelper mDragHelper;
48 private int mState = ViewDragHelper.STATE_IDLE;
49 private int mFromEdge = ViewDragHelper.EDGE_LEFT;
50 private boolean mbNavigated = false;
51 private int mOldX = 0;
52 private int mOldDx = 0;
53 private Observable mPageLoadTarget;
54 private Observable mPageLoadObservable;
55
56 private boolean mbCurrBMSynced = false;
57
58 private Tab mActiveTab;
59 private TitleBar mTitleBar;
60
61 private static final float mMinAlpha = 0.5f;
62 private static final int mMinProgress = 85;
63 private static final int mProgressWaitMS = 1000;
64 private static final int EDGE_SWIPE_INVALID_INDEX = -2;
65
66 private CountDownTimer mLoadTimer, mCommitTimer;
67
68 private int mCurrIndex = EDGE_SWIPE_INVALID_INDEX;
69 private int mPrevIndex;
70 private int mNextIndex;
71 private int mMaxIndex;
72
73 private EdgeSwipeModel mModel;
74 private EdgeSwipeView mView;
75
76 public EdgeSwipeController(View container,
77 int stationaryViewId,
78 int slidingViewId,
79 int slidingViewShadowId,
80 int opacityViewId,
81 int liveViewId,
82 int viewGroupId,
83 BaseUi ui) {
84 DraggableFrameLayout viewGroup = (DraggableFrameLayout)
85 container.findViewById(viewGroupId);
86
87 mActiveTab = ui.getActiveTab();
88 mTitleBar = ui.getTitleBar();
89
90 mModel = new EdgeSwipeModel(mActiveTab, mTitleBar);
91 mView = new EdgeSwipeView(
92 container,
93 stationaryViewId,
94 slidingViewId,
95 slidingViewShadowId,
96 opacityViewId,
97 liveViewId,
98 viewGroupId,
99 mTitleBar);
100
101 mPageLoadTarget = mActiveTab.getTabHistoryUpdateObservable();
102 mPageLoadObservable = Activator.activate(
103 new Observable.Observer() {
104 @Override
105 public void onChange(Object... params) {
106 if (mDragHelper == null ||
107 mPageLoadTarget == null) {
108 return;
109 }
110
111 synchronized (this) {
112 int index = (int) params[0];
113 if (mState == ViewDragHelper.STATE_IDLE && index == mCurrIndex) {
114 monitorProgressAtHistoryUpdate(index);
115 }
116 }
117 }
118 },
119 mPageLoadTarget
120 );
121
122 mDragHelper = ViewDragHelper.create(viewGroup, 0.5f, this);
123 mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT | ViewDragHelper.EDGE_RIGHT);
124 viewGroup.setDragHelper(mDragHelper);
125 }
126
127 private void swipeSessionCleanup() {
128 mView.goLive();
129 mModel.cleanup();
130 mCurrIndex = EDGE_SWIPE_INVALID_INDEX;
131 mState = ViewDragHelper.STATE_IDLE;
132 }
133
134 private boolean setState(int curState, int newState) {
135 if (mState == curState) {
136 mState = newState;
137 return true;
138 }
139 return false;
140 }
141
142 public void cleanup() {
143 if (mPageLoadObservable != null) {
144 mPageLoadObservable.onOff(false);
145 synchronized (this) {
146 mDragHelper.cancel();
147 swipeSessionCleanup();
148 }
149 }
150 }
151
152 public void onConfigurationChanged() {
153 synchronized (this) {
154 swipeSessionCleanup();
155 }
156 }
157
158 private void showCurrBMInStationaryView() {
159 if (!mbCurrBMSynced) {
160 Bitmap currBM = mModel.readSnapshot(mCurrIndex);
161 if (currBM != null) {
Pankaj Garg036365b2015-09-14 11:21:19 -0700162 mView.setStationaryViewBitmap(currBM, mModel.getColor(mCurrIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700163 mbCurrBMSynced = true;
164 }
165 }
166 }
167
168 private void showCurrBMInSlidingView() {
169 if (!mbCurrBMSynced) {
170 Bitmap currBM = mModel.readSnapshot(mCurrIndex);
Pankaj Garg036365b2015-09-14 11:21:19 -0700171 mView.setSlidingViewBitmap(currBM, mModel.getColor(mCurrIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700172 if (currBM != null) {
173 mbCurrBMSynced = true;
174 }
175 }
176 }
177
178 private Bitmap getGrayscale(Bitmap bitmap)
179 {
180 if (bitmap == null)
181 return null;
182
183 int height = bitmap.getHeight();
184 int width = bitmap.getWidth();
185
186 Bitmap gray = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
187 Canvas c = new Canvas(gray);
188 Paint paint = new Paint();
189 ColorMatrix cm = new ColorMatrix();
190
191 cm.setSaturation(0);
192
193 ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
194
195 paint.setColorFilter(f);
196
197 c.drawBitmap(bitmap, 0, 0, paint);
198 return gray;
199 }
200
201 private void monitorProgressAtLoad(final int pageIndex) {
202 if (mLoadTimer != null) {
203 mLoadTimer.cancel();
204 }
205
206 mLoadTimer = new CountDownTimer(mProgressWaitMS * 5, mProgressWaitMS) {
207 boolean mGrayBM = false;
208
209 public void onTick(long msRemain) {
210 if (msRemain > mProgressWaitMS * 4) {
211 return;
212 }
213 synchronized (this) {
214 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
215 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
216 swipeSessionCleanup();
217
218 }
219 cancel();
Sagar Dhawana4c552d2015-07-14 11:27:41 -0700220 } else if(mState == ViewDragHelper.STATE_DRAGGING) {
221 if (mGrayBM) {
222 return;
223 }
224 switch (mFromEdge) {
225 case ViewDragHelper.EDGE_LEFT:
226 mView.setSlidingViewBitmap(
Pankaj Garg036365b2015-09-14 11:21:19 -0700227 getGrayscale(getSnapshotOrFavicon(pageIndex)),
228 mModel.getColor(pageIndex));
Sagar Dhawana4c552d2015-07-14 11:27:41 -0700229 mGrayBM = true;
230 break;
231 case ViewDragHelper.EDGE_RIGHT:
232 mView.setStationaryViewBitmap(
Pankaj Garg036365b2015-09-14 11:21:19 -0700233 getGrayscale(getSnapshotOrFavicon(pageIndex)),
234 mModel.getColor(pageIndex));
Sagar Dhawana4c552d2015-07-14 11:27:41 -0700235 mGrayBM = true;
236 break;
237 }
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700238 } else {
239 if (mGrayBM) {
240 return;
241 }
242 mView.setStationaryViewBitmap(
Pankaj Garg036365b2015-09-14 11:21:19 -0700243 getGrayscale(getSnapshotOrFavicon(pageIndex)),
244 mModel.getColor(pageIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700245 mGrayBM = true;
246 }
247 }
248 }
249
250 public void onFinish() {
251 mGrayBM = false;
252 synchronized (this) {
253 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
254 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
255 swipeSessionCleanup();
256 }
257 cancel();
258 }
259 }
260 }
261 }.start();
262 }
263
Sagar Dhawan97ed6c92015-07-31 15:36:07 -0700264 private int lastCommittedHistoryIndex() {
265 WebView wv = mActiveTab.getWebView();
266 if (wv == null || wv.getLastCommittedHistoryIndex() == -1)
267 return 0; // WebView is null or No History has been committed for this tab
268 else
269 return wv.getLastCommittedHistoryIndex();
270 }
271
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700272 private void monitorProgressAtHistoryUpdate(final int pageIndex) {
273 if (mCommitTimer != null) {
274 mCommitTimer.cancel();
275 }
276
277 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress
Sagar Dhawan97ed6c92015-07-31 15:36:07 -0700278 && lastCommittedHistoryIndex() == pageIndex) {
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700279 swipeSessionCleanup();
280 return;
281 }
282
283 mCommitTimer = new CountDownTimer(mProgressWaitMS * 5, mProgressWaitMS) {
284 public void onTick(long msRemain) {
285 synchronized (this) {
286 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
287 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
288 swipeSessionCleanup();
289
290 }
291 cancel();
292 }
293 }
294 }
295
296 public void onFinish() {
297 synchronized (this) {
298 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
299 swipeSessionCleanup();
300 }
301 }
302 }
303 }.start();
304 }
305
306 private boolean isPortrait(Bitmap bitmap) {
307 return (bitmap.getHeight() < bitmap.getWidth());
308 }
309
310 private Bitmap getSnapshotOrFavicon(int index) {
311 Bitmap bm = mModel.readSnapshot(index);
312 if (bm == null || mView.isPortrait() != isPortrait(bm)) {
313 WebHistoryItem item = mActiveTab.getWebView()
314 .copyBackForwardList().getItemAtIndex(index);
315 if (item != null) {
316 bm = item.getFavicon();
317 }
318 }
319 return bm;
320 }
321
322 public void onViewDragStateChanged(int state) {
323 synchronized (this) {
324 if (mState != ViewDragHelper.STATE_SETTLING || state != ViewDragHelper.STATE_IDLE) {
325 return;
326 }
327
328 mView.hideSlidingViews();
329
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700330 if (mbNavigated) {
Pankaj Garg036365b2015-09-14 11:21:19 -0700331 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mCurrIndex),
332 mModel.getColor(mCurrIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700333 } else {
334 swipeSessionCleanup();
335 }
336
Sagar Dhawan95ce7df2015-07-28 18:02:05 -0700337 mView.setStationaryViewAlpha(1.0f);
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700338 mView.invalidate();
339
340 setState(ViewDragHelper.STATE_SETTLING, ViewDragHelper.STATE_IDLE);
341 }
342 }
343
344 public void onViewReleased(View releasedChild, float xvel, float yvel) {
345 synchronized (this) {
346 if (!setState(ViewDragHelper.STATE_DRAGGING, ViewDragHelper.STATE_SETTLING)) {
347 mOldX = 0;
348 mOldDx = 0;
349 return;
350 }
351
352 mbNavigated = true;
353
354 boolean bCrossedEventHorizon = Math.abs(mOldX) > mView.getWidth() / 2;
355
356 if (mCurrIndex >= 0) {
357 if ((xvel > 0 || (xvel == 0 && mOldX > 0 && bCrossedEventHorizon))
358 && mFromEdge == ViewDragHelper.EDGE_LEFT
359 && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex - 1)) {
360 mCurrIndex -= 1;
361 mActiveTab.getWebView().stopLoading();
362 mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
363 monitorProgressAtLoad(mCurrIndex);
364 mDragHelper.settleCapturedViewAt(
365 releasedChild.getMeasuredWidth(),
366 releasedChild.getTop());
367 } else if ((xvel < 0 || (xvel == 0 && mOldX < 0 && bCrossedEventHorizon))
368 && mFromEdge == ViewDragHelper.EDGE_RIGHT
369 && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex + 1)) {
370 mCurrIndex += 1;
371 mActiveTab.getWebView().stopLoading();
372 mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
373 monitorProgressAtLoad(mCurrIndex);
374 mDragHelper.settleCapturedViewAt(
375 -releasedChild.getMeasuredWidth(),
376 releasedChild.getTop());
377 mView.goDormant();
378 } else {
379 mbNavigated = false;
380 mDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
381 }
382 }
383 mOldX = 0;
384 mOldDx = 0;
385
386 mView.invalidate();
387 }
388 }
389
390 public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
391 float alpha = ((float) Math.abs(left)) / mView.getMeasuredWidth();
392
393 synchronized (this) {
394 switch (mFromEdge) {
395 case ViewDragHelper.EDGE_LEFT:
396 if (mView.isLive()) {
397 return;
398 }
Sagar Dhawan95ce7df2015-07-28 18:02:05 -0700399 mView.setStationaryViewAlpha(mMinAlpha + alpha * (1 - mMinAlpha));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700400
401 if (mState != ViewDragHelper.STATE_IDLE) {
402 mView.moveShadowView(left);
403 }
404
405 showCurrBMInSlidingView();
406
407 if (mPrevIndex >= 0) {
408 if (!mView.stationaryViewHasImage()) {
Pankaj Garg036365b2015-09-14 11:21:19 -0700409 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex),
410 mModel.getColor(mPrevIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700411 }
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700412 }
413 break;
414 case ViewDragHelper.EDGE_RIGHT:
Sagar Dhawan95ce7df2015-07-28 18:02:05 -0700415 mView.setStationaryViewAlpha(mMinAlpha + (1 - alpha) * (1 - mMinAlpha));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700416 if (mState != ViewDragHelper.STATE_IDLE) {
417 mView.moveShadowView(mView.getMeasuredWidth() + left);
418
419 if (!mView.slidingViewHasImage() && mNextIndex < mMaxIndex) {
Pankaj Garg036365b2015-09-14 11:21:19 -0700420 mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex),
421 mModel.getColor(mNextIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700422 }
423
424 showCurrBMInStationaryView();
425 if (mbCurrBMSynced) {
426 mView.goDormant();
427 }
428 }
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700429 break;
430 default:
431 break;
432 }
433 }
434 }
435
436 public void onEdgeDragStarted(int edgeFlags, int pointerId) {
437 synchronized (this) {
438 if (mActiveTab.isPrivateBrowsingEnabled()) {
439 mDragHelper.abort();
440 return;
441 }
442
443 if (mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE ||
444 !setState(ViewDragHelper.STATE_IDLE, ViewDragHelper.STATE_DRAGGING)) {
445 mDragHelper.abort();
446 return;
447 }
448
449 if ((edgeFlags & mFromEdge) != mFromEdge || mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
450 onEdgeTouched(edgeFlags, pointerId);
451 }
452
453 mbCurrBMSynced = false;
454
455 switch (mFromEdge) {
456 case ViewDragHelper.EDGE_LEFT:
457 mView.showSlidingViews();
458 mView.goDormant();
459 mPrevIndex = mCurrIndex - 1;
Pankaj Garg036365b2015-09-14 11:21:19 -0700460 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex),
461 mModel.getColor(mPrevIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700462 showCurrBMInSlidingView();
463 break;
464 case ViewDragHelper.EDGE_RIGHT:
465 mView.showSlidingViews();
466 mNextIndex = mCurrIndex + 1;
Pankaj Garg036365b2015-09-14 11:21:19 -0700467 mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex),
468 mModel.getColor(mNextIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700469 showCurrBMInStationaryView();
470 if (mbCurrBMSynced)
471 mView.goDormant();
472 break;
473 default:
474 break;
475 }
476 }
477 }
478
479 public int getOrderedChildIndex(int index) {
480 return mView.slidingViewIndex();
481 }
482
483 public void onEdgeTouched (int edgeFlags, int pointerId) {
484 synchronized (this) {
Sagar Dhawan97ed6c92015-07-31 15:36:07 -0700485 if (mActiveTab.getWebView() == null ||
486 mActiveTab.isPrivateBrowsingEnabled() ||
487 mActiveTab.isKeyboardShowing()) {
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700488 mDragHelper.abort();
489 return;
490 }
491
492 if (mState != ViewDragHelper.STATE_IDLE && mCurrIndex != EDGE_SWIPE_INVALID_INDEX) {
493 mDragHelper.abort();
494 return;
495 }
496
497 mView.init();
498
499 if (mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
Sagar Dhawan97ed6c92015-07-31 15:36:07 -0700500 mCurrIndex = lastCommittedHistoryIndex();
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700501 }
502
503 mMaxIndex = mActiveTab.getWebView().copyBackForwardList().getSize() - 1;
504 mModel.updateSnapshot(mCurrIndex);
505
506 if (ViewDragHelper.EDGE_LEFT == (edgeFlags & ViewDragHelper.EDGE_LEFT)) {
507 mFromEdge = ViewDragHelper.EDGE_LEFT;
508 mView.slidingViewTouched(mFromEdge);
509 if (mCurrIndex > 0) {
510 mModel.fetchSnapshot(mCurrIndex - 1);
511 }
512 } else if (ViewDragHelper.EDGE_RIGHT == (edgeFlags & ViewDragHelper.EDGE_RIGHT)) {
513 mFromEdge = ViewDragHelper.EDGE_RIGHT;
514 mView.slidingViewTouched(mFromEdge);
515 if (mCurrIndex < mMaxIndex) {
516 mModel.fetchSnapshot(mCurrIndex + 1);
517 }
518 }
519 }
520 }
521
522 public int getViewHorizontalDragRange(View child) {
523 return child.getMeasuredWidth();
524 }
525
526 public boolean tryCaptureView(View child, int pointerId) {
527 return (mState == ViewDragHelper.STATE_DRAGGING && mView.allowCapture(child));
528 }
529
530 public int clampViewPositionHorizontal(View child, int left, int dx) {
531 if (mOldX != 0 && Math.signum(dx) != Math.signum(mOldDx)) {
532 mOldDx = dx;
533 return mOldX;
534 }
535
536 switch (mFromEdge) {
537 case ViewDragHelper.EDGE_LEFT:
538 if (left < 0) {
539 mOldDx = dx;
540 return mOldX;
541 }
542 if (!mActiveTab.getWebView().canGoToHistoryIndex(mPrevIndex)) {
543 if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
544 return child.getMeasuredWidth() / 3;
545 }
546 }
547 break;
548 case ViewDragHelper.EDGE_RIGHT:
549 if (left > 0) {
550 mOldDx = dx;
551 return mOldX;
552 }
553 if (!mActiveTab.getWebView().canGoToHistoryIndex(mNextIndex)) {
554 if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
555 return -child.getMeasuredWidth() / 3;
556 }
557 }
558 break;
559 default:
560 break;
561 }
562
563 mOldX = left;
564 mOldDx = dx;
565 return left;
566 }
567}
568