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 17 package com.android.settings; 18 19 import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY; 20 21 import android.app.Activity; 22 import android.app.Dialog; 23 import android.app.admin.DevicePolicyManager; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.pm.PackageManager; 29 import android.os.Bundle; 30 import android.text.TextUtils; 31 import android.util.ArrayMap; 32 import android.util.Log; 33 import android.view.LayoutInflater; 34 import android.view.View; 35 import android.view.ViewGroup; 36 import android.widget.Button; 37 38 import androidx.annotation.NonNull; 39 import androidx.annotation.Nullable; 40 import androidx.annotation.VisibleForTesting; 41 import androidx.annotation.XmlRes; 42 import androidx.fragment.app.DialogFragment; 43 import androidx.fragment.app.Fragment; 44 import androidx.preference.Preference; 45 import androidx.preference.PreferenceGroup; 46 import androidx.preference.PreferenceScreen; 47 import androidx.recyclerview.widget.LinearLayoutManager; 48 import androidx.recyclerview.widget.RecyclerView; 49 50 import com.android.settings.core.InstrumentedPreferenceFragment; 51 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 52 import com.android.settings.flags.Flags; 53 import com.android.settings.restriction.UserRestrictionBindingHelper; 54 import com.android.settings.support.actionbar.HelpResourceProvider; 55 import com.android.settings.widget.HighlightablePreferenceGroupAdapter; 56 import com.android.settings.widget.LoadingViewController; 57 import com.android.settingslib.CustomDialogPreferenceCompat; 58 import com.android.settingslib.CustomEditTextPreferenceCompat; 59 import com.android.settingslib.core.instrumentation.Instrumentable; 60 import com.android.settingslib.preference.PreferenceScreenBindingHelper; 61 import com.android.settingslib.preference.PreferenceScreenCreator; 62 import com.android.settingslib.search.Indexable; 63 import com.android.settingslib.widget.LayoutPreference; 64 65 import com.google.android.material.appbar.AppBarLayout; 66 import com.google.android.setupcompat.util.WizardManagerHelper; 67 68 import java.util.UUID; 69 70 /** 71 * Base class for Settings fragments, with some helper functions and dialog management. 72 */ 73 public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceFragment 74 implements DialogCreatable, HelpResourceProvider, Indexable { 75 76 private static final String TAG = "SettingsPreferenceFragment"; 77 78 private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted"; 79 80 private static final int ORDER_FIRST = -1; 81 82 protected DevicePolicyManager mDevicePolicyManager; 83 private SettingsDialogFragment mDialogFragment; 84 // Cache the content resolver for async callbacks 85 private ContentResolver mContentResolver; 86 87 private RecyclerView.Adapter mCurrentRootAdapter; 88 private boolean mIsDataSetObserverRegistered = false; 89 private RecyclerView.AdapterDataObserver mDataSetObserver = 90 new RecyclerView.AdapterDataObserver() { 91 @Override 92 public void onChanged() { 93 onDataSetChanged(); 94 } 95 96 @Override 97 public void onItemRangeChanged(int positionStart, int itemCount) { 98 onDataSetChanged(); 99 } 100 101 @Override 102 public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { 103 onDataSetChanged(); 104 } 105 106 @Override 107 public void onItemRangeInserted(int positionStart, int itemCount) { 108 onDataSetChanged(); 109 } 110 111 @Override 112 public void onItemRangeRemoved(int positionStart, int itemCount) { 113 onDataSetChanged(); 114 } 115 116 @Override 117 public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { 118 onDataSetChanged(); 119 } 120 }; 121 122 @VisibleForTesting 123 ViewGroup mPinnedHeaderFrameLayout; 124 private AppBarLayout mAppBarLayout; 125 private LayoutPreference mHeader; 126 private View mEmptyView; 127 private LinearLayoutManager mLayoutManager; 128 private ArrayMap<String, Preference> mPreferenceCache; 129 private boolean mAnimationAllowed; 130 131 @VisibleForTesting 132 public HighlightablePreferenceGroupAdapter mAdapter; 133 private boolean mPreferenceHighlighted = false; 134 135 private @Nullable UserRestrictionBindingHelper mUserRestrictionBindingHelper; 136 137 @Override onAttach(Context context)138 public void onAttach(Context context) { 139 if (shouldSkipForInitialSUW() && !WizardManagerHelper.isDeviceProvisioned(getContext())) { 140 Log.w(TAG, "Skip " + getClass().getSimpleName() + " before SUW completed."); 141 finish(); 142 } 143 super.onAttach(context); 144 } 145 146 @Override onCreate(Bundle icicle)147 public void onCreate(Bundle icicle) { 148 super.onCreate(icicle); 149 150 mDevicePolicyManager = getContext().getSystemService(DevicePolicyManager.class); 151 if (icicle != null) { 152 mPreferenceHighlighted = icicle.getBoolean(SAVE_HIGHLIGHTED_KEY); 153 } 154 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(this /* host */); 155 156 if (isCatalystEnabled()) { 157 PreferenceScreenBindingHelper helper = getPreferenceScreenBindingHelper(); 158 if (helper != null) { 159 mUserRestrictionBindingHelper = new UserRestrictionBindingHelper(requireContext(), 160 helper); 161 } 162 } 163 } 164 165 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)166 public View onCreateView(LayoutInflater inflater, ViewGroup container, 167 Bundle savedInstanceState) { 168 final View root = super.onCreateView(inflater, container, savedInstanceState); 169 mPinnedHeaderFrameLayout = root.findViewById(R.id.pinned_header); 170 mAppBarLayout = getActivity().findViewById(R.id.app_bar); 171 return root; 172 } 173 174 @Override addPreferencesFromResource(@mlRes int preferencesResId)175 public void addPreferencesFromResource(@XmlRes int preferencesResId) { 176 super.addPreferencesFromResource(preferencesResId); 177 checkAvailablePrefs(getPreferenceScreen()); 178 } 179 180 @VisibleForTesting checkAvailablePrefs(PreferenceGroup preferenceGroup)181 void checkAvailablePrefs(PreferenceGroup preferenceGroup) { 182 if (preferenceGroup == null) return; 183 for (int i = 0; i < preferenceGroup.getPreferenceCount(); i++) { 184 Preference pref = preferenceGroup.getPreference(i); 185 if (pref instanceof SelfAvailablePreference 186 && !((SelfAvailablePreference) pref).isAvailable(getContext())) { 187 pref.setVisible(false); 188 } else if (pref instanceof PreferenceGroup) { 189 checkAvailablePrefs((PreferenceGroup) pref); 190 } 191 } 192 } 193 194 @Override getPreferenceScreenResId(@onNull Context context)195 protected final int getPreferenceScreenResId(@NonNull Context context) { 196 return getPreferenceScreenResId(); 197 } 198 199 /** Returns if catalyst is enabled on current screen. */ isCatalystEnabled()200 public final boolean isCatalystEnabled() { 201 return getPreferenceScreenCreator() != null; 202 } 203 getPreferenceScreenCreator()204 protected @Nullable PreferenceScreenCreator getPreferenceScreenCreator() { 205 if (!Flags.catalyst()) { 206 return null; 207 } 208 Context context = getContext(); 209 return context != null ? getPreferenceScreenCreator(context) : null; 210 } 211 setPinnedHeaderView(int layoutResId)212 public View setPinnedHeaderView(int layoutResId) { 213 final LayoutInflater inflater = getActivity().getLayoutInflater(); 214 final View pinnedHeader = 215 inflater.inflate(layoutResId, mPinnedHeaderFrameLayout, false); 216 setPinnedHeaderView(pinnedHeader); 217 return pinnedHeader; 218 } 219 setPinnedHeaderView(View pinnedHeader)220 public void setPinnedHeaderView(View pinnedHeader) { 221 mPinnedHeaderFrameLayout.addView(pinnedHeader); 222 mPinnedHeaderFrameLayout.setVisibility(View.VISIBLE); 223 } 224 showPinnedHeader(boolean show)225 public void showPinnedHeader(boolean show) { 226 mPinnedHeaderFrameLayout.setVisibility(show ? View.VISIBLE : View.INVISIBLE); 227 } 228 229 @Override onSaveInstanceState(Bundle outState)230 public void onSaveInstanceState(Bundle outState) { 231 super.onSaveInstanceState(outState); 232 233 if (mAdapter != null) { 234 outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mAdapter.isHighlightRequested()); 235 } 236 } 237 238 @Override onActivityCreated(Bundle savedInstanceState)239 public void onActivityCreated(Bundle savedInstanceState) { 240 super.onActivityCreated(savedInstanceState); 241 setHasOptionsMenu(true); 242 } 243 244 @Override onResume()245 public void onResume() { 246 super.onResume(); 247 highlightPreferenceIfNeeded(); 248 } 249 250 @Override onBindPreferences()251 protected void onBindPreferences() { 252 registerObserverIfNeeded(); 253 } 254 255 @Override onUnbindPreferences()256 protected void onUnbindPreferences() { 257 unregisterObserverIfNeeded(); 258 } 259 setLoading(boolean loading, boolean animate)260 public void setLoading(boolean loading, boolean animate) { 261 View loadingContainer = getView().findViewById(R.id.loading_container); 262 LoadingViewController.handleLoadingContainer(loadingContainer, getListView(), 263 !loading /* done */, 264 animate); 265 } 266 registerObserverIfNeeded()267 public void registerObserverIfNeeded() { 268 if (!mIsDataSetObserverRegistered) { 269 if (mCurrentRootAdapter != null) { 270 mCurrentRootAdapter.unregisterAdapterDataObserver(mDataSetObserver); 271 } 272 mCurrentRootAdapter = getListView().getAdapter(); 273 mCurrentRootAdapter.registerAdapterDataObserver(mDataSetObserver); 274 mIsDataSetObserverRegistered = true; 275 onDataSetChanged(); 276 } 277 } 278 unregisterObserverIfNeeded()279 public void unregisterObserverIfNeeded() { 280 if (mIsDataSetObserverRegistered) { 281 if (mCurrentRootAdapter != null) { 282 mCurrentRootAdapter.unregisterAdapterDataObserver(mDataSetObserver); 283 mCurrentRootAdapter = null; 284 } 285 mIsDataSetObserverRegistered = false; 286 } 287 } 288 highlightPreferenceIfNeeded()289 public void highlightPreferenceIfNeeded() { 290 if (!isAdded()) { 291 return; 292 } 293 if (mAdapter != null) { 294 mAdapter.requestHighlight(getView(), getListView(), mAppBarLayout); 295 } 296 } 297 298 /** 299 * Returns initial expanded child count. 300 * <p/> 301 * Only override this method if the initial expanded child must be determined at run time. 302 */ getInitialExpandedChildCount()303 public int getInitialExpandedChildCount() { 304 return 0; 305 } 306 307 /** 308 * Whether preference is allowing to be displayed to the user. 309 * 310 * @param preference to check if it can be displayed to the user (not hidding in expand area). 311 * @return {@code true} when preference is allowing to be displayed to the user. 312 * {@code false} when preference is hidden in expand area and not been displayed to the user. 313 */ isPreferenceExpanded(Preference preference)314 protected boolean isPreferenceExpanded(Preference preference) { 315 return ((mAdapter == null) 316 || (mAdapter.getPreferenceAdapterPosition(preference) != RecyclerView.NO_POSITION)); 317 } 318 319 /** 320 * Whether UI should be skipped in the initial SUW flow. 321 * 322 * @return {@code true} when UI should be skipped in the initial SUW flow. 323 * {@code false} when UI should not be skipped in the initial SUW flow. 324 */ shouldSkipForInitialSUW()325 protected boolean shouldSkipForInitialSUW() { 326 return false; 327 } 328 onDataSetChanged()329 protected void onDataSetChanged() { 330 highlightPreferenceIfNeeded(); 331 updateEmptyView(); 332 } 333 getHeaderView()334 public LayoutPreference getHeaderView() { 335 return mHeader; 336 } 337 setHeaderView(int resource)338 protected void setHeaderView(int resource) { 339 mHeader = new LayoutPreference(getPrefContext(), resource); 340 mHeader.setSelectable(false); 341 addPreferenceToTop(mHeader); 342 } 343 setHeaderView(View view)344 protected void setHeaderView(View view) { 345 mHeader = new LayoutPreference(getPrefContext(), view); 346 mHeader.setSelectable(false); 347 addPreferenceToTop(mHeader); 348 } 349 addPreferenceToTop(LayoutPreference preference)350 private void addPreferenceToTop(LayoutPreference preference) { 351 preference.setOrder(ORDER_FIRST); 352 if (getPreferenceScreen() != null) { 353 getPreferenceScreen().addPreference(preference); 354 } 355 } 356 357 @Override setPreferenceScreen(PreferenceScreen preferenceScreen)358 public void setPreferenceScreen(PreferenceScreen preferenceScreen) { 359 if (preferenceScreen != null && !preferenceScreen.isAttached()) { 360 // Without ids generated, the RecyclerView won't animate changes to the preferences. 361 preferenceScreen.setShouldUseGeneratedIds(mAnimationAllowed); 362 } 363 super.setPreferenceScreen(preferenceScreen); 364 if (preferenceScreen != null) { 365 if (mHeader != null) { 366 preferenceScreen.addPreference(mHeader); 367 } 368 } 369 } 370 371 @VisibleForTesting updateEmptyView()372 void updateEmptyView() { 373 if (mEmptyView == null) return; 374 if (getPreferenceScreen() != null) { 375 final View listContainer = getActivity().findViewById(android.R.id.list_container); 376 boolean show = (getPreferenceScreen().getPreferenceCount() 377 - (mHeader != null ? 1 : 0)) <= 0 378 || (listContainer != null && listContainer.getVisibility() != View.VISIBLE); 379 mEmptyView.setVisibility(show ? View.VISIBLE : View.GONE); 380 } else { 381 mEmptyView.setVisibility(View.VISIBLE); 382 } 383 } 384 setEmptyView(View v)385 public void setEmptyView(View v) { 386 if (mEmptyView != null) { 387 mEmptyView.setVisibility(View.GONE); 388 } 389 mEmptyView = v; 390 updateEmptyView(); 391 } 392 getEmptyView()393 public View getEmptyView() { 394 return mEmptyView; 395 } 396 397 @Override onCreateLayoutManager()398 public RecyclerView.LayoutManager onCreateLayoutManager() { 399 mLayoutManager = new LinearLayoutManager(getContext()); 400 return mLayoutManager; 401 } 402 403 @Override onCreateAdapter(PreferenceScreen preferenceScreen)404 protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { 405 final Bundle arguments = getArguments(); 406 String key = arguments == null ? null : arguments.getString(EXTRA_FRAGMENT_ARG_KEY); 407 if (Flags.catalyst() && key == null) { 408 Activity activity = getActivity(); 409 Intent intent = activity != null ? activity.getIntent() : null; 410 key = intent != null ? intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY) : null; 411 } 412 mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen, key, 413 mPreferenceHighlighted); 414 return mAdapter; 415 } 416 setAnimationAllowed(boolean animationAllowed)417 protected void setAnimationAllowed(boolean animationAllowed) { 418 mAnimationAllowed = animationAllowed; 419 } 420 cacheRemoveAllPrefs(PreferenceGroup group)421 protected void cacheRemoveAllPrefs(PreferenceGroup group) { 422 mPreferenceCache = new ArrayMap<>(); 423 final int N = group.getPreferenceCount(); 424 for (int i = 0; i < N; i++) { 425 Preference p = group.getPreference(i); 426 if (TextUtils.isEmpty(p.getKey())) { 427 continue; 428 } 429 mPreferenceCache.put(p.getKey(), p); 430 } 431 } 432 getCachedPreference(String key)433 protected Preference getCachedPreference(String key) { 434 return mPreferenceCache != null ? mPreferenceCache.remove(key) : null; 435 } 436 removeCachedPrefs(PreferenceGroup group)437 protected void removeCachedPrefs(PreferenceGroup group) { 438 for (Preference p : mPreferenceCache.values()) { 439 group.removePreference(p); 440 } 441 mPreferenceCache = null; 442 } 443 getCachedCount()444 protected int getCachedCount() { 445 return mPreferenceCache != null ? mPreferenceCache.size() : 0; 446 } 447 448 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) removePreference(String key)449 public boolean removePreference(String key) { 450 return removePreference(getPreferenceScreen(), key); 451 } 452 453 @VisibleForTesting removePreference(PreferenceGroup group, String key)454 boolean removePreference(PreferenceGroup group, String key) { 455 final int preferenceCount = group.getPreferenceCount(); 456 for (int i = 0; i < preferenceCount; i++) { 457 final Preference preference = group.getPreference(i); 458 final String curKey = preference.getKey(); 459 460 if (TextUtils.equals(curKey, key)) { 461 return group.removePreference(preference); 462 } 463 464 if (preference instanceof PreferenceGroup) { 465 if (removePreference((PreferenceGroup) preference, key)) { 466 return true; 467 } 468 } 469 } 470 return false; 471 } 472 473 /* 474 * The name is intentionally made different from Activity#finish(), so that 475 * users won't misunderstand its meaning. 476 */ finishFragment()477 public final void finishFragment() { 478 getActivity().onBackPressed(); 479 } 480 481 // Some helpers for functions used by the settings fragments when they were activities 482 483 /** 484 * Returns the ContentResolver from the owning Activity. 485 */ getContentResolver()486 protected ContentResolver getContentResolver() { 487 Context context = getActivity(); 488 if (context != null) { 489 mContentResolver = context.getContentResolver(); 490 } 491 return mContentResolver; 492 } 493 494 /** 495 * Returns the specified system service from the owning Activity. 496 */ getSystemService(final String name)497 protected Object getSystemService(final String name) { 498 return getActivity().getSystemService(name); 499 } 500 501 /** 502 * Returns the specified system service from the owning Activity. 503 */ getSystemService(final Class<T> serviceClass)504 protected <T> T getSystemService(final Class<T> serviceClass) { 505 return getActivity().getSystemService(serviceClass); 506 } 507 508 /** 509 * Returns the PackageManager from the owning Activity. 510 */ getPackageManager()511 protected PackageManager getPackageManager() { 512 return getActivity().getPackageManager(); 513 } 514 515 @Override onDestroy()516 public void onDestroy() { 517 if (mUserRestrictionBindingHelper != null) { 518 mUserRestrictionBindingHelper.close(); 519 mUserRestrictionBindingHelper = null; 520 } 521 super.onDestroy(); 522 } 523 524 @Override onDetach()525 public void onDetach() { 526 if (isRemoving()) { 527 if (mDialogFragment != null) { 528 mDialogFragment.dismiss(); 529 mDialogFragment = null; 530 } 531 RecyclerView view = getListView(); 532 if (view != null) { 533 view.clearOnScrollListeners(); 534 } 535 } 536 super.onDetach(); 537 } 538 539 // Dialog management 540 showDialog(int dialogId)541 protected void showDialog(int dialogId) { 542 if (mDialogFragment != null) { 543 Log.e(TAG, "Old dialog fragment not null!"); 544 } 545 mDialogFragment = SettingsDialogFragment.newInstance(this, dialogId); 546 mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId)); 547 } 548 549 @Override onCreateDialog(int dialogId)550 public Dialog onCreateDialog(int dialogId) { 551 return null; 552 } 553 554 @Override getDialogMetricsCategory(int dialogId)555 public int getDialogMetricsCategory(int dialogId) { 556 return 0; 557 } 558 removeDialog(int dialogId)559 protected void removeDialog(int dialogId) { 560 // mDialogFragment may not be visible yet in parent fragment's onResume(). 561 // To be able to dismiss dialog at that time, don't check 562 // mDialogFragment.isVisible(). 563 if (mDialogFragment != null && mDialogFragment.getDialogId() == dialogId) { 564 mDialogFragment.dismissAllowingStateLoss(); 565 } 566 mDialogFragment = null; 567 } 568 569 /** 570 * Sets the OnCancelListener of the dialog shown. This method can only be 571 * called after showDialog(int) and before removeDialog(int). The method 572 * does nothing otherwise. 573 */ setOnCancelListener(DialogInterface.OnCancelListener listener)574 protected void setOnCancelListener(DialogInterface.OnCancelListener listener) { 575 if (mDialogFragment != null) { 576 mDialogFragment.mOnCancelListener = listener; 577 } 578 } 579 580 /** 581 * Sets the OnDismissListener of the dialog shown. This method can only be 582 * called after showDialog(int) and before removeDialog(int). The method 583 * does nothing otherwise. 584 */ setOnDismissListener(DialogInterface.OnDismissListener listener)585 protected void setOnDismissListener(DialogInterface.OnDismissListener listener) { 586 if (mDialogFragment != null) { 587 mDialogFragment.mOnDismissListener = listener; 588 } 589 } 590 onDialogShowing()591 public void onDialogShowing() { 592 // override in subclass to attach a dismiss listener, for instance 593 } 594 595 @Override onDisplayPreferenceDialog(Preference preference)596 public void onDisplayPreferenceDialog(Preference preference) { 597 if (preference.getKey() == null) { 598 // Auto-key preferences that don't have a key, so the dialog can find them. 599 preference.setKey(UUID.randomUUID().toString()); 600 } 601 DialogFragment f = null; 602 if (preference instanceof RestrictedListPreference) { 603 f = RestrictedListPreference.RestrictedListPreferenceDialogFragment 604 .newInstance(preference.getKey()); 605 } else if (preference instanceof CustomListPreference) { 606 f = CustomListPreference.CustomListPreferenceDialogFragment 607 .newInstance(preference.getKey()); 608 } else if (preference instanceof CustomDialogPreferenceCompat) { 609 f = CustomDialogPreferenceCompat.CustomPreferenceDialogFragment 610 .newInstance(preference.getKey()); 611 } else if (preference instanceof CustomEditTextPreferenceCompat) { 612 f = CustomEditTextPreferenceCompat.CustomPreferenceDialogFragment 613 .newInstance(preference.getKey()); 614 } else { 615 super.onDisplayPreferenceDialog(preference); 616 return; 617 } 618 f.setTargetFragment(this, 0); 619 f.show(getFragmentManager(), "dialog_preference"); 620 onDialogShowing(); 621 } 622 623 public static class SettingsDialogFragment extends InstrumentedDialogFragment { 624 private static final String KEY_DIALOG_ID = "key_dialog_id"; 625 private static final String KEY_PARENT_FRAGMENT_ID = "key_parent_fragment_id"; 626 627 private Fragment mParentFragment; 628 629 private DialogInterface.OnCancelListener mOnCancelListener; 630 private DialogInterface.OnDismissListener mOnDismissListener; 631 newInstance(DialogCreatable fragment, int dialogId)632 public static SettingsDialogFragment newInstance(DialogCreatable fragment, int dialogId) { 633 if (!(fragment instanceof Fragment)) { 634 throw new IllegalArgumentException("fragment argument must be an instance of " 635 + Fragment.class.getName()); 636 } 637 638 final SettingsDialogFragment settingsDialogFragment = new SettingsDialogFragment(); 639 settingsDialogFragment.setParentFragment(fragment); 640 settingsDialogFragment.setDialogId(dialogId); 641 642 return settingsDialogFragment; 643 } 644 645 @Override getMetricsCategory()646 public int getMetricsCategory() { 647 if (mParentFragment == null) { 648 return Instrumentable.METRICS_CATEGORY_UNKNOWN; 649 } 650 final int metricsCategory = 651 ((DialogCreatable) mParentFragment).getDialogMetricsCategory(mDialogId); 652 if (metricsCategory <= 0) { 653 throw new IllegalStateException("Dialog must provide a metrics category"); 654 } 655 return metricsCategory; 656 } 657 658 @Override onSaveInstanceState(Bundle outState)659 public void onSaveInstanceState(Bundle outState) { 660 super.onSaveInstanceState(outState); 661 if (mParentFragment != null) { 662 outState.putInt(KEY_DIALOG_ID, mDialogId); 663 outState.putInt(KEY_PARENT_FRAGMENT_ID, mParentFragment.getId()); 664 } 665 } 666 667 @Override onStart()668 public void onStart() { 669 super.onStart(); 670 671 if (mParentFragment != null && mParentFragment instanceof SettingsPreferenceFragment) { 672 ((SettingsPreferenceFragment) mParentFragment).onDialogShowing(); 673 } 674 } 675 676 @Override onCreateDialog(Bundle savedInstanceState)677 public Dialog onCreateDialog(Bundle savedInstanceState) { 678 if (savedInstanceState != null) { 679 mDialogId = savedInstanceState.getInt(KEY_DIALOG_ID, 0); 680 mParentFragment = getParentFragment(); 681 int mParentFragmentId = savedInstanceState.getInt(KEY_PARENT_FRAGMENT_ID, -1); 682 if (mParentFragment == null) { 683 mParentFragment = getFragmentManager().findFragmentById(mParentFragmentId); 684 } 685 if (!(mParentFragment instanceof DialogCreatable)) { 686 throw new IllegalArgumentException( 687 (mParentFragment != null 688 ? mParentFragment.getClass().getName() 689 : mParentFragmentId) 690 + " must implement " 691 + DialogCreatable.class.getName()); 692 } 693 // This dialog fragment could be created from non-SettingsPreferenceFragment 694 if (mParentFragment instanceof SettingsPreferenceFragment) { 695 // restore mDialogFragment in mParentFragment 696 ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = this; 697 } 698 } 699 return ((DialogCreatable) mParentFragment).onCreateDialog(mDialogId); 700 } 701 702 @Override onCancel(DialogInterface dialog)703 public void onCancel(DialogInterface dialog) { 704 super.onCancel(dialog); 705 if (mOnCancelListener != null) { 706 mOnCancelListener.onCancel(dialog); 707 } 708 } 709 710 @Override onDismiss(DialogInterface dialog)711 public void onDismiss(DialogInterface dialog) { 712 super.onDismiss(dialog); 713 if (mOnDismissListener != null) { 714 mOnDismissListener.onDismiss(dialog); 715 } 716 } 717 getDialogId()718 public int getDialogId() { 719 return mDialogId; 720 } 721 722 @Override onDetach()723 public void onDetach() { 724 super.onDetach(); 725 726 // This dialog fragment could be created from non-SettingsPreferenceFragment 727 if (mParentFragment instanceof SettingsPreferenceFragment) { 728 // in case the dialog is not explicitly removed by removeDialog() 729 if (((SettingsPreferenceFragment) mParentFragment).mDialogFragment == this) { 730 ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = null; 731 } 732 } 733 } 734 setParentFragment(DialogCreatable fragment)735 private void setParentFragment(DialogCreatable fragment) { 736 mParentFragment = (Fragment) fragment; 737 } 738 setDialogId(int dialogId)739 private void setDialogId(int dialogId) { 740 mDialogId = dialogId; 741 } 742 } 743 hasNextButton()744 protected boolean hasNextButton() { 745 return ((ButtonBarHandler) getActivity()).hasNextButton(); 746 } 747 getNextButton()748 protected Button getNextButton() { 749 return ((ButtonBarHandler) getActivity()).getNextButton(); 750 } 751 finish()752 public void finish() { 753 Activity activity = getActivity(); 754 if (activity == null) return; 755 if (getFragmentManager().getBackStackEntryCount() > 0) { 756 getFragmentManager().popBackStack(); 757 } else { 758 activity.finish(); 759 } 760 } 761 getIntent()762 protected Intent getIntent() { 763 if (getActivity() == null) { 764 return null; 765 } 766 return getActivity().getIntent(); 767 } 768 setResult(int result, Intent intent)769 protected void setResult(int result, Intent intent) { 770 if (getActivity() == null) { 771 return; 772 } 773 getActivity().setResult(result, intent); 774 } 775 setResult(int result)776 protected void setResult(int result) { 777 if (getActivity() == null) { 778 return; 779 } 780 getActivity().setResult(result); 781 } 782 isFinishingOrDestroyed()783 protected boolean isFinishingOrDestroyed() { 784 final Activity activity = getActivity(); 785 return activity == null || activity.isFinishing() || activity.isDestroyed(); 786 } 787 replaceEnterprisePreferenceScreenTitle(String overrideKey, int resource)788 protected void replaceEnterprisePreferenceScreenTitle(String overrideKey, int resource) { 789 getActivity().setTitle(mDevicePolicyManager.getResources().getString( 790 overrideKey, () -> getString(resource))); 791 } 792 replaceEnterpriseStringSummary( String preferenceKey, String overrideKey, int resource)793 public void replaceEnterpriseStringSummary( 794 String preferenceKey, String overrideKey, int resource) { 795 Preference preference = findPreference(preferenceKey); 796 if (preference == null) { 797 Log.d(TAG, "Could not find enterprise preference " + preferenceKey); 798 return; 799 } 800 801 preference.setSummary( 802 mDevicePolicyManager.getResources().getString(overrideKey, 803 () -> getString(resource))); 804 } 805 replaceEnterpriseStringTitle( String preferenceKey, String overrideKey, int resource)806 public void replaceEnterpriseStringTitle( 807 String preferenceKey, String overrideKey, int resource) { 808 Preference preference = findPreference(preferenceKey); 809 if (preference == null) { 810 Log.d(TAG, "Could not find enterprise preference " + preferenceKey); 811 return; 812 } 813 814 preference.setTitle( 815 mDevicePolicyManager.getResources().getString(overrideKey, 816 () -> getString(resource))); 817 } 818 } 819