|
|
@ -6,6 +6,7 @@ import android.content.Context; |
|
|
|
import android.content.DialogInterface; |
|
|
|
import android.content.DialogInterface; |
|
|
|
import android.content.Intent; |
|
|
|
import android.content.Intent; |
|
|
|
import android.os.Bundle; |
|
|
|
import android.os.Bundle; |
|
|
|
|
|
|
|
import android.support.annotation.NonNull; |
|
|
|
import android.support.annotation.Nullable; |
|
|
|
import android.support.annotation.Nullable; |
|
|
|
import android.support.v7.app.AlertDialog; |
|
|
|
import android.support.v7.app.AlertDialog; |
|
|
|
import android.util.Log; |
|
|
|
import android.util.Log; |
|
|
@ -26,14 +27,15 @@ import acr.browser.lightning.utils.FileUtils; |
|
|
|
import acr.browser.lightning.view.LightningView; |
|
|
|
import acr.browser.lightning.view.LightningView; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @author Stefano Pacifici |
|
|
|
* A manager singleton that holds all the {@link LightningView} |
|
|
|
* @date 2015/09/14 |
|
|
|
* and tracks the current tab. It handles creation, deletion, |
|
|
|
|
|
|
|
* restoration, state saving, and switching of tabs. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Singleton |
|
|
|
@Singleton |
|
|
|
public class TabsManager { |
|
|
|
public class TabsManager { |
|
|
|
|
|
|
|
|
|
|
|
private static final String TAG = TabsManager.class.getSimpleName(); |
|
|
|
private static final String TAG = TabsManager.class.getSimpleName(); |
|
|
|
private final List<LightningView> mWebViewList = new ArrayList<>(); |
|
|
|
private final List<LightningView> mTabList = new ArrayList<>(1); |
|
|
|
private LightningView mCurrentTab; |
|
|
|
private LightningView mCurrentTab; |
|
|
|
private static final String BUNDLE_KEY = "WEBVIEW_"; |
|
|
|
private static final String BUNDLE_KEY = "WEBVIEW_"; |
|
|
|
private static final String BUNDLE_STORAGE = "SAVED_TABS.parcel"; |
|
|
|
private static final String BUNDLE_STORAGE = "SAVED_TABS.parcel"; |
|
|
@ -53,12 +55,12 @@ public class TabsManager { |
|
|
|
* @param intent the intent that started the browser activity. |
|
|
|
* @param intent the intent that started the browser activity. |
|
|
|
* @param incognito whether or not we are in incognito mode. |
|
|
|
* @param incognito whether or not we are in incognito mode. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public synchronized void restoreTabsAndHandleIntent(final Activity activity, |
|
|
|
public synchronized void restoreTabsAndHandleIntent(@NonNull final Activity activity, |
|
|
|
final Intent intent, |
|
|
|
@Nullable final Intent intent, |
|
|
|
final boolean incognito) { |
|
|
|
final boolean incognito) { |
|
|
|
// If incognito, only create one tab, do not handle intent
|
|
|
|
// If incognito, only create one tab, do not handle intent
|
|
|
|
// in order to protect user privacy
|
|
|
|
// in order to protect user privacy
|
|
|
|
if (incognito && mWebViewList.isEmpty()) { |
|
|
|
if (incognito && mTabList.isEmpty()) { |
|
|
|
newTab(activity, null, true); |
|
|
|
newTab(activity, null, true); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
@ -67,7 +69,7 @@ public class TabsManager { |
|
|
|
if (intent != null) { |
|
|
|
if (intent != null) { |
|
|
|
url = intent.getDataString(); |
|
|
|
url = intent.getDataString(); |
|
|
|
} |
|
|
|
} |
|
|
|
mWebViewList.clear(); |
|
|
|
mTabList.clear(); |
|
|
|
mCurrentTab = null; |
|
|
|
mCurrentTab = null; |
|
|
|
if (mPreferenceManager.getRestoreLostTabsEnabled()) { |
|
|
|
if (mPreferenceManager.getRestoreLostTabsEnabled()) { |
|
|
|
restoreState(activity); |
|
|
|
restoreState(activity); |
|
|
@ -91,65 +93,76 @@ public class TabsManager { |
|
|
|
newTab(activity, url, false); |
|
|
|
newTab(activity, url, false); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (mWebViewList.size() == 0) { |
|
|
|
if (mTabList.size() == 0) { |
|
|
|
newTab(activity, null, false); |
|
|
|
newTab(activity, null, false); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Return the tab at the given position in tabs list, or null if position is not in tabs list |
|
|
|
* Return the tab at the given position in tabs list, or |
|
|
|
* range. |
|
|
|
* null if position is not in tabs list range. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param position the index in tabs list |
|
|
|
* @param position the index in tabs list |
|
|
|
* @return the corespondent {@link LightningView}, or null if the index is invalid |
|
|
|
* @return the corespondent {@link LightningView}, |
|
|
|
|
|
|
|
* or null if the index is invalid |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
public synchronized LightningView getTabAtPosition(final int position) { |
|
|
|
public synchronized LightningView getTabAtPosition(final int position) { |
|
|
|
if (position < 0 || position >= mWebViewList.size()) { |
|
|
|
if (position < 0 || position >= mTabList.size()) { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return mWebViewList.get(position); |
|
|
|
return mTabList.get(position); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Try to low memory pressure |
|
|
|
* Frees memory for each tab in the |
|
|
|
|
|
|
|
* manager. Note: this will only work |
|
|
|
|
|
|
|
* on API < KITKAT as on KITKAT onward |
|
|
|
|
|
|
|
* the WebViews manage their own |
|
|
|
|
|
|
|
* memory correctly. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public synchronized void freeMemory() { |
|
|
|
public synchronized void freeMemory() { |
|
|
|
for (LightningView tab : mWebViewList) { |
|
|
|
for (LightningView tab : mTabList) { |
|
|
|
tab.freeMemory(); |
|
|
|
tab.freeMemory(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Shutdown the manager |
|
|
|
* Shutdown the manager. This destroys |
|
|
|
|
|
|
|
* all tabs and clears the references |
|
|
|
|
|
|
|
* to those tabs. Current tab is also |
|
|
|
|
|
|
|
* released for garbage collection. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public synchronized void shutdown() { |
|
|
|
public synchronized void shutdown() { |
|
|
|
for (LightningView tab : mWebViewList) { |
|
|
|
for (LightningView tab : mTabList) { |
|
|
|
tab.onDestroy(); |
|
|
|
tab.onDestroy(); |
|
|
|
} |
|
|
|
} |
|
|
|
mWebViewList.clear(); |
|
|
|
mTabList.clear(); |
|
|
|
mCurrentTab = null; |
|
|
|
mCurrentTab = null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Resume the tabs |
|
|
|
* Reinitializes the preferences for |
|
|
|
|
|
|
|
* all the tabs in the list. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param context |
|
|
|
* @param context the context needed |
|
|
|
|
|
|
|
* to initialize the preferences. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public synchronized void resume(final Context context) { |
|
|
|
public synchronized void resume(@NonNull final Context context) { |
|
|
|
for (LightningView tab : mWebViewList) { |
|
|
|
for (LightningView tab : mTabList) { |
|
|
|
tab.initializePreferences(context); |
|
|
|
tab.initializePreferences(context); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Forward network connection status to the webviews. |
|
|
|
* Forwards network connection status to the WebViews. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param isConnected |
|
|
|
* @param isConnected whether there is a network |
|
|
|
|
|
|
|
* connection or not. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public synchronized void notifyConnectionStatus(final boolean isConnected) { |
|
|
|
public synchronized void notifyConnectionStatus(final boolean isConnected) { |
|
|
|
for (LightningView tab : mWebViewList) { |
|
|
|
for (LightningView tab : mTabList) { |
|
|
|
final WebView webView = tab.getWebView(); |
|
|
|
final WebView webView = tab.getWebView(); |
|
|
|
if (webView != null) { |
|
|
|
if (webView != null) { |
|
|
|
webView.setNetworkAvailable(isConnected); |
|
|
|
webView.setNetworkAvailable(isConnected); |
|
|
@ -158,38 +171,45 @@ public class TabsManager { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @return The number of currently opened tabs |
|
|
|
* The current number of tabs in the manager. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @return the number of tabs in the list. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public synchronized int size() { |
|
|
|
public synchronized int size() { |
|
|
|
return mWebViewList.size(); |
|
|
|
return mTabList.size(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Create and return a new tab. The tab is automatically added to the tabs list. |
|
|
|
* Create and return a new tab. The tab is |
|
|
|
|
|
|
|
* automatically added to the tabs list. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param activity |
|
|
|
* @param activity the activity needed to create the tab. |
|
|
|
* @param url |
|
|
|
* @param url the URL to initialize the tab with. |
|
|
|
* @param isIncognito |
|
|
|
* @param isIncognito whether the tab is an incognito |
|
|
|
* @return |
|
|
|
* tab or not. |
|
|
|
|
|
|
|
* @return a valid initialized tab. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public synchronized LightningView newTab(final Activity activity, |
|
|
|
@NonNull |
|
|
|
final String url, |
|
|
|
public synchronized LightningView newTab(@NonNull final Activity activity, |
|
|
|
|
|
|
|
@Nullable final String url, |
|
|
|
final boolean isIncognito) { |
|
|
|
final boolean isIncognito) { |
|
|
|
final LightningView tab = new LightningView(activity, url, isIncognito); |
|
|
|
final LightningView tab = new LightningView(activity, url, isIncognito); |
|
|
|
mWebViewList.add(tab); |
|
|
|
mTabList.add(tab); |
|
|
|
return tab; |
|
|
|
return tab; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Remove a tab and return its reference or null if the position is not in tabs range |
|
|
|
* Removes a tab from the list and destroys the tab. |
|
|
|
|
|
|
|
* If the tab removed is the current tab, the reference |
|
|
|
|
|
|
|
* to the current tab will be nullified. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param position The position of the tab to remove |
|
|
|
* @param position The position of the tab to remove. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private synchronized void removeTab(final int position) { |
|
|
|
private synchronized void removeTab(final int position) { |
|
|
|
if (position >= mWebViewList.size()) { |
|
|
|
if (position >= mTabList.size()) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
final LightningView tab = mWebViewList.remove(position); |
|
|
|
final LightningView tab = mTabList.remove(position); |
|
|
|
if (mCurrentTab == tab) { |
|
|
|
if (mCurrentTab == tab) { |
|
|
|
mCurrentTab = null; |
|
|
|
mCurrentTab = null; |
|
|
|
} |
|
|
|
} |
|
|
@ -197,6 +217,13 @@ public class TabsManager { |
|
|
|
Log.d(Constants.TAG, tab.toString()); |
|
|
|
Log.d(Constants.TAG, tab.toString()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Deletes a tab from the manager. If the tab |
|
|
|
|
|
|
|
* being deleted is the current tab, this method |
|
|
|
|
|
|
|
* will switch the current tab to a new valid tab. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param position the position of the tab to delete. |
|
|
|
|
|
|
|
*/ |
|
|
|
public synchronized void deleteTab(int position) { |
|
|
|
public synchronized void deleteTab(int position) { |
|
|
|
final LightningView currentTab = getCurrentTab(); |
|
|
|
final LightningView currentTab = getCurrentTab(); |
|
|
|
int current = positionOf(currentTab); |
|
|
|
int current = positionOf(currentTab); |
|
|
@ -219,18 +246,24 @@ public class TabsManager { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Return the position of the given tab. |
|
|
|
* Return the position of the given tab. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param tab the tab to look for |
|
|
|
* @param tab the tab to look for. |
|
|
|
* @return the position of the tab or -1 if the tab is not in the list |
|
|
|
* @return the position of the tab or -1 |
|
|
|
|
|
|
|
* if the tab is not in the list. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public synchronized int positionOf(final LightningView tab) { |
|
|
|
public synchronized int positionOf(final LightningView tab) { |
|
|
|
return mWebViewList.indexOf(tab); |
|
|
|
return mTabList.indexOf(tab); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Saves the state of the current WebViews, |
|
|
|
|
|
|
|
* to a bundle which is then stored in persistent |
|
|
|
|
|
|
|
* storage and can be unparceled. |
|
|
|
|
|
|
|
*/ |
|
|
|
public void saveState() { |
|
|
|
public void saveState() { |
|
|
|
Bundle outState = new Bundle(ClassLoader.getSystemClassLoader()); |
|
|
|
Bundle outState = new Bundle(ClassLoader.getSystemClassLoader()); |
|
|
|
Log.d(Constants.TAG, "Saving tab state"); |
|
|
|
Log.d(Constants.TAG, "Saving tab state"); |
|
|
|
for (int n = 0; n < mWebViewList.size(); n++) { |
|
|
|
for (int n = 0; n < mTabList.size(); n++) { |
|
|
|
LightningView tab = mWebViewList.get(n); |
|
|
|
LightningView tab = mTabList.get(n); |
|
|
|
Bundle state = new Bundle(ClassLoader.getSystemClassLoader()); |
|
|
|
Bundle state = new Bundle(ClassLoader.getSystemClassLoader()); |
|
|
|
if (tab.getWebView() != null) { |
|
|
|
if (tab.getWebView() != null) { |
|
|
|
tab.getWebView().saveState(state); |
|
|
|
tab.getWebView().saveState(state); |
|
|
@ -240,7 +273,16 @@ public class TabsManager { |
|
|
|
FileUtils.writeBundleToStorage(mApp, outState, BUNDLE_STORAGE); |
|
|
|
FileUtils.writeBundleToStorage(mApp, outState, BUNDLE_STORAGE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void restoreState(Activity activity) { |
|
|
|
/** |
|
|
|
|
|
|
|
* Restores the previously saved tabs from the |
|
|
|
|
|
|
|
* bundle stored in peristent file storage. |
|
|
|
|
|
|
|
* It will create new tabs for each tab saved |
|
|
|
|
|
|
|
* and will delete the saved instance file when |
|
|
|
|
|
|
|
* restoration is complete. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param activity the Activity needed to create tabs. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void restoreState(@NonNull Activity activity) { |
|
|
|
Bundle savedState = FileUtils.readBundleFromStorage(mApp, BUNDLE_STORAGE); |
|
|
|
Bundle savedState = FileUtils.readBundleFromStorage(mApp, BUNDLE_STORAGE); |
|
|
|
if (savedState != null) { |
|
|
|
if (savedState != null) { |
|
|
|
Log.d(Constants.TAG, "Restoring previous WebView state now"); |
|
|
|
Log.d(Constants.TAG, "Restoring previous WebView state now"); |
|
|
@ -256,9 +298,10 @@ public class TabsManager { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Return the {@link WebView} associated to the current tab, or null if there is no current tab |
|
|
|
* Return the {@link WebView} associated to the current tab, |
|
|
|
|
|
|
|
* or null if there is no current tab. |
|
|
|
* |
|
|
|
* |
|
|
|
* @return a {@link WebView} or null |
|
|
|
* @return a {@link WebView} or null if there is no current tab. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
public synchronized WebView getCurrentWebView() { |
|
|
|
public synchronized WebView getCurrentWebView() { |
|
|
@ -266,9 +309,11 @@ public class TabsManager { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* TODO We should remove also this, but probably not |
|
|
|
* Return the current {@link LightningView} or null if |
|
|
|
|
|
|
|
* no current tab has been set. |
|
|
|
* |
|
|
|
* |
|
|
|
* @return |
|
|
|
* @return a {@link LightningView} or null if there |
|
|
|
|
|
|
|
* is no current tab. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
public synchronized LightningView getCurrentTab() { |
|
|
|
public synchronized LightningView getCurrentTab() { |
|
|
@ -276,19 +321,18 @@ public class TabsManager { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Switch the current tab to the one at the given position. It returns the selected. After this |
|
|
|
* Switch the current tab to the one at the given position. |
|
|
|
* call {@link TabsManager#getCurrentTab()} return the same reference returned by this method if |
|
|
|
* It returns the selected tab that has been switced to. |
|
|
|
* position is valid. |
|
|
|
|
|
|
|
* |
|
|
|
* |
|
|
|
* @return the selected tab or null if position is out of tabs range |
|
|
|
* @return the selected tab or null if position is out of tabs range. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
public synchronized LightningView switchToTab(final int position) { |
|
|
|
public synchronized LightningView switchToTab(final int position) { |
|
|
|
if (position < 0 || position >= mWebViewList.size()) { |
|
|
|
if (position < 0 || position >= mTabList.size()) { |
|
|
|
Log.e(TAG, "Returning a null LightningView requested for position: " + position); |
|
|
|
Log.e(TAG, "Returning a null LightningView requested for position: " + position); |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
final LightningView tab = mWebViewList.get(position); |
|
|
|
final LightningView tab = mTabList.get(position); |
|
|
|
if (tab != null) { |
|
|
|
if (tab != null) { |
|
|
|
mCurrentTab = tab; |
|
|
|
mCurrentTab = tab; |
|
|
|
} |
|
|
|
} |
|
|
|