[WIP]
Bug 1232439 (972193) - Create base classes for use in bookmark edit dialogs.
BookmarkToolbarDialog creates just the toolbar; its subclass
BookmarkFolderTreeDialog additionally includes a FolderTreeView for displaying a
list of folders.
MozReview-Commit-ID: HxYcd9AB9OY
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/BookmarkFolderTreeDialog.java
@@ -0,0 +1,44 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.gecko.util.UIAsyncTask;
+
+import android.view.View;
+
+public abstract class BookmarkFolderTreeDialog extends BookmarkToolbarDialog {
+ protected abstract int currentFolderId();
+
+ protected void setupFolderTree(View parentView, boolean nestedScroll) {
+ final FolderTreeView folderTreeView = (FolderTreeView) parentView.findViewById(R.id.folder_chooser);
+
+ if (nestedScroll) {
+ // If we're nested inside another NestedScrollView then we need to set the following to
+ // get scrolling flings to work.
+ folderTreeView.setNestedScrollingEnabled(false);
+ }
+
+ final BrowserDB db = BrowserDB.from(getContext());
+ (new UIAsyncTask.WithoutParams<BrowserDB.BookmarkFolderTree>(ThreadUtils.getBackgroundHandler()) {
+ @Override
+ public BrowserDB.BookmarkFolderTree doInBackground() {
+ return db.getBookmarkFolders(getContext().getContentResolver());
+ }
+
+ @Override
+ public void onPostExecute(BrowserDB.BookmarkFolderTree folderTree) {
+ if (folderTree == null) {
+ return;
+ }
+
+ folderTreeView.setFolderTree(folderTree, currentFolderId());
+ folderTreeView.setFolderSelectedListener(BookmarkFolderTreeDialog.this);
+ }
+ }).execute();
+ }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/BookmarkToolbarDialog.java
@@ -0,0 +1,125 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+import org.mozilla.gecko.db.BrowserContract;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.annotation.MenuRes;
+import android.support.annotation.StringRes;
+import android.support.design.widget.TextInputLayout;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.Toolbar;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.MenuItem;
+import android.view.View;
+
+public abstract class BookmarkToolbarDialog extends DialogFragment
+ implements FolderTreeView.FolderSelectedListener {
+ protected static final String ARG_FOLDER_ID = "folder_id";
+ protected static final String ARG_FOLDER_NAME = "folder_name";
+
+ // The fake Dekstop folder isn't selectable, so use its ID as our invalid folder ID value (the
+ // desktop folder doesn't actually exist in the database, so attempting to use it in the
+ // database as a parent folder ID will crash on a failed foreign key constraint rather than
+ // leading to invalid parent data).
+ protected static int INVALID_FOLDER_ID = BrowserContract.Bookmarks.FAKE_DESKTOP_FOLDER_ID;
+
+ protected abstract @StringRes int toolbarTitle();
+
+ protected void setupToolbar(View parentView, @MenuRes Integer menu) {
+ final Toolbar toolbar = (Toolbar) parentView.findViewById(R.id.bookmarks_toolbar);
+ toolbar.setTitle(toolbarTitle());
+
+ toolbar.setNavigationIcon(R.drawable.tabs_panel_nav_back);
+ toolbar.setNavigationOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ // The back button cancels and returns.
+ getFragmentManager().popBackStack();
+ }
+ });
+
+ setupToolbarMenu(toolbar, menu);
+ }
+
+ private void setupToolbarMenu(Toolbar toolbar, @MenuRes Integer menu) {
+ if (menu == null) {
+ return;
+ }
+
+ toolbar.inflateMenu(menu);
+ toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ final int id = item.getItemId();
+ return toolbarMenuItemClicked(id);
+ }
+ });
+ }
+
+ /*
+ * @return true to consume the click and prevent others from executing.
+ */
+ protected boolean toolbarMenuItemClicked(int menuItemId) {
+ return false;
+ }
+
+ /**
+ * Pops the fragment back stack.
+ */
+ @Override
+ public void folderSelected(String folderName, int folderId) {
+ final Fragment targetFragment = getTargetFragment();
+ if (targetFragment == null) {
+ getFragmentManager().popBackStack();
+ return;
+ }
+
+ // Send on the selected folder data to this fragment's target fragment.
+ final Intent intent = new Intent();
+ intent.putExtra(ARG_FOLDER_NAME, folderName);
+ intent.putExtra(ARG_FOLDER_ID, folderId);
+ targetFragment.onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, intent);
+ getFragmentManager().popBackStack();
+ }
+
+ protected static class EmptyTextWatcher implements TextWatcher {
+ private final TextInputLayout textInputLayout;
+ private final String errorText;
+
+ EmptyTextWatcher(TextInputLayout textInputLayout, String errorText) {
+ this.textInputLayout = textInputLayout;
+ this.errorText = errorText;
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ final boolean fieldIsEmpty = (s.toString().trim().length() > 0);
+
+ if (fieldIsEmpty) {
+ // This completely removes the layout space otherwise taken up by the error message.
+ textInputLayout.setErrorEnabled(false);
+ // setErrorEnabled destroys or creates the error view, but doesn't set the error
+ // view's text; setError sets the error view's text (creating and displaying the
+ // view first if needed), but only if you're not setting the same error text you set
+ // last time. So set the error text to null here so that the next time we set the
+ // actual error text it will update (i.e. recreate) the error view's text.
+ textInputLayout.setError(null);
+ } else {
+ textInputLayout.setError(errorText);
+ }
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {}
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/EditBookmarkDialog.java
+++ b/mobile/android/base/java/org/mozilla/gecko/EditBookmarkDialog.java
@@ -10,39 +10,39 @@ import org.mozilla.gecko.db.BrowserContr
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UIAsyncTask;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
-import android.support.v4.app.DialogFragment;
+import android.support.annotation.StringRes;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
-import android.support.v7.widget.Toolbar;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputLayout;
-import android.text.Editable;
-import android.view.MenuItem;
-import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
/**
- * A dialog that allows editing a bookmarks url, title, or keywords
+ * A dialog that allows editing a bookmark's url, title, folder, or keywords.
* <p>
* Invoked by calling one of the {@link org.mozilla.gecko.EditBookmarkDialog#show}
* methods.
*/
-public class EditBookmarkDialog extends DialogFragment {
+public class EditBookmarkDialog extends BookmarkToolbarDialog {
private Bookmark mBookmark;
+ private ViewGroup rootView;
+ private EditText nameText;
+ private EditText locationText;
+ private EditText keywordText;
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mBookmark.saveToBundle(outState);
}
@Override
@@ -56,153 +56,146 @@ public class EditBookmarkDialog extends
/**
* A private struct to make it easier to pass bookmark data across threads
*/
private static class Bookmark {
private static final String SAVED_STATE_ID = "id";
private static final String SAVED_STATE_TITLE = "title";
private static final String SAVED_STATE_URL = "url";
+ private static final String SAVED_STATE_FOLDER_ID = "folder_id";
+ private static final String SAVED_STATE_FOLDER_NAME = "folder_name";
private static final String SAVED_STATE_KEYWORD = "keyword";
final int id;
final String title;
final String url;
+ int folderId;
+ String folderName;
final String keyword;
- public Bookmark(int aId, String aTitle, String aUrl, String aKeyword) {
+ public Bookmark(int aId, String aTitle, String aUrl, int aFolderId, String aFolderName, String aKeyword) {
id = aId;
title = aTitle;
url = aUrl;
+ folderId = aFolderId;
+ folderName = aFolderName;
keyword = aKeyword;
}
static Bookmark newFromBundle(Bundle bundle) {
return new Bookmark(bundle.getInt(SAVED_STATE_ID),
bundle.getString(SAVED_STATE_TITLE),
bundle.getString(SAVED_STATE_URL),
+ bundle.getInt(SAVED_STATE_FOLDER_ID),
+ bundle.getString(SAVED_STATE_FOLDER_NAME),
bundle.getString(SAVED_STATE_KEYWORD));
}
void saveToBundle(Bundle bundle) {
bundle.putInt(SAVED_STATE_ID, id);
bundle.putString(SAVED_STATE_TITLE, title);
bundle.putString(SAVED_STATE_URL, url);
bundle.putString(SAVED_STATE_KEYWORD, keyword);
}
}
- private class BookmarkUriWatcher implements TextWatcher {
- private TextInputLayout textInputLayout;
-
- BookmarkUriWatcher(TextInputLayout textInputLayout) {
- this.textInputLayout = textInputLayout;
+ private void toolbarSaveClicked() {
+ if (locationText.getText().toString().trim().isEmpty()) {
+ // locationText is already showing an error message when it is empty - we give it focus
+ // to make it obvious to the user that they can't save a bookmark without a URL.
+ locationText.requestFocus();
+ return;
}
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- final boolean fieldIsEmpty = (s.toString().trim().length() > 0);
+ (new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
+ @Override
+ public Void doInBackground() {
+ final String newName = nameText.getText().toString().trim();
+ final String newUrl = locationText.getText().toString().trim();
+ final String newKeyword = keywordText.getText().toString().trim();
+ final BrowserDB db = BrowserDB.from(getContext());
+ db.updateBookmark(getContext().getContentResolver(),
+ mBookmark.id, newUrl, newName, newKeyword, mBookmark.folderId);
+ return null;
+ }
+
+ @Override
+ public void onPostExecute(Void result) {
+ Snackbar.make(rootView, R.string.bookmark_updated, Snackbar.LENGTH_SHORT).show();
+ getFragmentManager().popBackStack();
+ }
+ }).execute();
+ }
- if (fieldIsEmpty) {
- // This completely removes the layout space otherwise taken up by the error message.
- textInputLayout.setErrorEnabled(false);
- // setErrorEnabled destroys or creates the error view, but doesn't set the error
- // view's text; setError sets the error view's text (creating and displaying the
- // view first if needed), but only if you're not setting the same error text you set
- // last time. So set the error text to null here so that the next time we set the
- // actual error text it will update (i.e. recreate) the error view's text.
- textInputLayout.setError(null);
- } else {
- textInputLayout.setError(getString(R.string.bookmark_edit_location_empty_error));
+ private void toolbarRemoveClicked() {
+ final BrowserDB db = BrowserDB.from(getContext());
+ (new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
+ @Override
+ public Void doInBackground() {
+ db.removeBookmarkWithID(getContext().getContentResolver(), mBookmark.id);
+ return null;
}
+
+ @Override
+ public void onPostExecute(Void result) {
+ Snackbar.make(rootView, R.string.bookmark_removed, Snackbar.LENGTH_SHORT).show();
+ getFragmentManager().popBackStack();
+ }
+ }).execute();
+ }
+
+ @Override
+ protected boolean toolbarMenuItemClicked(int menuItemId) {
+ if (menuItemId == R.id.save) {
+ toolbarSaveClicked();
+ return true;
+ } else if (menuItemId == R.id.bin) {
+ toolbarRemoveClicked();
+ return true;
}
- @Override
- public void afterTextChanged(Editable s) {}
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ return false;
+ }
+
+ @Override
+ protected @StringRes int toolbarTitle() {
+ return R.string.bookmark_edit_title;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
final View editView = inflater.inflate(R.layout.bookmark_edit, container, false);
- final EditText nameText = ((EditText) editView.findViewById(R.id.edit_bookmark_name));
- final EditText locationText = ((EditText) editView.findViewById(R.id.edit_bookmark_location));
- final EditText keywordText = ((EditText) editView.findViewById(R.id.edit_bookmark_keyword));
+ rootView = container;
+ nameText = ((EditText) editView.findViewById(R.id.edit_bookmark_name));
+ locationText = ((EditText) editView.findViewById(R.id.edit_bookmark_location));
+ keywordText = ((EditText) editView.findViewById(R.id.edit_bookmark_keyword));
+ final EditText folderText = ((EditText) editView.findViewById(R.id.edit_bookmark_folder));
- final Toolbar toolbar = (Toolbar) editView.findViewById(R.id.toolbar);
- toolbar.setTitle(R.string.bookmark_edit_title);
- toolbar.setNavigationIcon(R.drawable.tabs_panel_nav_back);
- toolbar.inflateMenu(R.menu.edit_bookmarks_menu);
+ setupToolbar(editView, R.menu.bookmarks_dialog_menu_save_and_remove);
// Insert text BEFORE the cursor - when the user opens the dialog we want the cursor to be at the end of the name
// field. We've already requested focus in the layout file using <requestFocus/>, however using setText
// results in the cursor staying at the start of the text, whereas insert(0,...) results in the text being
// inserted in front of the cursor.
nameText.getText().insert(0, mBookmark.title);
locationText.setText(mBookmark.url);
keywordText.setText(mBookmark.keyword);
- final BrowserDB db = BrowserDB.from(getContext());
- toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- int id = item.getItemId();
- if (id == R.id.bin) {
- (new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
- @Override
- public Void doInBackground() {
- db.removeBookmarkWithID(getContext().getContentResolver(), mBookmark.id);
- return null;
- }
-
- @Override
- public void onPostExecute(Void result) {
- Snackbar.make(container, R.string.bookmark_removed, Snackbar.LENGTH_SHORT).show();
- getFragmentManager().popBackStack();
- }
- }).execute();
- }
- return false;
+ folderText.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ // TODO: launch new folder chooser dialog.
}
});
- toolbar.setNavigationOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (locationText.getText().toString().trim().isEmpty()) {
- // locationText is already showing an error message when it is empty - we give it focus
- // to make it obvious to the user that they can't save a bookmark without a URL.
- locationText.requestFocus();
- return;
- }
-
- (new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
- @Override
- public Void doInBackground() {
- String newUrl = locationText.getText().toString().trim();
- String newKeyword = keywordText.getText().toString().trim();
-
- db.updateBookmark(getContext().getContentResolver(), mBookmark.id, newUrl, nameText.getText().toString().trim(), newKeyword);
- return null;
- }
-
- @Override
- public void onPostExecute(Void result) {
- Snackbar.make(container, R.string.bookmark_updated, Snackbar.LENGTH_SHORT).show();
- getFragmentManager().popBackStack();
- }
- }).execute();
- }
- });
-
- BookmarkUriWatcher locationTextWatcher = new BookmarkUriWatcher((TextInputLayout) locationText.getParent());
- locationText.addTextChangedListener(locationTextWatcher);
+ final TextInputLayout locationTextLayout = (TextInputLayout) editView.findViewById(R.id.edit_bookmark_location_layout);
+ final String emptyURLErrorText = getString(R.string.bookmark_edit_location_empty_error);
+ locationText.addTextChangedListener(new EmptyTextWatcher(locationTextLayout, emptyURLErrorText));
return editView;
}
/**
* Show the Edit bookmark dialog for a particular url. If the url is bookmarked multiple times
* this will just edit the first instance it finds.
*
@@ -221,19 +214,23 @@ public class EditBookmarkDialog extends
final Cursor cursor = db.getBookmarkForUrl(cr, url);
if (cursor == null) {
return null;
}
Bookmark bookmark = null;
try {
cursor.moveToFirst();
+ final int folderId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.PARENT));
+ final String folderName = db.getBookmarkFolderName(cr, folderId);
bookmark = new Bookmark(cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID)),
cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.TITLE)),
cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.URL)),
+ folderId,
+ folderName == null ? "" : folderName,
cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.KEYWORD)));
} finally {
cursor.close();
}
return bookmark;
}
@Override
@@ -245,13 +242,13 @@ public class EditBookmarkDialog extends
show(fm, bookmark);
}
}).execute();
}
private void show(final FragmentManager fm, final Bookmark bookmark) {
this.mBookmark = bookmark;
- FragmentTransaction transaction = fm.beginTransaction();
+ final FragmentTransaction transaction = fm.beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.add(android.R.id.content, this).addToBackStack(null).commitAllowingStateLoss();
}
}
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -70,26 +70,38 @@
<!ENTITY bookmark_added "Bookmark added">
<!-- Localization note (bookmark_already_added) : This string is
used as a label in a toast. It is the verb "to bookmark", not
the noun "a bookmark". -->
<!ENTITY bookmark_already_added "Already bookmarked">
<!ENTITY bookmark_removed "Bookmark removed">
<!ENTITY bookmark_updated "Bookmark updated">
<!ENTITY bookmark_options "Options">
-<!-- Localization note (selected_folder): Indicates which Bookmarks folder is currently selected;
- selected is an adjective and not a verb here. -->
+<!-- Localization note (selected_folder): Alternate text for accessibility; not UI visible.
+ Indicates which Bookmarks folder is currently selected (selected is an adjective and not a verb
+ here). -->
<!ENTITY bookmarks_selected_folder "Selected folder">
<!ENTITY screenshot_added_to_bookmarks "Screenshot added to bookmarks">
<!-- Localization note (screenshot_folder_label_in_bookmarks): We save links to screenshots
the user takes. The folder we store these links in is located in the bookmarks list
and is labeled by this String. -->
<!ENTITY screenshot_folder_label_in_bookmarks "Screenshots">
<!ENTITY readinglist_smartfolder_label_in_bookmarks "Reading List">
+<!-- Localization note (bookmark_dialog_save_and_return): Alternate text for accessibility; not UI
+ visible. Save the data input in the current dialog and return to the previous dialog. -->
+<!ENTITY bookmark_dialog_save_and_return "Save and return">
+<!ENTITY bookmark_dialog_folder "Folder">
+
+<!ENTITY bookmark_edit_title "Edit Bookmark">
+<!ENTITY bookmark_edit_name "Name">
+<!ENTITY bookmark_edit_location "Location">
+<!ENTITY bookmark_edit_keyword "Keyword">
+<!ENTITY bookmark_edit_location_empty_error "Bookmark location must contain a URL">
+
<!-- Localization note (bookmark_folder_items): The variable is replaced by the number of items
in the folder. -->
<!ENTITY bookmark_folder_items "&formatD; items">
<!ENTITY bookmark_folder_one_item "1 item">
<!ENTITY reader_saved_offline "Saved offline">
<!-- Localization note (reader_switch_to_bookmarks) : This
string is used as an action in a snackbar - it lets you
@@ -545,22 +557,16 @@ size. -->
<!ENTITY pref_compact_tabs "Compact tabs">
<!ENTITY pref_compact_tabs_summary2 "Arrange tabs in two columns in portrait mode">
<!-- Localization note (page_removed): This string appears in a toast message when
any page is removed frome about:home. This includes pages that are in history,
bookmarks, or reading list. -->
<!ENTITY page_removed "Page removed">
-<!ENTITY bookmark_edit_title "Edit Bookmark">
-<!ENTITY bookmark_edit_name "Name">
-<!ENTITY bookmark_edit_location "Location">
-<!ENTITY bookmark_edit_keyword "Keyword">
-<!ENTITY bookmark_edit_location_empty_error "Bookmark location must contain a URL">
-
<!-- Localization note (site_settings_*) : These strings are used in the "Site Settings"
dialog that appears after selecting the "Edit Site Settings" context menu item. -->
<!ENTITY site_settings_title3 "Site Settings">
<!ENTITY site_settings_cancel "Cancel">
<!ENTITY site_settings_clear "Clear">
<!-- Localization note : These strings are used as alternate text for accessibility.
They are not visible in the UI. -->
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -337,16 +337,18 @@ gbjar.sources += ['java/org/mozilla/geck
'activitystream/ActivityStream.java',
'adjust/AdjustBrowserAppDelegate.java',
'animation/AnimationUtils.java',
'animation/HeightChangeAnimation.java',
'animation/PropertyAnimator.java',
'animation/Rotate3DAnimation.java',
'animation/ViewHelper.java',
'ANRReporter.java',
+ 'BookmarkFolderTreeDialog.java',
+ 'BookmarkToolbarDialog.java',
'BootReceiver.java',
'BrowserApp.java',
'BrowserLocaleManager.java',
'cleanup/FileCleanupController.java',
'cleanup/FileCleanupService.java',
'CustomEditText.java',
'customtabs/CustomTabsActivity.java',
'customtabs/GeckoCustomTabsService.java',
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/bookmark_dialog_toolbar.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bookmarks_toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:background="?attr/colorPrimary"
+ android:minHeight="?attr/actionBarSize"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
--- a/mobile/android/base/resources/layout/bookmark_edit.xml
+++ b/mobile/android/base/resources/layout/bookmark_edit.xml
@@ -1,30 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
android:background="@android:color/white"
android:theme="@style/GeckoAlertDialog">
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?attr/colorPrimary"
- android:minHeight="?attr/actionBarSize"
- android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
- android:title="@string/bookmark_edit_title"/>
+ <include layout="@layout/bookmark_dialog_toolbar"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/scrollview"
android:layout_gravity="center_horizontal">
<LinearLayout android:layout_width="match_parent"
@@ -45,30 +37,49 @@
android:hint="@string/bookmark_edit_name">
<requestFocus/>
</android.support.design.widget.TextInputEditText>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:id="@+id/edit_bookmark_location_layout"
app:hintAnimationEnabled="true">
<android.support.design.widget.TextInputEditText
android:id="@+id/edit_bookmark_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:hint="@string/bookmark_edit_location"
android:inputType="textNoSuggestions"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ app:hintAnimationEnabled="false">
+
+ <!-- Use focusableInTouchMode="false" so that the text edit receives a click event
+ the first time it's clicked - otherwise the first tap only focuses the field
+ and you don't get a click event until the second tap. -->
+ <android.support.design.widget.TextInputEditText
+ android:id="@+id/edit_bookmark_folder"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/bookmark_dialog_folder"
+ android:focusableInTouchMode="false"
+ android:maxLines="1"
+ android:editable="false"/>
+ </android.support.design.widget.TextInputLayout>
+
+ <android.support.design.widget.TextInputLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
app:hintAnimationEnabled="true">
<android.support.design.widget.TextInputEditText
android:id="@+id/edit_bookmark_keyword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:hint="@string/bookmark_edit_keyword"/>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/menu/bookmarks_dialog_menu_save.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <item android:id="@+id/save"
+ android:icon="@drawable/img_check"
+ android:title="@string/bookmark_dialog_save_and_return"
+ app:showAsAction="ifRoom"/>
+
+</menu>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/menu/bookmarks_dialog_menu_save_and_remove.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <item android:id="@+id/bin"
+ android:icon="@android:drawable/ic_menu_delete"
+ android:title="@string/bookmark_remove"
+ app:showAsAction="ifRoom"/>
+
+ <item android:id="@+id/save"
+ android:icon="@drawable/img_check"
+ android:title="@string/bookmark_dialog_save_and_return"
+ app:showAsAction="ifRoom"/>
+
+</menu>
deleted file mode 100644
--- a/mobile/android/base/resources/menu/edit_bookmarks_menu.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
- - License, v. 2.0. If a copy of the MPL was not distributed with this
- - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
-
- <item android:id="@+id/bin"
- android:icon="@android:drawable/ic_menu_delete"
- android:title="@string/bookmark_remove"
- app:showAsAction="always"/>
-</menu>
\ No newline at end of file
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -96,16 +96,26 @@
<string name="bookmark_already_added">&bookmark_already_added;</string>
<string name="bookmark_removed">&bookmark_removed;</string>
<string name="bookmark_updated">&bookmark_updated;</string>
<string name="bookmark_options">&bookmark_options;</string>
<string name="bookmarks_selected_folder">&bookmarks_selected_folder;</string>
<string name="screenshot_added_to_bookmarks">&screenshot_added_to_bookmarks;</string>
<string name="screenshot_folder_label_in_bookmarks">&screenshot_folder_label_in_bookmarks;</string>
<string name="readinglist_smartfolder_label_in_bookmarks">&readinglist_smartfolder_label_in_bookmarks;</string>
+
+ <string name="bookmark_dialog_save_and_return">&bookmark_dialog_save_and_return;</string>
+ <string name="bookmark_dialog_folder">&bookmark_dialog_folder;</string>
+
+ <string name="bookmark_edit_title">&bookmark_edit_title;</string>
+ <string name="bookmark_edit_name">&bookmark_edit_name;</string>
+ <string name="bookmark_edit_location">&bookmark_edit_location;</string>
+ <string name="bookmark_edit_keyword">&bookmark_edit_keyword;</string>
+ <string name="bookmark_edit_location_empty_error">&bookmark_edit_location_empty_error;</string>
+
<string name="bookmark_folder_items">&bookmark_folder_items;</string>
<string name="bookmark_folder_one_item">&bookmark_folder_one_item;</string>
<string name="reader_saved_offline">&reader_saved_offline;</string>
<string name="reader_switch_to_bookmarks">&reader_switch_to_bookmarks;</string>
<string name="history_today_section">&history_today_section;</string>
<string name="history_yesterday_section">&history_yesterday_section;</string>
@@ -431,22 +441,16 @@
<string name="pref_scroll_title_bar2">&pref_scroll_title_bar2;</string>
<string name="pref_scroll_title_bar_summary">&pref_scroll_title_bar_summary2;</string>
<string name="pref_compact_tabs">&pref_compact_tabs;</string>
<string name="pref_compact_tabs_summary">&pref_compact_tabs_summary2;</string>
<string name="page_removed">&page_removed;</string>
- <string name="bookmark_edit_title">&bookmark_edit_title;</string>
- <string name="bookmark_edit_name">&bookmark_edit_name;</string>
- <string name="bookmark_edit_location">&bookmark_edit_location;</string>
- <string name="bookmark_edit_keyword">&bookmark_edit_keyword;</string>
- <string name="bookmark_edit_location_empty_error">&bookmark_edit_location_empty_error;</string>
-
<string name="pref_use_master_password">&pref_use_master_password;</string>
<string name="masterpassword_create_title">&masterpassword_create_title;</string>
<string name="masterpassword_remove_title">&masterpassword_remove_title;</string>
<string name="masterpassword_password">&masterpassword_password;</string>
<string name="masterpassword_confirm">&masterpassword_confirm;</string>
<string name="button_ok">&button_ok;</string>
<string name="button_cancel">&button_cancel;</string>