--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -106,21 +106,22 @@ abstract public class GeckoApp
public static final String ACTION_ALERT_CLICK = "org.mozilla.gecko.ACTION_ALERT_CLICK";
public static final String ACTION_ALERT_CLEAR = "org.mozilla.gecko.ACTION_ALERT_CLEAR";
public static final String ACTION_WEBAPP = "org.mozilla.gecko.WEBAPP";
public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG";
public static final String ACTION_BOOKMARK = "org.mozilla.gecko.BOOKMARK";
public static final String ACTION_LOAD = "org.mozilla.gecko.LOAD";
public static final String ACTION_UPDATE = "org.mozilla.gecko.UPDATE";
public static final String ACTION_INIT_PW = "org.mozilla.gecko.INIT_PW";
- public static final String SAVED_STATE_URI = "uri";
- public static final String SAVED_STATE_TITLE = "title";
- public static final String SAVED_STATE_VIEWPORT = "viewport";
- public static final String SAVED_STATE_SCREEN = "screen";
- public static final String SAVED_STATE_SESSION = "session";
+ private static final String SAVED_STATE_URI = "uri";
+ private static final String SAVED_STATE_TITLE = "title";
+ private static final String SAVED_STATE_VIEWPORT = "viewport";
+ private static final String SAVED_STATE_SCREEN = "/sdcard/lastscreen.png";
+ private static final String SAVED_STATE_SESSION = "session";
+ private static final String SAVED_STATE_FILESIZE = "filesize";
StartupMode mStartupMode = null;
private LinearLayout mMainLayout;
private RelativeLayout mGeckoLayout;
public static SurfaceView cameraView;
public static GeckoApp mAppContext;
public static boolean mDOMFullScreen = false;
public static Menu sMenu;
@@ -136,25 +137,26 @@ abstract public class GeckoApp
private BroadcastReceiver mBatteryReceiver;
public static BrowserToolbar mBrowserToolbar;
public static DoorHangerPopup mDoorHangerPopup;
public static FormAssistPopup mFormAssistPopup;
public Favicons mFavicons;
private static LayerController mLayerController;
- private static PlaceholderLayerClient mPlaceholderLayerClient;
+ private static PlaceholderLayerClient mPlaceholderLayerClient = null;
private static GeckoLayerClient mLayerClient;
private AboutHomeContent mAboutHomeContent;
private static AbsoluteLayout mPluginContainer;
- public String mLastTitle;
- public String mLastSnapshotUri;
- public String mLastViewport;
- public byte[] mLastScreen;
+ public String mLastTitle = null;
+ public String mLastSnapshotUri = null;
+ public String mLastViewport = null;
+ public byte[] mLastScreen = null;
+ private int mLastScreenFilesize;
public int mOwnActivityDepth = 0;
private boolean mRestoreSession = false;
private boolean mInitialized = false;
private static final String HANDLER_MSG_TYPE = "type";
private static final int HANDLER_MSG_TYPE_INITIALIZE = 1;
static class ExtraMenuItem implements MenuItem.OnMenuItemClickListener {
@@ -539,97 +541,101 @@ abstract public class GeckoApp
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mOwnActivityDepth > 0)
return; // we're showing one of our own activities and likely won't get paged out
if (outState == null)
outState = new Bundle();
- new SessionSnapshotRunnable(null).run();
-
- outState.putString(SAVED_STATE_TITLE, mLastTitle);
- outState.putString(SAVED_STATE_VIEWPORT, mLastViewport);
- outState.putByteArray(SAVED_STATE_SCREEN, mLastScreen);
- outState.putBoolean(SAVED_STATE_SESSION, true);
+ getSessionSnapshot(outState);
}
- public class SessionSnapshotRunnable implements Runnable {
- Tab mThumbnailTab;
- SessionSnapshotRunnable(Tab thumbnailTab) {
- mThumbnailTab = thumbnailTab;
+ /* This function saves the state of the current browsing session, which includes:
+ * - a screenshot of currently selected tab
+ * - the title of the last history entry for the currently selected tab
+ * - a boolean to indicate a saved state
+ */
+ private void getSessionSnapshot(final Bundle outState) {
+ final Tab tab = Tabs.getInstance().getSelectedTab();
+ if (tab == null)
+ return;
+
+ outState.putInt(SAVED_STATE_FILESIZE, mLastScreen.length);
+ if (mLastScreen != null) {
+ try {
+ FileOutputStream fos = new FileOutputStream(SAVED_STATE_SCREEN);
+ fos.write(mLastScreen);
+ fos.close();
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Failed to write last screenshot to file!");
+ }
+ }
+
+ HistoryEntry lastHistoryEntry = tab.getLastHistoryEntry();
+ if (lastHistoryEntry != null) {
+ outState.putString(SAVED_STATE_TITLE, lastHistoryEntry.mTitle);
+ outState.putString(SAVED_STATE_URI, lastHistoryEntry.mUri);
}
- public void run() {
- if (mLayerClient == null)
- return;
-
- synchronized (mLayerClient) {
- if (!Tabs.getInstance().isSelectedTab(mThumbnailTab))
- return;
-
- HistoryEntry lastHistoryEntry = mThumbnailTab.getLastHistoryEntry();
- if (lastHistoryEntry == null)
- return;
-
- ViewportMetrics viewportMetrics = mLayerClient.getGeckoViewportMetrics();
- // If we don't have viewport metrics, the screenshot won't be right so bail
- if (viewportMetrics == null)
- return;
-
+ if (getLayerController().getLayerClient() == mSoftwareLayerClient) {
+ ViewportMetrics viewportMetrics = mSoftwareLayerClient.getGeckoViewportMetrics();
+ if (viewportMetrics != null) {
+ outState.putString(SAVED_STATE_VIEWPORT, viewportMetrics.toJSON());
+ }
+ }
+
+ outState.putBoolean(SAVED_STATE_SESSION, true);
+ }
+
+ void getAsyncThumbnailForTab(final Tab tab, boolean forceBigScreenshot) {
+ // This function captures and processes a screenshot asynchronously for any tab, and can specify the screenshot to be thumbnail sized or not
+ if (!tab.hasLoaded()) {
+ if (!forceBigScreenshot) {
+ byte[] thumbnail = BrowserDB.getThumbnailForUrl(getContentResolver(), tab.getURL());
+ if (thumbnail != null) {
+ processThumbnail(tab, null, thumbnail);
+ }
+ }
+ return;
+ }
+
+ HistoryEntry lastHistoryEntry = tab.getLastHistoryEntry();
+ if (lastHistoryEntry != null) {
+ ViewportMetrics viewportMetrics = mSoftwareLayerClient.getGeckoViewportMetrics();
+ // If we don't have viewport metrics, the screenshot won't be right so bail
+ if (viewportMetrics != null && mLastTitle != null && mLastSnapshotUri != null) {
String viewportJSON = viewportMetrics.toJSON();
// If the title, uri and viewport haven't changed, the old screenshot is probably valid
// Ordering of .equals() below is important since mLast* variables may be null
if (viewportJSON.equals(mLastViewport) &&
lastHistoryEntry.mTitle.equals(mLastTitle) &&
lastHistoryEntry.mUri.equals(mLastSnapshotUri))
- return;
-
- mLastViewport = viewportJSON;
- mLastTitle = lastHistoryEntry.mTitle;
- mLastSnapshotUri = lastHistoryEntry.mUri;
- getAndProcessThumbnailForTab(mThumbnailTab, true);
+ return;
}
}
- }
-
- void getAndProcessThumbnailForTab(final Tab tab, boolean forceBigSceenshot) {
- boolean isSelectedTab = Tabs.getInstance().isSelectedTab(tab);
- final Bitmap bitmap = isSelectedTab ? mLayerClient.getBitmap() : null;
-
- if (bitmap != null) {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
- processThumbnail(tab, bitmap, bos.toByteArray());
- } else {
- if (tab.getState() == Tab.STATE_DELAYED) {
- byte[] thumbnail = BrowserDB.getThumbnailForUrl(getContentResolver(), tab.getURL());
- if (thumbnail != null)
- processThumbnail(tab, null, thumbnail);
- return;
- }
-
- mLastScreen = null;
- View view = mLayerController.getView();
- int sw = forceBigSceenshot ? view.getWidth() : tab.getMinScreenshotWidth();
- int sh = forceBigSceenshot ? view.getHeight(): tab.getMinScreenshotHeight();
- int dw = forceBigSceenshot ? sw : tab.getThumbnailWidth();
- int dh = forceBigSceenshot ? sh : tab.getThumbnailHeight();
- GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), sw, sh, dw, dh));
- }
+
+ int sw = forceBigScreenshot ? mSoftwareLayerClient.getWidth() : tab.getMinScreenshotWidth();
+ int sh = forceBigScreenshot ? mSoftwareLayerClient.getHeight(): tab.getMinScreenshotHeight();
+ int dw = forceBigScreenshot ? sw : tab.getThumbnailWidth();
+ int dh = forceBigScreenshot ? sh : tab.getThumbnailHeight();
+ GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), sw, sh, dw, dh));
}
void processThumbnail(Tab thumbnailTab, Bitmap bitmap, byte[] compressed) {
- if (Tabs.getInstance().isSelectedTab(thumbnailTab)) {
+ if (Tabs.getInstance().isSelectedTab(thumbnailTab)
+ && bitmap.getWidth() == mSoftwareLayerClient.getWidth()
+ && bitmap.getHeight() == mSoftwareLayerClient.getHeight()) {
if (compressed == null) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
compressed = bos.toByteArray();
}
mLastScreen = compressed;
+ return;
}
if ("about:home".equals(thumbnailTab.getURL())) {
thumbnailTab.updateThumbnail(null);
return;
}
try {
if (bitmap == null)
@@ -954,17 +960,16 @@ abstract public class GeckoApp
mMainHandler.post(new Runnable() {
public void run() {
if (sMenu != null)
sMenu.findItem(R.id.settings).setEnabled(true);
}
});
setLaunchState(GeckoApp.LaunchState.GeckoRunning);
GeckoAppShell.sendPendingEventsToGecko();
- connectGeckoLayerClient();
} else if (event.equals("ToggleChrome:Hide")) {
mMainHandler.post(new Runnable() {
public void run() {
mBrowserToolbar.hide();
}
});
} else if (event.equals("ToggleChrome:Show")) {
mMainHandler.post(new Runnable() {
@@ -1248,23 +1253,31 @@ abstract public class GeckoApp
tab.setState(success ? Tab.STATE_SUCCESS : Tab.STATE_ERROR);
mMainHandler.post(new Runnable() {
public void run() {
if (Tabs.getInstance().isSelectedTab(tab))
mBrowserToolbar.setProgressVisibility(false);
Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.STOP);
+
+ if (mPlaceholderLayerClient != null) {
+ connectGeckoLayerClient();
+ }
+
+ if (Tabs.getInstance().isSelectedTab(tab)
+ && !tab.getURL().equals("about:home")) {
+ GeckoAppShell.getHandler().postDelayed(new Runnable() {
+ public void run() {
+ getAsyncThumbnailForTab(tab,true);
+ }
+ }, 2000);
+ }
}
});
-
- if (Tabs.getInstance().isSelectedTab(tab)) {
- Runnable r = new SessionSnapshotRunnable(tab);
- GeckoAppShell.getHandler().postDelayed(r, 500);
- }
}
void handleShowToast(final String message, final String duration) {
mMainHandler.post(new Runnable() {
public void run() {
Toast toast;
if (duration.equals("long"))
toast = Toast.makeText(mAppContext, message, Toast.LENGTH_LONG);
@@ -1619,18 +1632,19 @@ abstract public class GeckoApp
}
GeckoAppShell.loadMozGlue();
mMainHandler = new GeckoAppHandler();
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onCreate");
if (savedInstanceState != null) {
mLastTitle = savedInstanceState.getString(SAVED_STATE_TITLE);
mLastViewport = savedInstanceState.getString(SAVED_STATE_VIEWPORT);
- mLastScreen = savedInstanceState.getByteArray(SAVED_STATE_SCREEN);
+ mLastScreenFilesize = savedInstanceState.getInt(SAVED_STATE_FILESIZE);
mRestoreSession = savedInstanceState.getBoolean(SAVED_STATE_SESSION);
+ mLastSnapshotUri = savedInstanceState.getString(SAVED_STATE_URI);
}
super.onCreate(savedInstanceState);
mOrientation = getResources().getConfiguration().orientation;
setContentView(R.layout.gecko_app);
@@ -1647,16 +1661,29 @@ abstract public class GeckoApp
mConnectivityFilter = new IntentFilter();
mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mConnectivityReceiver = new GeckoConnectivityReceiver();
}
private void initialize() {
mInitialized = true;
+ byte[] lastScreen = null;
+
+ if (mLastScreenFilesize > 0) {
+ try {
+ FileInputStream fis = new FileInputStream(SAVED_STATE_SCREEN);
+ lastScreen = new byte[mLastScreenFilesize];
+ fis.read(lastScreen, 0, mLastScreenFilesize);
+ fis.close();
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Failed to read last screenshot from file!");
+ }
+ }
+
Intent intent = getIntent();
String action = intent.getAction();
String args = intent.getStringExtra("args");
if (args != null && args.contains("-profile")) {
Pattern p = Pattern.compile("(?:-profile\\s*)(\\w*)(\\s*)");
Matcher m = p.matcher(args);
if (m.find()) {
mProfile = GeckoProfile.get(this, m.group(1));
@@ -1739,17 +1766,23 @@ abstract public class GeckoApp
* and zoom a cached screenshot of the previous page. This call will return null if
* there is no cached screenshot; in that case, we have no choice but to display a
* checkerboard.
*
* TODO: Fall back to a built-in screenshot of the Fennec Start page for a nice first-
* run experience, perhaps?
*/
mLayerController = new LayerController(this);
- mPlaceholderLayerClient = new PlaceholderLayerClient(mLayerController, mLastViewport);
+ if (lastScreen != null) {
+ mLastScreen = lastScreen;
+ mPlaceholderLayerClient = PlaceholderLayerClient.createInstance(this);
+ mLayerController.setLayerClient(mPlaceholderLayerClient);
+ } else {
+ mLayerController.setLayerClient(mSoftwareLayerClient);
+ }
mGeckoLayout.addView(mLayerController.getView(), 0);
}
mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container);
mDoorHangerPopup = new DoorHangerPopup(this);
mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup);
@@ -2028,19 +2061,16 @@ abstract public class GeckoApp
return uri;
}
@Override
public void onPause()
{
Log.i(LOGTAG, "pause");
- Runnable r = new SessionSnapshotRunnable(null);
- GeckoAppShell.getHandler().post(r);
-
GeckoAppShell.sendEventToGecko(GeckoEvent.createPauseEvent(mOwnActivityDepth));
// The user is navigating away from this activity, but nothing
// has come to the foreground yet; for Gecko, we may want to
// stop repainting, for example.
// Whatever we do here should be fast, because we're blocking
// the next activity from showing up until we finish.
@@ -2562,16 +2592,23 @@ abstract public class GeckoApp
Log.e(LOGTAG, "error building JSON arguments");
}
if (type == AwesomeBar.Type.ADD) {
Log.d(LOGTAG, "Sending message to Gecko: " + SystemClock.uptimeMillis() + " - Tab:Add");
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Add", args.toString()));
} else {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Load", args.toString()));
}
+
+ // If placeholder snapshot screenshot is being shown and we're trying to load a request
+ // (switching tab, loading new url, etc.) that is different from the snapshot's url, then
+ // switch to showing the GeckoSoftwareLayerClient to load this new url
+ if (mPlaceholderLayerClient != null && mLastSnapshotUri != null && !mLastSnapshotUri.equals(url)) {
+ connectGeckoLayerClient();
+ }
}
public void loadUrl(String url, AwesomeBar.Type type) {
loadRequest(url, type, null, false);
}
/**
* Open the url as a new tab, and mark the selected tab as its "parent".