1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/home/PinSiteDialog.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,238 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.home; 1.10 + 1.11 +import org.mozilla.gecko.R; 1.12 +import org.mozilla.gecko.db.BrowserDB.URLColumns; 1.13 + 1.14 +import android.content.Context; 1.15 +import android.database.Cursor; 1.16 +import android.os.Bundle; 1.17 +import android.support.v4.app.DialogFragment; 1.18 +import android.support.v4.app.LoaderManager; 1.19 +import android.support.v4.app.LoaderManager.LoaderCallbacks; 1.20 +import android.support.v4.content.Loader; 1.21 +import android.support.v4.widget.CursorAdapter; 1.22 +import android.text.Editable; 1.23 +import android.text.TextUtils; 1.24 +import android.text.TextWatcher; 1.25 +import android.view.KeyEvent; 1.26 +import android.view.LayoutInflater; 1.27 +import android.view.View; 1.28 +import android.view.ViewGroup; 1.29 +import android.view.WindowManager; 1.30 +import android.widget.AdapterView; 1.31 +import android.widget.EditText; 1.32 +import android.widget.ListView; 1.33 + 1.34 +/** 1.35 + * Dialog fragment that displays frecency search results, for pinning a site, in a GridView. 1.36 + */ 1.37 +class PinSiteDialog extends DialogFragment { 1.38 + // Listener for url selection 1.39 + public static interface OnSiteSelectedListener { 1.40 + public void onSiteSelected(String url, String title); 1.41 + } 1.42 + 1.43 + // Cursor loader ID for search query 1.44 + private static final int LOADER_ID_SEARCH = 0; 1.45 + 1.46 + // Holds the current search term to use in the query 1.47 + private String mSearchTerm; 1.48 + 1.49 + // Adapter for the list of search results 1.50 + private SearchAdapter mAdapter; 1.51 + 1.52 + // Search entry 1.53 + private EditText mSearch; 1.54 + 1.55 + // Search results 1.56 + private ListView mList; 1.57 + 1.58 + // Callbacks used for the search loader 1.59 + private CursorLoaderCallbacks mLoaderCallbacks; 1.60 + 1.61 + // Bookmark selected listener 1.62 + private OnSiteSelectedListener mOnSiteSelectedListener; 1.63 + 1.64 + public static PinSiteDialog newInstance() { 1.65 + return new PinSiteDialog(); 1.66 + } 1.67 + 1.68 + private PinSiteDialog() { 1.69 + } 1.70 + 1.71 + @Override 1.72 + public void onCreate(Bundle savedInstanceState) { 1.73 + super.onCreate(savedInstanceState); 1.74 + 1.75 + setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Holo_Light_Dialog); 1.76 + } 1.77 + 1.78 + @Override 1.79 + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 1.80 + // All list views are styled to look the same with a global activity theme. 1.81 + // If the style of the list changes, inflate it from an XML. 1.82 + return inflater.inflate(R.layout.pin_site_dialog, container, false); 1.83 + } 1.84 + 1.85 + @Override 1.86 + public void onViewCreated(View view, Bundle savedInstanceState) { 1.87 + super.onViewCreated(view, savedInstanceState); 1.88 + 1.89 + mSearch = (EditText) view.findViewById(R.id.search); 1.90 + mSearch.addTextChangedListener(new TextWatcher() { 1.91 + @Override 1.92 + public void afterTextChanged(Editable s) { 1.93 + } 1.94 + 1.95 + @Override 1.96 + public void beforeTextChanged(CharSequence s, int start, int count, int after) { 1.97 + } 1.98 + 1.99 + @Override 1.100 + public void onTextChanged(CharSequence s, int start, int before, int count) { 1.101 + setSearchTerm(mSearch.getText().toString()); 1.102 + filter(mSearchTerm); 1.103 + } 1.104 + }); 1.105 + 1.106 + mSearch.setOnKeyListener(new View.OnKeyListener() { 1.107 + @Override 1.108 + public boolean onKey(View v, int keyCode, KeyEvent event) { 1.109 + if (keyCode != KeyEvent.KEYCODE_ENTER || mOnSiteSelectedListener == null) { 1.110 + return false; 1.111 + } 1.112 + 1.113 + // If the user manually entered a search term or URL, wrap the value in 1.114 + // a special URI until we can get a valid URL for this bookmark. 1.115 + final String text = mSearch.getText().toString().trim(); 1.116 + if (!TextUtils.isEmpty(text)) { 1.117 + final String url = TopSitesPanel.encodeUserEnteredUrl(text); 1.118 + mOnSiteSelectedListener.onSiteSelected(url, text); 1.119 + dismiss(); 1.120 + } 1.121 + 1.122 + return true; 1.123 + } 1.124 + }); 1.125 + 1.126 + mSearch.setOnFocusChangeListener(new View.OnFocusChangeListener() { 1.127 + @Override 1.128 + public void onFocusChange(View v, boolean hasFocus) { 1.129 + if (hasFocus) { 1.130 + getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); 1.131 + } 1.132 + } 1.133 + }); 1.134 + 1.135 + mList = (HomeListView) view.findViewById(R.id.list); 1.136 + mList.setOnItemClickListener(new AdapterView.OnItemClickListener() { 1.137 + @Override 1.138 + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1.139 + if (mOnSiteSelectedListener != null) { 1.140 + final Cursor c = mAdapter.getCursor(); 1.141 + if (c == null || !c.moveToPosition(position)) { 1.142 + return; 1.143 + } 1.144 + 1.145 + final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL)); 1.146 + final String title = c.getString(c.getColumnIndexOrThrow(URLColumns.TITLE)); 1.147 + mOnSiteSelectedListener.onSiteSelected(url, title); 1.148 + } 1.149 + 1.150 + // Dismiss the fragment and the dialog. 1.151 + dismiss(); 1.152 + } 1.153 + }); 1.154 + } 1.155 + 1.156 + @Override 1.157 + public void onActivityCreated(Bundle savedInstanceState) { 1.158 + super.onActivityCreated(savedInstanceState); 1.159 + 1.160 + final LoaderManager manager = getLoaderManager(); 1.161 + 1.162 + // Initialize the search adapter 1.163 + mAdapter = new SearchAdapter(getActivity()); 1.164 + mList.setAdapter(mAdapter); 1.165 + 1.166 + // Create callbacks before the initial loader is started 1.167 + mLoaderCallbacks = new CursorLoaderCallbacks(); 1.168 + 1.169 + // Reconnect to the loader only if present 1.170 + manager.initLoader(LOADER_ID_SEARCH, null, mLoaderCallbacks); 1.171 + 1.172 + // If there is a search term, put it in the text field 1.173 + if (!TextUtils.isEmpty(mSearchTerm)) { 1.174 + mSearch.setText(mSearchTerm); 1.175 + mSearch.selectAll(); 1.176 + } 1.177 + 1.178 + // Always start with an empty filter 1.179 + filter(""); 1.180 + } 1.181 + 1.182 + @Override 1.183 + public void onDestroyView() { 1.184 + super.onDestroyView(); 1.185 + 1.186 + // Discard any additional site selection as the dialog 1.187 + // is getting destroyed (see bug 935542). 1.188 + setOnSiteSelectedListener(null); 1.189 + } 1.190 + 1.191 + public void setSearchTerm(String searchTerm) { 1.192 + mSearchTerm = searchTerm; 1.193 + } 1.194 + 1.195 + private void filter(String searchTerm) { 1.196 + // Restart loaders with the new search term 1.197 + SearchLoader.restart(getLoaderManager(), LOADER_ID_SEARCH, mLoaderCallbacks, searchTerm); 1.198 + } 1.199 + 1.200 + public void setOnSiteSelectedListener(OnSiteSelectedListener listener) { 1.201 + mOnSiteSelectedListener = listener; 1.202 + } 1.203 + 1.204 + private static class SearchAdapter extends CursorAdapter { 1.205 + private LayoutInflater mInflater; 1.206 + 1.207 + public SearchAdapter(Context context) { 1.208 + super(context, null, 0); 1.209 + mInflater = LayoutInflater.from(context); 1.210 + } 1.211 + 1.212 + @Override 1.213 + public void bindView(View view, Context context, Cursor cursor) { 1.214 + TwoLinePageRow row = (TwoLinePageRow) view; 1.215 + row.setShowIcons(false); 1.216 + row.updateFromCursor(cursor); 1.217 + } 1.218 + 1.219 + @Override 1.220 + public View newView(Context context, Cursor cursor, ViewGroup parent) { 1.221 + return (TwoLinePageRow) mInflater.inflate(R.layout.home_item_row, parent, false); 1.222 + } 1.223 + } 1.224 + 1.225 + private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> { 1.226 + @Override 1.227 + public Loader<Cursor> onCreateLoader(int id, Bundle args) { 1.228 + return SearchLoader.createInstance(getActivity(), args); 1.229 + } 1.230 + 1.231 + @Override 1.232 + public void onLoadFinished(Loader<Cursor> loader, Cursor c) { 1.233 + mAdapter.swapCursor(c); 1.234 + } 1.235 + 1.236 + @Override 1.237 + public void onLoaderReset(Loader<Cursor> loader) { 1.238 + mAdapter.swapCursor(null); 1.239 + } 1.240 + } 1.241 +}