SettingsPreferenceFragment.java revision 906ff6fdf989ad4006c74d2bf37fc0e5d4da05f8
1/*
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;
21import android.app.Fragment;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.DialogInterface;
25import android.content.pm.PackageManager;
26import android.graphics.drawable.Drawable;
27import android.os.Bundle;
28import android.preference.Preference;
29import android.preference.PreferenceFragment;
30import android.preference.PreferenceGroupAdapter;
31import android.text.TextUtils;
32import android.util.Log;
33import android.view.Menu;
34import android.view.MenuInflater;
35import android.view.MenuItem;
36import android.widget.Button;
37import android.widget.ListAdapter;
38import android.widget.ListView;
39
40/**
41 * Base class for Settings fragments, with some helper functions and dialog management.
42 */
43public class SettingsPreferenceFragment extends PreferenceFragment implements DialogCreatable {
44
45    private static final String TAG = "SettingsPreferenceFragment";
46
47    private static final int MENU_HELP = Menu.FIRST + 100;
48    private static final int HIGHLIGHT_DURATION_MILLIS = 300;
49
50    private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
51
52    private SettingsDialogFragment mDialogFragment;
53
54    private String mHelpUrl;
55
56    // Cache the content resolver for async callbacks
57    private ContentResolver mContentResolver;
58
59    private boolean mPreferenceHighlighted = false;
60
61    @Override
62    public void onCreate(Bundle icicle) {
63        super.onCreate(icicle);
64
65        if (icicle != null) {
66            mPreferenceHighlighted = icicle.getBoolean(SAVE_HIGHLIGHTED_KEY);
67        }
68
69        // Prepare help url and enable menu if necessary
70        int helpResource = getHelpResource();
71        if (helpResource != 0) {
72            mHelpUrl = getResources().getString(helpResource);
73        }
74    }
75
76    @Override
77    public void onSaveInstanceState(Bundle outState) {
78        super.onSaveInstanceState(outState);
79
80        outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
81    }
82
83    @Override
84    public void onActivityCreated(Bundle savedInstanceState) {
85        super.onActivityCreated(savedInstanceState);
86        if (!TextUtils.isEmpty(mHelpUrl)) {
87            setHasOptionsMenu(true);
88        }
89
90        final Bundle args = getArguments();
91        if (args != null) {
92            final String key = args.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
93            if (hasListView() && !TextUtils.isEmpty(key)) {
94                highlightPreference(key);
95            }
96        }
97    }
98
99    private void highlightPreference(String key) {
100        final int position = findPositionFromKey(key);
101        if (position >= 0) {
102            final ListView listView = getListView();
103            final ListAdapter adapter = listView.getAdapter();
104
105            if (adapter instanceof PreferenceGroupAdapter) {
106                final Drawable drawable = getHighlightDrawable();
107                ((PreferenceGroupAdapter) adapter).setHighlightedDrawable(drawable);
108                ((PreferenceGroupAdapter) adapter).setHighlighted(position);
109
110                listView.post(new Runnable() {
111                    @Override
112                    public void run() {
113                        listView.setSelection(position);
114                        if (!mPreferenceHighlighted) {
115                            listView.postDelayed(new Runnable() {
116                                @Override
117                                public void run() {
118                                    final int centerX = listView.getWidth() / 2;
119                                    final int centerY = listView.getChildAt(0).getHeight() / 2;
120                                    drawable.setHotspot(0, centerX, centerY);
121                                    drawable.clearHotspots();
122                                    ((PreferenceGroupAdapter) adapter).setHighlighted(-1);
123                                }
124                            }, HIGHLIGHT_DURATION_MILLIS);
125
126                            mPreferenceHighlighted = true;
127                        }
128                    }
129                });
130            }
131        }
132
133    }
134
135    private Drawable getHighlightDrawable() {
136        return getResources().getDrawable(R.drawable.preference_highlight);
137    }
138
139    private int findPositionFromKey(String key) {
140        final ListAdapter adapter = getListView().getAdapter();
141        final int count = adapter.getCount();
142        for (int n = 0; n < count; n++) {
143            Object item = adapter.getItem(n);
144            if (item instanceof Preference) {
145                Preference preference = (Preference) item;
146                final String preferenceKey = preference.getKey();
147                if (preferenceKey != null && preferenceKey.equals(key)) {
148                    return n;
149                }
150            }
151        }
152        return -1;
153    }
154
155    protected void removePreference(String key) {
156        Preference pref = findPreference(key);
157        if (pref != null) {
158            getPreferenceScreen().removePreference(pref);
159        }
160    }
161
162    /**
163     * Override this if you want to show a help item in the menu, by returning the resource id.
164     * @return the resource id for the help url
165     */
166    protected int getHelpResource() {
167        return 0;
168    }
169
170    @Override
171    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
172        if (mHelpUrl != null && getActivity() != null) {
173            MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_label);
174            HelpUtils.prepareHelpMenuItem(getActivity(), helpItem, mHelpUrl);
175        }
176    }
177
178    /*
179     * The name is intentionally made different from Activity#finish(), so that
180     * users won't misunderstand its meaning.
181     */
182    public final void finishFragment() {
183        getActivity().onBackPressed();
184    }
185
186    // Some helpers for functions used by the settings fragments when they were activities
187
188    /**
189     * Returns the ContentResolver from the owning Activity.
190     */
191    protected ContentResolver getContentResolver() {
192        Context context = getActivity();
193        if (context != null) {
194            mContentResolver = context.getContentResolver();
195        }
196        return mContentResolver;
197    }
198
199    /**
200     * Returns the specified system service from the owning Activity.
201     */
202    protected Object getSystemService(final String name) {
203        return getActivity().getSystemService(name);
204    }
205
206    /**
207     * Returns the PackageManager from the owning Activity.
208     */
209    protected PackageManager getPackageManager() {
210        return getActivity().getPackageManager();
211    }
212
213    @Override
214    public void onDetach() {
215        if (isRemoving()) {
216            if (mDialogFragment != null) {
217                mDialogFragment.dismiss();
218                mDialogFragment = null;
219            }
220        }
221        super.onDetach();
222    }
223
224    // Dialog management
225
226    protected void showDialog(int dialogId) {
227        if (mDialogFragment != null) {
228            Log.e(TAG, "Old dialog fragment not null!");
229        }
230        mDialogFragment = new SettingsDialogFragment(this, dialogId);
231        mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId));
232    }
233
234    public Dialog onCreateDialog(int dialogId) {
235        return null;
236    }
237
238    protected void removeDialog(int dialogId) {
239        // mDialogFragment may not be visible yet in parent fragment's onResume().
240        // To be able to dismiss dialog at that time, don't check
241        // mDialogFragment.isVisible().
242        if (mDialogFragment != null && mDialogFragment.getDialogId() == dialogId) {
243            mDialogFragment.dismiss();
244        }
245        mDialogFragment = null;
246    }
247
248    /**
249     * Sets the OnCancelListener of the dialog shown. This method can only be
250     * called after showDialog(int) and before removeDialog(int). The method
251     * does nothing otherwise.
252     */
253    protected void setOnCancelListener(DialogInterface.OnCancelListener listener) {
254        if (mDialogFragment != null) {
255            mDialogFragment.mOnCancelListener = listener;
256        }
257    }
258
259    /**
260     * Sets the OnDismissListener of the dialog shown. This method can only be
261     * called after showDialog(int) and before removeDialog(int). The method
262     * does nothing otherwise.
263     */
264    protected void setOnDismissListener(DialogInterface.OnDismissListener listener) {
265        if (mDialogFragment != null) {
266            mDialogFragment.mOnDismissListener = listener;
267        }
268    }
269
270    public void onDialogShowing() {
271        // override in subclass to attach a dismiss listener, for instance
272    }
273
274    public static class SettingsDialogFragment extends DialogFragment {
275        private static final String KEY_DIALOG_ID = "key_dialog_id";
276        private static final String KEY_PARENT_FRAGMENT_ID = "key_parent_fragment_id";
277
278        private int mDialogId;
279
280        private Fragment mParentFragment;
281
282        private DialogInterface.OnCancelListener mOnCancelListener;
283        private DialogInterface.OnDismissListener mOnDismissListener;
284
285        public SettingsDialogFragment() {
286            /* do nothing */
287        }
288
289        public SettingsDialogFragment(DialogCreatable fragment, int dialogId) {
290            mDialogId = dialogId;
291            if (!(fragment instanceof Fragment)) {
292                throw new IllegalArgumentException("fragment argument must be an instance of "
293                        + Fragment.class.getName());
294            }
295            mParentFragment = (Fragment) fragment;
296        }
297
298        @Override
299        public void onSaveInstanceState(Bundle outState) {
300            super.onSaveInstanceState(outState);
301            if (mParentFragment != null) {
302                outState.putInt(KEY_DIALOG_ID, mDialogId);
303                outState.putInt(KEY_PARENT_FRAGMENT_ID, mParentFragment.getId());
304            }
305        }
306
307        @Override
308        public void onStart() {
309            super.onStart();
310
311            if (mParentFragment != null && mParentFragment instanceof SettingsPreferenceFragment) {
312                ((SettingsPreferenceFragment) mParentFragment).onDialogShowing();
313            }
314        }
315
316        @Override
317        public Dialog onCreateDialog(Bundle savedInstanceState) {
318            if (savedInstanceState != null) {
319                mDialogId = savedInstanceState.getInt(KEY_DIALOG_ID, 0);
320                mParentFragment = getParentFragment();
321                int mParentFragmentId = savedInstanceState.getInt(KEY_PARENT_FRAGMENT_ID, -1);
322                if (!(mParentFragment instanceof DialogCreatable)) {
323                    throw new IllegalArgumentException(
324                            (mParentFragment != null
325                                    ? mParentFragment.getClass().getName()
326                                    : mParentFragmentId)
327                                    + " must implement "
328                                    + DialogCreatable.class.getName());
329                }
330                // This dialog fragment could be created from non-SettingsPreferenceFragment
331                if (mParentFragment instanceof SettingsPreferenceFragment) {
332                    // restore mDialogFragment in mParentFragment
333                    ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = this;
334                }
335            }
336            return ((DialogCreatable) mParentFragment).onCreateDialog(mDialogId);
337        }
338
339        @Override
340        public void onCancel(DialogInterface dialog) {
341            super.onCancel(dialog);
342            if (mOnCancelListener != null) {
343                mOnCancelListener.onCancel(dialog);
344            }
345        }
346
347        @Override
348        public void onDismiss(DialogInterface dialog) {
349            super.onDismiss(dialog);
350            if (mOnDismissListener != null) {
351                mOnDismissListener.onDismiss(dialog);
352            }
353        }
354
355        public int getDialogId() {
356            return mDialogId;
357        }
358
359        @Override
360        public void onDetach() {
361            super.onDetach();
362
363            // This dialog fragment could be created from non-SettingsPreferenceFragment
364            if (mParentFragment instanceof SettingsPreferenceFragment) {
365                // in case the dialog is not explicitly removed by removeDialog()
366                if (((SettingsPreferenceFragment) mParentFragment).mDialogFragment == this) {
367                    ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = null;
368                }
369            }
370        }
371    }
372
373    protected boolean hasNextButton() {
374        return ((ButtonBarHandler)getActivity()).hasNextButton();
375    }
376
377    protected Button getNextButton() {
378        return ((ButtonBarHandler)getActivity()).getNextButton();
379    }
380
381    public void finish() {
382        getActivity().onBackPressed();
383    }
384
385    public boolean startFragment(
386            Fragment caller, String fragmentClass, int requestCode, Bundle extras) {
387        if (getActivity() instanceof SettingsActivity) {
388            SettingsActivity sa = (SettingsActivity) getActivity();
389            sa.startPreferencePanel(fragmentClass, extras,
390                    R.string.lock_settings_picker_title, null, caller, requestCode);
391            return true;
392        } else {
393            Log.w(TAG, "Parent isn't Settings activity, thus there's no way to launch the "
394                    + "given Fragment (name: " + fragmentClass + ", requestCode: " + requestCode
395                    + ")");
396            return false;
397        }
398    }
399
400}
401