blob: c82daa77be8323ac7386705f7160faabaef4ca2a [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
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700318 if (mbNavigated) {
319 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mCurrIndex));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700320 } else {
321 swipeSessionCleanup();
322 }
323
Sagar Dhawan95ce7df2015-07-28 18:02:05 -0700324 mView.setStationaryViewAlpha(1.0f);
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700325 mView.invalidate();
326
327 setState(ViewDragHelper.STATE_SETTLING, ViewDragHelper.STATE_IDLE);
328 }
329 }
330
331 public void onViewReleased(View releasedChild, float xvel, float yvel) {
332 synchronized (this) {
333 if (!setState(ViewDragHelper.STATE_DRAGGING, ViewDragHelper.STATE_SETTLING)) {
334 mOldX = 0;
335 mOldDx = 0;
336 return;
337 }
338
339 mbNavigated = true;
340
341 boolean bCrossedEventHorizon = Math.abs(mOldX) > mView.getWidth() / 2;
342
343 if (mCurrIndex >= 0) {
344 if ((xvel > 0 || (xvel == 0 && mOldX > 0 && bCrossedEventHorizon))
345 && mFromEdge == ViewDragHelper.EDGE_LEFT
346 && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex - 1)) {
347 mCurrIndex -= 1;
348 mActiveTab.getWebView().stopLoading();
349 mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
350 monitorProgressAtLoad(mCurrIndex);
351 mDragHelper.settleCapturedViewAt(
352 releasedChild.getMeasuredWidth(),
353 releasedChild.getTop());
354 } else if ((xvel < 0 || (xvel == 0 && mOldX < 0 && bCrossedEventHorizon))
355 && mFromEdge == ViewDragHelper.EDGE_RIGHT
356 && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex + 1)) {
357 mCurrIndex += 1;
358 mActiveTab.getWebView().stopLoading();
359 mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
360 monitorProgressAtLoad(mCurrIndex);
361 mDragHelper.settleCapturedViewAt(
362 -releasedChild.getMeasuredWidth(),
363 releasedChild.getTop());
364 mView.goDormant();
365 } else {
366 mbNavigated = false;
367 mDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
368 }
369 }
370 mOldX = 0;
371 mOldDx = 0;
372
373 mView.invalidate();
374 }
375 }
376
377 public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
378 float alpha = ((float) Math.abs(left)) / mView.getMeasuredWidth();
379
380 synchronized (this) {
381 switch (mFromEdge) {
382 case ViewDragHelper.EDGE_LEFT:
383 if (mView.isLive()) {
384 return;
385 }
Sagar Dhawan95ce7df2015-07-28 18:02:05 -0700386 mView.setStationaryViewAlpha(mMinAlpha + alpha * (1 - mMinAlpha));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700387
388 if (mState != ViewDragHelper.STATE_IDLE) {
389 mView.moveShadowView(left);
390 }
391
392 showCurrBMInSlidingView();
393
394 if (mPrevIndex >= 0) {
395 if (!mView.stationaryViewHasImage()) {
396 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex));
397 }
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700398 }
399 break;
400 case ViewDragHelper.EDGE_RIGHT:
Sagar Dhawan95ce7df2015-07-28 18:02:05 -0700401 mView.setStationaryViewAlpha(mMinAlpha + (1 - alpha) * (1 - mMinAlpha));
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700402 if (mState != ViewDragHelper.STATE_IDLE) {
403 mView.moveShadowView(mView.getMeasuredWidth() + left);
404
405 if (!mView.slidingViewHasImage() && mNextIndex < mMaxIndex) {
406 mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex));
407 }
408
409 showCurrBMInStationaryView();
410 if (mbCurrBMSynced) {
411 mView.goDormant();
412 }
413 }
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700414 break;
415 default:
416 break;
417 }
418 }
419 }
420
421 public void onEdgeDragStarted(int edgeFlags, int pointerId) {
422 synchronized (this) {
423 if (mActiveTab.isPrivateBrowsingEnabled()) {
424 mDragHelper.abort();
425 return;
426 }
427
428 if (mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE ||
429 !setState(ViewDragHelper.STATE_IDLE, ViewDragHelper.STATE_DRAGGING)) {
430 mDragHelper.abort();
431 return;
432 }
433
434 if ((edgeFlags & mFromEdge) != mFromEdge || mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
435 onEdgeTouched(edgeFlags, pointerId);
436 }
437
438 mbCurrBMSynced = false;
439
440 switch (mFromEdge) {
441 case ViewDragHelper.EDGE_LEFT:
442 mView.showSlidingViews();
443 mView.goDormant();
444 mPrevIndex = mCurrIndex - 1;
445 mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex));
446 showCurrBMInSlidingView();
447 break;
448 case ViewDragHelper.EDGE_RIGHT:
449 mView.showSlidingViews();
450 mNextIndex = mCurrIndex + 1;
451 mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex));
452 showCurrBMInStationaryView();
453 if (mbCurrBMSynced)
454 mView.goDormant();
455 break;
456 default:
457 break;
458 }
459 }
460 }
461
462 public int getOrderedChildIndex(int index) {
463 return mView.slidingViewIndex();
464 }
465
466 public void onEdgeTouched (int edgeFlags, int pointerId) {
467 synchronized (this) {
Sagar Dhawan33551ff2015-07-08 17:24:44 -0700468 if (mActiveTab.isPrivateBrowsingEnabled() || mActiveTab.isKeyboardShowing()) {
Pankaj Garg1c13cab2015-05-12 11:52:17 -0700469 mDragHelper.abort();
470 return;
471 }
472
473 if (mState != ViewDragHelper.STATE_IDLE && mCurrIndex != EDGE_SWIPE_INVALID_INDEX) {
474 mDragHelper.abort();
475 return;
476 }
477
478 mView.init();
479
480 if (mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
481 mCurrIndex = mActiveTab.getWebView().getLastCommittedHistoryIndex();
482 }
483
484 mMaxIndex = mActiveTab.getWebView().copyBackForwardList().getSize() - 1;
485 mModel.updateSnapshot(mCurrIndex);
486
487 if (ViewDragHelper.EDGE_LEFT == (edgeFlags & ViewDragHelper.EDGE_LEFT)) {
488 mFromEdge = ViewDragHelper.EDGE_LEFT;
489 mView.slidingViewTouched(mFromEdge);
490 if (mCurrIndex > 0) {
491 mModel.fetchSnapshot(mCurrIndex - 1);
492 }
493 } else if (ViewDragHelper.EDGE_RIGHT == (edgeFlags & ViewDragHelper.EDGE_RIGHT)) {
494 mFromEdge = ViewDragHelper.EDGE_RIGHT;
495 mView.slidingViewTouched(mFromEdge);
496 if (mCurrIndex < mMaxIndex) {
497 mModel.fetchSnapshot(mCurrIndex + 1);
498 }
499 }
500 }
501 }
502
503 public int getViewHorizontalDragRange(View child) {
504 return child.getMeasuredWidth();
505 }
506
507 public boolean tryCaptureView(View child, int pointerId) {
508 return (mState == ViewDragHelper.STATE_DRAGGING && mView.allowCapture(child));
509 }
510
511 public int clampViewPositionHorizontal(View child, int left, int dx) {
512 if (mOldX != 0 && Math.signum(dx) != Math.signum(mOldDx)) {
513 mOldDx = dx;
514 return mOldX;
515 }
516
517 switch (mFromEdge) {
518 case ViewDragHelper.EDGE_LEFT:
519 if (left < 0) {
520 mOldDx = dx;
521 return mOldX;
522 }
523 if (!mActiveTab.getWebView().canGoToHistoryIndex(mPrevIndex)) {
524 if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
525 return child.getMeasuredWidth() / 3;
526 }
527 }
528 break;
529 case ViewDragHelper.EDGE_RIGHT:
530 if (left > 0) {
531 mOldDx = dx;
532 return mOldX;
533 }
534 if (!mActiveTab.getWebView().canGoToHistoryIndex(mNextIndex)) {
535 if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
536 return -child.getMeasuredWidth() / 3;
537 }
538 }
539 break;
540 default:
541 break;
542 }
543
544 mOldX = left;
545 mOldDx = dx;
546 return left;
547 }
548}
549