blob: 5d6edd5208e44432f7d3e1bee9542f1ddb41504f [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) {
162 mView.setStationaryViewBitmap(currBM);
163 mbCurrBMSynced = true;
164 }
165 }
166 }
167
168 private void showCurrBMInSlidingView() {
169 if (!mbCurrBMSynced) {
170 Bitmap currBM = mModel.readSnapshot(mCurrIndex);
171 mView.setSlidingViewBitmap(currBM);
172 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(
227 getGrayscale(getSnapshotOrFavicon(pageIndex)));
228 mGrayBM = true;
229 break;
230 case ViewDragHelper.EDGE_RIGHT:
231 mView.setStationaryViewBitmap(
232 getGrayscale(getSnapshotOrFavicon(pageIndex)));
233 mGrayBM = true;
234 break;
235 }
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700236 } else {
237 if (mGrayBM) {
238 return;
239 }
240 mView.setStationaryViewBitmap(
241 getGrayscale(getSnapshotOrFavicon(pageIndex)));
242 mGrayBM = true;
243 }
244 }
245 }
246
247 public void onFinish() {
248 mGrayBM = false;
249 synchronized (this) {
250 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
251 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
252 swipeSessionCleanup();
253 }
254 cancel();
255 }
256 }
257 }
258 }.start();
259 }
260
Sagar Dhawan97ed6c92015-07-31 15:36:07 -0700261 private int lastCommittedHistoryIndex() {
262 WebView wv = mActiveTab.getWebView();
263 if (wv == null || wv.getLastCommittedHistoryIndex() == -1)
264 return 0; // WebView is null or No History has been committed for this tab
265 else
266 return wv.getLastCommittedHistoryIndex();
267 }
268
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700269 private void monitorProgressAtHistoryUpdate(final int pageIndex) {
270 if (mCommitTimer != null) {
271 mCommitTimer.cancel();
272 }
273
274 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress
Sagar Dhawan97ed6c92015-07-31 15:36:07 -0700275 && lastCommittedHistoryIndex() == pageIndex) {
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700276 swipeSessionCleanup();
277 return;
278 }
279
280 mCommitTimer = new CountDownTimer(mProgressWaitMS * 5, mProgressWaitMS) {
281 public void onTick(long msRemain) {
282 synchronized (this) {
283 if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
284 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
285 swipeSessionCleanup();
286
287 }
288 cancel();
289 }
290 }
291 }
292
293 public void onFinish() {
294 synchronized (this) {
295 if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
296 swipeSessionCleanup();
297 }
298 }
299 }
300 }.start();
301 }
302
303 private boolean isPortrait(Bitmap bitmap) {
304 return (bitmap.getHeight() < bitmap.getWidth());
305 }
306
307 private Bitmap getSnapshotOrFavicon(int index) {
308 Bitmap bm = mModel.readSnapshot(index);
309 if (bm == null || mView.isPortrait() != isPortrait(bm)) {
310 WebHistoryItem item = mActiveTab.getWebView()
311 .copyBackForwardList().getItemAtIndex(index);
312 if (item != null) {
313 bm = item.getFavicon();
314 }
315 }
316 return bm;
317 }
318
319 public void onViewDragStateChanged(int state) {
320 synchronized (this) {
321 if (mState != ViewDragHelper.STATE_SETTLING || state != ViewDragHelper.STATE_IDLE) {
322 return;
323 }
324
325 mView.hideSlidingViews();
326
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700327 if (mbNavigated) {
328 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mCurrIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700329 } else {
330 swipeSessionCleanup();
331 }
332
Sagar Dhawan95ce7df2015-07-28 18:02:05 -0700333 mView.setStationaryViewAlpha(1.0f);
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700334 mView.invalidate();
335
336 setState(ViewDragHelper.STATE_SETTLING, ViewDragHelper.STATE_IDLE);
337 }
338 }
339
340 public void onViewReleased(View releasedChild, float xvel, float yvel) {
341 synchronized (this) {
342 if (!setState(ViewDragHelper.STATE_DRAGGING, ViewDragHelper.STATE_SETTLING)) {
343 mOldX = 0;
344 mOldDx = 0;
345 return;
346 }
347
348 mbNavigated = true;
349
350 boolean bCrossedEventHorizon = Math.abs(mOldX) > mView.getWidth() / 2;
351
352 if (mCurrIndex >= 0) {
353 if ((xvel > 0 || (xvel == 0 && mOldX > 0 && bCrossedEventHorizon))
354 && mFromEdge == ViewDragHelper.EDGE_LEFT
355 && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex - 1)) {
356 mCurrIndex -= 1;
357 mActiveTab.getWebView().stopLoading();
358 mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
359 monitorProgressAtLoad(mCurrIndex);
360 mDragHelper.settleCapturedViewAt(
361 releasedChild.getMeasuredWidth(),
362 releasedChild.getTop());
363 } else if ((xvel < 0 || (xvel == 0 && mOldX < 0 && bCrossedEventHorizon))
364 && mFromEdge == ViewDragHelper.EDGE_RIGHT
365 && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex + 1)) {
366 mCurrIndex += 1;
367 mActiveTab.getWebView().stopLoading();
368 mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
369 monitorProgressAtLoad(mCurrIndex);
370 mDragHelper.settleCapturedViewAt(
371 -releasedChild.getMeasuredWidth(),
372 releasedChild.getTop());
373 mView.goDormant();
374 } else {
375 mbNavigated = false;
376 mDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
377 }
378 }
379 mOldX = 0;
380 mOldDx = 0;
381
382 mView.invalidate();
383 }
384 }
385
386 public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
387 float alpha = ((float) Math.abs(left)) / mView.getMeasuredWidth();
388
389 synchronized (this) {
390 switch (mFromEdge) {
391 case ViewDragHelper.EDGE_LEFT:
392 if (mView.isLive()) {
393 return;
394 }
Sagar Dhawan95ce7df2015-07-28 18:02:05 -0700395 mView.setStationaryViewAlpha(mMinAlpha + alpha * (1 - mMinAlpha));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700396
397 if (mState != ViewDragHelper.STATE_IDLE) {
398 mView.moveShadowView(left);
399 }
400
401 showCurrBMInSlidingView();
402
403 if (mPrevIndex >= 0) {
404 if (!mView.stationaryViewHasImage()) {
405 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex));
406 }
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700407 }
408 break;
409 case ViewDragHelper.EDGE_RIGHT:
Sagar Dhawan95ce7df2015-07-28 18:02:05 -0700410 mView.setStationaryViewAlpha(mMinAlpha + (1 - alpha) * (1 - mMinAlpha));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700411 if (mState != ViewDragHelper.STATE_IDLE) {
412 mView.moveShadowView(mView.getMeasuredWidth() + left);
413
414 if (!mView.slidingViewHasImage() && mNextIndex < mMaxIndex) {
415 mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex));
416 }
417
418 showCurrBMInStationaryView();
419 if (mbCurrBMSynced) {
420 mView.goDormant();
421 }
422 }
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700423 break;
424 default:
425 break;
426 }
427 }
428 }
429
430 public void onEdgeDragStarted(int edgeFlags, int pointerId) {
431 synchronized (this) {
432 if (mActiveTab.isPrivateBrowsingEnabled()) {
433 mDragHelper.abort();
434 return;
435 }
436
437 if (mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE ||
438 !setState(ViewDragHelper.STATE_IDLE, ViewDragHelper.STATE_DRAGGING)) {
439 mDragHelper.abort();
440 return;
441 }
442
443 if ((edgeFlags & mFromEdge) != mFromEdge || mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
444 onEdgeTouched(edgeFlags, pointerId);
445 }
446
447 mbCurrBMSynced = false;
448
449 switch (mFromEdge) {
450 case ViewDragHelper.EDGE_LEFT:
451 mView.showSlidingViews();
452 mView.goDormant();
453 mPrevIndex = mCurrIndex - 1;
454 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex));
455 showCurrBMInSlidingView();
456 break;
457 case ViewDragHelper.EDGE_RIGHT:
458 mView.showSlidingViews();
459 mNextIndex = mCurrIndex + 1;
460 mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex));
461 showCurrBMInStationaryView();
462 if (mbCurrBMSynced)
463 mView.goDormant();
464 break;
465 default:
466 break;
467 }
468 }
469 }
470
471 public int getOrderedChildIndex(int index) {
472 return mView.slidingViewIndex();
473 }
474
475 public void onEdgeTouched (int edgeFlags, int pointerId) {
476 synchronized (this) {
Sagar Dhawan97ed6c92015-07-31 15:36:07 -0700477 if (mActiveTab.getWebView() == null ||
478 mActiveTab.isPrivateBrowsingEnabled() ||
479 mActiveTab.isKeyboardShowing()) {
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700480 mDragHelper.abort();
481 return;
482 }
483
484 if (mState != ViewDragHelper.STATE_IDLE && mCurrIndex != EDGE_SWIPE_INVALID_INDEX) {
485 mDragHelper.abort();
486 return;
487 }
488
489 mView.init();
490
491 if (mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
Sagar Dhawan97ed6c92015-07-31 15:36:07 -0700492 mCurrIndex = lastCommittedHistoryIndex();
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700493 }
494
495 mMaxIndex = mActiveTab.getWebView().copyBackForwardList().getSize() - 1;
496 mModel.updateSnapshot(mCurrIndex);
497
498 if (ViewDragHelper.EDGE_LEFT == (edgeFlags & ViewDragHelper.EDGE_LEFT)) {
499 mFromEdge = ViewDragHelper.EDGE_LEFT;
500 mView.slidingViewTouched(mFromEdge);
501 if (mCurrIndex > 0) {
502 mModel.fetchSnapshot(mCurrIndex - 1);
503 }
504 } else if (ViewDragHelper.EDGE_RIGHT == (edgeFlags & ViewDragHelper.EDGE_RIGHT)) {
505 mFromEdge = ViewDragHelper.EDGE_RIGHT;
506 mView.slidingViewTouched(mFromEdge);
507 if (mCurrIndex < mMaxIndex) {
508 mModel.fetchSnapshot(mCurrIndex + 1);
509 }
510 }
511 }
512 }
513
514 public int getViewHorizontalDragRange(View child) {
515 return child.getMeasuredWidth();
516 }
517
518 public boolean tryCaptureView(View child, int pointerId) {
519 return (mState == ViewDragHelper.STATE_DRAGGING && mView.allowCapture(child));
520 }
521
522 public int clampViewPositionHorizontal(View child, int left, int dx) {
523 if (mOldX != 0 && Math.signum(dx) != Math.signum(mOldDx)) {
524 mOldDx = dx;
525 return mOldX;
526 }
527
528 switch (mFromEdge) {
529 case ViewDragHelper.EDGE_LEFT:
530 if (left < 0) {
531 mOldDx = dx;
532 return mOldX;
533 }
534 if (!mActiveTab.getWebView().canGoToHistoryIndex(mPrevIndex)) {
535 if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
536 return child.getMeasuredWidth() / 3;
537 }
538 }
539 break;
540 case ViewDragHelper.EDGE_RIGHT:
541 if (left > 0) {
542 mOldDx = dx;
543 return mOldX;
544 }
545 if (!mActiveTab.getWebView().canGoToHistoryIndex(mNextIndex)) {
546 if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
547 return -child.getMeasuredWidth() / 3;
548 }
549 }
550 break;
551 default:
552 break;
553 }
554
555 mOldX = left;
556 mOldDx = dx;
557 return left;
558 }
559}
560