michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.home; michael@0: michael@0: import java.util.List; michael@0: michael@0: import org.mozilla.gecko.R; michael@0: import org.mozilla.gecko.db.BrowserContract.Bookmarks; michael@0: import org.mozilla.gecko.db.BrowserDB; michael@0: import org.mozilla.gecko.home.BookmarksListAdapter.FolderInfo; michael@0: import org.mozilla.gecko.home.BookmarksListAdapter.OnRefreshFolderListener; michael@0: import org.mozilla.gecko.home.BookmarksListAdapter.RefreshType; michael@0: import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; michael@0: michael@0: import android.app.Activity; michael@0: import android.content.Context; michael@0: import android.content.res.Configuration; michael@0: import android.database.Cursor; michael@0: import android.os.Bundle; michael@0: import android.support.v4.app.LoaderManager.LoaderCallbacks; michael@0: import android.support.v4.content.Loader; michael@0: import android.view.LayoutInflater; michael@0: import android.view.View; michael@0: import android.view.ViewGroup; michael@0: import android.view.ViewStub; michael@0: import android.widget.ImageView; michael@0: import android.widget.TextView; michael@0: michael@0: /** michael@0: * A page in about:home that displays a ListView of bookmarks. michael@0: */ michael@0: public class BookmarksPanel extends HomeFragment { michael@0: public static final String LOGTAG = "GeckoBookmarksPanel"; michael@0: michael@0: // Cursor loader ID for list of bookmarks. michael@0: private static final int LOADER_ID_BOOKMARKS_LIST = 0; michael@0: michael@0: // Information about the target bookmarks folder. michael@0: private static final String BOOKMARKS_FOLDER_INFO = "folder_info"; michael@0: michael@0: // Refresh type for folder refreshing loader. michael@0: private static final String BOOKMARKS_REFRESH_TYPE = "refresh_type"; michael@0: michael@0: // List of bookmarks. michael@0: private BookmarksListView mList; michael@0: michael@0: // Adapter for list of bookmarks. michael@0: private BookmarksListAdapter mListAdapter; michael@0: michael@0: // Adapter's parent stack. michael@0: private List mSavedParentStack; michael@0: michael@0: // Reference to the View to display when there are no results. michael@0: private View mEmptyView; michael@0: michael@0: // Callback for cursor loaders. michael@0: private CursorLoaderCallbacks mLoaderCallbacks; michael@0: michael@0: @Override michael@0: public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { michael@0: final View view = inflater.inflate(R.layout.home_bookmarks_panel, container, false); michael@0: michael@0: mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list); michael@0: michael@0: mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() { michael@0: @Override michael@0: public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) { michael@0: final int type = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE)); michael@0: if (type == Bookmarks.TYPE_FOLDER) { michael@0: // We don't show a context menu for folders michael@0: return null; michael@0: } michael@0: final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id); michael@0: info.url = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.URL)); michael@0: info.title = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.TITLE)); michael@0: info.bookmarkId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID)); michael@0: return info; michael@0: } michael@0: }); michael@0: michael@0: return view; michael@0: } michael@0: michael@0: @Override michael@0: public void onViewCreated(View view, Bundle savedInstanceState) { michael@0: super.onViewCreated(view, savedInstanceState); michael@0: michael@0: OnUrlOpenListener listener = null; michael@0: try { michael@0: listener = (OnUrlOpenListener) getActivity(); michael@0: } catch (ClassCastException e) { michael@0: throw new ClassCastException(getActivity().toString() michael@0: + " must implement HomePager.OnUrlOpenListener"); michael@0: } michael@0: michael@0: mList.setTag(HomePager.LIST_TAG_BOOKMARKS); michael@0: mList.setOnUrlOpenListener(listener); michael@0: michael@0: registerForContextMenu(mList); michael@0: } michael@0: michael@0: @Override michael@0: public void onActivityCreated(Bundle savedInstanceState) { michael@0: super.onActivityCreated(savedInstanceState); michael@0: michael@0: final Activity activity = getActivity(); michael@0: michael@0: // Setup the list adapter. michael@0: mListAdapter = new BookmarksListAdapter(activity, null, mSavedParentStack); michael@0: mListAdapter.setOnRefreshFolderListener(new OnRefreshFolderListener() { michael@0: @Override michael@0: public void onRefreshFolder(FolderInfo folderInfo, RefreshType refreshType) { michael@0: // Restart the loader with folder as the argument. michael@0: Bundle bundle = new Bundle(); michael@0: bundle.putParcelable(BOOKMARKS_FOLDER_INFO, folderInfo); michael@0: bundle.putParcelable(BOOKMARKS_REFRESH_TYPE, refreshType); michael@0: getLoaderManager().restartLoader(LOADER_ID_BOOKMARKS_LIST, bundle, mLoaderCallbacks); michael@0: } michael@0: }); michael@0: mList.setAdapter(mListAdapter); michael@0: michael@0: // Create callbacks before the initial loader is started. michael@0: mLoaderCallbacks = new CursorLoaderCallbacks(); michael@0: loadIfVisible(); michael@0: } michael@0: michael@0: @Override michael@0: public void onDestroyView() { michael@0: mList = null; michael@0: mListAdapter = null; michael@0: mEmptyView = null; michael@0: super.onDestroyView(); michael@0: } michael@0: michael@0: @Override michael@0: public void onConfigurationChanged(Configuration newConfig) { michael@0: super.onConfigurationChanged(newConfig); michael@0: michael@0: // Reattach the fragment, forcing a reinflation of its view. michael@0: // We use commitAllowingStateLoss() instead of commit() here to avoid michael@0: // an IllegalStateException. If the phone is rotated while Fennec michael@0: // is in the background, onConfigurationChanged() is fired. michael@0: // onConfigurationChanged() is called before onResume(), so michael@0: // using commit() would throw an IllegalStateException since it can't michael@0: // be used between the Activity's onSaveInstanceState() and michael@0: // onResume(). michael@0: if (isVisible()) { michael@0: // The parent stack is saved just so that the folder state can be michael@0: // restored on rotation. michael@0: mSavedParentStack = mListAdapter.getParentStack(); michael@0: michael@0: getFragmentManager().beginTransaction() michael@0: .detach(this) michael@0: .attach(this) michael@0: .commitAllowingStateLoss(); michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: protected void load() { michael@0: getLoaderManager().initLoader(LOADER_ID_BOOKMARKS_LIST, null, mLoaderCallbacks); michael@0: } michael@0: michael@0: private void updateUiFromCursor(Cursor c) { michael@0: if ((c == null || c.getCount() == 0) && mEmptyView == null) { michael@0: // Set empty page view. We delay this so that the empty view won't flash. michael@0: final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub); michael@0: mEmptyView = emptyViewStub.inflate(); michael@0: michael@0: final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image); michael@0: emptyIcon.setImageResource(R.drawable.icon_bookmarks_empty); michael@0: michael@0: final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text); michael@0: emptyText.setText(R.string.home_bookmarks_empty); michael@0: michael@0: mList.setEmptyView(mEmptyView); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Loader for the list for bookmarks. michael@0: */ michael@0: private static class BookmarksLoader extends SimpleCursorLoader { michael@0: private final FolderInfo mFolderInfo; michael@0: private final RefreshType mRefreshType; michael@0: michael@0: public BookmarksLoader(Context context) { michael@0: this(context, new FolderInfo(Bookmarks.FIXED_ROOT_ID), RefreshType.CHILD); michael@0: } michael@0: michael@0: public BookmarksLoader(Context context, FolderInfo folderInfo, RefreshType refreshType) { michael@0: super(context); michael@0: mFolderInfo = folderInfo; michael@0: mRefreshType = refreshType; michael@0: } michael@0: michael@0: @Override michael@0: public Cursor loadCursor() { michael@0: return BrowserDB.getBookmarksInFolder(getContext().getContentResolver(), mFolderInfo.id); michael@0: } michael@0: michael@0: @Override michael@0: public void onContentChanged() { michael@0: // Invalidate the cached value that keeps track of whether or michael@0: // not desktop bookmarks exist. michael@0: BrowserDB.invalidateCachedState(); michael@0: super.onContentChanged(); michael@0: } michael@0: michael@0: public FolderInfo getFolderInfo() { michael@0: return mFolderInfo; michael@0: } michael@0: michael@0: public RefreshType getRefreshType() { michael@0: return mRefreshType; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Loader callbacks for the LoaderManager of this fragment. michael@0: */ michael@0: private class CursorLoaderCallbacks implements LoaderCallbacks { michael@0: @Override michael@0: public Loader onCreateLoader(int id, Bundle args) { michael@0: if (args == null) { michael@0: return new BookmarksLoader(getActivity()); michael@0: } else { michael@0: FolderInfo folderInfo = (FolderInfo) args.getParcelable(BOOKMARKS_FOLDER_INFO); michael@0: RefreshType refreshType = (RefreshType) args.getParcelable(BOOKMARKS_REFRESH_TYPE); michael@0: return new BookmarksLoader(getActivity(), folderInfo, refreshType); michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onLoadFinished(Loader loader, Cursor c) { michael@0: BookmarksLoader bl = (BookmarksLoader) loader; michael@0: mListAdapter.swapCursor(c, bl.getFolderInfo(), bl.getRefreshType()); michael@0: updateUiFromCursor(c); michael@0: } michael@0: michael@0: @Override michael@0: public void onLoaderReset(Loader loader) { michael@0: if (mList != null) { michael@0: mListAdapter.swapCursor(null); michael@0: } michael@0: } michael@0: } michael@0: }