1 /* 2 * Copyright (C) 2007 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 17 package android.preference; 18 19 import android.app.Dialog; 20 import android.content.Context; 21 import android.content.DialogInterface; 22 import android.os.Bundle; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.text.TextUtils; 26 import android.util.AttributeSet; 27 import android.view.LayoutInflater; 28 import android.view.View; 29 import android.view.Window; 30 import android.widget.Adapter; 31 import android.widget.AdapterView; 32 import android.widget.ListAdapter; 33 import android.widget.ListView; 34 35 /** 36 * Represents a top-level {@link Preference} that 37 * is the root of a Preference hierarchy. A {@link PreferenceActivity} 38 * points to an instance of this class to show the preferences. To instantiate 39 * this class, use {@link PreferenceManager#createPreferenceScreen(Context)}. 40 * <ul> 41 * This class can appear in two places: 42 * <li> When a {@link PreferenceActivity} points to this, it is used as the root 43 * and is not shown (only the contained preferences are shown). 44 * <li> When it appears inside another preference hierarchy, it is shown and 45 * serves as the gateway to another screen of preferences (either by showing 46 * another screen of preferences as a {@link Dialog} or via a 47 * {@link Context#startActivity(android.content.Intent)} from the 48 * {@link Preference#getIntent()}). The children of this {@link PreferenceScreen} 49 * are NOT shown in the screen that this {@link PreferenceScreen} is shown in. 50 * Instead, a separate screen will be shown when this preference is clicked. 51 * </ul> 52 * <p>Here's an example XML layout of a PreferenceScreen:</p> 53 * <pre> 54 <PreferenceScreen 55 xmlns:android="http://schemas.android.com/apk/res/android" 56 android:key="first_preferencescreen"> 57 <CheckBoxPreference 58 android:key="wifi enabled" 59 android:title="WiFi" /> 60 <PreferenceScreen 61 android:key="second_preferencescreen" 62 android:title="WiFi settings"> 63 <CheckBoxPreference 64 android:key="prefer wifi" 65 android:title="Prefer WiFi" /> 66 ... other preferences here ... 67 </PreferenceScreen> 68 </PreferenceScreen> </pre> 69 * <p> 70 * In this example, the "first_preferencescreen" will be used as the root of the 71 * hierarchy and given to a {@link PreferenceActivity}. The first screen will 72 * show preferences "WiFi" (which can be used to quickly enable/disable WiFi) 73 * and "WiFi settings". The "WiFi settings" is the "second_preferencescreen" and when 74 * clicked will show another screen of preferences such as "Prefer WiFi" (and 75 * the other preferences that are children of the "second_preferencescreen" tag). 76 * 77 * <div class="special reference"> 78 * <h3>Developer Guides</h3> 79 * <p>For information about building a settings UI with Preferences, 80 * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a> 81 * guide.</p> 82 * </div> 83 * 84 * @see PreferenceCategory 85 */ 86 public final class PreferenceScreen extends PreferenceGroup implements AdapterView.OnItemClickListener, 87 DialogInterface.OnDismissListener { 88 89 private ListAdapter mRootAdapter; 90 91 private Dialog mDialog; 92 93 private ListView mListView; 94 95 /** 96 * Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}. 97 * @hide- 98 */ PreferenceScreen(Context context, AttributeSet attrs)99 public PreferenceScreen(Context context, AttributeSet attrs) { 100 super(context, attrs, com.android.internal.R.attr.preferenceScreenStyle); 101 } 102 103 /** 104 * Returns an adapter that can be attached to a {@link PreferenceActivity} 105 * or {@link PreferenceFragment} to show the preferences contained in this 106 * {@link PreferenceScreen}. 107 * <p> 108 * This {@link PreferenceScreen} will NOT appear in the returned adapter, instead 109 * it appears in the hierarchy above this {@link PreferenceScreen}. 110 * <p> 111 * This adapter's {@link Adapter#getItem(int)} should always return a 112 * subclass of {@link Preference}. 113 * 114 * @return An adapter that provides the {@link Preference} contained in this 115 * {@link PreferenceScreen}. 116 */ getRootAdapter()117 public ListAdapter getRootAdapter() { 118 if (mRootAdapter == null) { 119 mRootAdapter = onCreateRootAdapter(); 120 } 121 122 return mRootAdapter; 123 } 124 125 /** 126 * Creates the root adapter. 127 * 128 * @return An adapter that contains the preferences contained in this {@link PreferenceScreen}. 129 * @see #getRootAdapter() 130 */ onCreateRootAdapter()131 protected ListAdapter onCreateRootAdapter() { 132 return new PreferenceGroupAdapter(this); 133 } 134 135 /** 136 * Binds a {@link ListView} to the preferences contained in this {@link PreferenceScreen} via 137 * {@link #getRootAdapter()}. It also handles passing list item clicks to the corresponding 138 * {@link Preference} contained by this {@link PreferenceScreen}. 139 * 140 * @param listView The list view to attach to. 141 */ bind(ListView listView)142 public void bind(ListView listView) { 143 listView.setOnItemClickListener(this); 144 listView.setAdapter(getRootAdapter()); 145 146 onAttachedToActivity(); 147 } 148 149 @Override onClick()150 protected void onClick() { 151 if (getIntent() != null || getFragment() != null || getPreferenceCount() == 0) { 152 return; 153 } 154 155 showDialog(null); 156 } 157 showDialog(Bundle state)158 private void showDialog(Bundle state) { 159 Context context = getContext(); 160 if (mListView != null) { 161 mListView.setAdapter(null); 162 } 163 164 LayoutInflater inflater = (LayoutInflater) 165 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 166 View childPrefScreen = inflater.inflate( 167 com.android.internal.R.layout.preference_list_fragment, null); 168 mListView = (ListView) childPrefScreen.findViewById(android.R.id.list); 169 bind(mListView); 170 171 // Set the title bar if title is available, else no title bar 172 final CharSequence title = getTitle(); 173 Dialog dialog = mDialog = new Dialog(context, context.getThemeResId()); 174 if (TextUtils.isEmpty(title)) { 175 dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); 176 } else { 177 dialog.setTitle(title); 178 } 179 dialog.setContentView(childPrefScreen); 180 dialog.setOnDismissListener(this); 181 if (state != null) { 182 dialog.onRestoreInstanceState(state); 183 } 184 185 // Add the screen to the list of preferences screens opened as dialogs 186 getPreferenceManager().addPreferencesScreen(dialog); 187 188 dialog.show(); 189 } 190 onDismiss(DialogInterface dialog)191 public void onDismiss(DialogInterface dialog) { 192 mDialog = null; 193 getPreferenceManager().removePreferencesScreen(dialog); 194 } 195 196 /** 197 * Used to get a handle to the dialog. 198 * This is useful for cases where we want to manipulate the dialog 199 * as we would with any other activity or view. 200 */ getDialog()201 public Dialog getDialog() { 202 return mDialog; 203 } 204 onItemClick(AdapterView parent, View view, int position, long id)205 public void onItemClick(AdapterView parent, View view, int position, long id) { 206 // If the list has headers, subtract them from the index. 207 if (parent instanceof ListView) { 208 position -= ((ListView) parent).getHeaderViewsCount(); 209 } 210 Object item = getRootAdapter().getItem(position); 211 if (!(item instanceof Preference)) return; 212 213 final Preference preference = (Preference) item; 214 preference.performClick(this); 215 } 216 217 @Override isOnSameScreenAsChildren()218 protected boolean isOnSameScreenAsChildren() { 219 return false; 220 } 221 222 @Override onSaveInstanceState()223 protected Parcelable onSaveInstanceState() { 224 final Parcelable superState = super.onSaveInstanceState(); 225 final Dialog dialog = mDialog; 226 if (dialog == null || !dialog.isShowing()) { 227 return superState; 228 } 229 230 final SavedState myState = new SavedState(superState); 231 myState.isDialogShowing = true; 232 myState.dialogBundle = dialog.onSaveInstanceState(); 233 return myState; 234 } 235 236 @Override onRestoreInstanceState(Parcelable state)237 protected void onRestoreInstanceState(Parcelable state) { 238 if (state == null || !state.getClass().equals(SavedState.class)) { 239 // Didn't save state for us in onSaveInstanceState 240 super.onRestoreInstanceState(state); 241 return; 242 } 243 244 SavedState myState = (SavedState) state; 245 super.onRestoreInstanceState(myState.getSuperState()); 246 if (myState.isDialogShowing) { 247 showDialog(myState.dialogBundle); 248 } 249 } 250 251 private static class SavedState extends BaseSavedState { 252 boolean isDialogShowing; 253 Bundle dialogBundle; 254 SavedState(Parcel source)255 public SavedState(Parcel source) { 256 super(source); 257 isDialogShowing = source.readInt() == 1; 258 dialogBundle = source.readBundle(); 259 } 260 261 @Override writeToParcel(Parcel dest, int flags)262 public void writeToParcel(Parcel dest, int flags) { 263 super.writeToParcel(dest, flags); 264 dest.writeInt(isDialogShowing ? 1 : 0); 265 dest.writeBundle(dialogBundle); 266 } 267 SavedState(Parcelable superState)268 public SavedState(Parcelable superState) { 269 super(superState); 270 } 271 272 public static final Parcelable.Creator<SavedState> CREATOR = 273 new Parcelable.Creator<SavedState>() { 274 public SavedState createFromParcel(Parcel in) { 275 return new SavedState(in); 276 } 277 278 public SavedState[] newArray(int size) { 279 return new SavedState[size]; 280 } 281 }; 282 } 283 284 } 285