Bug 1060544 - Part 1: allow persisting/restoring home panel state r=liuche

All home panel state is currently discarded when you navigate to another page.
We want to be able to restore state (e.g. to return to the currently opened bookmarks folder,
instead of returning to the root of the bookmarks folder). This commit adds storage and a framework
to allow homepanels to persist data.

MozReview-Commit-ID: GWDX7UZrIZs

--HG--
extra : rebase_source : d0959196e14496b7970ad14df39937c67013624f
This commit is contained in:
Andrzej Hunt 2016-05-09 09:12:28 -07:00
Родитель c403e8c457
Коммит a3be820a38
5 изменённых файлов: 141 добавлений и 16 удалений

Просмотреть файл

@ -43,6 +43,7 @@ import org.mozilla.gecko.home.HomeBanner;
import org.mozilla.gecko.home.HomeConfig;
import org.mozilla.gecko.home.HomeConfig.PanelType;
import org.mozilla.gecko.home.HomeConfigPrefsBackend;
import org.mozilla.gecko.home.HomeFragment;
import org.mozilla.gecko.home.HomePager;
import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
@ -2312,7 +2313,7 @@ public class BrowserApp extends GeckoApp
if (isUserSearchTerm && SwitchBoard.isInExperiment(getContext(), Experiments.SEARCH_TERM)) {
showBrowserSearchAfterAnimation(animator);
} else {
showHomePagerWithAnimator(panelId, animator);
showHomePagerWithAnimator(panelId, null, animator);
}
animator.start();
@ -2503,14 +2504,20 @@ public class BrowserApp extends GeckoApp
return;
}
// History will only store that we were visiting about:home, however the specific panel
// isn't stored. (We are able to navigate directly to homepanels using an about:home?panel=...
// URL, but the reverse doesn't apply: manually switching panels doesn't update the URL.)
// Hence we need to restore the panel, in addition to panel state, here.
if (isAboutHome(tab)) {
String panelId = AboutPages.getPanelIdFromAboutHomeUrl(tab.getURL());
Bundle panelRestoreData = null;
if (panelId == null) {
// No panel was specified in the URL. Try loading the most recent
// home panel for this tab.
panelId = tab.getMostRecentHomePanel();
panelRestoreData = tab.getMostRecentHomePanelData();
}
showHomePager(panelId);
showHomePager(panelId, panelRestoreData);
if (mDynamicToolbar.isEnabled()) {
mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
@ -2595,14 +2602,14 @@ public class BrowserApp extends GeckoApp
mHomePagerContainer.setVisibility(View.VISIBLE);
}
private void showHomePager(String panelId) {
showHomePagerWithAnimator(panelId, null);
private void showHomePager(String panelId, Bundle panelRestoreData) {
showHomePagerWithAnimator(panelId, panelRestoreData, null);
}
private void showHomePagerWithAnimator(String panelId, PropertyAnimator animator) {
private void showHomePagerWithAnimator(String panelId, Bundle panelRestoreData, PropertyAnimator animator) {
if (isHomePagerVisible()) {
// Home pager already visible, make sure it shows the correct panel.
mHomePager.showPanel(panelId);
mHomePager.showPanel(panelId, panelRestoreData);
return;
}
@ -2635,6 +2642,17 @@ public class BrowserApp extends GeckoApp
}
});
// Set this listener to persist restore data (via the Tab) every time panel state changes.
mHomePager.setPanelStateChangelistener(new HomeFragment.PanelStateChangeListener() {
@Override
public void onStateChanged(Bundle bundle) {
final Tab currentTab = Tabs.getInstance().getSelectedTab();
if (currentTab != null) {
currentTab.setMostRecentHomePanelData(bundle);
}
}
});
// Don't show the banner in guest mode.
if (!Restrictions.isUserRestricted()) {
final ViewStub homeBannerStub = (ViewStub) findViewById(R.id.home_banner_stub);
@ -2655,7 +2673,9 @@ public class BrowserApp extends GeckoApp
mHomePagerContainer.setVisibility(View.VISIBLE);
mHomePager.load(getSupportLoaderManager(),
getSupportFragmentManager(),
panelId, animator);
panelId,
panelRestoreData,
animator);
// Hide the web content so it cannot be focused by screen readers.
hideWebContentOnPropertyAnimationEnd(animator);
@ -2811,7 +2831,8 @@ public class BrowserApp extends GeckoApp
// To prevent overdraw, the HomePager is hidden when BrowserSearch is displayed:
// reverse that.
showHomePager(Tabs.getInstance().getSelectedTab().getMostRecentHomePanel());
showHomePager(Tabs.getInstance().getSelectedTab().getMostRecentHomePanel(),
Tabs.getInstance().getSelectedTab().getMostRecentHomePanelData());
mBrowserSearchContainer.setVisibility(View.INVISIBLE);

Просмотреть файл

@ -36,6 +36,7 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@ -88,6 +89,13 @@ public class Tab {
private String mMostRecentHomePanel;
private boolean mShouldShowToolbarWithoutAnimationOnFirstSelection;
/*
* Bundle containing restore data for the panel referenced in mMostRecentHomePanel. This can be
* e.g. the most recent folder for the bookmarks panel, or any other state that should be
* persisted. This is then used e.g. when returning to homepanels via history.
*/
private Bundle mMostRecentHomePanelData;
private int mHistoryIndex;
private int mHistorySize;
private boolean mCanDoBack;
@ -219,8 +227,17 @@ public class Tab {
return mMostRecentHomePanel;
}
public Bundle getMostRecentHomePanelData() {
return mMostRecentHomePanelData;
}
public void setMostRecentHomePanel(String panelId) {
mMostRecentHomePanel = panelId;
mMostRecentHomePanelData = null;
}
public void setMostRecentHomePanelData(Bundle data) {
mMostRecentHomePanelData = data;
}
public Bitmap getThumbnailBitmap(int width, int height) {

Просмотреть файл

@ -18,29 +18,42 @@ import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HomeAdapter extends FragmentStatePagerAdapter {
private final Context mContext;
private final ArrayList<PanelInfo> mPanelInfos;
private final HashMap<String, Fragment> mPanels;
private final Map<String, HomeFragment> mPanels;
private final Map<String, Bundle> mRestoreBundles;
private boolean mCanLoadHint;
private OnAddPanelListener mAddPanelListener;
private HomeFragment.PanelStateChangeListener mPanelStateChangeListener = null;
public interface OnAddPanelListener {
void onAddPanel(String title);
}
public void setPanelStateChangeListener(HomeFragment.PanelStateChangeListener listener) {
mPanelStateChangeListener = listener;
for (Fragment fragment : mPanels.values()) {
((HomeFragment) fragment).setPanelStateChangeListener(listener);
}
}
public HomeAdapter(Context context, FragmentManager fm) {
super(fm);
mContext = context;
mCanLoadHint = HomeFragment.DEFAULT_CAN_LOAD_HINT;
mPanelInfos = new ArrayList<PanelInfo>();
mPanels = new HashMap<String, Fragment>();
mPanelInfos = new ArrayList<>();
mPanels = new HashMap<>();
mRestoreBundles = new HashMap<>();
}
@Override
@ -66,16 +79,40 @@ public class HomeAdapter extends FragmentStatePagerAdapter {
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
mPanels.put(mPanelInfos.get(position).getId(), fragment);
final HomeFragment fragment = (HomeFragment) super.instantiateItem(container, position);
fragment.setPanelStateChangeListener(mPanelStateChangeListener);
final String id = mPanelInfos.get(position).getId();
mPanels.put(id, fragment);
if (mRestoreBundles.containsKey(id)) {
fragment.restoreData(mRestoreBundles.get(id));
mRestoreBundles.remove(id);
}
return fragment;
}
public void setRestoreData(int position, Bundle data) {
final String id = mPanelInfos.get(position).getId();
final HomeFragment fragment = mPanels.get(id);
// We have no guarantees as to whether our desired fragment is instantiated yet: therefore
// we might need to either pass data to the fragment, or store it for later.
if (fragment != null) {
fragment.restoreData(data);
} else {
mRestoreBundles.put(id, data);
}
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
final String id = mPanelInfos.get(position).getId();
super.destroyItem(container, position, object);
mPanels.remove(mPanelInfos.get(position).getId());
mPanels.remove(id);
}
public void setOnAddPanelListener(OnAddPanelListener listener) {

Просмотреть файл

@ -77,6 +77,30 @@ public abstract class HomeFragment extends Fragment {
// Helper for opening a tab in the background.
private OnUrlOpenInBackgroundListener mUrlOpenInBackgroundListener;
protected PanelStateChangeListener mPanelStateChangeListener = null;
/**
* Listener to notify when a home panels' state has changed in a way that needs to be stored
* for history/restoration. E.g. when a folder is opened/closed in bookmarks.
*/
public interface PanelStateChangeListener {
/**
* @param bundle Data that should be persisted, and passed to this panel if restored at a later
* stage.
*/
void onStateChanged(Bundle bundle);
}
public void restoreData(Bundle data) {
// Do nothing
}
public void setPanelStateChangeListener(
PanelStateChangeListener mPanelStateChangeListener) {
this.mPanelStateChangeListener = mPanelStateChangeListener;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);

Просмотреть файл

@ -49,6 +49,7 @@ public class HomePager extends ViewPager {
private final ConfigLoaderCallbacks mConfigLoaderCallbacks;
private String mInitialPanelId;
private Bundle mRestoreData;
// Cached original ViewPager background.
private final Drawable mOriginalBackground;
@ -63,6 +64,8 @@ public class HomePager extends ViewPager {
// Listens for when the current panel changes.
private OnPanelChangeListener mPanelChangedListener;
private HomeFragment.PanelStateChangeListener mPanelStateChangeListener;
// This is mostly used by UI tests to easily fetch
// specific list views at runtime.
public static final String LIST_TAG_HISTORY = "history";
@ -197,11 +200,12 @@ public class HomePager extends ViewPager {
*
* @param fm FragmentManager for the adapter
*/
public void load(LoaderManager lm, FragmentManager fm, String panelId, PropertyAnimator animator) {
public void load(LoaderManager lm, FragmentManager fm, String panelId, Bundle restoreData, PropertyAnimator animator) {
mLoadState = LoadState.LOADING;
mVisible = true;
mInitialPanelId = panelId;
mRestoreData = restoreData;
// Update the home banner message each time the HomePager is loaded.
if (mHomeBanner != null) {
@ -213,6 +217,7 @@ public class HomePager extends ViewPager {
final HomeAdapter adapter = new HomeAdapter(mContext, fm);
adapter.setOnAddPanelListener(mAddPanelListener);
adapter.setPanelStateChangeListener(mPanelStateChangeListener);
adapter.setCanLoadHint(true);
setAdapter(adapter);
@ -281,6 +286,10 @@ public class HomePager extends ViewPager {
}
}
private void restorePanelData(int item, Bundle data) {
((HomeAdapter) getAdapter()).setRestoreData(item, data);
}
/**
* Shows a home panel. If the given panelId is null,
* the default panel will be shown. No action will be taken if:
@ -292,7 +301,7 @@ public class HomePager extends ViewPager {
*
* @param panelId of the home panel to be shown.
*/
public void showPanel(String panelId) {
public void showPanel(String panelId, Bundle restoreData) {
if (!mVisible) {
return;
}
@ -300,6 +309,7 @@ public class HomePager extends ViewPager {
switch (mLoadState) {
case LOADING:
mInitialPanelId = panelId;
mRestoreData = restoreData;
break;
case LOADED:
@ -310,6 +320,9 @@ public class HomePager extends ViewPager {
if (position > -1) {
setCurrentItem(position);
if (restoreData != null) {
restorePanelData(position, restoreData);
}
}
break;
@ -420,6 +433,10 @@ public class HomePager extends ViewPager {
final int itemPosition = (mInitialPanelId == null) ? -1 : adapter.getItemPosition(mInitialPanelId);
if (itemPosition > -1) {
setCurrentItem(itemPosition, false);
if (mRestoreData != null) {
restorePanelData(itemPosition, mRestoreData);
mRestoreData = null; // Release data since it's no longer needed
}
mInitialPanelId = null;
} else {
setCurrentItem(mDefaultPageIndex, false);
@ -441,6 +458,15 @@ public class HomePager extends ViewPager {
mPanelChangedListener = listener;
}
public void setPanelStateChangeListener(HomeFragment.PanelStateChangeListener listener) {
mPanelStateChangeListener = listener;
HomeAdapter adapter = (HomeAdapter) getAdapter();
if (adapter != null) {
adapter.setPanelStateChangeListener(listener);
}
}
/**
* Notify listeners of newly selected panel.
*