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