Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / android / shell / java / src / org / chromium / chrome / shell / ChromeShellActivity.java
1 // Copyright 2014 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.shell;
6
7 import android.app.Activity;
8 import android.content.Intent;
9 import android.os.Bundle;
10 import android.text.TextUtils;
11 import android.util.Log;
12 import android.view.KeyEvent;
13 import android.view.Menu;
14 import android.view.MenuItem;
15 import android.view.View;
16 import android.view.ViewGroup;
17 import android.widget.Toast;
18
19 import org.chromium.base.ApiCompatibilityUtils;
20 import org.chromium.base.BaseSwitches;
21 import org.chromium.base.CommandLine;
22 import org.chromium.base.ContentUriUtils;
23 import org.chromium.base.MemoryPressureListener;
24 import org.chromium.base.VisibleForTesting;
25 import org.chromium.base.library_loader.ProcessInitException;
26 import org.chromium.chrome.browser.DevToolsServer;
27 import org.chromium.chrome.browser.FileProviderHelper;
28 import org.chromium.chrome.browser.appmenu.AppMenuHandler;
29 import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
30 import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils;
31 import org.chromium.chrome.browser.nfc.BeamController;
32 import org.chromium.chrome.browser.nfc.BeamProvider;
33 import org.chromium.chrome.browser.printing.PrintingControllerFactory;
34 import org.chromium.chrome.browser.printing.TabPrinter;
35 import org.chromium.chrome.browser.share.ShareHelper;
36 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
37 import org.chromium.chrome.shell.sync.SyncController;
38 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
39 import org.chromium.content.app.ContentApplication;
40 import org.chromium.content.browser.ActivityContentVideoViewClient;
41 import org.chromium.content.browser.BrowserStartupController;
42 import org.chromium.content.browser.ContentViewCore;
43 import org.chromium.content.browser.DeviceUtils;
44 import org.chromium.printing.PrintManagerDelegateImpl;
45 import org.chromium.printing.PrintingController;
46 import org.chromium.sync.signin.AccountManagerHelper;
47 import org.chromium.sync.signin.ChromeSigninController;
48 import org.chromium.ui.base.ActivityWindowAndroid;
49 import org.chromium.ui.base.WindowAndroid;
50
51 /**
52  * The {@link android.app.Activity} component of a basic test shell to test Chrome features.
53  */
54 public class ChromeShellActivity extends Activity implements AppMenuPropertiesDelegate {
55     private static final String TAG = "ChromeShellActivity";
56
57     /**
58      * Factory used to set up a mock ActivityWindowAndroid for testing.
59      */
60     public interface ActivityWindowAndroidFactory {
61         /**
62          * @return ActivityWindowAndroid for the given activity.
63          */
64         public ActivityWindowAndroid getActivityWindowAndroid(Activity activity);
65     }
66
67     private static ActivityWindowAndroidFactory sWindowAndroidFactory =
68             new ActivityWindowAndroidFactory() {
69                 @Override
70                 public ActivityWindowAndroid getActivityWindowAndroid(Activity activity) {
71                     return new ActivityWindowAndroid(activity);
72                 }
73             };
74
75     private WindowAndroid mWindow;
76     private TabManager mTabManager;
77     private ChromeShellToolbar mToolbar;
78     private DevToolsServer mDevToolsServer;
79     private SyncController mSyncController;
80     private PrintingController mPrintingController;
81
82     /**
83      * Factory used to set up a mock AppMenuHandler for testing.
84      */
85     public interface AppMenuHandlerFactory {
86         /**
87          * @return AppMenuHandler for the given activity and menu resource id.
88          */
89         public AppMenuHandler getAppMenuHandler(Activity activity,
90                 AppMenuPropertiesDelegate delegate, int menuResourceId);
91     }
92
93     private static AppMenuHandlerFactory sAppMenuHandlerFactory =
94             new AppMenuHandlerFactory() {
95                 @Override
96                 public AppMenuHandler getAppMenuHandler(Activity activity,
97                         AppMenuPropertiesDelegate delegate, int menuResourceId) {
98                     return new AppMenuHandler(activity, delegate, menuResourceId);
99                 }
100             };
101     private AppMenuHandler mAppMenuHandler;
102
103     @Override
104     protected void onCreate(final Bundle savedInstanceState) {
105         super.onCreate(savedInstanceState);
106
107         ContentApplication.initCommandLine(this);
108         waitForDebuggerIfNeeded();
109
110         DeviceUtils.addDeviceSpecificUserAgentSwitch(this);
111
112         BrowserStartupController.StartupCallback callback =
113                 new BrowserStartupController.StartupCallback() {
114                     @Override
115                     public void onSuccess(boolean alreadyStarted) {
116                         finishInitialization(savedInstanceState);
117                     }
118
119                     @Override
120                     public void onFailure() {
121                         Toast.makeText(ChromeShellActivity.this,
122                                        R.string.browser_process_initialization_failed,
123                                        Toast.LENGTH_SHORT).show();
124                         Log.e(TAG, "Chromium browser process initialization failed");
125                         finish();
126                     }
127                 };
128         try {
129             BrowserStartupController.get(this).startBrowserProcessesAsync(callback);
130         } catch (ProcessInitException e) {
131             Log.e(TAG, "Unable to load native library.", e);
132             System.exit(-1);
133         }
134     }
135
136     private void finishInitialization(final Bundle savedInstanceState) {
137         setContentView(R.layout.chrome_shell_activity);
138         mTabManager = (TabManager) findViewById(R.id.tab_manager);
139
140         mWindow = sWindowAndroidFactory.getActivityWindowAndroid(this);
141         mWindow.restoreInstanceState(savedInstanceState);
142         mTabManager.initialize(mWindow, new ActivityContentVideoViewClient(this) {
143             @Override
144             public void enterFullscreenVideo(View view) {
145                 super.enterFullscreenVideo(view);
146                 if (mTabManager != null) {
147                     mTabManager.setOverlayVideoMode(true);
148                 }
149             }
150
151             @Override
152             public void exitFullscreenVideo() {
153                 super.exitFullscreenVideo();
154                 if (mTabManager != null) {
155                     mTabManager.setOverlayVideoMode(false);
156                 }
157             }
158         });
159
160         String startupUrl = getUrlFromIntent(getIntent());
161         if (!TextUtils.isEmpty(startupUrl)) {
162             mTabManager.setStartupUrl(startupUrl);
163         }
164         mToolbar = (ChromeShellToolbar) findViewById(R.id.toolbar);
165         mAppMenuHandler = sAppMenuHandlerFactory.getAppMenuHandler(this, this, R.menu.main_menu);
166         mToolbar.setMenuHandler(mAppMenuHandler);
167
168         mDevToolsServer = new DevToolsServer("chrome_shell");
169         mDevToolsServer.setRemoteDebuggingEnabled(
170                 true, DevToolsServer.Security.ALLOW_DEBUG_PERMISSION);
171
172         mPrintingController = PrintingControllerFactory.create(this);
173
174         mSyncController = SyncController.get(this);
175         // In case this method is called after the first onStart(), we need to inform the
176         // SyncController that we have started.
177         mSyncController.onStart();
178         ContentUriUtils.setFileProviderUtil(new FileProviderHelper());
179
180         BeamController.registerForBeam(this, new BeamProvider() {
181             @Override
182             public String getTabUrlForBeam() {
183                 ChromeShellTab tab = getActiveTab();
184                 if (tab == null) return null;
185                 return tab.getUrl();
186             }
187         });
188     }
189
190     @Override
191     protected void onDestroy() {
192         super.onDestroy();
193
194         if (mDevToolsServer != null) mDevToolsServer.destroy();
195         mDevToolsServer = null;
196     }
197
198     @Override
199     protected void onSaveInstanceState(Bundle outState) {
200         // TODO(dtrainor): Save/restore the tab state.
201         if (mWindow != null) mWindow.saveInstanceState(outState);
202     }
203
204     @Override
205     public boolean onKeyUp(int keyCode, KeyEvent event) {
206         if (keyCode == KeyEvent.KEYCODE_BACK) {
207             if (mTabManager.isTabSwitcherVisible()) {
208                 mTabManager.hideTabSwitcher();
209                 return true;
210             }
211             ChromeShellTab tab = getActiveTab();
212             if (tab != null && tab.canGoBack()) {
213                 tab.goBack();
214                 return true;
215             }
216         }
217         return super.onKeyUp(keyCode, event);
218     }
219
220     @Override
221     protected void onNewIntent(Intent intent) {
222         if (MemoryPressureListener.handleDebugIntent(this, intent.getAction())) return;
223
224         String url = getUrlFromIntent(intent);
225         if (!TextUtils.isEmpty(url)) {
226             ChromeShellTab tab = getActiveTab();
227             if (tab != null) tab.loadUrlWithSanitization(url);
228         }
229     }
230
231     @Override
232     protected void onStop() {
233         super.onStop();
234
235         if (mToolbar != null) mToolbar.hideSuggestions();
236
237         ContentViewCore viewCore = getActiveContentViewCore();
238         if (viewCore != null) viewCore.onHide();
239     }
240
241     @Override
242     protected void onStart() {
243         super.onStart();
244
245         ContentViewCore viewCore = getActiveContentViewCore();
246         if (viewCore != null) viewCore.onShow();
247
248         if (mSyncController != null) {
249             mSyncController.onStart();
250         }
251     }
252
253     @Override
254     public void onActivityResult(int requestCode, int resultCode, Intent data) {
255         mWindow.onActivityResult(requestCode, resultCode, data);
256     }
257
258     /**
259      * @return The {@link WindowAndroid} associated with this activity.
260      */
261     public WindowAndroid getWindowAndroid() {
262         return mWindow;
263     }
264
265     /**
266      * @return The {@link ChromeShellTab} that is currently visible.
267      */
268     public ChromeShellTab getActiveTab() {
269         return mTabManager != null ? mTabManager.getCurrentTab() : null;
270     }
271
272     /**
273      * @return The ContentViewCore of the active tab.
274      */
275     public ContentViewCore getActiveContentViewCore() {
276         ChromeShellTab tab = getActiveTab();
277         return tab != null ? tab.getContentViewCore() : null;
278     }
279
280     /**
281      * Creates a {@link ChromeShellTab} with a URL specified by {@code url}.
282      *
283      * @param url The URL the new {@link ChromeShellTab} should start with.
284      */
285     @VisibleForTesting
286     public void createTab(String url) {
287         mTabManager.createTab(url, TabLaunchType.FROM_EXTERNAL_APP);
288     }
289
290     /**
291      * Closes all current tabs.
292      */
293     public void closeAllTabs() {
294         mTabManager.closeAllTabs();
295     }
296
297     /**
298      * Override the menu key event to show AppMenu.
299      */
300     @Override
301     public boolean onKeyDown(int keyCode, KeyEvent event) {
302         if (keyCode == KeyEvent.KEYCODE_MENU && event.getRepeatCount() == 0) {
303             if (mToolbar != null) mToolbar.hideSuggestions();
304             mAppMenuHandler.showAppMenu(findViewById(R.id.menu_button), true, false);
305             return true;
306         }
307         return super.onKeyDown(keyCode, event);
308     }
309
310     @SuppressWarnings("deprecation")
311     @Override
312     public boolean onOptionsItemSelected(MenuItem item) {
313         ChromeShellTab activeTab = getActiveTab();
314         if (activeTab != null) {
315             ViewGroup containerView = activeTab.getContentViewCore().getContainerView();
316             if (containerView.isFocusable() && containerView.isFocusableInTouchMode()) {
317                 containerView.requestFocus();
318             }
319         }
320         int id = item.getItemId();
321         if (id == R.id.signin) {
322             if (ChromeSigninController.get(this).isSignedIn()) {
323                 SyncController.openSignOutDialog(getFragmentManager());
324             } else if (AccountManagerHelper.get(this).hasGoogleAccounts()) {
325                 SyncController.openSigninDialog(getFragmentManager());
326             } else {
327                 Toast.makeText(this, R.string.signin_no_account, Toast.LENGTH_SHORT).show();
328             }
329             return true;
330         } else if (id == R.id.print) {
331             if (activeTab != null) {
332                 mPrintingController.startPrint(new TabPrinter(activeTab),
333                         new PrintManagerDelegateImpl(this));
334             }
335             return true;
336         } else if (id == R.id.distill_page) {
337             if (activeTab != null) {
338                 DomDistillerTabUtils.distillCurrentPageAndView(
339                         activeTab.getContentViewCore().getWebContents());
340             }
341             return true;
342         } else if (id == R.id.back_menu_id) {
343             if (activeTab != null && activeTab.canGoBack()) {
344                 activeTab.goBack();
345             }
346             return true;
347         } else if (id == R.id.forward_menu_id) {
348             if (activeTab != null && activeTab.canGoForward()) {
349                 activeTab.goForward();
350             }
351             return true;
352         } else if (id == R.id.new_tab_menu_id) {
353             mTabManager.createNewTab();
354             return true;
355         } else if (id == R.id.share_menu_id || id == R.id.direct_share_menu_id) {
356             ShareHelper.share(item.getItemId() == R.id.direct_share_menu_id, this,
357                     activeTab.getTitle(), activeTab.getUrl(), null,
358                     Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
359             return true;
360         } else {
361             return super.onOptionsItemSelected(item);
362         }
363     }
364
365     private void waitForDebuggerIfNeeded() {
366         if (CommandLine.getInstance().hasSwitch(BaseSwitches.WAIT_FOR_JAVA_DEBUGGER)) {
367             Log.e(TAG, "Waiting for Java debugger to connect...");
368             android.os.Debug.waitForDebugger();
369             Log.e(TAG, "Java debugger connected. Resuming execution.");
370         }
371     }
372
373     private static String getUrlFromIntent(Intent intent) {
374         return intent != null ? intent.getDataString() : null;
375     }
376
377     @Override
378     public boolean shouldShowAppMenu() {
379         return true;
380     }
381
382     @Override
383     public void prepareMenu(Menu menu) {
384         menu.setGroupVisible(R.id.MAIN_MENU, true);
385         ChromeShellTab activeTab = getActiveTab();
386
387         // Disable the "Back" menu item if there is no page to go to.
388         MenuItem backMenuItem = menu.findItem(R.id.back_menu_id);
389         backMenuItem.setEnabled(activeTab != null ? activeTab.canGoBack() : false);
390
391         // Disable the "Forward" menu item if there is no page to go to.
392         MenuItem forwardMenuItem = menu.findItem(R.id.forward_menu_id);
393         forwardMenuItem.setEnabled(activeTab != null ? activeTab.canGoForward() : false);
394
395         // ChromeShell does not know about bookmarks yet
396         menu.findItem(R.id.bookmark_this_page_id).setEnabled(true);
397
398         MenuItem signinItem = menu.findItem(R.id.signin);
399         if (ChromeSigninController.get(this).isSignedIn()) {
400             signinItem.setTitle(ChromeSigninController.get(this).getSignedInAccountName());
401         } else {
402             signinItem.setTitle(R.string.signin_sign_in);
403         }
404
405         menu.findItem(R.id.print).setVisible(ApiCompatibilityUtils.isPrintingSupported());
406
407         MenuItem distillPageItem = menu.findItem(R.id.distill_page);
408         if (CommandLine.getInstance().hasSwitch(ChromeShellSwitches.ENABLE_DOM_DISTILLER)) {
409             String url = activeTab != null ? activeTab.getUrl() : null;
410             distillPageItem.setEnabled(!DomDistillerUrlUtils.isDistilledPage(url));
411             distillPageItem.setVisible(true);
412         } else {
413             distillPageItem.setVisible(false);
414         }
415         ShareHelper.configureDirectShareMenuItem(this, menu.findItem(R.id.direct_share_menu_id));
416     }
417
418     @VisibleForTesting
419     public AppMenuHandler getAppMenuHandler() {
420         return mAppMenuHandler;
421     }
422
423     @Override
424     public int getMenuThemeResourceId() {
425         return R.style.OverflowMenuTheme;
426     }
427
428     @Override
429     public int getMenuButtonStartPaddingDimenId() {
430         return 0;
431     }
432
433     @VisibleForTesting
434     public static void setActivityWindowAndroidFactory(ActivityWindowAndroidFactory factory) {
435         sWindowAndroidFactory = factory;
436     }
437
438     @VisibleForTesting
439     public static void setAppMenuHandlerFactory(AppMenuHandlerFactory factory) {
440         sAppMenuHandlerFactory = factory;
441     }
442 }