|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.home; |
|
7 |
|
8 import org.mozilla.gecko.R; |
|
9 import org.mozilla.gecko.db.BrowserDB.URLColumns; |
|
10 |
|
11 import android.content.Context; |
|
12 import android.database.Cursor; |
|
13 import android.os.Bundle; |
|
14 import android.support.v4.app.DialogFragment; |
|
15 import android.support.v4.app.LoaderManager; |
|
16 import android.support.v4.app.LoaderManager.LoaderCallbacks; |
|
17 import android.support.v4.content.Loader; |
|
18 import android.support.v4.widget.CursorAdapter; |
|
19 import android.text.Editable; |
|
20 import android.text.TextUtils; |
|
21 import android.text.TextWatcher; |
|
22 import android.view.KeyEvent; |
|
23 import android.view.LayoutInflater; |
|
24 import android.view.View; |
|
25 import android.view.ViewGroup; |
|
26 import android.view.WindowManager; |
|
27 import android.widget.AdapterView; |
|
28 import android.widget.EditText; |
|
29 import android.widget.ListView; |
|
30 |
|
31 /** |
|
32 * Dialog fragment that displays frecency search results, for pinning a site, in a GridView. |
|
33 */ |
|
34 class PinSiteDialog extends DialogFragment { |
|
35 // Listener for url selection |
|
36 public static interface OnSiteSelectedListener { |
|
37 public void onSiteSelected(String url, String title); |
|
38 } |
|
39 |
|
40 // Cursor loader ID for search query |
|
41 private static final int LOADER_ID_SEARCH = 0; |
|
42 |
|
43 // Holds the current search term to use in the query |
|
44 private String mSearchTerm; |
|
45 |
|
46 // Adapter for the list of search results |
|
47 private SearchAdapter mAdapter; |
|
48 |
|
49 // Search entry |
|
50 private EditText mSearch; |
|
51 |
|
52 // Search results |
|
53 private ListView mList; |
|
54 |
|
55 // Callbacks used for the search loader |
|
56 private CursorLoaderCallbacks mLoaderCallbacks; |
|
57 |
|
58 // Bookmark selected listener |
|
59 private OnSiteSelectedListener mOnSiteSelectedListener; |
|
60 |
|
61 public static PinSiteDialog newInstance() { |
|
62 return new PinSiteDialog(); |
|
63 } |
|
64 |
|
65 private PinSiteDialog() { |
|
66 } |
|
67 |
|
68 @Override |
|
69 public void onCreate(Bundle savedInstanceState) { |
|
70 super.onCreate(savedInstanceState); |
|
71 |
|
72 setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Holo_Light_Dialog); |
|
73 } |
|
74 |
|
75 @Override |
|
76 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { |
|
77 // All list views are styled to look the same with a global activity theme. |
|
78 // If the style of the list changes, inflate it from an XML. |
|
79 return inflater.inflate(R.layout.pin_site_dialog, container, false); |
|
80 } |
|
81 |
|
82 @Override |
|
83 public void onViewCreated(View view, Bundle savedInstanceState) { |
|
84 super.onViewCreated(view, savedInstanceState); |
|
85 |
|
86 mSearch = (EditText) view.findViewById(R.id.search); |
|
87 mSearch.addTextChangedListener(new TextWatcher() { |
|
88 @Override |
|
89 public void afterTextChanged(Editable s) { |
|
90 } |
|
91 |
|
92 @Override |
|
93 public void beforeTextChanged(CharSequence s, int start, int count, int after) { |
|
94 } |
|
95 |
|
96 @Override |
|
97 public void onTextChanged(CharSequence s, int start, int before, int count) { |
|
98 setSearchTerm(mSearch.getText().toString()); |
|
99 filter(mSearchTerm); |
|
100 } |
|
101 }); |
|
102 |
|
103 mSearch.setOnKeyListener(new View.OnKeyListener() { |
|
104 @Override |
|
105 public boolean onKey(View v, int keyCode, KeyEvent event) { |
|
106 if (keyCode != KeyEvent.KEYCODE_ENTER || mOnSiteSelectedListener == null) { |
|
107 return false; |
|
108 } |
|
109 |
|
110 // If the user manually entered a search term or URL, wrap the value in |
|
111 // a special URI until we can get a valid URL for this bookmark. |
|
112 final String text = mSearch.getText().toString().trim(); |
|
113 if (!TextUtils.isEmpty(text)) { |
|
114 final String url = TopSitesPanel.encodeUserEnteredUrl(text); |
|
115 mOnSiteSelectedListener.onSiteSelected(url, text); |
|
116 dismiss(); |
|
117 } |
|
118 |
|
119 return true; |
|
120 } |
|
121 }); |
|
122 |
|
123 mSearch.setOnFocusChangeListener(new View.OnFocusChangeListener() { |
|
124 @Override |
|
125 public void onFocusChange(View v, boolean hasFocus) { |
|
126 if (hasFocus) { |
|
127 getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); |
|
128 } |
|
129 } |
|
130 }); |
|
131 |
|
132 mList = (HomeListView) view.findViewById(R.id.list); |
|
133 mList.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
|
134 @Override |
|
135 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
|
136 if (mOnSiteSelectedListener != null) { |
|
137 final Cursor c = mAdapter.getCursor(); |
|
138 if (c == null || !c.moveToPosition(position)) { |
|
139 return; |
|
140 } |
|
141 |
|
142 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL)); |
|
143 final String title = c.getString(c.getColumnIndexOrThrow(URLColumns.TITLE)); |
|
144 mOnSiteSelectedListener.onSiteSelected(url, title); |
|
145 } |
|
146 |
|
147 // Dismiss the fragment and the dialog. |
|
148 dismiss(); |
|
149 } |
|
150 }); |
|
151 } |
|
152 |
|
153 @Override |
|
154 public void onActivityCreated(Bundle savedInstanceState) { |
|
155 super.onActivityCreated(savedInstanceState); |
|
156 |
|
157 final LoaderManager manager = getLoaderManager(); |
|
158 |
|
159 // Initialize the search adapter |
|
160 mAdapter = new SearchAdapter(getActivity()); |
|
161 mList.setAdapter(mAdapter); |
|
162 |
|
163 // Create callbacks before the initial loader is started |
|
164 mLoaderCallbacks = new CursorLoaderCallbacks(); |
|
165 |
|
166 // Reconnect to the loader only if present |
|
167 manager.initLoader(LOADER_ID_SEARCH, null, mLoaderCallbacks); |
|
168 |
|
169 // If there is a search term, put it in the text field |
|
170 if (!TextUtils.isEmpty(mSearchTerm)) { |
|
171 mSearch.setText(mSearchTerm); |
|
172 mSearch.selectAll(); |
|
173 } |
|
174 |
|
175 // Always start with an empty filter |
|
176 filter(""); |
|
177 } |
|
178 |
|
179 @Override |
|
180 public void onDestroyView() { |
|
181 super.onDestroyView(); |
|
182 |
|
183 // Discard any additional site selection as the dialog |
|
184 // is getting destroyed (see bug 935542). |
|
185 setOnSiteSelectedListener(null); |
|
186 } |
|
187 |
|
188 public void setSearchTerm(String searchTerm) { |
|
189 mSearchTerm = searchTerm; |
|
190 } |
|
191 |
|
192 private void filter(String searchTerm) { |
|
193 // Restart loaders with the new search term |
|
194 SearchLoader.restart(getLoaderManager(), LOADER_ID_SEARCH, mLoaderCallbacks, searchTerm); |
|
195 } |
|
196 |
|
197 public void setOnSiteSelectedListener(OnSiteSelectedListener listener) { |
|
198 mOnSiteSelectedListener = listener; |
|
199 } |
|
200 |
|
201 private static class SearchAdapter extends CursorAdapter { |
|
202 private LayoutInflater mInflater; |
|
203 |
|
204 public SearchAdapter(Context context) { |
|
205 super(context, null, 0); |
|
206 mInflater = LayoutInflater.from(context); |
|
207 } |
|
208 |
|
209 @Override |
|
210 public void bindView(View view, Context context, Cursor cursor) { |
|
211 TwoLinePageRow row = (TwoLinePageRow) view; |
|
212 row.setShowIcons(false); |
|
213 row.updateFromCursor(cursor); |
|
214 } |
|
215 |
|
216 @Override |
|
217 public View newView(Context context, Cursor cursor, ViewGroup parent) { |
|
218 return (TwoLinePageRow) mInflater.inflate(R.layout.home_item_row, parent, false); |
|
219 } |
|
220 } |
|
221 |
|
222 private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> { |
|
223 @Override |
|
224 public Loader<Cursor> onCreateLoader(int id, Bundle args) { |
|
225 return SearchLoader.createInstance(getActivity(), args); |
|
226 } |
|
227 |
|
228 @Override |
|
229 public void onLoadFinished(Loader<Cursor> loader, Cursor c) { |
|
230 mAdapter.swapCursor(c); |
|
231 } |
|
232 |
|
233 @Override |
|
234 public void onLoaderReset(Loader<Cursor> loader) { |
|
235 mAdapter.swapCursor(null); |
|
236 } |
|
237 } |
|
238 } |