OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.chrome.browser.tab; | |
6 | |
7 import android.annotation.TargetApi; | |
8 import android.app.ActivityManager; | |
9 import android.content.Context; | |
10 import android.content.Intent; | |
11 import android.graphics.Rect; | |
12 import android.media.AudioManager; | |
13 import android.os.Build; | |
14 import android.util.Log; | |
15 import android.util.Pair; | |
16 import android.view.KeyEvent; | |
17 import android.view.View; | |
18 | |
19 import org.chromium.base.ObserverList.RewindableIterator; | |
20 import org.chromium.chrome.R; | |
21 import org.chromium.chrome.browser.ChromeActivity; | |
22 import org.chromium.chrome.browser.ChromeApplication; | |
23 import org.chromium.chrome.browser.ChromeWebContentsDelegateAndroid; | |
24 import org.chromium.chrome.browser.RepostFormWarningDialog; | |
25 import org.chromium.chrome.browser.document.DocumentUtils; | |
26 import org.chromium.chrome.browser.document.DocumentWebContentsDelegate; | |
27 import org.chromium.chrome.browser.media.MediaCaptureNotificationService; | |
28 import org.chromium.chrome.browser.policy.PolicyAuditor; | |
29 import org.chromium.chrome.browser.policy.PolicyAuditor.AuditEvent; | |
30 import org.chromium.chrome.browser.tabmodel.TabCreatorManager.TabCreator; | |
31 import org.chromium.chrome.browser.tabmodel.TabModel; | |
32 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; | |
33 import org.chromium.chrome.browser.tabmodel.TabModelUtils; | |
34 import org.chromium.chrome.browser.util.FeatureUtilities; | |
35 import org.chromium.content_public.browser.InvalidateTypes; | |
36 import org.chromium.content_public.browser.WebContents; | |
37 import org.chromium.ui.WindowOpenDisposition; | |
38 | |
39 /** | |
40 * A basic {@link ChromeWebContentsDelegateAndroid} that forwards some calls to the registered | |
41 * {@link TabObserver}s. | |
42 */ | |
43 public class TabChromeWebContentsDelegateAndroid | |
44 extends ChromeWebContentsDelegateAndroid { | |
Maria
2015/09/22 21:01:50
What do you think about merging this class with Ch
| |
45 /** Used for logging. */ | |
46 private static final String TAG = "TabChromeWebContentsDelegateAndroid"; | |
Maria
2015/09/22 21:01:50
I think the presubmit will make you switch to usin
aurimas (slooooooooow)
2015/09/22 22:44:46
Done
| |
47 | |
48 private final Tab mTab; | |
49 private final ChromeActivity mActivity; | |
50 | |
51 | |
Maria
2015/09/22 21:01:50
remove one newline
aurimas (slooooooooow)
2015/09/22 22:44:46
Done
| |
52 private final Runnable mCloseContentsRunnable = new Runnable() { | |
53 @Override | |
54 public void run() { | |
55 boolean isSelected = mActivity.getTabModelSelector().getCurrentTab() == mTab; | |
56 mActivity.getTabModelSelector().closeTab(mTab); | |
57 | |
58 // If the parent Tab belongs to another Activity, fire the Intent to bring it back. | |
59 if (isSelected && mTab.getParentIntent() != null | |
60 && mActivity.getIntent() != mTab.getParentIntent()) { | |
61 boolean mayLaunch = FeatureUtilities.isDocumentMode(mActivity) | |
62 ? isParentInAndroidOverview() : true; | |
63 if (mayLaunch) mActivity.startActivity(mTab.getParentIntent()); | |
64 } | |
65 } | |
66 | |
67 /** If the API allows it, returns whether a Task still exists for the pa rent Activity. */ | |
68 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
69 private boolean isParentInAndroidOverview() { | |
70 ActivityManager activityManager = | |
71 (ActivityManager) mActivity.getSystemService(Context.ACTIVIT Y_SERVICE); | |
72 for (ActivityManager.AppTask task : activityManager.getAppTasks()) { | |
73 Intent taskIntent = DocumentUtils.getBaseIntentFromTask(task); | |
74 if (taskIntent != null && taskIntent.filterEquals(mTab.getParent Intent())) { | |
75 return true; | |
76 } | |
77 } | |
78 return false; | |
79 } | |
80 }; | |
81 | |
82 public TabChromeWebContentsDelegateAndroid(Tab tab, ChromeActivity activity) { | |
83 mTab = tab; | |
84 mActivity = activity; | |
85 } | |
86 | |
87 @Override | |
88 public void onLoadProgressChanged(int progress) { | |
89 if (!mTab.isLoading()) return; | |
90 mTab.notifyLoadProgress(mTab.getProgress()); | |
91 } | |
92 | |
93 @Override | |
94 public void onLoadStarted() { | |
95 RewindableIterator<TabObserver> observers = mTab.getTabObservers(); | |
96 while (observers.hasNext()) { | |
97 observers.next().onLoadStarted(mTab); | |
98 } | |
99 } | |
100 | |
101 @Override | |
102 public void onLoadStopped() { | |
103 RewindableIterator<TabObserver> observers = mTab.getTabObservers(); | |
104 while (observers.hasNext()) { | |
105 observers.next().onLoadStopped(mTab); | |
106 } | |
107 } | |
108 | |
109 @Override | |
110 public void onUpdateUrl(String url) { | |
111 RewindableIterator<TabObserver> observers = mTab.getTabObservers(); | |
112 while (observers.hasNext()) { | |
113 observers.next().onUpdateUrl(mTab, url); | |
114 } | |
115 } | |
116 | |
117 @Override | |
118 public void showRepostFormWarningDialog() { | |
119 mTab.resetSwipeRefreshHandler(); | |
120 RepostFormWarningDialog warningDialog = new RepostFormWarningDialog( | |
121 new Runnable() { | |
122 @Override | |
123 public void run() { | |
124 mTab.getWebContents().getNavigationController().cancelPe ndingReload(); | |
125 } | |
126 }, new Runnable() { | |
127 @Override | |
128 public void run() { | |
129 mTab.getWebContents().getNavigationController().continue PendingReload(); | |
130 } | |
131 }); | |
132 warningDialog.show(mActivity.getFragmentManager(), null); | |
133 } | |
134 | |
135 @Override | |
136 public void toggleFullscreenModeForTab(boolean enableFullscreen) { | |
137 if (mTab.getFullscreenManager() != null) { | |
138 mTab.getFullscreenManager().setPersistentFullscreenMode(enableFullsc reen); | |
139 } | |
140 | |
141 RewindableIterator<TabObserver> observers = mTab.getTabObservers(); | |
142 while (observers.hasNext()) { | |
143 observers.next().onToggleFullscreenMode(mTab, enableFullscreen); | |
144 } | |
145 } | |
146 | |
147 @Override | |
148 public void navigationStateChanged(int flags) { | |
149 if ((flags & InvalidateTypes.TAB) != 0) { | |
150 MediaCaptureNotificationService.updateMediaNotificationForTab( | |
151 mTab.getApplicationContext(), mTab.getId(), isCapturingAudio (), | |
152 isCapturingVideo(), mTab.getUrl()); | |
153 } | |
154 if ((flags & InvalidateTypes.TITLE) != 0) { | |
155 // Update cached title then notify observers. | |
156 mTab.updateTitle(); | |
157 } | |
158 if ((flags & InvalidateTypes.URL) != 0) { | |
159 RewindableIterator<TabObserver> observers = mTab.getTabObservers(); | |
160 while (observers.hasNext()) { | |
161 observers.next().onUrlUpdated(mTab); | |
162 } | |
163 } | |
164 } | |
165 | |
166 @Override | |
167 public void visibleSSLStateChanged() { | |
168 RewindableIterator<TabObserver> observers = mTab.getTabObservers(); | |
169 while (observers.hasNext()) { | |
170 observers.next().onSSLStateUpdated(mTab); | |
171 } | |
172 } | |
173 | |
174 @Override | |
175 public void webContentsCreated(WebContents sourceWebContents, long openerRen derFrameId, | |
176 String frameName, String targetUrl, WebContents newWebContents) { | |
177 RewindableIterator<TabObserver> observers = mTab.getTabObservers(); | |
178 while (observers.hasNext()) { | |
179 observers.next().webContentsCreated(mTab, sourceWebContents, openerR enderFrameId, | |
180 frameName, targetUrl, newWebContents); | |
181 } | |
182 // The URL can't be taken from the WebContents if it's paused. Save it for later. | |
183 assert mWebContentsUrlMapping == null; | |
184 mWebContentsUrlMapping = Pair.create(newWebContents, targetUrl); | |
185 | |
186 // TODO(dfalcantara): Re-remove this once crbug.com/508366 is fixed. | |
187 TabCreator tabCreator = mActivity.getTabCreator(mTab.isIncognito()); | |
188 | |
189 if (tabCreator != null && tabCreator.createsTabsAsynchronously()) { | |
190 DocumentWebContentsDelegate.getInstance().attachDelegate(newWebConte nts); | |
191 } | |
192 } | |
193 | |
194 @Override | |
195 public void rendererUnresponsive() { | |
196 super.rendererUnresponsive(); | |
197 mTab.handleRendererUnresponsive(); | |
198 } | |
199 | |
200 @Override | |
201 public void rendererResponsive() { | |
202 super.rendererResponsive(); | |
203 mTab.handleRendererResponsive(); | |
204 } | |
205 | |
206 @Override | |
207 public boolean isFullscreenForTabOrPending() { | |
208 return mTab.getFullscreenManager() == null | |
209 ? false : mTab.getFullscreenManager().getPersistentFullscreenMod e(); | |
210 } | |
211 | |
212 @Override | |
213 public void openNewTab(String url, String extraHeaders, byte[] postData, int disposition, | |
214 boolean isRendererInitiated) { | |
215 mTab.openNewTab(url, extraHeaders, postData, disposition, true, isRender erInitiated); | |
216 } | |
217 | |
218 private Pair<WebContents, String> mWebContentsUrlMapping; | |
219 | |
220 protected TabModel getTabModel() { | |
221 // TODO(dfalcantara): Remove this when DocumentActivity.getTabModelSelec tor() | |
222 // can return a TabModelSelector that activateContent s() can use. | |
223 return mActivity.getTabModelSelector().getModel(mTab.isIncognito()); | |
224 } | |
225 | |
226 @Override | |
227 public boolean shouldResumeRequestsForCreatedWindow() { | |
228 // Pause the WebContents if an Activity has to be created for it first. | |
229 TabCreator tabCreator = mActivity.getTabCreator(mTab.isIncognito()); | |
230 assert tabCreator != null; | |
231 return !tabCreator.createsTabsAsynchronously(); | |
232 } | |
233 | |
234 @Override | |
235 public boolean addNewContents(WebContents sourceWebContents, WebContents web Contents, | |
236 int disposition, Rect initialPosition, boolean userGesture) { | |
237 assert mWebContentsUrlMapping.first == webContents; | |
238 | |
239 TabCreator tabCreator = mActivity.getTabCreator(mTab.isIncognito()); | |
240 assert tabCreator != null; | |
241 | |
242 // Grab the URL, which might not be available via the Tab. | |
243 String url = mWebContentsUrlMapping.second; | |
244 mWebContentsUrlMapping = null; | |
245 | |
246 // Skip opening a new Tab if it doesn't make sense. | |
247 if (mTab.isClosing()) return false; | |
248 | |
249 // Creating new Tabs asynchronously requires starting a new Activity to create the Tab, | |
250 // so the Tab returned will always be null. There's no way to know sync hronously | |
251 // whether the Tab is created, so assume it's always successful. | |
252 boolean createdSuccessfully = tabCreator.createTabWithWebContents( | |
253 webContents, mTab.getId(), TabLaunchType.FROM_LONGPRESS_FOREGROU ND, url); | |
254 boolean success = tabCreator.createsTabsAsynchronously() || createdSucce ssfully; | |
255 if (success && disposition == WindowOpenDisposition.NEW_POPUP) { | |
256 PolicyAuditor auditor = | |
257 ((ChromeApplication) mTab.getApplicationContext()).getPolicy Auditor(); | |
258 auditor.notifyAuditEvent(mTab.getApplicationContext(), | |
259 AuditEvent.OPEN_POPUP_URL_SUCCESS, url, ""); | |
260 } | |
261 | |
262 return success; | |
263 } | |
264 | |
265 @Override | |
266 public void activateContents() { | |
267 boolean activityIsDestroyed = false; | |
268 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { | |
269 activityIsDestroyed = mActivity.isDestroyed(); | |
270 } | |
271 if (activityIsDestroyed || !mTab.isInitialized()) { | |
272 Log.e(TAG, "Activity destroyed before calling activateContents(). B ailing out."); | |
273 return; | |
274 } | |
275 | |
276 TabModel model = getTabModel(); | |
277 int index = model.indexOf(mTab); | |
278 if (index == TabModel.INVALID_TAB_INDEX) return; | |
279 TabModelUtils.setIndex(model, index); | |
280 bringActivityToForeground(); | |
281 } | |
282 | |
283 /** | |
284 * Brings chrome's Activity to foreground, if it is not so. | |
285 */ | |
286 protected void bringActivityToForeground() { | |
287 // This intent is sent in order to get the activity back to the foregrou nd if it was | |
288 // not already. The previous call will activate the right tab in the con text of the | |
289 // TabModel but will only show the tab to the user if Chrome was already in the | |
290 // foreground. | |
291 // The intent is getting the tabId mostly because it does not cost much to do so. | |
292 // When receiving the intent, the tab associated with the tabId should a lready be | |
293 // active. | |
294 // Note that calling only the intent in order to activate the tab is sli ghtly slower | |
295 // because it will change the tab when the intent is handled, which happ ens after | |
296 // Chrome gets back to the foreground. | |
297 Intent newIntent = Tab.createBringTabToFrontIntent(mTab.getId()); | |
298 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | |
299 | |
300 mTab.getApplicationContext().startActivity(newIntent); | |
301 } | |
302 | |
303 @Override | |
304 public void closeContents() { | |
305 // Execute outside of callback, otherwise we end up deleting the native | |
306 // objects in the middle of executing methods on them. | |
307 mTab.getHandler().removeCallbacks(mCloseContentsRunnable); | |
308 mTab.getHandler().post(mCloseContentsRunnable); | |
Maria
2015/09/22 21:01:50
I think instead of exposing tab's handler for this
aurimas (slooooooooow)
2015/09/22 22:44:46
Done
| |
309 } | |
310 | |
311 @Override | |
312 public boolean takeFocus(boolean reverse) { | |
313 if (reverse) { | |
314 View menuButton = mActivity.findViewById(R.id.menu_button); | |
315 if (menuButton == null || !menuButton.isShown()) { | |
316 menuButton = mActivity.findViewById(R.id.document_menu_button); | |
317 } | |
318 if (menuButton != null && menuButton.isShown()) { | |
319 return menuButton.requestFocus(); | |
320 } | |
321 | |
322 View tabSwitcherButton = mActivity.findViewById(R.id.tab_switcher_bu tton); | |
323 if (tabSwitcherButton != null && tabSwitcherButton.isShown()) { | |
324 return tabSwitcherButton.requestFocus(); | |
325 } | |
326 } else { | |
327 View urlBar = mActivity.findViewById(R.id.url_bar); | |
328 if (urlBar != null) return urlBar.requestFocus(); | |
329 } | |
330 return false; | |
331 } | |
332 | |
333 @Override | |
334 public void handleKeyboardEvent(KeyEvent event) { | |
335 if (event.getAction() == KeyEvent.ACTION_DOWN) { | |
336 if (mActivity.onKeyDown(event.getKeyCode(), event)) return; | |
337 | |
338 // Handle the Escape key here (instead of in KeyboardShortcuts.java) , so it doesn't | |
339 // interfere with other parts of the activity (e.g. the URL bar). | |
340 if (event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE && event.hasNoModi fiers()) { | |
341 WebContents wc = mTab.getWebContents(); | |
342 if (wc != null) wc.stop(); | |
343 return; | |
344 } | |
345 } | |
346 handleMediaKey(event); | |
347 } | |
348 | |
349 /** | |
350 * Redispatches unhandled media keys. This allows bluetooth headphones with play/pause or | |
351 * other buttons to function correctly. | |
352 */ | |
353 @TargetApi(19) | |
354 private void handleMediaKey(KeyEvent e) { | |
355 if (Build.VERSION.SDK_INT < 19) return; | |
356 switch (e.getKeyCode()) { | |
357 case KeyEvent.KEYCODE_MUTE: | |
358 case KeyEvent.KEYCODE_HEADSETHOOK: | |
359 case KeyEvent.KEYCODE_MEDIA_PLAY: | |
360 case KeyEvent.KEYCODE_MEDIA_PAUSE: | |
361 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: | |
362 case KeyEvent.KEYCODE_MEDIA_STOP: | |
363 case KeyEvent.KEYCODE_MEDIA_NEXT: | |
364 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: | |
365 case KeyEvent.KEYCODE_MEDIA_REWIND: | |
366 case KeyEvent.KEYCODE_MEDIA_RECORD: | |
367 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: | |
368 case KeyEvent.KEYCODE_MEDIA_CLOSE: | |
369 case KeyEvent.KEYCODE_MEDIA_EJECT: | |
370 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: | |
371 AudioManager am = (AudioManager) mActivity.getSystemService( | |
372 Context.AUDIO_SERVICE); | |
373 am.dispatchMediaKeyEvent(e); | |
374 break; | |
375 default: | |
376 break; | |
377 } | |
378 } | |
379 | |
380 /** | |
381 * @return Whether audio is being captured. | |
382 */ | |
383 private boolean isCapturingAudio() { | |
384 return !mTab.isClosing() | |
385 && ChromeWebContentsDelegateAndroid.nativeIsCapturingAudio(mTab. getWebContents()); | |
386 } | |
387 | |
388 /** | |
389 * @return Whether video is being captured. | |
390 */ | |
391 private boolean isCapturingVideo() { | |
392 return !mTab.isClosing() | |
393 && ChromeWebContentsDelegateAndroid.nativeIsCapturingVideo(mTab. getWebContents()); | |
394 } | |
395 } | |
OLD | NEW |