blob: d5751531e653cee4f8f161b0e90fcd06196615f8 [file] [log] [blame]
Amith Yamasanid7993472010-08-18 13:59:28 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import android.app.Dialog;
20import android.app.DialogFragment;
Daisuke Miyakawab5647c52010-09-10 18:04:02 -070021import android.app.Fragment;
Amith Yamasanid7993472010-08-18 13:59:28 -070022import android.content.ContentResolver;
Amith Yamasani350938e2013-04-09 10:22:47 -070023import android.content.Context;
Hung-ying Tyan0ee51e02011-01-25 16:42:14 +080024import android.content.DialogInterface;
Amith Yamasanid7993472010-08-18 13:59:28 -070025import android.content.pm.PackageManager;
Amith Yamasanid7993472010-08-18 13:59:28 -070026import android.os.Bundle;
Amith Yamasani9627a8e2012-09-23 12:54:14 -070027import android.preference.Preference;
Amith Yamasanid7993472010-08-18 13:59:28 -070028import android.preference.PreferenceFragment;
Fabrice Di Meglioc1457322014-04-04 19:07:50 -070029import android.preference.PreferenceGroup;
30import android.preference.PreferenceGroupAdapter;
Amith Yamasanib0b37ae2012-04-23 15:35:36 -070031import android.text.TextUtils;
Amith Yamasanid7993472010-08-18 13:59:28 -070032import android.util.Log;
Amith Yamasanib0b37ae2012-04-23 15:35:36 -070033import android.view.Menu;
34import android.view.MenuInflater;
35import android.view.MenuItem;
Daisuke Miyakawa9c8bde52010-08-25 11:58:37 -070036import android.widget.Button;
Fabrice Di Meglioc1457322014-04-04 19:07:50 -070037import android.widget.ListAdapter;
Amith Yamasanid7993472010-08-18 13:59:28 -070038
Daisuke Miyakawaf58090d2010-09-12 17:27:33 -070039/**
Amith Yamasanid7993472010-08-18 13:59:28 -070040 * Base class for Settings fragments, with some helper functions and dialog management.
41 */
Gilles Debunne64650542011-08-23 11:01:35 -070042public class SettingsPreferenceFragment extends PreferenceFragment implements DialogCreatable {
Amith Yamasanid7993472010-08-18 13:59:28 -070043
44 private static final String TAG = "SettingsPreferenceFragment";
45
Amith Yamasanib0b37ae2012-04-23 15:35:36 -070046 private static final int MENU_HELP = Menu.FIRST + 100;
Fabrice Di Meglioc1457322014-04-04 19:07:50 -070047 private static final int HIGHLIGHT_DURATION_MILLIS = 750;
Amith Yamasanib0b37ae2012-04-23 15:35:36 -070048
Amith Yamasanid7993472010-08-18 13:59:28 -070049 private SettingsDialogFragment mDialogFragment;
50
Amith Yamasanib0b37ae2012-04-23 15:35:36 -070051 private String mHelpUrl;
52
Amith Yamasani350938e2013-04-09 10:22:47 -070053 // Cache the content resolver for async callbacks
54 private ContentResolver mContentResolver;
55
Amith Yamasanib0b37ae2012-04-23 15:35:36 -070056 @Override
57 public void onCreate(Bundle icicle) {
58 super.onCreate(icicle);
59
60 // Prepare help url and enable menu if necessary
61 int helpResource = getHelpResource();
62 if (helpResource != 0) {
63 mHelpUrl = getResources().getString(helpResource);
Amith Yamasanib0b37ae2012-04-23 15:35:36 -070064 }
65 }
66
Daisuke Miyakawab5647c52010-09-10 18:04:02 -070067 @Override
Amith Yamasanid7993472010-08-18 13:59:28 -070068 public void onActivityCreated(Bundle savedInstanceState) {
69 super.onActivityCreated(savedInstanceState);
Amith Yamasanib3a593e2012-04-23 18:03:52 -070070 if (!TextUtils.isEmpty(mHelpUrl)) {
71 setHasOptionsMenu(true);
72 }
Fabrice Di Meglioc1457322014-04-04 19:07:50 -070073
74 final Bundle args = getArguments();
75 if (args != null) {
76 final String key = args.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
77 final int position = findPositionFromKey(getPreferenceScreen(), key);
78 if (position >= 0) {
79 final ListAdapter adapter = getListView().getAdapter();
80 if (adapter instanceof PreferenceGroupAdapter) {
81 ((PreferenceGroupAdapter) adapter).setActivated(position);
82
83 getListView().postDelayed(new Runnable() {
84 @Override
85 public void run() {
86 ((PreferenceGroupAdapter) adapter).setActivated(-1);
87 ((PreferenceGroupAdapter) adapter).notifyDataSetChanged();
88 }
89 }, HIGHLIGHT_DURATION_MILLIS);
90 }
91 }
92 }
93 }
94
95 private int findPositionFromKey(PreferenceGroup group, String key) {
96 if (group != null) {
97 int count = group.getPreferenceCount();
98 for (int n = 0; n < count; n++) {
99 final Preference preference = group.getPreference(n);
100 final String preferenceKey = preference.getKey();
101 if (preferenceKey != null && preferenceKey.equals(key)) {
102 return n;
103 }
104 if (preference instanceof PreferenceGroup) {
105 PreferenceGroup nestedGroup = (PreferenceGroup) preference;
106 final int nestedPosition = findPositionFromKey(nestedGroup, key);
107 if (nestedPosition >= 0) {
108 return n + 1 + nestedPosition;
109 }
110 }
111 }
112 }
113 return -1;
Amith Yamasanid7993472010-08-18 13:59:28 -0700114 }
115
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700116 protected void removePreference(String key) {
117 Preference pref = findPreference(key);
118 if (pref != null) {
119 getPreferenceScreen().removePreference(pref);
120 }
121 }
122
Amith Yamasanib0b37ae2012-04-23 15:35:36 -0700123 /**
124 * Override this if you want to show a help item in the menu, by returning the resource id.
125 * @return the resource id for the help url
126 */
127 protected int getHelpResource() {
128 return 0;
129 }
130
131 @Override
132 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Amith Yamasaniaeb57ed2012-12-06 14:40:51 -0800133 if (mHelpUrl != null && getActivity() != null) {
Amith Yamasanib0b37ae2012-04-23 15:35:36 -0700134 MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_label);
Amith Yamasaniaeb57ed2012-12-06 14:40:51 -0800135 HelpUtils.prepareHelpMenuItem(getActivity(), helpItem, mHelpUrl);
Amith Yamasanib0b37ae2012-04-23 15:35:36 -0700136 }
137 }
138
Daisuke Miyakawab5647c52010-09-10 18:04:02 -0700139 /*
140 * The name is intentionally made different from Activity#finish(), so that
141 * users won't misunderstand its meaning.
142 */
143 public final void finishFragment() {
144 getActivity().onBackPressed();
145 }
146
Amith Yamasanid7993472010-08-18 13:59:28 -0700147 // Some helpers for functions used by the settings fragments when they were activities
148
149 /**
150 * Returns the ContentResolver from the owning Activity.
151 */
152 protected ContentResolver getContentResolver() {
Amith Yamasani350938e2013-04-09 10:22:47 -0700153 Context context = getActivity();
154 if (context != null) {
155 mContentResolver = context.getContentResolver();
156 }
157 return mContentResolver;
Amith Yamasanid7993472010-08-18 13:59:28 -0700158 }
159
160 /**
161 * Returns the specified system service from the owning Activity.
162 */
163 protected Object getSystemService(final String name) {
164 return getActivity().getSystemService(name);
165 }
166
167 /**
Amith Yamasanid7993472010-08-18 13:59:28 -0700168 * Returns the PackageManager from the owning Activity.
169 */
170 protected PackageManager getPackageManager() {
171 return getActivity().getPackageManager();
172 }
173
Dianne Hackborn0385cf12011-01-24 16:22:13 -0800174 @Override
175 public void onDetach() {
176 if (isRemoving()) {
177 if (mDialogFragment != null) {
178 mDialogFragment.dismiss();
179 mDialogFragment = null;
180 }
181 }
182 super.onDetach();
183 }
184
Amith Yamasanid7993472010-08-18 13:59:28 -0700185 // Dialog management
186
187 protected void showDialog(int dialogId) {
188 if (mDialogFragment != null) {
189 Log.e(TAG, "Old dialog fragment not null!");
190 }
191 mDialogFragment = new SettingsDialogFragment(this, dialogId);
Fabrice Di Meglio377dd622014-02-12 20:05:57 -0800192 mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId));
Amith Yamasanid7993472010-08-18 13:59:28 -0700193 }
194
195 public Dialog onCreateDialog(int dialogId) {
196 return null;
197 }
198
199 protected void removeDialog(int dialogId) {
Hung-ying Tyanadc83d82011-01-24 15:05:27 +0800200 // mDialogFragment may not be visible yet in parent fragment's onResume().
201 // To be able to dismiss dialog at that time, don't check
202 // mDialogFragment.isVisible().
203 if (mDialogFragment != null && mDialogFragment.getDialogId() == dialogId) {
Amith Yamasanid7993472010-08-18 13:59:28 -0700204 mDialogFragment.dismiss();
205 }
206 mDialogFragment = null;
207 }
208
Hung-ying Tyan0ee51e02011-01-25 16:42:14 +0800209 /**
210 * Sets the OnCancelListener of the dialog shown. This method can only be
211 * called after showDialog(int) and before removeDialog(int). The method
212 * does nothing otherwise.
213 */
214 protected void setOnCancelListener(DialogInterface.OnCancelListener listener) {
215 if (mDialogFragment != null) {
216 mDialogFragment.mOnCancelListener = listener;
217 }
218 }
219
220 /**
221 * Sets the OnDismissListener of the dialog shown. This method can only be
222 * called after showDialog(int) and before removeDialog(int). The method
223 * does nothing otherwise.
224 */
225 protected void setOnDismissListener(DialogInterface.OnDismissListener listener) {
226 if (mDialogFragment != null) {
227 mDialogFragment.mOnDismissListener = listener;
228 }
229 }
230
Amith Yamasanic861cf82012-10-02 14:51:46 -0700231 public void onDialogShowing() {
232 // override in subclass to attach a dismiss listener, for instance
233 }
234
Amith Yamasani43c69782010-12-01 09:04:36 -0800235 public static class SettingsDialogFragment extends DialogFragment {
Svetoslav Ganov749ba652010-12-09 14:53:02 -0800236 private static final String KEY_DIALOG_ID = "key_dialog_id";
237 private static final String KEY_PARENT_FRAGMENT_ID = "key_parent_fragment_id";
238
Amith Yamasanid7993472010-08-18 13:59:28 -0700239 private int mDialogId;
240
Svetoslav Ganov749ba652010-12-09 14:53:02 -0800241 private Fragment mParentFragment;
242
Hung-ying Tyan0ee51e02011-01-25 16:42:14 +0800243 private DialogInterface.OnCancelListener mOnCancelListener;
244 private DialogInterface.OnDismissListener mOnDismissListener;
245
Svetoslav Ganov749ba652010-12-09 14:53:02 -0800246 public SettingsDialogFragment() {
247 /* do nothing */
248 }
Amith Yamasanid7993472010-08-18 13:59:28 -0700249
Amith Yamasani43c69782010-12-01 09:04:36 -0800250 public SettingsDialogFragment(DialogCreatable fragment, int dialogId) {
Amith Yamasanid7993472010-08-18 13:59:28 -0700251 mDialogId = dialogId;
Svetoslav Ganov749ba652010-12-09 14:53:02 -0800252 if (!(fragment instanceof Fragment)) {
253 throw new IllegalArgumentException("fragment argument must be an instance of "
254 + Fragment.class.getName());
255 }
256 mParentFragment = (Fragment) fragment;
257 }
258
259 @Override
Dianne Hackborn300768f2011-01-27 20:39:21 -0800260 public void onSaveInstanceState(Bundle outState) {
261 super.onSaveInstanceState(outState);
262 if (mParentFragment != null) {
263 outState.putInt(KEY_DIALOG_ID, mDialogId);
264 outState.putInt(KEY_PARENT_FRAGMENT_ID, mParentFragment.getId());
265 }
266 }
267
268 @Override
Amith Yamasanic861cf82012-10-02 14:51:46 -0700269 public void onStart() {
270 super.onStart();
271
272 if (mParentFragment != null && mParentFragment instanceof SettingsPreferenceFragment) {
273 ((SettingsPreferenceFragment) mParentFragment).onDialogShowing();
274 }
275 }
276
277 @Override
Dianne Hackborn300768f2011-01-27 20:39:21 -0800278 public Dialog onCreateDialog(Bundle savedInstanceState) {
Svetoslav Ganov749ba652010-12-09 14:53:02 -0800279 if (savedInstanceState != null) {
280 mDialogId = savedInstanceState.getInt(KEY_DIALOG_ID, 0);
Fabrice Di Meglio377dd622014-02-12 20:05:57 -0800281 mParentFragment = getParentFragment();
Svetoslav Ganov749ba652010-12-09 14:53:02 -0800282 int mParentFragmentId = savedInstanceState.getInt(KEY_PARENT_FRAGMENT_ID, -1);
Fabrice Di Meglio377dd622014-02-12 20:05:57 -0800283 if (!(mParentFragment instanceof DialogCreatable)) {
284 throw new IllegalArgumentException(
285 (mParentFragment != null
286 ? mParentFragment.getClass().getName()
287 : mParentFragmentId)
288 + " must implement "
289 + DialogCreatable.class.getName());
Svetoslav Ganov749ba652010-12-09 14:53:02 -0800290 }
Amith Yamasani8875ede2011-01-31 12:46:57 -0800291 // This dialog fragment could be created from non-SettingsPreferenceFragment
292 if (mParentFragment instanceof SettingsPreferenceFragment) {
293 // restore mDialogFragment in mParentFragment
294 ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = this;
295 }
Svetoslav Ganov749ba652010-12-09 14:53:02 -0800296 }
Svetoslav Ganov749ba652010-12-09 14:53:02 -0800297 return ((DialogCreatable) mParentFragment).onCreateDialog(mDialogId);
Amith Yamasanid7993472010-08-18 13:59:28 -0700298 }
299
Hung-ying Tyan0ee51e02011-01-25 16:42:14 +0800300 @Override
301 public void onCancel(DialogInterface dialog) {
302 super.onCancel(dialog);
303 if (mOnCancelListener != null) {
304 mOnCancelListener.onCancel(dialog);
305 }
306 }
307
308 @Override
309 public void onDismiss(DialogInterface dialog) {
310 super.onDismiss(dialog);
311 if (mOnDismissListener != null) {
312 mOnDismissListener.onDismiss(dialog);
313 }
314 }
Amith Yamasani8875ede2011-01-31 12:46:57 -0800315
Amith Yamasanid7993472010-08-18 13:59:28 -0700316 public int getDialogId() {
317 return mDialogId;
318 }
Hung-ying Tyan18eb39d2011-01-28 16:17:27 +0800319
320 @Override
321 public void onDetach() {
322 super.onDetach();
323
Amith Yamasani8875ede2011-01-31 12:46:57 -0800324 // This dialog fragment could be created from non-SettingsPreferenceFragment
325 if (mParentFragment instanceof SettingsPreferenceFragment) {
326 // in case the dialog is not explicitly removed by removeDialog()
327 if (((SettingsPreferenceFragment) mParentFragment).mDialogFragment == this) {
328 ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = null;
329 }
Hung-ying Tyan18eb39d2011-01-28 16:17:27 +0800330 }
331 }
Amith Yamasanid7993472010-08-18 13:59:28 -0700332 }
Daisuke Miyakawa9c8bde52010-08-25 11:58:37 -0700333
334 protected boolean hasNextButton() {
Daisuke Miyakawa79c5fd92011-01-15 14:58:00 -0800335 return ((ButtonBarHandler)getActivity()).hasNextButton();
Daisuke Miyakawa9c8bde52010-08-25 11:58:37 -0700336 }
337
338 protected Button getNextButton() {
Daisuke Miyakawa79c5fd92011-01-15 14:58:00 -0800339 return ((ButtonBarHandler)getActivity()).getNextButton();
Daisuke Miyakawa9c8bde52010-08-25 11:58:37 -0700340 }
341
Daisuke Miyakawa6ebf8612010-09-10 09:48:51 -0700342 public void finish() {
343 getActivity().onBackPressed();
344 }
345
Daisuke Miyakawab5647c52010-09-10 18:04:02 -0700346 public boolean startFragment(
347 Fragment caller, String fragmentClass, int requestCode, Bundle extras) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800348 if (getActivity() instanceof SettingsActivity) {
349 SettingsActivity sa = (SettingsActivity) getActivity();
350 sa.startPreferencePanel(fragmentClass, extras,
Gilles Debunne64650542011-08-23 11:01:35 -0700351 R.string.lock_settings_picker_title, null, caller, requestCode);
Daisuke Miyakawa25af1502010-09-24 11:29:31 -0700352 return true;
Daisuke Miyakawab5647c52010-09-10 18:04:02 -0700353 } else {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800354 Log.w(TAG, "Parent isn't Settings activity, thus there's no way to launch the "
Daisuke Miyakawa25af1502010-09-24 11:29:31 -0700355 + "given Fragment (name: " + fragmentClass + ", requestCode: " + requestCode
356 + ")");
Daisuke Miyakawab5647c52010-09-10 18:04:02 -0700357 return false;
358 }
359 }
360
Amith Yamasanid7993472010-08-18 13:59:28 -0700361}