/* -*- 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.db.BrowserContract.Bookmarks; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UIAsyncTask; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.app.AlertDialog; import android.content.DialogInterface; import android.database.Cursor; import android.support.design.widget.Snackbar; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; /** * A dialog that allows editing a bookmarks url, title, or keywords *

* Invoked by calling one of the {@link org.mozilla.gecko.EditBookmarkDialog#show(String)} * methods. */ public class EditBookmarkDialog { private final Context mContext; public EditBookmarkDialog(Context context) { mContext = context; } /** * A private struct to make it easier to pass bookmark data across threads */ private class Bookmark { final int id; final String title; final String url; final String keyword; public Bookmark(int aId, String aTitle, String aUrl, String aKeyword) { id = aId; title = aTitle; url = aUrl; keyword = aKeyword; } } /** * This text watcher to enable or disable the OK button if the dialog contains * valid information. This class is overridden to do data checking on different fields. * By itself, it always enables the button. * * Callers can also assign a paired partner to the TextWatcher, and callers will check * that both are enabled before enabling the ok button. */ private class EditBookmarkTextWatcher implements TextWatcher { // A stored reference to the dialog containing the text field being watched protected AlertDialog mDialog; // A stored text watcher to do the real verification of a field protected EditBookmarkTextWatcher mPairedTextWatcher; // Whether or not the ok button should be enabled. protected boolean mEnabled = true; public EditBookmarkTextWatcher(AlertDialog aDialog) { mDialog = aDialog; } public void setPairedTextWatcher(EditBookmarkTextWatcher aTextWatcher) { mPairedTextWatcher = aTextWatcher; } public boolean isEnabled() { return mEnabled; } // Textwatcher interface @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // Disable if the we're disabled or the paired partner is disabled boolean enabled = mEnabled && (mPairedTextWatcher == null || mPairedTextWatcher.isEnabled()); mDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled); } @Override public void afterTextChanged(Editable s) {} @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} } /** * A version of the EditBookmarkTextWatcher for the url field of the dialog. * Only checks if the field is empty or not. */ private class LocationTextWatcher extends EditBookmarkTextWatcher { public LocationTextWatcher(AlertDialog aDialog) { super(aDialog); } // Disables the ok button if the location field is empty. @Override public void onTextChanged(CharSequence s, int start, int before, int count) { mEnabled = (s.toString().trim().length() > 0); super.onTextChanged(s, start, before, count); } } /** * A version of the EditBookmarkTextWatcher for the keyword field of the dialog. * Checks if the field has any (non leading or trailing) spaces. */ private class KeywordTextWatcher extends EditBookmarkTextWatcher { public KeywordTextWatcher(AlertDialog aDialog) { super(aDialog); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // Disable if the keyword contains spaces mEnabled = (s.toString().trim().indexOf(' ') == -1); super.onTextChanged(s, start, before, count); } } /** * 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. * * @param url The url of the bookmark to edit. The dialog will look up other information like the id, * current title, or keywords associated with this url. If the url isn't bookmarked, the * dialog will fail silently. If the url is bookmarked multiple times, this will only show * information about the first it finds. */ public void show(final String url) { final ContentResolver cr = mContext.getContentResolver(); final BrowserDB db = BrowserDB.from(mContext); (new UIAsyncTask.WithoutParams(ThreadUtils.getBackgroundHandler()) { @Override public Bookmark doInBackground() { final Cursor cursor = db.getBookmarkForUrl(cr, url); if (cursor == null) { return null; } Bookmark bookmark = null; try { cursor.moveToFirst(); bookmark = new Bookmark(cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID)), cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.TITLE)), cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.URL)), cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.KEYWORD))); } finally { cursor.close(); } return bookmark; } @Override public void onPostExecute(Bookmark bookmark) { if (bookmark == null) { return; } show(bookmark.id, bookmark.title, bookmark.url, bookmark.keyword); } }).execute(); } /** * Show the Edit bookmark dialog for a set of data. This will show the dialog whether * a bookmark with this url exists or not, but the results will NOT be saved if the id * is not a valid bookmark id. * * @param id The id of the bookmark to change. If there is no bookmark with this ID, the dialog * will fail silently. * @param title The initial title to show in the dialog * @param url The initial url to show in the dialog * @param keyword The initial keyword to show in the dialog */ public void show(final int id, final String title, final String url, final String keyword) { final Context context = mContext; AlertDialog.Builder editPrompt = new AlertDialog.Builder(context); final View editView = LayoutInflater.from(context).inflate(R.layout.bookmark_edit, null); editPrompt.setTitle(R.string.bookmark_edit_title); editPrompt.setView(editView); 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)); nameText.setText(title); locationText.setText(url); keywordText.setText(keyword); final BrowserDB db = BrowserDB.from(mContext); editPrompt.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { (new UIAsyncTask.WithoutParams(ThreadUtils.getBackgroundHandler()) { @Override public Void doInBackground() { String newUrl = locationText.getText().toString().trim(); String newKeyword = keywordText.getText().toString().trim(); db.updateBookmark(context.getContentResolver(), id, newUrl, nameText.getText().toString(), newKeyword); return null; } @Override public void onPostExecute(Void result) { SnackbarBuilder.builder((Activity) context) .message(R.string.bookmark_updated) .duration(Snackbar.LENGTH_LONG) .buildAndShow(); } }).execute(); } }); editPrompt.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { // do nothing } }); final AlertDialog dialog = editPrompt.create(); // Create our TextWatchers LocationTextWatcher locationTextWatcher = new LocationTextWatcher(dialog); KeywordTextWatcher keywordTextWatcher = new KeywordTextWatcher(dialog); // Cross reference the TextWatchers locationTextWatcher.setPairedTextWatcher(keywordTextWatcher); keywordTextWatcher.setPairedTextWatcher(locationTextWatcher); // Add the TextWatcher Listeners locationText.addTextChangedListener(locationTextWatcher); keywordText.addTextChangedListener(keywordTextWatcher); dialog.show(); } }