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