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