Index: chrome/android/java_staging/src/org/chromium/chrome/browser/bookmark/SelectBookmarkFolderFragment.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/bookmark/SelectBookmarkFolderFragment.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/bookmark/SelectBookmarkFolderFragment.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d4bcc4268d979961c0b0ed2af61ecf09061cd5d3 |
--- /dev/null |
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/bookmark/SelectBookmarkFolderFragment.java |
@@ -0,0 +1,362 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.bookmark; |
+ |
+import static org.chromium.chrome.browser.ChromeBrowserProviderClient.INVALID_BOOKMARK_ID; |
+ |
+import android.content.Context; |
+import android.content.res.Resources; |
+import android.graphics.Typeface; |
+import android.graphics.drawable.BitmapDrawable; |
+import android.os.Bundle; |
+import android.util.Log; |
+import android.view.LayoutInflater; |
+import android.view.View; |
+import android.view.View.OnClickListener; |
+import android.view.ViewGroup; |
+import android.widget.ArrayAdapter; |
+import android.widget.Button; |
+import android.widget.ListView; |
+import android.widget.TextView; |
+ |
+import com.google.android.apps.chrome.R; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.chrome.browser.ChromeBrowserProvider.BookmarkNode; |
+import org.chromium.chrome.browser.ChromeBrowserProvider.Type; |
+import org.chromium.chrome.browser.ChromeBrowserProviderClient; |
+import org.chromium.chrome.browser.widget.TintedDrawable; |
+import org.chromium.sync.AndroidSyncSettings; |
+import org.chromium.ui.base.LocalizationUtils; |
+ |
+/** |
+ * The user interface for selecting a bookmark folder. |
+ */ |
+public class SelectBookmarkFolderFragment extends AsyncTaskFragment implements OnClickListener { |
+ /** |
+ * Defines the constants used as arguments to this fragment. |
+ * @see android.app.Fragment#setArguments(Bundle) |
+ */ |
+ private static class Arguments { |
+ private Arguments() {} |
+ |
+ public static final String ALLOW_FOLDER_ADDITION = "allowAdd"; |
+ public static final String FOLDER_ID_TO_SELECT = "selectedFolder"; |
+ public static final String IS_FOLDER = "isFolder"; |
+ } |
+ |
+ private Button mNewFolderButton; |
+ |
+ private ListView mFoldersList; |
+ private FolderListAdapter mFoldersAdapter; |
+ private TextView mEmptyFoldersView; |
+ |
+ private boolean mAllowFolderAddition; |
+ private long mFolderIdToSelect; |
+ // Used to determine if a bookmark's or a folder's parent is being changed. |
+ private boolean mIsFolder; |
+ |
+ private OnActionListener mActionListener; |
+ |
+ /** |
+ * The maximum depth that will be indented. Folders with a depth greater than this will |
+ * all appear at this same depth. |
+ */ |
+ private int mMaximumFolderIndentDepth = 8; |
+ |
+ /** |
+ * Constructs a new fragment in charge of handling bookmark folder selection. |
+ * @param allowFolderAddition Whether this fragment should allow additional folders to be added |
+ * as children. |
+ * @param folderIdToSelect The ID of the folder to select when shown initially. |
+ * @return The selection fragment. |
+ */ |
+ public static SelectBookmarkFolderFragment newInstance( |
+ boolean allowFolderAddition, long folderIdToSelect, boolean isFolder) { |
+ SelectBookmarkFolderFragment fragment = new SelectBookmarkFolderFragment(); |
+ Bundle arguments = new Bundle(); |
+ arguments.putBoolean(Arguments.ALLOW_FOLDER_ADDITION, allowFolderAddition); |
+ arguments.putLong(Arguments.FOLDER_ID_TO_SELECT, folderIdToSelect); |
+ arguments.putBoolean(Arguments.IS_FOLDER, isFolder); |
+ fragment.setArguments(arguments); |
+ return fragment; |
+ } |
+ |
+ /** |
+ * Retrieves the current action listener for this fragment. |
+ * Used by testing to intercept calls as a proxy. |
+ */ |
+ @VisibleForTesting |
+ public OnActionListener getOnActionListenerForTest() { |
+ return mActionListener; |
+ } |
+ |
+ /** |
+ * Sets the action listener for this fragment. |
+ * @param listener The listener to be set. |
+ */ |
+ public void setOnActionListener(OnActionListener listener) { |
+ mActionListener = listener; |
+ } |
+ |
+ @Override |
+ public void onCreate(Bundle savedInstanceState) { |
+ super.onCreate(savedInstanceState); |
+ |
+ mAllowFolderAddition = getArguments().getBoolean(Arguments.ALLOW_FOLDER_ADDITION); |
+ mFolderIdToSelect = getArguments().getLong( |
+ Arguments.FOLDER_ID_TO_SELECT, INVALID_BOOKMARK_ID); |
+ mIsFolder = getArguments().getBoolean(Arguments.IS_FOLDER); |
+ } |
+ |
+ @Override |
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, |
+ Bundle savedInstanceState) { |
+ View contentView = inflater.inflate(R.layout.select_bookmark_folder, container, false); |
+ |
+ mNewFolderButton = (Button) contentView.findViewById(R.id.new_folder_btn); |
+ if (mAllowFolderAddition) { |
+ mNewFolderButton.setOnClickListener(this); |
+ } else { |
+ mNewFolderButton.setVisibility(View.GONE); |
+ } |
+ |
+ mFoldersList = (ListView) contentView.findViewById(R.id.bookmark_folder_list); |
+ mEmptyFoldersView = (TextView) contentView.findViewById(R.id.empty_folders); |
+ mFoldersList.setEmptyView(mEmptyFoldersView); |
+ |
+ if (mFoldersAdapter != null) mFoldersList.setAdapter(mFoldersAdapter); |
+ return contentView; |
+ } |
+ |
+ @Override |
+ public void onActivityCreated(Bundle savedInstanceState) { |
+ super.onActivityCreated(savedInstanceState); |
+ |
+ if (mFoldersAdapter == null) { |
+ mFoldersAdapter = new FolderListAdapter(getActivity().getApplicationContext()); |
+ mFoldersList.setAdapter(mFoldersAdapter); |
+ |
+ long selectedFolder = INVALID_BOOKMARK_ID; |
+ if (savedInstanceState == null) { |
+ // Only use the folder ID passed in from the intent if the activity is not being |
+ // restored. During restoration, the ListView will handle reselecting the |
+ // previously selected entity and we do not want to override that with the initial |
+ // value. |
+ selectedFolder = mFolderIdToSelect; |
+ } |
+ |
+ loadAllFolders(selectedFolder); |
+ } else { |
+ if (!areFoldersLoaded()) { |
+ loadAllFolders(mFolderIdToSelect); |
+ } |
+ } |
+ |
+ mMaximumFolderIndentDepth = |
+ getResources().getInteger(R.integer.select_bookmark_folder_max_depth_indent); |
+ } |
+ |
+ @Override |
+ public void onClick(View v) { |
+ if (mActionListener == null) { |
+ Log.d(getClass().getName(), "No OnResultListener specified -- onClick == NoOp"); |
+ return; |
+ } |
+ if (v == mNewFolderButton) { |
+ long parentId = INVALID_BOOKMARK_ID; |
+ String parentName = null; |
+ for (int i = 0; i < mFoldersAdapter.getCount(); i++) { |
+ FolderListEntry selectedFolder = mFoldersAdapter.getItem(i); |
+ if (selectedFolder.mFolder.id() == mFolderIdToSelect) { |
+ parentId = selectedFolder.mFolder.id(); |
+ parentName = selectedFolder.mFolder.name(); |
+ } |
+ } |
+ mActionListener.triggerNewFolderCreation(parentId, parentName); |
+ } |
+ } |
+ |
+ /** |
+ * @return Whether or not the bookmark folders have been loaded asynchronously yet. |
+ */ |
+ @VisibleForTesting |
+ public boolean areFoldersLoaded() { |
+ return mFoldersAdapter.getCount() > 0; |
+ } |
+ |
+ private void loadAllFolders(long folderId) { |
+ if (isFragmentAsyncTaskRunning()) return; |
+ runFragmentAsyncTask(new LoadAllFoldersTask(folderId), |
+ getActivity().getString(R.string.loading_bookmark)); |
+ } |
+ |
+ private void handleLoadAllFolders(BookmarkNode result, long selectedFolderId, |
+ boolean syncEnabled) { |
+ if (getActivity() == null || getActivity().isFinishing()) return; |
+ |
+ mFoldersAdapter.clear(); |
+ if (result == null) { |
+ mEmptyFoldersView.setText(R.string.bookmark_folder_tree_error); |
+ } else { |
+ mEmptyFoldersView.setText(R.string.no_bookmark_folders); |
+ |
+ // The root node is just a placeholder, so directly add it's children. |
+ for (BookmarkNode child : result.children()) { |
+ if (!syncEnabled) { |
+ Type type = child.type(); |
+ if (type == Type.BOOKMARK_BAR || type == Type.OTHER_NODE) { |
+ continue; |
+ } |
+ } |
+ addFolderItem(child, 0, selectedFolderId); |
+ } |
+ } |
+ } |
+ |
+ private void addFolderItem(BookmarkNode folder, int depth, long selectedFolderId) { |
+ boolean isSelectedFolder = (folder.id() == selectedFolderId); |
+ mFoldersAdapter.add(new FolderListEntry(folder, depth, isSelectedFolder)); |
+ // Hiding sub folders will prevent current folder to be moved under a sub folder. |
+ if (folder.id() != selectedFolderId || !mIsFolder) { |
+ for (BookmarkNode child : folder.children()) { |
+ addFolderItem(child, depth + 1, selectedFolderId); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Data object used in the list adapter. |
+ */ |
+ private static class FolderListEntry { |
+ final BookmarkNode mFolder; |
+ final int mDepth; |
+ final boolean mIsSelectedFolder; |
+ |
+ FolderListEntry(BookmarkNode folder, int depth, boolean isSelectedFolder) { |
+ mFolder = folder; |
+ mDepth = depth; |
+ mIsSelectedFolder = isSelectedFolder; |
+ } |
+ |
+ @Override |
+ public String toString() { |
+ return mFolder.name(); |
+ } |
+ } |
+ |
+ /** |
+ * List adapter for the folder selection view. |
+ */ |
+ private class FolderListAdapter extends ArrayAdapter<FolderListEntry> { |
+ private final int mDefaultPaddingLeft; |
+ private final int mPaddingLeftInc; |
+ |
+ public FolderListAdapter(Context context) { |
+ super(context, R.layout.select_bookmark_folder_item); |
+ |
+ Resources resources = context.getResources(); |
+ mDefaultPaddingLeft = |
+ resources.getDimensionPixelSize(R.dimen.select_bookmark_folder_item_left); |
+ mPaddingLeftInc = |
+ resources.getDimensionPixelSize(R.dimen.select_bookmark_folder_item_inc_left); |
+ } |
+ |
+ @Override |
+ public View getView(int position, View convertView, ViewGroup parent) { |
+ TextView view = (TextView) super.getView(position, convertView, parent); |
+ final FolderListEntry entry = getItem(position); |
+ |
+ BitmapDrawable icon = TintedDrawable.constructTintedDrawable( |
+ getResources(), R.drawable.eb_others); |
+ ApiCompatibilityUtils.setCompoundDrawablesRelativeWithIntrinsicBounds( |
+ view, icon, null, null, null); |
+ |
+ // TODO: For folders that exceed the maximum depth, come up with a UI treatment to |
+ // give some indication of that. |
+ int paddingLeft = mDefaultPaddingLeft |
+ + Math.min(entry.mDepth, mMaximumFolderIndentDepth) * mPaddingLeftInc; |
+ if (LocalizationUtils.isLayoutRtl()) { |
+ view.setPadding(0, 0, paddingLeft, 0); |
+ } else { |
+ view.setPadding(paddingLeft, 0, 0, 0); |
+ } |
+ view.setTypeface(null, entry.mIsSelectedFolder ? Typeface.BOLD : Typeface.NORMAL); |
+ view.setBackgroundResource(R.drawable.btn_bg_holo); |
+ view.setOnClickListener(new OnClickListener() { |
+ @Override |
+ public void onClick(View v) { |
+ executeFolderSelection(entry.mFolder.id(), entry.mFolder.name()); |
+ } |
+ }); |
+ return view; |
+ } |
+ |
+ @Override |
+ public long getItemId(int position) { |
+ return getCount() > 0 ? getItem(position).mFolder.id() : -1; |
+ } |
+ |
+ @Override |
+ public boolean hasStableIds() { |
+ return true; |
+ } |
+ } |
+ |
+ /** |
+ * Select the folder to be used for the Add/Edit Fragment. |
+ * @param folderId Id of the selected folder. |
+ * @param folderName Name of the selected folder. |
+ */ |
+ public void executeFolderSelection(long folderId, String folderName) { |
+ getFragmentManager().popBackStackImmediate(); |
+ ((AddEditBookmarkFragment) getTargetFragment()).setParentFolderInfo( |
+ folderId, folderName); |
+ } |
+ |
+ /** |
+ * Asynchronously retrieves all the bookmark folders that the user can edit, |
+ * showing a progress dialog if the task takes too long. |
+ */ |
+ private class LoadAllFoldersTask extends FragmentAsyncTask { |
+ private final Context mContext; |
+ private final long mFolderId; |
+ private BookmarkNode mResult; |
+ |
+ LoadAllFoldersTask(long folderId) { |
+ mContext = getActivity().getApplicationContext(); |
+ mFolderId = folderId; |
+ } |
+ |
+ @Override |
+ protected void runBackgroundTask() { |
+ mResult = ChromeBrowserProviderClient.getEditableBookmarkFolderHierarchy(mContext); |
+ } |
+ |
+ @Override |
+ protected void onTaskFinished() { |
+ handleLoadAllFolders(mResult, mFolderId, AndroidSyncSettings.isSyncEnabled(mContext)); |
+ } |
+ |
+ @Override |
+ protected void setDependentUIEnabled(boolean enabled) { |
+ mNewFolderButton.setEnabled(enabled); |
+ } |
+ } |
+ |
+ /** |
+ * Listener to handle actions triggered by this fragment. |
+ */ |
+ public static interface OnActionListener { |
+ /** |
+ * Triggered when the user asks to create a new subfolder. |
+ * @param selectedFolderId The currently selected folder ID, which should be used as the |
+ * default parent of the newly added folder. |
+ * @param selectedFolderName The currently selected folder name. |
+ */ |
+ public void triggerNewFolderCreation(long selectedFolderId, String selectedFolderName); |
+ } |
+} |