• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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