| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser; | 5 package org.chromium.chrome.browser; |
| 6 | 6 |
| 7 import android.content.Context; | 7 import android.content.Context; |
| 8 import android.view.ViewGroup; |
| 8 import android.view.ViewGroup.LayoutParams; | 9 import android.view.ViewGroup.LayoutParams; |
| 9 | 10 |
| 10 import org.chromium.base.TraceEvent; | 11 import org.chromium.base.TraceEvent; |
| 12 import org.chromium.base.annotations.CalledByNative; |
| 11 import org.chromium.base.metrics.RecordUserAction; | 13 import org.chromium.base.metrics.RecordUserAction; |
| 12 import org.chromium.chrome.R; | 14 import org.chromium.chrome.R; |
| 13 import org.chromium.chrome.browser.tab.Tab; | 15 import org.chromium.chrome.browser.tab.Tab; |
| 14 import org.chromium.content.browser.ContentViewCore; | |
| 15 import org.chromium.content.browser.OverscrollRefreshHandler; | 16 import org.chromium.content.browser.OverscrollRefreshHandler; |
| 17 import org.chromium.content_public.browser.WebContents; |
| 16 import org.chromium.third_party.android.swiperefresh.SwipeRefreshLayout; | 18 import org.chromium.third_party.android.swiperefresh.SwipeRefreshLayout; |
| 17 | 19 |
| 18 /** | 20 /** |
| 19 * An overscroll handler implemented in terms a modified version of the Android | 21 * An overscroll handler implemented in terms a modified version of the Android |
| 20 * compat library's SwipeRefreshLayout effect. | 22 * compat library's SwipeRefreshLayout effect. |
| 21 */ | 23 */ |
| 22 public class SwipeRefreshHandler implements OverscrollRefreshHandler { | 24 public class SwipeRefreshHandler implements OverscrollRefreshHandler { |
| 23 // Synthetic delay between the {@link #didStopRefreshing()} signal and the | 25 // Synthetic delay between the {@link #didStopRefreshing()} signal and the |
| 24 // call to stop the refresh animation. | 26 // call to stop the refresh animation. |
| 25 private static final int STOP_REFRESH_ANIMATION_DELAY_MS = 500; | 27 private static final int STOP_REFRESH_ANIMATION_DELAY_MS = 500; |
| 26 | 28 |
| 27 // Max allowed duration of the refresh animation after a refresh signal, | 29 // Max allowed duration of the refresh animation after a refresh signal, |
| 28 // guarding against cases where the page reload fails or takes too long. | 30 // guarding against cases where the page reload fails or takes too long. |
| 29 private static final int MAX_REFRESH_ANIMATION_DURATION_MS = 7500; | 31 private static final int MAX_REFRESH_ANIMATION_DURATION_MS = 7500; |
| 30 | 32 |
| 31 // The modified AppCompat version of the refresh effect, handling all core | 33 // The modified AppCompat version of the refresh effect, handling all core |
| 32 // logic, rendering and animation. | 34 // logic, rendering and animation. |
| 33 private final SwipeRefreshLayout mSwipeRefreshLayout; | 35 private final SwipeRefreshLayout mSwipeRefreshLayout; |
| 34 | 36 |
| 35 // The Tab where the swipe occurs. | 37 // The Tab where the swipe occurs. |
| 36 private Tab mTab; | 38 private Tab mTab; |
| 37 | 39 |
| 38 // The ContentViewCore with which the handler is associated. The handler | 40 // The container view the SwipeRefreshHandler instance is currently |
| 39 // will set/unset itself as the default OverscrollRefreshHandler as the | 41 // associated with. |
| 40 // association changes. | 42 private ViewGroup mContainerView; |
| 41 private ContentViewCore mContentViewCore; | |
| 42 | 43 |
| 43 // Async runnable for ending the refresh animation after the page first | 44 // Async runnable for ending the refresh animation after the page first |
| 44 // loads a frame. This is used to provide a reasonable minimum animation tim
e. | 45 // loads a frame. This is used to provide a reasonable minimum animation tim
e. |
| 45 private Runnable mStopRefreshingRunnable; | 46 private Runnable mStopRefreshingRunnable; |
| 46 | 47 |
| 47 // Handles removing the layout from the view hierarchy. This is posted to e
nsure it does not | 48 // Handles removing the layout from the view hierarchy. This is posted to e
nsure it does not |
| 48 // conflict with pending Android draws. | 49 // conflict with pending Android draws. |
| 49 private Runnable mDetachLayoutRunnable; | 50 private Runnable mDetachLayoutRunnable; |
| 50 | 51 |
| 51 // Accessibility utterance used to indicate refresh activation. | 52 // Accessibility utterance used to indicate refresh activation. |
| 52 private String mAccessibilityRefreshString; | 53 private String mAccessibilityRefreshString; |
| 53 | 54 |
| 55 // Pointer to the C++ SwipeRefreshHandler |
| 56 private long mNativeSwipeRefreshHandler; |
| 57 |
| 54 /** | 58 /** |
| 55 * Simple constructor to use when creating an OverscrollRefresh instance fro
m code. | 59 * Simple constructor to use when creating an OverscrollRefresh instance fro
m code. |
| 56 * | 60 * |
| 57 * @param context The associated context. | 61 * @param context The associated context. |
| 58 * @param tab The Tab where the swipe occurs. | 62 * @param tab The Tab where the swipe occurs. |
| 59 */ | 63 */ |
| 60 public SwipeRefreshHandler(Context context, Tab tab) { | 64 public SwipeRefreshHandler(final Context context, Tab tab, WebContents webCo
ntents) { |
| 61 mTab = tab; | 65 mTab = tab; |
| 62 mContentViewCore = mTab.getContentViewCore(); | 66 mContainerView = mTab.getContentViewCore().getContainerView(); |
| 63 | 67 |
| 64 mSwipeRefreshLayout = new SwipeRefreshLayout(context); | 68 mSwipeRefreshLayout = new SwipeRefreshLayout(context); |
| 65 mSwipeRefreshLayout.setLayoutParams( | 69 mSwipeRefreshLayout.setLayoutParams( |
| 66 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P
ARENT)); | 70 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_P
ARENT)); |
| 67 mSwipeRefreshLayout.setColorSchemeResources(R.color.light_active_color); | 71 mSwipeRefreshLayout.setColorSchemeResources(R.color.light_active_color); |
| 68 // SwipeRefreshLayout.LARGE layouts appear broken on JellyBean. | 72 // SwipeRefreshLayout.LARGE layouts appear broken on JellyBean. |
| 69 mSwipeRefreshLayout.setSize(SwipeRefreshLayout.DEFAULT); | 73 mSwipeRefreshLayout.setSize(SwipeRefreshLayout.DEFAULT); |
| 70 mSwipeRefreshLayout.setEnabled(false); | 74 mSwipeRefreshLayout.setEnabled(false); |
| 71 | 75 |
| 72 setEnabled(true); | 76 setEnabled(true); |
| 73 mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefres
hListener() { | 77 mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefres
hListener() { |
| 74 @Override | 78 @Override |
| 75 public void onRefresh() { | 79 public void onRefresh() { |
| 76 cancelStopRefreshingRunnable(); | 80 cancelStopRefreshingRunnable(); |
| 77 mSwipeRefreshLayout.postDelayed( | 81 mSwipeRefreshLayout.postDelayed( |
| 78 getStopRefreshingRunnable(), MAX_REFRESH_ANIMATION_DURAT
ION_MS); | 82 getStopRefreshingRunnable(), MAX_REFRESH_ANIMATION_DURAT
ION_MS); |
| 79 if (mAccessibilityRefreshString == null) { | 83 if (mAccessibilityRefreshString == null) { |
| 80 int resId = R.string.accessibility_swipe_refresh; | 84 int resId = R.string.accessibility_swipe_refresh; |
| 81 mAccessibilityRefreshString = | 85 mAccessibilityRefreshString = context.getResources().getStri
ng(resId); |
| 82 mContentViewCore.getContext().getResources().getStri
ng(resId); | |
| 83 } | 86 } |
| 84 mSwipeRefreshLayout.announceForAccessibility(mAccessibilityRefre
shString); | 87 mSwipeRefreshLayout.announceForAccessibility(mAccessibilityRefre
shString); |
| 85 mTab.reload(); | 88 mTab.reload(); |
| 86 RecordUserAction.record("MobilePullGestureReload"); | 89 RecordUserAction.record("MobilePullGestureReload"); |
| 87 } | 90 } |
| 88 }); | 91 }); |
| 89 mSwipeRefreshLayout.setOnResetListener(new SwipeRefreshLayout.OnResetLis
tener() { | 92 mSwipeRefreshLayout.setOnResetListener(new SwipeRefreshLayout.OnResetLis
tener() { |
| 90 @Override | 93 @Override |
| 91 public void onReset() { | 94 public void onReset() { |
| 92 if (mDetachLayoutRunnable != null) return; | 95 if (mDetachLayoutRunnable != null) return; |
| 93 mDetachLayoutRunnable = new Runnable() { | 96 mDetachLayoutRunnable = new Runnable() { |
| 94 @Override | 97 @Override |
| 95 public void run() { | 98 public void run() { |
| 96 mDetachLayoutRunnable = null; | 99 mDetachLayoutRunnable = null; |
| 97 detachSwipeRefreshLayoutIfNecessary(); | 100 detachSwipeRefreshLayoutIfNecessary(); |
| 98 } | 101 } |
| 99 }; | 102 }; |
| 100 mSwipeRefreshLayout.post(mDetachLayoutRunnable); | 103 mSwipeRefreshLayout.post(mDetachLayoutRunnable); |
| 101 } | 104 } |
| 102 }); | 105 }); |
| 103 | 106 webContents.setOverscrollRefreshHandler(this); |
| 104 mContentViewCore.setOverscrollRefreshHandler(this); | |
| 105 } | 107 } |
| 106 | 108 |
| 107 /** | 109 /** |
| 108 * Destroys and cleans up itself. | 110 * Destroys and cleans up itself. |
| 109 */ | 111 */ |
| 110 public void destroy() { | 112 public void destroy() { |
| 111 setEnabled(false); | 113 setEnabled(false); |
| 112 cancelStopRefreshingRunnable(); | 114 cancelStopRefreshingRunnable(); |
| 113 mSwipeRefreshLayout.setOnRefreshListener(null); | 115 mSwipeRefreshLayout.setOnRefreshListener(null); |
| 114 mContentViewCore.setOverscrollRefreshHandler(null); | |
| 115 } | 116 } |
| 116 | 117 |
| 117 /** | 118 /** |
| 118 * Notify the SwipeRefreshLayout that a refresh action has completed. | 119 * Notify the SwipeRefreshLayout that a refresh action has completed. |
| 119 * Defer the notification by a reasonable minimum to ensure sufficient | 120 * Defer the notification by a reasonable minimum to ensure sufficient |
| 120 * visiblity of the animation. | 121 * visiblity of the animation. |
| 121 */ | 122 */ |
| 122 public void didStopRefreshing() { | 123 public void didStopRefreshing() { |
| 123 if (!mSwipeRefreshLayout.isRefreshing()) return; | 124 if (!mSwipeRefreshLayout.isRefreshing()) return; |
| 124 cancelStopRefreshingRunnable(); | 125 cancelStopRefreshingRunnable(); |
| 125 mSwipeRefreshLayout.postDelayed( | 126 mSwipeRefreshLayout.postDelayed( |
| 126 getStopRefreshingRunnable(), STOP_REFRESH_ANIMATION_DELAY_MS); | 127 getStopRefreshingRunnable(), STOP_REFRESH_ANIMATION_DELAY_MS); |
| 127 } | 128 } |
| 128 | 129 |
| 129 @Override | 130 @Override |
| 131 @CalledByNative |
| 130 public boolean start() { | 132 public boolean start() { |
| 131 attachSwipeRefreshLayoutIfNecessary(); | 133 attachSwipeRefreshLayoutIfNecessary(); |
| 132 return mSwipeRefreshLayout.start(); | 134 return mSwipeRefreshLayout.start(); |
| 133 } | 135 } |
| 134 | 136 |
| 135 @Override | 137 @Override |
| 138 @CalledByNative |
| 136 public void pull(float delta) { | 139 public void pull(float delta) { |
| 137 TraceEvent.begin("SwipeRefreshHandler.pull"); | 140 TraceEvent.begin("SwipeRefreshHandler.pull"); |
| 138 mSwipeRefreshLayout.pull(delta); | 141 mSwipeRefreshLayout.pull(delta); |
| 139 TraceEvent.end("SwipeRefreshHandler.pull"); | 142 TraceEvent.end("SwipeRefreshHandler.pull"); |
| 140 } | 143 } |
| 141 | 144 |
| 142 @Override | 145 @Override |
| 146 @CalledByNative |
| 143 public void release(boolean allowRefresh) { | 147 public void release(boolean allowRefresh) { |
| 144 TraceEvent.begin("SwipeRefreshHandler.release"); | 148 TraceEvent.begin("SwipeRefreshHandler.release"); |
| 145 mSwipeRefreshLayout.release(allowRefresh); | 149 mSwipeRefreshLayout.release(allowRefresh); |
| 146 TraceEvent.end("SwipeRefreshHandler.release"); | 150 TraceEvent.end("SwipeRefreshHandler.release"); |
| 147 } | 151 } |
| 148 | 152 |
| 149 @Override | 153 @Override |
| 154 @CalledByNative |
| 150 public void reset() { | 155 public void reset() { |
| 151 cancelStopRefreshingRunnable(); | 156 cancelStopRefreshingRunnable(); |
| 152 mSwipeRefreshLayout.reset(); | 157 mSwipeRefreshLayout.reset(); |
| 153 } | 158 } |
| 154 | 159 |
| 155 @Override | 160 @Override |
| 156 public void setEnabled(boolean enabled) { | 161 public void setEnabled(boolean enabled) { |
| 157 mSwipeRefreshLayout.setEnabled(enabled); | 162 mSwipeRefreshLayout.setEnabled(enabled); |
| 158 if (!enabled) reset(); | 163 if (!enabled) reset(); |
| 159 } | 164 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 181 }; | 186 }; |
| 182 } | 187 } |
| 183 return mStopRefreshingRunnable; | 188 return mStopRefreshingRunnable; |
| 184 } | 189 } |
| 185 | 190 |
| 186 // The animation view is attached/detached on-demand to minimize overlap | 191 // The animation view is attached/detached on-demand to minimize overlap |
| 187 // with composited SurfaceView content. | 192 // with composited SurfaceView content. |
| 188 private void attachSwipeRefreshLayoutIfNecessary() { | 193 private void attachSwipeRefreshLayoutIfNecessary() { |
| 189 cancelDetachLayoutRunnable(); | 194 cancelDetachLayoutRunnable(); |
| 190 if (mSwipeRefreshLayout.getParent() == null) { | 195 if (mSwipeRefreshLayout.getParent() == null) { |
| 191 mContentViewCore.getContainerView().addView(mSwipeRefreshLayout); | 196 mContainerView.addView(mSwipeRefreshLayout); |
| 192 } | 197 } |
| 193 } | 198 } |
| 194 | 199 |
| 195 private void detachSwipeRefreshLayoutIfNecessary() { | 200 private void detachSwipeRefreshLayoutIfNecessary() { |
| 196 cancelDetachLayoutRunnable(); | 201 cancelDetachLayoutRunnable(); |
| 197 if (mSwipeRefreshLayout.getParent() != null) { | 202 if (mSwipeRefreshLayout.getParent() != null) { |
| 198 mContentViewCore.getContainerView().removeView(mSwipeRefreshLayout); | 203 mContainerView.removeView(mSwipeRefreshLayout); |
| 199 } | 204 } |
| 200 } | 205 } |
| 201 } | 206 } |
| OLD | NEW |