PageRenderTime 60ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 1ms

/WebVox/src/com/marvin/webvox/BrowserActivity.java

http://eyes-free.googlecode.com/
Java | 1668 lines | 1094 code | 158 blank | 416 comment | 277 complexity | 7061e778ccf953fd7c2ebe0b8df697e4 MD5 | raw file
Possible License(s): GPL-3.0, Apache-2.0
  1. /*
  2. * Copyright (C) 2006 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.marvin.webvox;
  17. import com.marvin.webvox.R;
  18. //import com.google.android.googleapps.IGoogleLoginService;
  19. //import com.google.android.googlelogin.GoogleLoginServiceConstants;
  20. import android.app.Activity;
  21. import android.app.AlertDialog;
  22. import android.app.ProgressDialog;
  23. import android.app.SearchManager;
  24. import android.content.ActivityNotFoundException;
  25. import android.content.BroadcastReceiver;
  26. import android.content.ComponentName;
  27. import android.content.ContentResolver;
  28. import android.content.ContentUris;
  29. import android.content.ContentValues;
  30. import android.content.Context;
  31. import android.content.DialogInterface;
  32. import android.content.Intent;
  33. import android.content.IntentFilter;
  34. import android.content.ServiceConnection;
  35. import android.content.DialogInterface.OnCancelListener;
  36. import android.content.pm.PackageInfo;
  37. import android.content.pm.PackageManager;
  38. import android.content.pm.ResolveInfo;
  39. import android.content.res.AssetManager;
  40. import android.content.res.Configuration;
  41. import android.content.res.Resources;
  42. import android.database.Cursor;
  43. import android.database.sqlite.SQLiteDatabase;
  44. import android.database.sqlite.SQLiteException;
  45. import android.graphics.Bitmap;
  46. import android.graphics.BitmapFactory;
  47. import android.graphics.Canvas;
  48. import android.graphics.DrawFilter;
  49. import android.graphics.Paint;
  50. import android.graphics.PaintFlagsDrawFilter;
  51. import android.graphics.Picture;
  52. import android.graphics.PixelFormat;
  53. import android.graphics.Rect;
  54. import android.graphics.drawable.Drawable;
  55. import android.hardware.SensorListener;
  56. import android.hardware.SensorManager;
  57. import android.net.ConnectivityManager;
  58. import android.net.NetworkInfo;
  59. import android.net.Uri;
  60. //import android.net.WebAddress;
  61. //import android.net.http.EventHandler;
  62. import android.net.http.SslCertificate;
  63. //import android.net.http.SslError;
  64. import android.os.AsyncTask;
  65. import android.os.Bundle;
  66. import android.os.Debug;
  67. import android.os.Environment;
  68. import android.os.Handler;
  69. import android.os.IBinder;
  70. import android.os.Message;
  71. import android.os.PowerManager;
  72. import android.os.Process;
  73. import android.os.RemoteException;
  74. //import android.os.ServiceManager;
  75. import android.os.SystemClock;
  76. import android.provider.Browser;
  77. import android.provider.ContactsContract;
  78. import android.provider.ContactsContract.Intents.Insert;
  79. //import android.provider.Downloads;
  80. import android.provider.MediaStore;
  81. //import android.text.IClipboard;
  82. import android.speech.tts.TextToSpeech;
  83. import android.text.TextUtils;
  84. import android.text.format.DateFormat;
  85. //import android.text.util.Regex;
  86. import android.util.AttributeSet;
  87. import android.util.Log;
  88. import android.view.ContextMenu;
  89. import android.view.Gravity;
  90. import android.view.KeyEvent;
  91. import android.view.LayoutInflater;
  92. import android.view.Menu;
  93. import android.view.MenuInflater;
  94. import android.view.MenuItem;
  95. import android.view.View;
  96. import android.view.ViewGroup;
  97. import android.view.Window;
  98. import android.view.WindowManager;
  99. import android.view.ContextMenu.ContextMenuInfo;
  100. import android.view.MenuItem.OnMenuItemClickListener;
  101. import android.view.animation.AlphaAnimation;
  102. import android.view.animation.Animation;
  103. import android.view.animation.AnimationSet;
  104. import android.view.animation.DecelerateInterpolator;
  105. import android.view.animation.ScaleAnimation;
  106. import android.view.animation.TranslateAnimation;
  107. import android.webkit.CookieManager;
  108. import android.webkit.CookieSyncManager;
  109. import android.webkit.DownloadListener;
  110. import android.webkit.GeolocationPermissions;
  111. import android.webkit.HttpAuthHandler;
  112. //import android.webkit.PluginManager;
  113. import android.webkit.SslErrorHandler;
  114. import android.webkit.URLUtil;
  115. //import android.webkit.ValueCallback;
  116. import android.webkit.WebChromeClient;
  117. //import android.webkit.WebChromeClient.CustomViewCallback;
  118. import android.webkit.WebHistoryItem;
  119. import android.webkit.WebIconDatabase;
  120. import android.webkit.WebStorage;
  121. import android.webkit.WebView;
  122. import android.webkit.WebViewClient;
  123. import android.widget.EditText;
  124. import android.widget.FrameLayout;
  125. import android.widget.LinearLayout;
  126. import android.widget.TextView;
  127. import android.widget.Toast;
  128. import java.io.BufferedOutputStream;
  129. import java.io.ByteArrayOutputStream;
  130. import java.io.File;
  131. import java.io.FileInputStream;
  132. import java.io.FileOutputStream;
  133. import java.io.IOException;
  134. import java.io.InputStream;
  135. import java.net.MalformedURLException;
  136. import java.net.URI;
  137. import java.net.URISyntaxException;
  138. import java.net.URL;
  139. import java.net.URLEncoder;
  140. import java.text.ParseException;
  141. import java.util.Date;
  142. import java.util.Enumeration;
  143. import java.util.HashMap;
  144. import java.util.LinkedList;
  145. import java.util.List;
  146. import java.util.Vector;
  147. import java.util.regex.Matcher;
  148. import java.util.regex.Pattern;
  149. import java.util.zip.ZipEntry;
  150. import java.util.zip.ZipFile;
  151. public class BrowserActivity extends Activity
  152. implements View.OnCreateContextMenuListener,
  153. DownloadListener {
  154. /* Define some aliases to make these debugging flags easier to refer to.
  155. * This file imports android.provider.Browser, so we can't just refer to "Browser.DEBUG".
  156. */
  157. private final static boolean DEBUG = com.marvin.webvox.Browser.DEBUG;
  158. private final static boolean LOGV_ENABLED = com.marvin.webvox.Browser.LOGV_ENABLED;
  159. private final static boolean LOGD_ENABLED = com.marvin.webvox.Browser.LOGD_ENABLED;
  160. // private IGoogleLoginService mGls = null;
  161. private ServiceConnection mGlsConnection = null;
  162. private SensorManager mSensorManager = null;
  163. // These are single-character shortcuts for searching popular sources.
  164. private static final int SHORTCUT_INVALID = 0;
  165. private static final int SHORTCUT_GOOGLE_SEARCH = 1;
  166. private static final int SHORTCUT_WIKIPEDIA_SEARCH = 2;
  167. private static final int SHORTCUT_DICTIONARY_SEARCH = 3;
  168. private static final int SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH = 4;
  169. /* Whitelisted webpages
  170. private static HashSet<String> sWhiteList;
  171. static {
  172. sWhiteList = new HashSet<String>();
  173. sWhiteList.add("cnn.com/");
  174. sWhiteList.add("espn.go.com/");
  175. sWhiteList.add("nytimes.com/");
  176. sWhiteList.add("engadget.com/");
  177. sWhiteList.add("yahoo.com/");
  178. sWhiteList.add("msn.com/");
  179. sWhiteList.add("amazon.com/");
  180. sWhiteList.add("consumerist.com/");
  181. sWhiteList.add("google.com/m/news");
  182. }
  183. */
  184. private void setupHomePage() {
  185. final Runnable getAccount = new Runnable() {
  186. public void run() {
  187. // Lower priority
  188. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  189. // get the default home page
  190. String homepage = mSettings.getHomePage();
  191. try {
  192. // if (mGls == null) return;
  193. if (!homepage.startsWith("http://www.google.")) return;
  194. if (homepage.indexOf('?') == -1) return;
  195. // String hostedUser = mGls.getAccount(GoogleLoginServiceConstants.PREFER_HOSTED);
  196. // String googleUser = mGls.getAccount(GoogleLoginServiceConstants.REQUIRE_GOOGLE);
  197. // three cases:
  198. //
  199. // hostedUser == googleUser
  200. // The device has only a google account
  201. //
  202. // hostedUser != googleUser
  203. // The device has a hosted account and a google account
  204. //
  205. // hostedUser != null, googleUser == null
  206. // The device has only a hosted account (so far)
  207. // developers might have no accounts at all
  208. // if (hostedUser == null) return;
  209. // if (googleUser == null || !hostedUser.equals(googleUser)) {
  210. // String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1);
  211. // homepage = homepage.replace("?", "/a/" + domain + "?");
  212. // }
  213. // } catch (RemoteException ignore) {
  214. // Login service died; carry on
  215. } catch (RuntimeException ignore) {
  216. // Login service died; carry on
  217. } finally {
  218. finish(homepage);
  219. }
  220. }
  221. private void finish(final String homepage) {
  222. mHandler.post(new Runnable() {
  223. public void run() {
  224. mSettings.setHomePage(BrowserActivity.this, homepage);
  225. resumeAfterCredentials();
  226. // as this is running in a separate thread,
  227. // BrowserActivity's onDestroy() may have been called,
  228. // which also calls unbindService().
  229. if (mGlsConnection != null) {
  230. // we no longer need to keep GLS open
  231. unbindService(mGlsConnection);
  232. mGlsConnection = null;
  233. }
  234. } });
  235. } };
  236. final boolean[] done = { false };
  237. // Open a connection to the Google Login Service. The first
  238. // time the connection is established, set up the homepage depending on
  239. // the account in a background thread.
  240. mGlsConnection = new ServiceConnection() {
  241. public void onServiceConnected(ComponentName className, IBinder service) {
  242. // mGls = IGoogleLoginService.Stub.asInterface(service);
  243. if (done[0] == false) {
  244. done[0] = true;
  245. Thread account = new Thread(getAccount);
  246. account.setName("GLSAccount");
  247. account.start();
  248. }
  249. }
  250. public void onServiceDisconnected(ComponentName className) {
  251. // mGls = null;
  252. }
  253. };
  254. // bindService(GoogleLoginServiceConstants.SERVICE_INTENT,
  255. // mGlsConnection, Context.BIND_AUTO_CREATE);
  256. }
  257. private static class ClearThumbnails extends AsyncTask<File, Void, Void> {
  258. @Override
  259. public Void doInBackground(File... files) {
  260. if (files != null) {
  261. for (File f : files) {
  262. if (!f.delete()) {
  263. Log.e(LOGTAG, f.getPath() + " was not deleted");
  264. }
  265. }
  266. }
  267. return null;
  268. }
  269. }
  270. /**
  271. * This layout holds everything you see below the status bar, including the
  272. * error console, the custom view container, and the webviews.
  273. */
  274. private FrameLayout mBrowserFrameLayout;
  275. @Override public void onCreate(Bundle icicle) {
  276. mTts = new TextToSpeech(this, null);
  277. scriptdb = new ScriptDatabase(this);
  278. scriptdb.onUpgrade(scriptdb.getWritableDatabase(), -10, 10);
  279. if (LOGV_ENABLED) {
  280. Log.v(LOGTAG, this + " onStart");
  281. }
  282. super.onCreate(icicle);
  283. // test the browser in OpenGL
  284. // requestWindowFeature(Window.FEATURE_OPENGL);
  285. setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
  286. mResolver = getContentResolver();
  287. // If this was a web search request, pass it on to the default web
  288. // search provider and finish this activity.
  289. if (handleWebSearchIntent(getIntent())) {
  290. finish();
  291. return;
  292. }
  293. //
  294. // start MASF proxy service
  295. //
  296. //Intent proxyServiceIntent = new Intent();
  297. //proxyServiceIntent.setComponent
  298. // (new ComponentName(
  299. // "com.android.masfproxyservice",
  300. // "com.android.masfproxyservice.MasfProxyService"));
  301. //startService(proxyServiceIntent, null);
  302. mSecLockIcon = Resources.getSystem().getDrawable(
  303. android.R.drawable.ic_secure);
  304. mMixLockIcon = Resources.getSystem().getDrawable(
  305. android.R.drawable.ic_partial_secure);
  306. // FrameLayout frameLayout = (FrameLayout) getWindow().getDecorView()
  307. // .findViewById(com.android.internal.R.id.content);
  308. mBrowserFrameLayout = (FrameLayout) LayoutInflater.from(this)
  309. .inflate(R.layout.custom_screen, null);
  310. mContentView = (FrameLayout) mBrowserFrameLayout.findViewById(
  311. R.id.main_content);
  312. mErrorConsoleContainer = (LinearLayout) mBrowserFrameLayout
  313. .findViewById(R.id.error_console);
  314. mCustomViewContainer = (FrameLayout) mBrowserFrameLayout
  315. .findViewById(R.id.fullscreen_custom_content);
  316. //frameLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS);
  317. setContentView(mBrowserFrameLayout);
  318. mTitleBar = new TitleBar(this);
  319. // Create the tab control and our initial tab
  320. mTabControl = new TabControl(this);
  321. // Open the icon database and retain all the bookmark urls for favicons
  322. retainIconsOnStartup();
  323. // Keep a settings instance handy.
  324. mSettings = BrowserSettings.getInstance();
  325. mSettings.setTabControl(mTabControl);
  326. mSettings.loadFromDb(this);
  327. PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
  328. mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser");
  329. /* enables registration for changes in network status from
  330. http stack */
  331. mNetworkStateChangedFilter = new IntentFilter();
  332. mNetworkStateChangedFilter.addAction(
  333. ConnectivityManager.CONNECTIVITY_ACTION);
  334. mNetworkStateIntentReceiver = new BroadcastReceiver() {
  335. @Override
  336. public void onReceive(Context context, Intent intent) {
  337. if (intent.getAction().equals(
  338. ConnectivityManager.CONNECTIVITY_ACTION)) {
  339. boolean noConnectivity = intent.getBooleanExtra(
  340. ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
  341. onNetworkToggle(!noConnectivity);
  342. }
  343. }
  344. };
  345. IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
  346. filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
  347. filter.addDataScheme("package");
  348. mPackageInstallationReceiver = new BroadcastReceiver() {
  349. @Override
  350. public void onReceive(Context context, Intent intent) {
  351. final String action = intent.getAction();
  352. final String packageName = intent.getData()
  353. .getSchemeSpecificPart();
  354. final boolean replacing = intent.getBooleanExtra(
  355. Intent.EXTRA_REPLACING, false);
  356. if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
  357. // if it is replacing, refreshPlugins() when adding
  358. return;
  359. }
  360. PackageManager pm = BrowserActivity.this.getPackageManager();
  361. PackageInfo pkgInfo = null;
  362. try {
  363. pkgInfo = pm.getPackageInfo(packageName,
  364. PackageManager.GET_PERMISSIONS);
  365. } catch (PackageManager.NameNotFoundException e) {
  366. return;
  367. }
  368. if (pkgInfo != null) {
  369. String permissions[] = pkgInfo.requestedPermissions;
  370. if (permissions == null) {
  371. return;
  372. }
  373. boolean permissionOk = false;
  374. /*
  375. for (String permit : permissions) {
  376. if (PluginManager.PLUGIN_PERMISSION.equals(permit)) {
  377. permissionOk = true;
  378. break;
  379. }
  380. }
  381. if (permissionOk) {
  382. PluginManager.getInstance(BrowserActivity.this)
  383. .refreshPlugins(
  384. Intent.ACTION_PACKAGE_ADDED
  385. .equals(action));
  386. }
  387. */
  388. }
  389. }
  390. };
  391. registerReceiver(mPackageInstallationReceiver, filter);
  392. if (!mTabControl.restoreState(icicle)) {
  393. // clear up the thumbnail directory if we can't restore the state as
  394. // none of the files in the directory are referenced any more.
  395. new ClearThumbnails().execute(
  396. mTabControl.getThumbnailDir().listFiles());
  397. // there is no quit on Android. But if we can't restore the state,
  398. // we can treat it as a new Browser, remove the old session cookies.
  399. CookieManager.getInstance().removeSessionCookie();
  400. final Intent intent = getIntent();
  401. final Bundle extra = intent.getExtras();
  402. // Create an initial tab.
  403. // If the intent is ACTION_VIEW and data is not null, the Browser is
  404. // invoked to view the content by another application. In this case,
  405. // the tab will be close when exit.
  406. UrlData urlData = getUrlDataFromIntent(intent);
  407. final TabControl.Tab t = mTabControl.createNewTab(
  408. Intent.ACTION_VIEW.equals(intent.getAction()) &&
  409. intent.getData() != null,
  410. intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), urlData.mUrl);
  411. mTabControl.setCurrentTab(t);
  412. attachTabToContentView(t);
  413. WebView webView = t.getWebView();
  414. if (extra != null) {
  415. int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0);
  416. if (scale > 0 && scale <= 1000) {
  417. webView.setInitialScale(scale);
  418. }
  419. }
  420. // If we are not restoring from an icicle, then there is a high
  421. // likely hood this is the first run. So, check to see if the
  422. // homepage needs to be configured and copy any plugins from our
  423. // asset directory to the data partition.
  424. if ((extra == null || !extra.getBoolean("testing"))
  425. && !mSettings.isLoginInitialized()) {
  426. setupHomePage();
  427. }
  428. if (urlData.isEmpty()) {
  429. if (mSettings.isLoginInitialized()) {
  430. webView.loadUrl(mSettings.getHomePage());
  431. } else {
  432. waitForCredentials();
  433. }
  434. } else {
  435. if (extra != null) {
  436. // urlData.setPostData(extra
  437. // .getByteArray(Browser.EXTRA_POST_DATA));
  438. }
  439. urlData.loadIn(webView);
  440. }
  441. } else {
  442. // TabControl.restoreState() will create a new tab even if
  443. // restoring the state fails.
  444. attachTabToContentView(mTabControl.getCurrentTab());
  445. }
  446. // Read JavaScript flags if it exists.
  447. String jsFlags = mSettings.getJsFlags();
  448. if (jsFlags.trim().length() != 0) {
  449. // mTabControl.getCurrentWebView().setJsFlags(jsFlags);
  450. }
  451. }
  452. @Override
  453. protected void onNewIntent(Intent intent) {
  454. TabControl.Tab current = mTabControl.getCurrentTab();
  455. // When a tab is closed on exit, the current tab index is set to -1.
  456. // Reset before proceed as Browser requires the current tab to be set.
  457. if (current == null) {
  458. // Try to reset the tab in case the index was incorrect.
  459. current = mTabControl.getTab(0);
  460. if (current == null) {
  461. // No tabs at all so just ignore this intent.
  462. return;
  463. }
  464. mTabControl.setCurrentTab(current);
  465. attachTabToContentView(current);
  466. resetTitleAndIcon(current.getWebView());
  467. }
  468. final String action = intent.getAction();
  469. final int flags = intent.getFlags();
  470. if (Intent.ACTION_MAIN.equals(action) ||
  471. (flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
  472. // just resume the browser
  473. return;
  474. }
  475. if (Intent.ACTION_VIEW.equals(action)
  476. || Intent.ACTION_SEARCH.equals(action)
  477. || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
  478. || Intent.ACTION_WEB_SEARCH.equals(action)) {
  479. // If this was a search request (e.g. search query directly typed into the address bar),
  480. // pass it on to the default web search provider.
  481. if (handleWebSearchIntent(intent)) {
  482. return;
  483. }
  484. UrlData urlData = getUrlDataFromIntent(intent);
  485. if (urlData.isEmpty()) {
  486. urlData = new UrlData(mSettings.getHomePage());
  487. }
  488. // urlData.setPostData(intent
  489. // .getByteArrayExtra(Browser.EXTRA_POST_DATA));
  490. final String appId = intent
  491. .getStringExtra(Browser.EXTRA_APPLICATION_ID);
  492. if (Intent.ACTION_VIEW.equals(action)
  493. && !getPackageName().equals(appId)
  494. && (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
  495. TabControl.Tab appTab = mTabControl.getTabFromId(appId);
  496. if (appTab != null) {
  497. Log.i(LOGTAG, "Reusing tab for " + appId);
  498. // Dismiss the subwindow if applicable.
  499. dismissSubWindow(appTab);
  500. // Since we might kill the WebView, remove it from the
  501. // content view first.
  502. removeTabFromContentView(appTab);
  503. // Recreate the main WebView after destroying the old one.
  504. // If the WebView has the same original url and is on that
  505. // page, it can be reused.
  506. boolean needsLoad =
  507. mTabControl.recreateWebView(appTab, urlData.mUrl);
  508. if (current != appTab) {
  509. switchToTab(mTabControl.getTabIndex(appTab));
  510. if (needsLoad) {
  511. urlData.loadIn(appTab.getWebView());
  512. }
  513. } else {
  514. // If the tab was the current tab, we have to attach
  515. // it to the view system again.
  516. attachTabToContentView(appTab);
  517. if (needsLoad) {
  518. urlData.loadIn(appTab.getWebView());
  519. }
  520. }
  521. return;
  522. } else {
  523. // No matching application tab, try to find a regular tab
  524. // with a matching url.
  525. appTab = mTabControl.findUnusedTabWithUrl(urlData.mUrl);
  526. if (appTab != null) {
  527. if (current != appTab) {
  528. switchToTab(mTabControl.getTabIndex(appTab));
  529. }
  530. // Otherwise, we are already viewing the correct tab.
  531. } else {
  532. // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url
  533. // will be opened in a new tab unless we have reached
  534. // MAX_TABS. Then the url will be opened in the current
  535. // tab. If a new tab is created, it will have "true" for
  536. // exit on close.
  537. openTabAndShow(urlData, true, appId);
  538. }
  539. }
  540. } else {
  541. if ("about:debug".equals(urlData.mUrl)) {
  542. mSettings.toggleDebugSettings();
  543. return;
  544. }
  545. // Get rid of the subwindow if it exists
  546. dismissSubWindow(current);
  547. urlData.loadIn(current.getWebView());
  548. }
  549. }
  550. }
  551. private int parseUrlShortcut(String url) {
  552. if (url == null) return SHORTCUT_INVALID;
  553. // FIXME: quick search, need to be customized by setting
  554. if (url.length() > 2 && url.charAt(1) == ' ') {
  555. switch (url.charAt(0)) {
  556. case 'g': return SHORTCUT_GOOGLE_SEARCH;
  557. case 'w': return SHORTCUT_WIKIPEDIA_SEARCH;
  558. case 'd': return SHORTCUT_DICTIONARY_SEARCH;
  559. case 'l': return SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH;
  560. }
  561. }
  562. return SHORTCUT_INVALID;
  563. }
  564. /**
  565. * Launches the default web search activity with the query parameters if the given intent's data
  566. * are identified as plain search terms and not URLs/shortcuts.
  567. * @return true if the intent was handled and web search activity was launched, false if not.
  568. */
  569. private boolean handleWebSearchIntent(Intent intent) {
  570. if (intent == null) return false;
  571. String url = null;
  572. final String action = intent.getAction();
  573. if (Intent.ACTION_VIEW.equals(action)) {
  574. Uri data = intent.getData();
  575. if (data != null) url = data.toString();
  576. } else if (Intent.ACTION_SEARCH.equals(action)
  577. || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
  578. || Intent.ACTION_WEB_SEARCH.equals(action)) {
  579. url = intent.getStringExtra(SearchManager.QUERY);
  580. }
  581. return handleWebSearchRequest(url, intent.getBundleExtra(SearchManager.APP_DATA),
  582. intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
  583. }
  584. /**
  585. * Launches the default web search activity with the query parameters if the given url string
  586. * was identified as plain search terms and not URL/shortcut.
  587. * @return true if the request was handled and web search activity was launched, false if not.
  588. */
  589. private boolean handleWebSearchRequest(String inUrl, Bundle appData, String extraData) {
  590. if (inUrl == null) return false;
  591. // In general, we shouldn't modify URL from Intent.
  592. // But currently, we get the user-typed URL from search box as well.
  593. String url = fixUrl(inUrl).trim();
  594. // URLs and site specific search shortcuts are handled by the regular flow of control, so
  595. // return early.
  596. // if (Regex.WEB_URL_PATTERN.matcher(url).matches() ||
  597. if ( ACCEPTED_URI_SCHEMA.matcher(url).matches()
  598. || parseUrlShortcut(url) != SHORTCUT_INVALID) {
  599. return false;
  600. }
  601. Browser.updateVisitedHistory(mResolver, url, false);
  602. Browser.addSearchUrl(mResolver, url);
  603. Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
  604. intent.addCategory(Intent.CATEGORY_DEFAULT);
  605. intent.putExtra(SearchManager.QUERY, url);
  606. if (appData != null) {
  607. intent.putExtra(SearchManager.APP_DATA, appData);
  608. }
  609. if (extraData != null) {
  610. intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
  611. }
  612. intent.putExtra(Browser.EXTRA_APPLICATION_ID, getPackageName());
  613. startActivity(intent);
  614. return true;
  615. }
  616. private UrlData getUrlDataFromIntent(Intent intent) {
  617. String url = null;
  618. if (intent != null) {
  619. final String action = intent.getAction();
  620. if (Intent.ACTION_VIEW.equals(action)) {
  621. url = smartUrlFilter(intent.getData());
  622. if (url != null && url.startsWith("content:")) {
  623. /* Append mimetype so webview knows how to display */
  624. String mimeType = intent.resolveType(getContentResolver());
  625. if (mimeType != null) {
  626. url += "?" + mimeType;
  627. }
  628. }
  629. // if ("inline:".equals(url)) {
  630. // return new InlinedUrlData(
  631. // intent.getStringExtra(Browser.EXTRA_INLINE_CONTENT),
  632. // intent.getType(),
  633. // intent.getStringExtra(Browser.EXTRA_INLINE_ENCODING),
  634. // intent.getStringExtra(Browser.EXTRA_INLINE_FAILURL));
  635. // }
  636. } else if (Intent.ACTION_SEARCH.equals(action)
  637. || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
  638. || Intent.ACTION_WEB_SEARCH.equals(action)) {
  639. url = intent.getStringExtra(SearchManager.QUERY);
  640. if (url != null) {
  641. mLastEnteredUrl = url;
  642. // Don't add Urls, just search terms.
  643. // Urls will get added when the page is loaded.
  644. // if (!Regex.WEB_URL_PATTERN.matcher(url).matches()) {
  645. // Browser.updateVisitedHistory(mResolver, url, false);
  646. // }
  647. // In general, we shouldn't modify URL from Intent.
  648. // But currently, we get the user-typed URL from search box as well.
  649. url = fixUrl(url);
  650. url = smartUrlFilter(url);
  651. String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&";
  652. if (url.contains(searchSource)) {
  653. String source = null;
  654. final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
  655. // if (appData != null) {
  656. // source = appData.getString(SearchManager.SOURCE);
  657. // }
  658. if (TextUtils.isEmpty(source)) {
  659. source = GOOGLE_SEARCH_SOURCE_UNKNOWN;
  660. }
  661. url = url.replace(searchSource, "&source=android-"+source+"&");
  662. }
  663. }
  664. }
  665. }
  666. return new UrlData(url);
  667. }
  668. /* package */ static String fixUrl(String inUrl) {
  669. // FIXME: Converting the url to lower case
  670. // duplicates functionality in smartUrlFilter().
  671. // However, changing all current callers of fixUrl to
  672. // call smartUrlFilter in addition may have unwanted
  673. // consequences, and is deferred for now.
  674. int colon = inUrl.indexOf(':');
  675. boolean allLower = true;
  676. for (int index = 0; index < colon; index++) {
  677. char ch = inUrl.charAt(index);
  678. if (!Character.isLetter(ch)) {
  679. break;
  680. }
  681. allLower &= Character.isLowerCase(ch);
  682. if (index == colon - 1 && !allLower) {
  683. inUrl = inUrl.substring(0, colon).toLowerCase()
  684. + inUrl.substring(colon);
  685. }
  686. }
  687. if (inUrl.startsWith("http://") || inUrl.startsWith("https://"))
  688. return inUrl;
  689. if (inUrl.startsWith("http:") ||
  690. inUrl.startsWith("https:")) {
  691. if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) {
  692. inUrl = inUrl.replaceFirst("/", "//");
  693. } else inUrl = inUrl.replaceFirst(":", "://");
  694. }
  695. return inUrl;
  696. }
  697. /**
  698. * Looking for the pattern like this
  699. *
  700. * *
  701. * * *
  702. * *** * *******
  703. * * *
  704. * * *
  705. * *
  706. */
  707. private final SensorListener mSensorListener = new SensorListener() {
  708. private long mLastGestureTime;
  709. private float[] mPrev = new float[3];
  710. private float[] mPrevDiff = new float[3];
  711. private float[] mDiff = new float[3];
  712. private float[] mRevertDiff = new float[3];
  713. public void onSensorChanged(int sensor, float[] values) {
  714. boolean show = false;
  715. float[] diff = new float[3];
  716. for (int i = 0; i < 3; i++) {
  717. diff[i] = values[i] - mPrev[i];
  718. if (Math.abs(diff[i]) > 1) {
  719. show = true;
  720. }
  721. if ((diff[i] > 1.0 && mDiff[i] < 0.2)
  722. || (diff[i] < -1.0 && mDiff[i] > -0.2)) {
  723. // start track when there is a big move, or revert
  724. mRevertDiff[i] = mDiff[i];
  725. mDiff[i] = 0;
  726. } else if (diff[i] > -0.2 && diff[i] < 0.2) {
  727. // reset when it is flat
  728. mDiff[i] = mRevertDiff[i] = 0;
  729. }
  730. mDiff[i] += diff[i];
  731. mPrevDiff[i] = diff[i];
  732. mPrev[i] = values[i];
  733. }
  734. if (false) {
  735. // only shows if we think the delta is big enough, in an attempt
  736. // to detect "serious" moves left/right or up/down
  737. Log.d("BrowserSensorHack", "sensorChanged " + sensor + " ("
  738. + values[0] + ", " + values[1] + ", " + values[2] + ")"
  739. + " diff(" + diff[0] + " " + diff[1] + " " + diff[2]
  740. + ")");
  741. Log.d("BrowserSensorHack", " mDiff(" + mDiff[0] + " "
  742. + mDiff[1] + " " + mDiff[2] + ")" + " mRevertDiff("
  743. + mRevertDiff[0] + " " + mRevertDiff[1] + " "
  744. + mRevertDiff[2] + ")");
  745. }
  746. long now = android.os.SystemClock.uptimeMillis();
  747. if (now - mLastGestureTime > 1000) {
  748. mLastGestureTime = 0;
  749. float y = mDiff[1];
  750. float z = mDiff[2];
  751. float ay = Math.abs(y);
  752. float az = Math.abs(z);
  753. float ry = mRevertDiff[1];
  754. float rz = mRevertDiff[2];
  755. float ary = Math.abs(ry);
  756. float arz = Math.abs(rz);
  757. boolean gestY = ay > 2.5f && ary > 1.0f && ay > ary;
  758. boolean gestZ = az > 3.5f && arz > 1.0f && az > arz;
  759. if ((gestY || gestZ) && !(gestY && gestZ)) {
  760. WebView view = mTabControl.getCurrentWebView();
  761. if (view != null) {
  762. if (gestZ) {
  763. if (z < 0) {
  764. view.zoomOut();
  765. } else {
  766. view.zoomIn();
  767. }
  768. } else {
  769. view.flingScroll(0, Math.round(y * 100));
  770. }
  771. }
  772. mLastGestureTime = now;
  773. }
  774. }
  775. }
  776. public void onAccuracyChanged(int sensor, int accuracy) {
  777. // TODO Auto-generated method stub
  778. }
  779. };
  780. @Override protected void onResume() {
  781. super.onResume();
  782. if (LOGV_ENABLED) {
  783. Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this);
  784. }
  785. if (!mActivityInPause) {
  786. Log.e(LOGTAG, "BrowserActivity is already resumed.");
  787. return;
  788. }
  789. mTabControl.resumeCurrentTab();
  790. mActivityInPause = false;
  791. resumeWebViewTimers();
  792. if (mWakeLock.isHeld()) {
  793. mHandler.removeMessages(RELEASE_WAKELOCK);
  794. mWakeLock.release();
  795. }
  796. if (mCredsDlg != null) {
  797. if (!mHandler.hasMessages(CANCEL_CREDS_REQUEST)) {
  798. // In case credential request never comes back
  799. mHandler.sendEmptyMessageDelayed(CANCEL_CREDS_REQUEST, 6000);
  800. }
  801. }
  802. registerReceiver(mNetworkStateIntentReceiver,
  803. mNetworkStateChangedFilter);
  804. WebView.enablePlatformNotifications();
  805. if (mSettings.doFlick()) {
  806. if (mSensorManager == null) {
  807. mSensorManager = (SensorManager) getSystemService(
  808. Context.SENSOR_SERVICE);
  809. }
  810. mSensorManager.registerListener(mSensorListener,
  811. SensorManager.SENSOR_ACCELEROMETER,
  812. SensorManager.SENSOR_DELAY_FASTEST);
  813. } else {
  814. mSensorManager = null;
  815. }
  816. }
  817. /**
  818. * Since the actual title bar is embedded in the WebView, and removing it
  819. * would change its appearance, create a temporary title bar to go at
  820. * the top of the screen while the menu is open.
  821. */
  822. private TitleBar mFakeTitleBar;
  823. /**
  824. * Holder for the fake title bar. It will have a foreground shadow, as well
  825. * as a white background, so the fake title bar looks like the real one.
  826. */
  827. private ViewGroup mFakeTitleBarHolder;
  828. /**
  829. * Layout parameters for the fake title bar within mFakeTitleBarHolder
  830. */
  831. private FrameLayout.LayoutParams mFakeTitleBarParams
  832. = new FrameLayout.LayoutParams(
  833. ViewGroup.LayoutParams.FILL_PARENT,
  834. ViewGroup.LayoutParams.WRAP_CONTENT);
  835. /**
  836. * Keeps track of whether the options menu is open. This is important in
  837. * determining whether to show or hide the title bar overlay.
  838. */
  839. private boolean mOptionsMenuOpen;
  840. /**
  841. * Only meaningful when mOptionsMenuOpen is true. This variable keeps track
  842. * of whether the configuration has changed. The first onMenuOpened call
  843. * after a configuration change is simply a reopening of the same menu
  844. * (i.e. mIconView did not change).
  845. */
  846. private boolean mConfigChanged;
  847. /**
  848. * Whether or not the options menu is in its smaller, icon menu form. When
  849. * true, we want the title bar overlay to be up. When false, we do not.
  850. * Only meaningful if mOptionsMenuOpen is true.
  851. */
  852. private boolean mIconView;
  853. @Override
  854. public boolean onMenuOpened(int featureId, Menu menu) {
  855. if (Window.FEATURE_OPTIONS_PANEL == featureId) {
  856. if (mOptionsMenuOpen) {
  857. if (mConfigChanged) {
  858. // We do not need to make any changes to the state of the
  859. // title bar, since the only thing that happened was a
  860. // change in orientation
  861. mConfigChanged = false;
  862. } else {
  863. if (mIconView) {
  864. // Switching the menu to expanded view, so hide the
  865. // title bar.
  866. hideFakeTitleBar();
  867. mIconView = false;
  868. } else {
  869. // Switching the menu back to icon view, so show the
  870. // title bar once again.
  871. showFakeTitleBar();
  872. mIconView = true;
  873. }
  874. }
  875. } else {
  876. // The options menu is closed, so open it, and show the title
  877. showFakeTitleBar();
  878. mOptionsMenuOpen = true;
  879. mConfigChanged = false;
  880. mIconView = true;
  881. }
  882. }
  883. return true;
  884. }
  885. /**
  886. * Special class used exclusively for the shadow drawn underneath the fake
  887. * title bar. The shadow does not need to be drawn if the WebView
  888. * underneath is scrolled to the top, because it will draw directly on top
  889. * of the embedded shadow.
  890. */
  891. private static class Shadow extends View {
  892. private WebView mWebView;
  893. public Shadow(Context context, AttributeSet attrs) {
  894. super(context, attrs);
  895. }
  896. public void setWebView(WebView view) {
  897. mWebView = view;
  898. }
  899. @Override
  900. public void draw(Canvas canvas) {
  901. // In general onDraw is the method to override, but we care about
  902. // whether or not the background gets drawn, which happens in draw()
  903. if (mWebView == null || mWebView.getScrollY() > getHeight()) {
  904. super.draw(canvas);
  905. }
  906. // Need to invalidate so that if the scroll position changes, we
  907. // still draw as appropriate.
  908. invalidate();
  909. }
  910. }
  911. private void showFakeTitleBar() {
  912. final View decor = getWindow().peekDecorView();
  913. if (mFakeTitleBar == null && mActiveTabsPage == null
  914. && !mActivityInPause && decor != null
  915. && decor.getWindowToken() != null) {
  916. Rect visRect = new Rect();
  917. if (!mBrowserFrameLayout.getGlobalVisibleRect(visRect)) {
  918. if (LOGD_ENABLED) {
  919. Log.d(LOGTAG, "showFakeTitleBar visRect failed");
  920. }
  921. return;
  922. }
  923. final WebView webView = getTopWindow();
  924. mFakeTitleBar = new TitleBar(this);
  925. mFakeTitleBar.setTitleAndUrl(null, webView.getUrl());
  926. mFakeTitleBar.setProgress(webView.getProgress());
  927. mFakeTitleBar.setFavicon(webView.getFavicon());
  928. updateLockIconToLatest();
  929. WindowManager manager
  930. = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
  931. // Add the title bar to the window manager so it can receive touches
  932. // while the menu is up
  933. WindowManager.LayoutParams params
  934. = new WindowManager.LayoutParams(
  935. ViewGroup.LayoutParams.FILL_PARENT,
  936. ViewGroup.LayoutParams.WRAP_CONTENT,
  937. WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
  938. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  939. PixelFormat.TRANSLUCENT);
  940. params.gravity = Gravity.TOP;
  941. WebView mainView = mTabControl.getCurrentWebView();
  942. boolean atTop = mainView != null && mainView.getScrollY() == 0;
  943. params.windowAnimations = atTop ? 0 : R.style.TitleBar;
  944. // XXX : Without providing an offset, the fake title bar will be
  945. // placed underneath the status bar. Use the global visible rect
  946. // of mBrowserFrameLayout to determine the bottom of the status bar
  947. params.y = visRect.top;
  948. // Add a holder for the title bar. It also holds a shadow to show
  949. // below the title bar.
  950. if (mFakeTitleBarHolder == null) {
  951. mFakeTitleBarHolder = (ViewGroup) LayoutInflater.from(this)
  952. .inflate(R.layout.title_bar_bg, null);
  953. }
  954. Shadow shadow = (Shadow) mFakeTitleBarHolder.findViewById(
  955. R.id.shadow);
  956. shadow.setWebView(mainView);
  957. mFakeTitleBarHolder.addView(mFakeTitleBar, 0, mFakeTitleBarParams);
  958. manager.addView(mFakeTitleBarHolder, params);
  959. }
  960. }
  961. @Override
  962. public void onOptionsMenuClosed(Menu menu) {
  963. mOptionsMenuOpen = false;
  964. if (!mInLoad) {
  965. hideFakeTitleBar();
  966. } else if (!mIconView) {
  967. // The page is currently loading, and we are in expanded mode, so
  968. // we were not showing the menu. Show it once again. It will be
  969. // removed when the page finishes.
  970. showFakeTitleBar();
  971. }
  972. }
  973. private void hideFakeTitleBar() {
  974. if (mFakeTitleBar == null) return;
  975. WindowManager.LayoutParams params = (WindowManager.LayoutParams)
  976. mFakeTitleBarHolder.getLayoutParams();
  977. WebView mainView = mTabControl.getCurrentWebView();
  978. // Although we decided whether or not to animate based on the current
  979. // scroll position, the scroll position may have changed since the
  980. // fake title bar was displayed. Make sure it has the appropriate
  981. // animation/lack thereof before removing.
  982. params.windowAnimations = mainView != null && mainView.getScrollY() == 0
  983. ? 0 : R.style.TitleBar;
  984. WindowManager manager
  985. = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
  986. manager.updateViewLayout(mFakeTitleBarHolder, params);
  987. mFakeTitleBarHolder.removeView(mFakeTitleBar);
  988. manager.removeView(mFakeTitleBarHolder);
  989. mFakeTitleBar = null;
  990. }
  991. /**
  992. * Special method for the fake title bar to call when displaying its context
  993. * menu, since it is in its own Window, and its parent does not show a
  994. * context menu.
  995. */
  996. /* package */ void showTitleBarContextMenu() {
  997. if (null == mTitleBar.getParent()) {
  998. return;
  999. }
  1000. openContextMenu(mTitleBar);
  1001. }
  1002. /**
  1003. * onSaveInstanceState(Bundle map)
  1004. * onSaveInstanceState is called right before onStop(). The map contains
  1005. * the saved state.
  1006. */
  1007. @Override protected void onSaveInstanceState(Bundle outState) {
  1008. if (LOGV_ENABLED) {
  1009. Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this);
  1010. }
  1011. // the default implementation requires each view to have an id. As the
  1012. // browser handles the state itself and it doesn't use id for the views,
  1013. // don't call the default implementation. Otherwise it will trigger the
  1014. // warning like this, "couldn't save which view has focus because the
  1015. // focused view XXX has no id".
  1016. // Save all the tabs
  1017. mTabControl.saveState(outState);
  1018. }
  1019. @Override protected void onPause() {
  1020. super.onPause();
  1021. if (mActivityInPause) {
  1022. Log.e(LOGTAG, "BrowserActivity is already paused.");
  1023. return;
  1024. }
  1025. mTabControl.pauseCurrentTab();
  1026. mActivityInPause = true;
  1027. if (mTabControl.getCurrentIndex() >= 0 && !pauseWebViewTimers()) {
  1028. mWakeLock.acquire();
  1029. mHandler.sendMessageDelayed(mHandler
  1030. .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
  1031. }
  1032. // Clear the credentials toast if it is up
  1033. if (mCredsDlg != null && mCredsDlg.isShowing()) {
  1034. mCredsDlg.dismiss();
  1035. }
  1036. mCredsDlg = null;
  1037. // FIXME: This removes the active tabs page and resets the menu to
  1038. // MAIN_MENU. A better solution might be to do this work in onNewIntent
  1039. // but then we would need to save it in onSaveInstanceState and restore
  1040. // it in onCreate/onRestoreInstanceState
  1041. if (mActiveTabsPage != null) {
  1042. removeActiveTabPage(true);
  1043. }
  1044. cancelStopToast();
  1045. // unregister network state listener
  1046. unregisterReceiver(mNetworkStateIntentReceiver);
  1047. WebView.disablePlatformNotifications();
  1048. if (mSensorManager != null) {
  1049. mSensorManager.unregisterListener(mSensorListener);
  1050. }
  1051. }
  1052. @Override protected void onDestroy() {
  1053. if (LOGV_ENABLED) {
  1054. Log.v(LOGTAG, "BrowserActivity.onDestroy: this=" + this);
  1055. }
  1056. super.onDestroy();
  1057. if (mTabControl == null) return;
  1058. // Remove the current tab and sub window
  1059. TabControl.Tab t = mTabControl.getCurrentTab();
  1060. if (t != null) {
  1061. dismissSubWindow(t);
  1062. removeTabFromContentView(t);
  1063. }
  1064. // Destroy all the tabs
  1065. mTabControl.destroy();
  1066. WebIconDatabase.getInstance().close();
  1067. if (mGlsConnection != null) {
  1068. unbindService(mGlsConnection);
  1069. mGlsConnection = null;
  1070. }
  1071. //
  1072. // stop MASF proxy service
  1073. //
  1074. //Intent proxyServiceIntent = new Intent();
  1075. //proxyServiceIntent.setComponent
  1076. // (new ComponentName(
  1077. // "com.android.masfproxyservice",
  1078. // "com.android.masfproxyservice.MasfProxyService"));
  1079. //stopService(proxyServiceIntent);
  1080. unregisterReceiver(mPackageInstallationReceiver);
  1081. }
  1082. @Override
  1083. public void onConfigurationChanged(Configuration newConfig) {
  1084. mConfigChanged = true;
  1085. super.onConfigurationChanged(newConfig);
  1086. if (mPageInfoDialog != null) {
  1087. mPageInfoDialog.dismiss();
  1088. showPageInfo(
  1089. mPageInfoView,
  1090. mPageInfoFromShowSSLCertificateOnError.booleanValue());
  1091. }
  1092. if (mSSLCertificateDialog != null) {
  1093. mSSLCertificateDialog.dismiss();
  1094. showSSLCertificate(
  1095. mSSLCertificateView);
  1096. }
  1097. /*
  1098. if (mSSLCertificateOnErrorDialog != null) {
  1099. mSSLCertificateOnErrorDialog.dismiss();
  1100. showSSLCertificateOnError(
  1101. mSSLCertificateOnErrorView,
  1102. mSSLCertificateOnErrorHandler,
  1103. mSSLCertificateOnErrorError);
  1104. }
  1105. */
  1106. if (mHttpAuthenticationDialog != null) {
  1107. String title = "";
  1108. // String title = ((TextView) mHttpAuthenticationDialog
  1109. // .findViewById(com.android.internal.R.id.alertTitle)).getText()
  1110. // .toString();
  1111. String name = ((TextView) mHttpAuthenticationDialog
  1112. .findViewById(R.id.username_edit)).getText().toString();
  1113. String password = ((TextView) mHttpAuthenticationDialog
  1114. .findViewById(R.id.password_edit)).getText().toString();
  1115. int focusId = mHttpAuthenticationDialog.getCurrentFocus()
  1116. .getId();
  1117. mHttpAuthenticationDialog.dismiss();
  1118. showHttpAuthentication(mHttpAuthHandler, null, null, title,
  1119. name, password, focusId);
  1120. }
  1121. if (mFindDialog != null && mFindDialog.isShowing()) {
  1122. mFindDialog.onConfigurationChanged(newConfig);
  1123. }
  1124. }
  1125. @Override public void onLowMemory() {
  1126. super.onLowMemory();
  1127. mTabControl.freeMemory();
  1128. }
  1129. private boolean resumeWebViewTimers() {
  1130. if ((!mActivityInPause && !mPageStarted) ||
  1131. (mActivityInPause && mPageStarted)) {
  1132. CookieSyncManager.getInstance().startSync();
  1133. WebView w = mTabControl.getCurrentWebView();
  1134. if (w != null) {
  1135. w.resumeTimers();
  1136. }
  1137. return true;
  1138. } else {
  1139. return false;
  1140. }
  1141. }
  1142. private boolean pauseWebViewTimers() {
  1143. if (mActivityInPause && !mPageStarted) {
  1144. CookieSyncManager.getInstance().stopSync();
  1145. WebView w = mTabControl.getCurrentWebView();
  1146. if (w != null) {
  1147. w.pauseTimers();
  1148. }
  1149. return true;
  1150. } else {
  1151. return false;
  1152. }
  1153. }
  1154. // FIXME: Do we want to call this when loading google for the first time?
  1155. /*
  1156. * This function is called when we are launching for the first time. We
  1157. * are waiting for the login credentials before loading Google home
  1158. * pages. This way the user will be logged in straight away.
  1159. */
  1160. private void waitForCredentials() {
  1161. // Show a toast
  1162. mCredsDlg = new ProgressDialog(this);
  1163. mCredsDlg.setIndeterminate(true);
  1164. mCredsDlg.setMessage(getText(R.string.retrieving_creds_dlg_msg));
  1165. // If the user cancels the operation, then cancel the Google
  1166. // Credentials request.
  1167. mCredsDlg.setCancelMessage(mHandler.obtainMessage(CANCEL_CREDS_REQUEST));
  1168. mCredsDlg.show();
  1169. // We set a timeout for the retrieval of credentials in onResume()
  1170. // as that is when we have freed up some CPU time to get
  1171. // the login credentials.
  1172. }
  1173. /*
  1174. * If we have received the credentials or we have timed out and we are
  1175. * showing the credentials dialog, then it is time to move on.
  1176. */
  1177. private void resumeAfterCredentials() {
  1178. if (mCredsDlg == null) {
  1179. return;
  1180. }
  1181. // Clear the toast
  1182. if (mCredsDlg.isShowing()) {
  1183. mCredsDlg.dismiss();
  1184. }
  1185. mCredsDlg = null;
  1186. // Clear any pending timeout
  1187. mHandler.removeMessages(CANCEL_CREDS_REQUEST);
  1188. // Load the page
  1189. WebView w = mTabControl.getCurrentWebView();
  1190. if (w != null) {
  1191. w.loadUrl(mSettings.getHomePage());
  1192. }
  1193. // Update the settings, need to do this last as it can take a moment
  1194. // to persist the settings. In the mean time we could be loading
  1195. // content.
  1196. mSettings.setLoginInitialized(this);
  1197. }
  1198. // Open the icon database and retain all the icons for visited sites.
  1199. private void retainIconsOnStartup() {
  1200. /*
  1201. final WebIconDatabase db = WebIconDatabase.getInstance();
  1202. db.open(getDir("icons", 0).getPath());
  1203. try {
  1204. Cursor c = Browser.getAllBookmarks(mResolver);
  1205. if (!c.moveToFirst()) {
  1206. c.deactivate();
  1207. return;
  1208. }
  1209. int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
  1210. do {
  1211. String url = c.getString(urlIndex);
  1212. db.retainIconForPageUrl(url);
  1213. } while (c.moveToNext());
  1214. c.deactivate();
  1215. } catch (IllegalStateException e) {
  1216. Log.e(LOGTAG, "retainIconsOnStartup", e);
  1217. }
  1218. */
  1219. }
  1220. // Helper method for getting the top window.
  1221. WebView getTopWindow() {
  1222. return mTabControl.getCurrentTopWebView();
  1223. }
  1224. @Override
  1225. public boolean onCreateOptionsMenu(Menu menu) {
  1226. super.onCreateOptionsMenu(menu);
  1227. MenuInflater inflater = getMenuInflater();
  1228. inflater.inflate(R.menu.browser, menu);
  1229. mMenu = menu;
  1230. updateInLoadMenuItems();
  1231. return true;
  1232. }
  1233. /**
  1234. * As the menu can be open when loading state changes
  1235. * we must manually update the state of the stop/reload menu
  1236. * item
  1237. */
  1238. private void updateInLoadMenuItems() {
  1239. if (mMenu == null) {
  1240. return;
  1241. }
  1242. MenuItem src = mInLoad ?
  1243. mMenu.findItem(R.id.stop_menu_id):
  1244. mMenu.findItem(R.id.reload_menu_id);
  1245. MenuItem dest = mMenu.findItem(R.id.stop_reload_menu_id);
  1246. dest.setIcon(src.getIcon());
  1247. dest.setTitle(src.getTitle());
  1248. }
  1249. @Override
  1250. public boolean onContextItemSelected(MenuItem item) {
  1251. // chording is not an issue with context menus, but we use the same
  1252. // options selector, so set mCanChord to true so we can access them.
  1253. mCanChord = true;
  1254. int id = item.getItemId();
  1255. switch (id) {
  1256. // For the context menu from the title bar
  1257. case R.id.title_bar_share_page_url:
  1258. case R.id.title_bar_copy_page_url:
  1259. WebView mainView = mTabControl.getCurrentWebView();
  1260. if (null == mainView) {
  1261. return false;
  1262. }
  1263. if (id == R.id.title_bar_share_page_url) {
  1264. Browser.sendString(this, mainView.getUrl());
  1265. } else {
  1266. copy(mainView.getUrl());
  1267. }
  1268. break;
  1269. // -- Browser context menu
  1270. case R.id.open_context_menu_id:
  1271. case R.id.open_newtab_context_menu_id:
  1272. case R.id.bookmark_context_menu_id:
  1273. case R.id.save_link_context_menu_id:
  1274. case R.id.share_link_context_menu_id:
  1275. case R.id.copy_link_context_menu_id:
  1276. final WebView webView = getTopWindow();
  1277. if (null == webView) {
  1278. return false;
  1279. }
  1280. final HashMap hrefMap = new HashMap();
  1281. hrefMap.put("webview", webView);
  1282. final Message msg = mHandler.obtainMessage(
  1283. FOCUS_NODE_HREF, id, 0, hrefMap);
  1284. webView.requestFocusNodeHref(msg);
  1285. break;
  1286. default:
  1287. // For other context menus
  1288. return onOptionsItemSelected(item);
  1289. }
  1290. mCanChord = false;
  1291. return true;
  1292. }
  1293. private Bundle createGoogleSearchSourceBundle(String source) {
  1294. Bundle bundle = new Bundle();
  1295. // bundle.putString(SearchManager.SOURCE, source);
  1296. return bundle;
  1297. }
  1298. /**
  1299. * Overriding this to insert a local information bundle
  1300. */
  1301. @Override
  1302. public boolean onSearchRequested() {
  1303. if (mOptionsMenuOpen) closeOptionsMenu();
  1304. String url = (getTopWindow() == null) ? null : getTopWindow().getUrl();
  1305. startSearch(mSettings.getHomePage().equals(url) ? null : url, true,
  1306. createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), false);
  1307. return true;
  1308. }
  1309. @Override
  1310. public void startSearch(String initialQuery, boolean selectInitialQuery,
  1311. Bundle appSearchData, boolean globalSearch) {
  1312. if (appSearchData == null) {
  1313. appSearchData = createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_TYPE);
  1314. }
  1315. super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
  1316. }
  1317. /**
  1318. * Switch tabs. Called by the TitleBarSet when sliding the title bar
  1319. * results in changing tabs.
  1320. * @param index Index of the tab to change to, as defined by
  1321. * mTabControl.getTabIndex(Tab t).
  1322. * @return boolean True if we successfully switched to a different tab. If
  1323. * the indexth tab is null, or if that tab is the same as
  1324. * the current one, return false.
  1325. */
  1326. /* package */ boolean switchToTab(int index) {
  1327. TabControl.Tab tab = mTabControl.getTab(index);
  1328. TabControl.Tab currentTab = mTabControl.getCurrentTab();
  1329. if (tab == null || tab == currentTab) {
  1330. return false;
  1331. }
  1332. if (currentTab != null) {
  1333. // currentTab may be null if it was just removed. In that case,
  1334. // we do not need to remove it
  1335. removeTabFromContentView(currentTab);
  1336. }
  1337. mTabControl.setCurrentTab(tab);
  1338. attachTabToContentView(tab);
  1339. resetTitleIconAndProgress();
  1340. updateLockIconToLatest();
  1341. return true;
  1342. }
  1343. /* package */ TabControl.Tab openTabToHomePage() {
  1344. return openTabAndShow(mSettings.getHomePage(), false, null);
  1345. }
  1346. /* package */ void closeCurrentWindow() {
  1347. final TabControl.Tab current = mTabControl.getCurrentTab();
  1348. if (mTabControl.getTabCount() == 1) {
  1349. // This is the last tab. Open a new one, with the home
  1350. // page and close the current one.
  1351. TabControl.Tab newTab = openTabToHomePage();
  1352. closeTab(current);
  1353. return;
  1354. }
  1355. final TabControl.Tab parent = current.getParentTab();
  1356. int indexToShow = -1;
  1357. if (parent != null) {
  1358. indexToShow = mTabControl.getTabIndex(parent);
  1359. } else {
  1360. final int currentIndex = mTabControl.getCurrentIndex();
  1361. // Try to move to the tab to the right
  1362. indexToShow = currentIndex + 1;
  1363. if (indexToShow > mTabControl.getTabCount() - 1) {
  1364. // Try to move to the tab to the left
  1365. indexToShow = currentIndex - 1;
  1366. }
  1367. }
  1368. if (switchToTab(indexToShow)) {
  1369. // Close window
  1370. closeTab(current);
  1371. }
  1372. }
  1373. private ActiveTabsPage mActiveTabsPage;
  1374. /**
  1375. * Remove the active tabs page.
  1376. * @param needToAttach If true, the active tabs page did not attach a tab
  1377. * to the content view, so we need to do that here.
  1378. */
  1379. /* package */ void removeActiveTabPage(boolean needToAttach) {
  1380. mContentView.removeView(mActiveTabsPage);
  1381. mActiveTabsPage = null;
  1382. mMenuState = R.id.MAIN_MENU;
  1383. if (needToAttach) {
  1384. attachTabToContentView(mTabControl.getCurrentTab());
  1385. }
  1386. getTopWindow().requestFocus();
  1387. }
  1388. @Override
  1389. public boolean onOptionsItemSelected(MenuItem item) {
  1390. if (!mCanChord) {
  1391. // The user has already fired a shortcut with this hold down of the
  1392. // menu key.
  1393. return false;
  1394. }
  1395. if (null == getTopWindow()) {
  1396. return false;
  1397. }
  1398. if (mMenuIsDown) {
  1399. // The shortcut action consumes the MENU. Even if it is still down,
  1400. // it won't trigger the next shortcut action. In the case of the
  1401. // shortcut action triggering a new activity, like Bookmarks, we
  1402. // won't get onKeyUp for MENU. So it is important to reset it here.
  1403. mMenuIsDown = false;
  1404. }
  1405. switch (item.getItemId()) {
  1406. // -- Main menu
  1407. case R.id.new_tab_menu_id:
  1408. openTabToHomePage();
  1409. break;
  1410. case R.id.goto_menu_id:
  1411. onSearchRequested();
  1412. break;
  1413. case R.id.bookmarks_menu_id:
  1414. bookmarksOrHistoryPicker(false);
  1415. break;
  1416. case R.id.active_tabs_menu_id:
  1417. mActiveTabsPage = new ActiveTabsPage(this, mTabControl);
  1418. removeTabFromContentView(mTabControl.getCurrentTab());
  1419. hideFakeTitleBar();
  1420. mContentView.addView(mActiveTabsPage, COVER_SCREEN_PARAMS);
  1421. mActiveTabsPage.requestFocus();
  1422. mMenuState = EMPTY_MENU;
  1423. break;
  1424. case R.id.add_bookmark_menu_id:
  1425. Intent i = new Intent(BrowserActivity.this,
  1426. AddBookmarkPage.class);
  1427. WebView w = getTopWindow();
  1428. i.putExtra("url", w.getUrl());
  1429. i.putExtra("title", w.getTitle());
  1430. // i.putExtra("touch_icon_url", w.getTouchIconUrl());
  1431. i.putExtra("thumbnail", createScreenshot(w));
  1432. startActivity(i);
  1433. break;
  1434. case R.id.stop_reload_menu_id:
  1435. if (mInLoad) {
  1436. stopLoading();
  1437. } else {
  1438. getTopWindow().reload();
  1439. }
  1440. break;
  1441. case R.id.back_menu_id:
  1442. getTopWindow().goBack();
  1443. break;
  1444. case R.id.forward_menu_id:
  1445. getTopWindow().goForward();
  1446. break;
  1447. case R.id.close_menu_id:
  1448. // Close the subwindow if it exists.
  1449. if (mTabControl.getCurrentSubWindow() != null) {
  1450. dismissSubWindow(mTabControl.getCurrentTab());
  1451. break;
  1452. }
  1453. closeCurrentWindow();
  1454. break;
  1455. case R.id.homepage_menu_id:
  1456. TabControl.Tab current = mTabControl.getCurrentTab();
  1457. if (current != null) {
  1458. dismissSubWindow(current);
  1459. current.getWebView().loadUrl(mSettings.getHomePage());
  1460. }
  1461. break;
  1462. case R.id.preferences_menu_id:
  1463. Intent intent = new Intent(this,
  1464. BrowserPreferencesPage.class);
  1465. startActivityForResult(intent, PREFERENCES_PAGE);
  1466. break;
  1467. case R.id.scripts_menu_id:
  1468. Intent scriptsIntent = new Intent(this,
  1469. ScriptListActivity.class);
  1470. startActivity(scriptsIntent);
  1471. break;
  1472. case R.id.find_menu_id:
  1473. if (null == mFindDialog) {
  1474. mFindDialog = new FindDialog(this);
  1475. }
  1476. mFindDialog.setWebView(getTopWindow());
  1477. mFindDialog.show();
  1478. mMenuState = EMPTY_MENU;
  1479. break;
  1480. case R.id.select_text_id:
  1481. // getTopWindow().emulateShiftHeld();
  1482. break;
  1483. case R.id.page_info_menu_id:
  1484. showPageInfo(mTabControl.getCurrentTab(), false);
  1485. break;
  1486. case R.id.classic_history_menu_id:
  1487. bookmarksOrHistoryPicker(true);
  1488. break;
  1489. case R.id.share_page_menu_id:
  1490. // Browser.sendString(this, getTopWindow().getUrl(),
  1491. // getText(R.string.choosertitle_sharevia).toString());
  1492. break;
  1493. case R.id.dump_nav_menu_id:
  1494. getTopWindow().debugDump();
  1495. break;
  1496. case R.id.zoom_in_menu_id:
  1497. getTopWindow().zoomIn();
  1498. break;
  1499. case R.id.zoom_out_menu_id:
  1500. getTopWindow().zoomOut();
  1501. break;
  1502. case R.id.view_downloads_menu_id:
  1503. viewDownloads(null);
  1504. break;
  1505. case R.id.window_one_menu_id:
  1506. case R.id.window_two_menu_id:
  1507. case R.id.window_three_menu_id:
  1508. case R.id.window_four_menu_id:
  1509. case R.id.window_five_menu_id: