content: /*
   * Copyright (C) 2007 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.preference;
  
  import android.annotation.CallSuper;
  import android.annotation.DrawableRes;
  import android.annotation.LayoutRes;
  import android.annotation.Nullable;
  import android.annotation.StringRes;
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.content.res.TypedArray;
  import android.graphics.drawable.Drawable;
  import android.os.Build;
  import android.os.Bundle;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.text.TextUtils;
  import android.util.AttributeSet;
  import android.view.AbsSavedState;
  import android.view.KeyEvent;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.ImageView;
  import android.widget.ListView;
  import android.widget.TextView;
  
  import com.android.internal.util.CharSequences;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Set;
  
  /**
   * Represents the basic Preference UI building
   * block displayed by a {@link PreferenceActivity} in the form of a
   * {@link ListView}. This class provides the {@link View} to be displayed in
   * the activity and associates with a {@link SharedPreferences} to
   * store/retrieve the preference data.
   * <p>
   * When specifying a preference hierarchy in XML, each element can point to a
   * subclass of {@link Preference}, similar to the view hierarchy and layouts.
   * <p>
   * This class contains a {@code key} that will be used as the key into the
   * {@link SharedPreferences}. It is up to the subclass to decide how to store
   * the value.
   *
   * <div class="special reference">
   * <h3>Developer Guides</h3>
   * <p>For information about building a settings UI with Preferences,
   * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
   * guide.</p>
   * </div>
   *
   * @attr ref android.R.styleable#Preference_icon
   * @attr ref android.R.styleable#Preference_key
   * @attr ref android.R.styleable#Preference_title
   * @attr ref android.R.styleable#Preference_summary
   * @attr ref android.R.styleable#Preference_order
   * @attr ref android.R.styleable#Preference_fragment
   * @attr ref android.R.styleable#Preference_layout
   * @attr ref android.R.styleable#Preference_widgetLayout
   * @attr ref android.R.styleable#Preference_enabled
   * @attr ref android.R.styleable#Preference_selectable
   * @attr ref android.R.styleable#Preference_dependency
   * @attr ref android.R.styleable#Preference_persistent
   * @attr ref android.R.styleable#Preference_defaultValue
   * @attr ref android.R.styleable#Preference_shouldDisableView
   * @attr ref android.R.styleable#Preference_recycleEnabled
   * @attr ref android.R.styleable#Preference_singleLineTitle
   * @attr ref android.R.styleable#Preference_iconSpaceReserved
   *
   * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
   *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
   *      Preference Library</a> for consistent behavior across all devices. For more information on
   *      using the AndroidX Preference Library see
   *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
   */
  @Deprecated
  public class Preference implements Comparable<Preference> {
      /**
       * Specify for {@link #setOrder(int)} if a specific order is not required.
       */
      public static final int DEFAULT_ORDER = Integer.MAX_VALUE;
  
      private Context mContext;
  
      @Nullable
      private PreferenceManager mPreferenceManager;
  
      /**
       * The data store that should be used by this Preference to store / retrieve data. If null then
       * {@link PreferenceManager#getPreferenceDataStore()} needs to be checked. If that one is null
       * too it means that we are using {@link android.content.SharedPreferences} to store the data.
       */
      @Nullable
      private PreferenceDataStore mPreferenceDataStore;
  
      /**
       * Set when added to hierarchy since we need a unique ID within that
       * hierarchy.
       */
      private long mId;
  
      private OnPreferenceChangeListener mOnChangeListener;
      private OnPreferenceClickListener mOnClickListener;
  
      private int mOrder = DEFAULT_ORDER;
      private CharSequence mTitle;
      private int mTitleRes;
      @UnsupportedAppUsage
      private CharSequence mSummary;
      /**
       * mIconResId is overridden by mIcon, if mIcon is specified.
       */
      private int mIconResId;
      private Drawable mIcon;
      private String mKey;
      private Intent mIntent;
      private String mFragment;
      private Bundle mExtras;
      private boolean mEnabled = true;
      private boolean mSelectable = true;
      private boolean mRequiresKey;
      private boolean mPersistent = true;
      private String mDependencyKey;
      private Object mDefaultValue;
      private boolean mDependencyMet = true;
      private boolean mParentDependencyMet = true;
      private boolean mRecycleEnabled = true;
      private boolean mHasSingleLineTitleAttr;
      private boolean mSingleLineTitle = true;
      private boolean mIconSpaceReserved;
  
      /**
       * @see #setShouldDisableView(boolean)
       */
      private boolean mShouldDisableView = true;
  
      @UnsupportedAppUsage
      private int mLayoutResId = com.android.internal.R.layout.preference;
      @UnsupportedAppUsage
      private int mWidgetLayoutResId;
  
      private OnPreferenceChangeInternalListener mListener;
  
      private List<Preference> mDependents;
  
      private PreferenceGroup mParentGroup;
  
      private boolean mBaseMethodCalled;
  
      /**
       * Interface definition for a callback to be invoked when the value of this
       * {@link Preference} has been changed by the user and is
       * about to be set and/or persisted.  This gives the client a chance
       * to prevent setting and/or persisting the value.
       *
       * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
       *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
       *      Preference Library</a> for consistent behavior across all devices.
       *      For more information on using the AndroidX Preference Library see
       *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
       */
      @Deprecated
      public interface OnPreferenceChangeListener {
          /**
           * Called when a Preference has been changed by the user. This is
           * called before the state of the Preference is about to be updated and
           * before the state is persisted.
           *
           * @param preference The changed Preference.
           * @param newValue The new value of the Preference.
           * @return True to update the state of the Preference with the new value.
           */
          boolean onPreferenceChange(Preference preference, Object newValue);
      }
  
      /**
       * Interface definition for a callback to be invoked when a {@link Preference} is
       * clicked.
       *
       * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
       *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
       *      Preference Library</a> for consistent behavior across all devices.
       *      For more information on using the AndroidX Preference Library see
       *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
       */
      @Deprecated
      public interface OnPreferenceClickListener {
          /**
           * Called when a Preference has been clicked.
           *
           * @param preference The Preference that was clicked.
           * @return True if the click was handled.
           */
          boolean onPreferenceClick(Preference preference);
      }
  
      /**
       * Interface definition for a callback to be invoked when this
       * {@link Preference} is changed or, if this is a group, there is an
       * addition/removal of {@link Preference}(s). This is used internally.
       */
      interface OnPreferenceChangeInternalListener {
          /**
           * Called when this Preference has changed.
           *
           * @param preference This preference.
           */
          void onPreferenceChange(Preference preference);
  
          /**
           * Called when this group has added/removed {@link Preference}(s).
           *
           * @param preference This Preference.
           */
          void onPreferenceHierarchyChange(Preference preference);
      }
  
      /**
       * Perform inflation from XML and apply a class-specific base style. This
       * constructor of Preference allows subclasses to use their own base style
       * when they are inflating. For example, a {@link CheckBoxPreference}
       * constructor calls this version of the super class constructor and
       * supplies {@code android.R.attr.checkBoxPreferenceStyle} for
       * <var>defStyleAttr</var>. This allows the theme's checkbox preference
       * style to modify all of the base preference attributes as well as the
       * {@link CheckBoxPreference} class's attributes.
       *
       * @param context The Context this is associated with, through which it can
       *            access the current theme, resources,
       *            {@link SharedPreferences}, etc.
       * @param attrs The attributes of the XML tag that is inflating the
       *            preference.
       * @param defStyleAttr An attribute in the current theme that contains a
       *            reference to a style resource that supplies default values for
       *            the view. Can be 0 to not look for defaults.
       * @param defStyleRes A resource identifier of a style resource that
       *            supplies default values for the view, used only if
       *            defStyleAttr is 0 or can not be found in the theme. Can be 0
       *            to not look for defaults.
       * @see #Preference(Context, AttributeSet)
       */
      public Preference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
          mContext = context;
  
          final TypedArray a = context.obtainStyledAttributes(
                  attrs, com.android.internal.R.styleable.Preference, defStyleAttr, defStyleRes);
          for (int i = a.getIndexCount() - 1; i >= 0; i--) {
              int attr = a.getIndex(i);
              switch (attr) {
                  case com.android.internal.R.styleable.Preference_icon:
                      mIconResId = a.getResourceId(attr, 0);
                      break;
  
                  case com.android.internal.R.styleable.Preference_key:
                      mKey = a.getString(attr);
                      break;
  
                  case com.android.internal.R.styleable.Preference_title:
                      mTitleRes = a.getResourceId(attr, 0);
                      mTitle = a.getText(attr);
                      break;
  
                  case com.android.internal.R.styleable.Preference_summary:
                      mSummary = a.getText(attr);
                      break;
  
                  case com.android.internal.R.styleable.Preference_order:
                      mOrder = a.getInt(attr, mOrder);
                      break;
  
                  case com.android.internal.R.styleable.Preference_fragment:
                      mFragment = a.getString(attr);
                      break;
  
                  case com.android.internal.R.styleable.Preference_layout:
                      mLayoutResId = a.getResourceId(attr, mLayoutResId);
                      break;
  
                  case com.android.internal.R.styleable.Preference_widgetLayout:
                      mWidgetLayoutResId = a.getResourceId(attr, mWidgetLayoutResId);
                      break;
  
                  case com.android.internal.R.styleable.Preference_enabled:
                      mEnabled = a.getBoolean(attr, true);
                      break;
  
                  case com.android.internal.R.styleable.Preference_selectable:
                      mSelectable = a.getBoolean(attr, true);
                      break;
  
                  case com.android.internal.R.styleable.Preference_persistent:
                      mPersistent = a.getBoolean(attr, mPersistent);
                      break;
  
                  case com.android.internal.R.styleable.Preference_dependency:
                      mDependencyKey = a.getString(attr);
                      break;
  
                  case com.android.internal.R.styleable.Preference_defaultValue:
                      mDefaultValue = onGetDefaultValue(a, attr);
                      break;
  
                  case com.android.internal.R.styleable.Preference_shouldDisableView:
                      mShouldDisableView = a.getBoolean(attr, mShouldDisableView);
                      break;
  
                  case com.android.internal.R.styleable.Preference_recycleEnabled:
                      mRecycleEnabled = a.getBoolean(attr, mRecycleEnabled);
                      break;
  
                  case com.android.internal.R.styleable.Preference_singleLineTitle:
                      mSingleLineTitle = a.getBoolean(attr, mSingleLineTitle);
                      mHasSingleLineTitleAttr = true;
                      break;
  
                  case com.android.internal.R.styleable.Preference_iconSpaceReserved:
                      mIconSpaceReserved = a.getBoolean(attr, mIconSpaceReserved);
                      break;
             }
          }
          a.recycle();
      }
  
      /**
       * Perform inflation from XML and apply a class-specific base style. This
       * constructor of Preference allows subclasses to use their own base style
       * when they are inflating. For example, a {@link CheckBoxPreference}
       * constructor calls this version of the super class constructor and
       * supplies {@code android.R.attr.checkBoxPreferenceStyle} for
       * <var>defStyleAttr</var>. This allows the theme's checkbox preference
       * style to modify all of the base preference attributes as well as the
       * {@link CheckBoxPreference} class's attributes.
       *
       * @param context The Context this is associated with, through which it can
       *            access the current theme, resources,
       *            {@link SharedPreferences}, etc.
       * @param attrs The attributes of the XML tag that is inflating the
       *            preference.
       * @param defStyleAttr An attribute in the current theme that contains a
       *            reference to a style resource that supplies default values for
       *            the view. Can be 0 to not look for defaults.
       * @see #Preference(Context, AttributeSet)
       */
      public Preference(Context context, AttributeSet attrs, int defStyleAttr) {
          this(context, attrs, defStyleAttr, 0);
      }
  
      /**
       * Constructor that is called when inflating a Preference from XML. This is
       * called when a Preference is being constructed from an XML file, supplying
       * attributes that were specified in the XML file. This version uses a
       * default style of 0, so the only attribute values applied are those in the
       * Context's Theme and the given AttributeSet.
       *
       * @param context The Context this is associated with, through which it can
       *            access the current theme, resources, {@link SharedPreferences},
       *            etc.
       * @param attrs The attributes of the XML tag that is inflating the
       *            preference.
       * @see #Preference(Context, AttributeSet, int)
       */
      public Preference(Context context, AttributeSet attrs) {
          this(context, attrs, com.android.internal.R.attr.preferenceStyle);
      }
  
      /**
       * Constructor to create a Preference.
       *
       * @param context The Context in which to store Preference values.
       */
      public Preference(Context context) {
          this(context, null);
      }
  
      /**
       * Called when a Preference is being inflated and the default value
       * attribute needs to be read. Since different Preference types have
       * different value types, the subclass should get and return the default
       * value which will be its value type.
       * <p>
       * For example, if the value type is String, the body of the method would
       * proxy to {@link TypedArray#getString(int)}.
       *
       * @param a The set of attributes.
       * @param index The index of the default value attribute.
       * @return The default value of this preference type.
       */
      protected Object onGetDefaultValue(TypedArray a, int index) {
          return null;
      }
  
      /**
       * Sets an {@link Intent} to be used for
       * {@link Context#startActivity(Intent)} when this Preference is clicked.
       *
       * @param intent The intent associated with this Preference.
       */
      public void setIntent(Intent intent) {
          mIntent = intent;
      }
  
      /**
       * Return the {@link Intent} associated with this Preference.
       *
       * @return The {@link Intent} last set via {@link #setIntent(Intent)} or XML. 
       */
      public Intent getIntent() {
          return mIntent;
      }
  
      /**
       * Sets the class name of a fragment to be shown when this Preference is clicked.
       *
       * @param fragment The class name of the fragment associated with this Preference.
       */
      public void setFragment(String fragment) {
          mFragment = fragment;
      }
  
      /**
       * Return the fragment class name associated with this Preference.
       *
       * @return The fragment class name last set via {@link #setFragment} or XML.
       */
      public String getFragment() {
          return mFragment;
      }
  
      /**
       * Sets a {@link PreferenceDataStore} to be used by this Preference instead of using
       * {@link android.content.SharedPreferences}.
       *
       * <p>The data store will remain assigned even if the Preference is moved around the preference
       * hierarchy. It will also override a data store propagated from the {@link PreferenceManager}
       * that owns this Preference.
       *
       * @param dataStore The {@link PreferenceDataStore} to be used by this Preference.
       * @see PreferenceManager#setPreferenceDataStore(PreferenceDataStore)
       */
      public void setPreferenceDataStore(PreferenceDataStore dataStore) {
          mPreferenceDataStore = dataStore;
      }
  
      /**
       * Returns {@link PreferenceDataStore} used by this Preference. Returns {@code null} if
       * {@link android.content.SharedPreferences} is used instead.
       *
       * <p>By default preferences always use {@link android.content.SharedPreferences}. To make this
       * preference to use the {@link PreferenceDataStore} you need to assign your implementation
       * to the Preference itself via {@link #setPreferenceDataStore(PreferenceDataStore)} or to its
       * {@link PreferenceManager} via
       * {@link PreferenceManager#setPreferenceDataStore(PreferenceDataStore)}.
       *
       * @return The {@link PreferenceDataStore} used by this Preference or {@code null} if none.
       */
      @Nullable
      public PreferenceDataStore getPreferenceDataStore() {
          if (mPreferenceDataStore != null) {
              return mPreferenceDataStore;
          } else if (mPreferenceManager != null) {
              return mPreferenceManager.getPreferenceDataStore();
          }
  
          return null;
      }
  
      /**
       * Return the extras Bundle object associated with this preference, creating
       * a new Bundle if there currently isn't one.  You can use this to get and
       * set individual extra key/value pairs.
       */
      public Bundle getExtras() {
          if (mExtras == null) {
              mExtras = new Bundle();
          }
          return mExtras;
      }
  
      /**
       * Return the extras Bundle object associated with this preference, returning {@code null} if
       * there is not currently one.
       */
      public Bundle peekExtras() {
          return mExtras;
      }
  
      /**
       * Sets the layout resource that is inflated as the {@link View} to be shown
       * for this Preference. In most cases, the default layout is sufficient for
       * custom Preference objects and only the widget layout needs to be changed.
       * <p>
       * This layout should contain a {@link ViewGroup} with ID
       * {@link android.R.id#widget_frame} to be the parent of the specific widget
       * for this Preference. It should similarly contain
       * {@link android.R.id#title} and {@link android.R.id#summary}.
       *
       * @param layoutResId The layout resource ID to be inflated and returned as
       *            a {@link View}.
       * @see #setWidgetLayoutResource(int)
       */
      public void setLayoutResource(@LayoutRes int layoutResId) {
          if (layoutResId != mLayoutResId) {
              // Layout changed
              mRecycleEnabled = false;
          }
  
          mLayoutResId = layoutResId;
      }
  
      /**
       * Gets the layout resource that will be shown as the {@link View} for this Preference.
       *
       * @return The layout resource ID.
       */
      @LayoutRes
      public int getLayoutResource() {
          return mLayoutResId;
      }
  
      /**
       * Sets the layout for the controllable widget portion of this Preference. This
       * is inflated into the main layout. For example, a {@link CheckBoxPreference}
       * would specify a custom layout (consisting of just the CheckBox) here,
       * instead of creating its own main layout.
       *
       * @param widgetLayoutResId The layout resource ID to be inflated into the
       *            main layout.
       * @see #setLayoutResource(int)
       */
      public void setWidgetLayoutResource(@LayoutRes int widgetLayoutResId) {
          if (widgetLayoutResId != mWidgetLayoutResId) {
              // Layout changed
              mRecycleEnabled = false;
          }
          mWidgetLayoutResId = widgetLayoutResId;
      }
  
      /**
       * Gets the layout resource for the controllable widget portion of this Preference.
       *
       * @return The layout resource ID.
       */
      @LayoutRes
      public int getWidgetLayoutResource() {
          return mWidgetLayoutResId;
      }
  
      /**
       * Gets the View that will be shown in the {@link PreferenceActivity}.
       *
       * @param convertView The old View to reuse, if possible. Note: You should
       *            check that this View is non-null and of an appropriate type
       *            before using. If it is not possible to convert this View to
       *            display the correct data, this method can create a new View.
       * @param parent The parent that this View will eventually be attached to.
       * @return Returns the same Preference object, for chaining multiple calls
       *         into a single statement.
       * @see #onCreateView(ViewGroup)
       * @see #onBindView(View)
       */
      public View getView(View convertView, ViewGroup parent) {
          if (convertView == null) {
              convertView = onCreateView(parent);
          }
          onBindView(convertView);
          return convertView;
      }
  
      /**
       * Creates the View to be shown for this Preference in the
       * {@link PreferenceActivity}. The default behavior is to inflate the main
       * layout of this Preference (see {@link #setLayoutResource(int)}. If
       * changing this behavior, please specify a {@link ViewGroup} with ID
       * {@link android.R.id#widget_frame}.
       * <p>
       * Make sure to call through to the superclass's implementation.
       *
       * @param parent The parent that this View will eventually be attached to.
       * @return The View that displays this Preference.
       * @see #onBindView(View)
       */
      @CallSuper
      protected View onCreateView(ViewGroup parent) {
          final LayoutInflater layoutInflater =
                  (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  
          final View layout = layoutInflater.inflate(mLayoutResId, parent, false);
  
          final ViewGroup widgetFrame = (ViewGroup) layout
                  .findViewById(com.android.internal.R.id.widget_frame);
          if (widgetFrame != null) {
              if (mWidgetLayoutResId != 0) {
                  layoutInflater.inflate(mWidgetLayoutResId, widgetFrame);
              } else {
                  widgetFrame.setVisibility(View.GONE);
              }
          }
          return layout;
      }
  
      /**
       * Binds the created View to the data for this Preference.
       * <p>
       * This is a good place to grab references to custom Views in the layout and
       * set properties on them.
       * <p>
       * Make sure to call through to the superclass's implementation.
       *
       * @param view The View that shows this Preference.
       * @see #onCreateView(ViewGroup)
       */
      @CallSuper
      protected void onBindView(View view) {
          final TextView titleView = (TextView) view.findViewById(com.android.internal.R.id.title);
          if (titleView != null) {
              final CharSequence title = getTitle();
              if (!TextUtils.isEmpty(title)) {
                  titleView.setText(title);
                  titleView.setVisibility(View.VISIBLE);
                  if (mHasSingleLineTitleAttr) {
                      titleView.setSingleLine(mSingleLineTitle);
                  }
              } else {
                  titleView.setVisibility(View.GONE);
              }
          }
  
          final TextView summaryView = (TextView) view.findViewById(
                  com.android.internal.R.id.summary);
          if (summaryView != null) {
              final CharSequence summary = getSummary();
              if (!TextUtils.isEmpty(summary)) {
                  summaryView.setText(summary);
                  summaryView.setVisibility(View.VISIBLE);
              } else {
                  summaryView.setVisibility(View.GONE);
              }
          }
  
          final ImageView imageView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
          if (imageView != null) {
              if (mIconResId != 0 || mIcon != null) {
                  if (mIcon == null) {
                      mIcon = getContext().getDrawable(mIconResId);
                  }
                  if (mIcon != null) {
                      imageView.setImageDrawable(mIcon);
                  }
              }
              if (mIcon != null) {
                  imageView.setVisibility(View.VISIBLE);
              } else {
                  imageView.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
              }
          }
  
          final View imageFrame = view.findViewById(com.android.internal.R.id.icon_frame);
          if (imageFrame != null) {
              if (mIcon != null) {
                  imageFrame.setVisibility(View.VISIBLE);
              } else {
                  imageFrame.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
              }
          }
  
          if (mShouldDisableView) {
              setEnabledStateOnViews(view, isEnabled());
          }
      }
  
      /**
       * Makes sure the view (and any children) get the enabled state changed.
       */
      private void setEnabledStateOnViews(View v, boolean enabled) {
          v.setEnabled(enabled);
  
          if (v instanceof ViewGroup) {
              final ViewGroup vg = (ViewGroup) v;
              for (int i = vg.getChildCount() - 1; i >= 0; i--) {
                  setEnabledStateOnViews(vg.getChildAt(i), enabled);
              }
          }
      }
  
      /**
       * Sets the order of this Preference with respect to other Preference objects on the same level.
       * If this is not specified, the default behavior is to sort alphabetically. The
       * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order Preference objects
       * based on the order they appear in the XML.
       *
       * @param order the order for this Preference. A lower value will be shown first. Use
       *              {@link #DEFAULT_ORDER} to sort alphabetically or allow ordering from XML
       * @see PreferenceGroup#setOrderingAsAdded(boolean)
       * @see #DEFAULT_ORDER
       */
      public void setOrder(int order) {
          if (order != mOrder) {
              mOrder = order;
  
              // Reorder the list
              notifyHierarchyChanged();
          }
      }
  
      /**
       * Gets the order of this Preference with respect to other Preference objects on the same level.
       *
       * @return the order of this Preference
       * @see #setOrder(int)
       */
      public int getOrder() {
          return mOrder;
      }
  
      /**
       * Sets the title for this Preference with a CharSequence. This title will be placed into the ID
       * {@link android.R.id#title} within the View created by {@link #onCreateView(ViewGroup)}.
       *
       * @param title the title for this Preference
       */
      public void setTitle(CharSequence title) {
          if (title == null && mTitle != null || title != null && !title.equals(mTitle)) {
              mTitleRes = 0;
              mTitle = title;
              notifyChanged();
          }
      }
  
      /**
       * Sets the title for this Preference with a resource ID.
       *
       * @see #setTitle(CharSequence)
       * @param titleResId the title as a resource ID
       */
      public void setTitle(@StringRes int titleResId) {
          setTitle(mContext.getString(titleResId));
          mTitleRes = titleResId;
      }
  
      /**
       * Returns the title resource ID of this Preference. If the title did not come from a resource,
       * {@code 0} is returned.
       *
       * @return the title resource
       * @see #setTitle(int)
       */
      @StringRes
      public int getTitleRes() {
          return mTitleRes;
      }
  
      /**
       * Returns the title of this Preference.
       *
       * @return the title
       * @see #setTitle(CharSequence)
       */
      public CharSequence getTitle() {
          return mTitle;
      }
  
      /**
       * Sets the icon for this Preference with a Drawable. This icon will be placed into the ID
       * {@link android.R.id#icon} within the View created by {@link #onCreateView(ViewGroup)}.
       *
       * @param icon the optional icon for this Preference
       */
      public void setIcon(Drawable icon) {
          if ((icon == null && mIcon != null) || (icon != null && mIcon != icon)) {
              mIcon = icon;
  
              notifyChanged();
          }
      }
  
      /**
       * Sets the icon for this Preference with a resource ID.
       *
       * @see #setIcon(Drawable)
       * @param iconResId the icon as a resource ID
       */
      public void setIcon(@DrawableRes int iconResId) {
          if (mIconResId != iconResId) {
              mIconResId = iconResId;
              setIcon(mContext.getDrawable(iconResId));
          }
      }
  
      /**
       * Returns the icon of this Preference.
       *
       * @return the icon
       * @see #setIcon(Drawable)
       */
      public Drawable getIcon() {
          if (mIcon == null && mIconResId != 0) {
              mIcon = getContext().getDrawable(mIconResId);
          }
          return mIcon;
      }
  
      /**
       * Returns the summary of this Preference.
       *
       * @return the summary
       * @see #setSummary(CharSequence)
       */
      public CharSequence getSummary() {
          return mSummary;
      }
  
      /**
       * Sets the summary for this Preference with a CharSequence.
       *
       * @param summary the summary for the preference
       */
      public void setSummary(CharSequence summary) {
          if (summary == null && mSummary != null || summary != null && !summary.equals(mSummary)) {
              mSummary = summary;
              notifyChanged();
          }
      }
  
      /**
       * Sets the summary for this Preference with a resource ID.
       *
       * @see #setSummary(CharSequence)
       * @param summaryResId the summary as a resource
       */
      public void setSummary(@StringRes int summaryResId) {
          setSummary(mContext.getString(summaryResId));
      }
  
      /**
       * Sets whether this Preference is enabled. If disabled, it will
       * not handle clicks.
       *
       * @param enabled set {@code true} to enable it
       */
      public void setEnabled(boolean enabled) {
          if (mEnabled != enabled) {
              mEnabled = enabled;
  
              // Enabled state can change dependent preferences' states, so notify
              notifyDependencyChange(shouldDisableDependents());
  
              notifyChanged();
          }
      }
  
      /**
       * Checks whether this Preference should be enabled in the list.
       *
       * @return {@code true} if this Preference is enabled, false otherwise
       */
      public boolean isEnabled() {
          return mEnabled && mDependencyMet && mParentDependencyMet;
      }
  
      /**
       * Sets whether this Preference is selectable.
       *
       * @param selectable set {@code true} to make it selectable
       */
      public void setSelectable(boolean selectable) {
          if (mSelectable != selectable) {
              mSelectable = selectable;
              notifyChanged();
          }
      }
  
      /**
       * Checks whether this Preference should be selectable in the list.
       *
       * @return {@code true} if it is selectable, {@code false} otherwise
       */
      public boolean isSelectable() {
          return mSelectable;
      }
  
      /**
       * Sets whether this Preference should disable its view when it gets disabled.
       *
       * <p>For example, set this and {@link #setEnabled(boolean)} to false for preferences that are
       * only displaying information and 1) should not be clickable 2) should not have the view set to
       * the disabled state.
       *
       * @param shouldDisableView set {@code true} if this preference should disable its view when
       *                          the preference is disabled
       */
      public void setShouldDisableView(boolean shouldDisableView) {
          mShouldDisableView = shouldDisableView;
          notifyChanged();
      }
  
      /**
       * Checks whether this Preference should disable its view when it's action is disabled.
       *
       * @see #setShouldDisableView(boolean)
       * @return {@code true} if it should disable the view
       */
      public boolean getShouldDisableView() {
          return mShouldDisableView;
      }
  
      /**
       * Sets whether this Preference has enabled to have its view recycled when used in the list
       * view. By default the recycling is enabled.
       *
       * <p>The value can be changed only before this preference is added to the preference hierarchy.
       *
       * <p>If view recycling is not allowed then each time the list view populates this preference
       * the {@link #getView(View, ViewGroup)} method receives a {@code null} convert view and needs
       * to recreate the view. Otherwise view gets recycled and only {@link #onBindView(View)} gets
       * called.
       *
       * @param enabled set {@code true} if this preference view should be recycled
       */
      @CallSuper
      public void setRecycleEnabled(boolean enabled) {
          mRecycleEnabled = enabled;
          notifyChanged();
      }
  
      /**
       * Checks whether this Preference has enabled to have its view recycled when used in the list
       * view.
       *
       * @see #setRecycleEnabled(boolean)
       * @return {@code true} if this preference view should be recycled
       */
      public boolean isRecycleEnabled() {
          return mRecycleEnabled;
      }
  
      /**
       * Sets whether to constrain the title of this Preference to a single line instead of
       * letting it wrap onto multiple lines.
       *
       * @param singleLineTitle set {@code true} if the title should be constrained to one line
       */
      public void setSingleLineTitle(boolean singleLineTitle) {
          mHasSingleLineTitleAttr = true;
          mSingleLineTitle = singleLineTitle;
          notifyChanged();
      }
  
      /**
       * Gets whether the title of this preference is constrained to a single line.
       *
       * @see #setSingleLineTitle(boolean)
       * @return {@code true} if the title of this preference is constrained to a single line
       */
      public boolean isSingleLineTitle() {
          return mSingleLineTitle;
      }
  
      /**
       * Sets whether to reserve the space of this Preference icon view when no icon is provided.
       *
       * @param iconSpaceReserved set {@code true} if the space for the icon view should be reserved
       */
      public void setIconSpaceReserved(boolean iconSpaceReserved) {
          mIconSpaceReserved = iconSpaceReserved;
          notifyChanged();
      }
  
      /**
       * Gets whether the space this preference icon view is reserved.
       *
       * @see #setIconSpaceReserved(boolean)
       * @return {@code true} if the space of this preference icon view is reserved
       */
      public boolean isIconSpaceReserved() {
          return mIconSpaceReserved;
      }
      /**
       * Returns a unique ID for this Preference.  This ID should be unique across all
       * Preference objects in a hierarchy.
       *
       * @return A unique ID for this Preference.
       */
      @UnsupportedAppUsage
      long getId() {
          return mId;
      }
  
      /**
       * Processes a click on the preference. This includes saving the value to
       * the {@link SharedPreferences}. However, the overridden method should
       * call {@link #callChangeListener(Object)} to make sure the client wants to
       * update the preference's state with the new value.
       */
      protected void onClick() {
      }
  
      /**
       * Sets the key for this Preference, which is used as a key to the {@link SharedPreferences} or
       * {@link PreferenceDataStore}. This should be unique for the package.
       *
       * @param key The key for the preference.
       */
      public void setKey(String key) {
          mKey = key;
  
          if (mRequiresKey && !hasKey()) {
              requireKey();
          }
      }
  
      /**
       * Gets the key for this Preference, which is also the key used for storing values into
       * {@link SharedPreferences} or {@link PreferenceDataStore}.
       *
       * @return The key.
       */
      public String getKey() {
          return mKey;
      }
  
      /**
       * Checks whether the key is present, and if it isn't throws an
       * exception. This should be called by subclasses that persist their preferences.
       *
       * @throws IllegalStateException If there is no key assigned.
       */
      void requireKey() {
          if (mKey == null) {
              throw new IllegalStateException("Preference does not have a key assigned.");
          }
  
          mRequiresKey = true;
      }
  
      /**
       * Checks whether this Preference has a valid key.
       *
       * @return True if the key exists and is not a blank string, false otherwise.
       */
      public boolean hasKey() {
          return !TextUtils.isEmpty(mKey);
      }
  
      /**
       * Checks whether this Preference is persistent. If it is, it stores its value(s) into
       * the persistent {@link SharedPreferences} storage by default or into
       * {@link PreferenceDataStore} if assigned.
       *
       * @return True if it is persistent.
       */
      public boolean isPersistent() {
          return mPersistent;
      }
  
      /**
       * Checks whether, at the given time this method is called, this Preference should store/restore
       * its value(s) into the {@link SharedPreferences} or into {@link PreferenceDataStore} if
       * assigned. This, at minimum, checks whether this Preference is persistent and it currently has
       * a key. Before you save/restore from the storage, check this first.
       *
       * @return True if it should persist the value.
       */
      protected boolean shouldPersist() {
          return mPreferenceManager != null && isPersistent() && hasKey();
      }
  
      /**
       * Sets whether this Preference is persistent. When persistent, it stores its value(s) into
       * the persistent {@link SharedPreferences} storage by default or into
       * {@link PreferenceDataStore} if assigned.
       *
       * @param persistent set {@code true} if it should store its value(s) into the storage.
       */
      public void setPersistent(boolean persistent) {
          mPersistent = persistent;
      }
  
      /**
       * Call this method after the user changes the preference, but before the
       * internal state is set. This allows the client to ignore the user value.
       *
       * @param newValue The new value of this Preference.
       * @return True if the user value should be set as the preference
       *         value (and persisted).
       */
      protected boolean callChangeListener(Object newValue) {
          return mOnChangeListener == null || mOnChangeListener.onPreferenceChange(this, newValue);
      }
  
      /**
       * Sets the callback to be invoked when this Preference is changed by the
       * user (but before the internal state has been updated).
       *
       * @param onPreferenceChangeListener The callback to be invoked.
       */
      public void setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener) {
          mOnChangeListener = onPreferenceChangeListener;
      }
  
      /**
       * Returns the callback to be invoked when this Preference is changed by the
       * user (but before the internal state has been updated).
       *
       * @return The callback to be invoked.
       */
      public OnPreferenceChangeListener getOnPreferenceChangeListener() {
          return mOnChangeListener;
      }
  
      /**
       * Sets the callback to be invoked when this Preference is clicked.
       *
       * @param onPreferenceClickListener The callback to be invoked.
       */
      public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
          mOnClickListener = onPreferenceClickListener;
      }
  
      /**
       * Returns the callback to be invoked when this Preference is clicked.
       *
       * @return The callback to be invoked.
       */
      public OnPreferenceClickListener getOnPreferenceClickListener() {
          return mOnClickListener;
      }
  
      /**
       * Called when a click should be performed.
       *
       * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
       *            listener should be called in the proper order (between other
       *            processing). May be {@code null}.
       * @hide
       */
      @UnsupportedAppUsage
      public void performClick(PreferenceScreen preferenceScreen) {
  
          if (!isEnabled()) {
              return;
          }
  
          onClick();
  
          if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
              return;
          }
  
          PreferenceManager preferenceManager = getPreferenceManager();
          if (preferenceManager != null) {
              PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
                      .getOnPreferenceTreeClickListener();
              if (preferenceScreen != null && listener != null
                      && listener.onPreferenceTreeClick(preferenceScreen, this)) {
                  return;
              }
          }
  
          if (mIntent != null) {
              Context context = getContext();
              context.startActivity(mIntent);
          }
      }
  
      /**
       * Allows a Preference to intercept key events without having focus.
       * For example, SeekBarPreference uses this to intercept +/- to adjust
       * the progress.
       * @return True if the Preference handled the key. Returns false by default.
       * @hide
       */
      @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
      public boolean onKey(View v, int keyCode, KeyEvent event) {
          return false;
      }
  
      /**
       * Returns the {@link android.content.Context} of this Preference.
       * Each Preference in a Preference hierarchy can be
       * from different Context (for example, if multiple activities provide preferences into a single
       * {@link PreferenceActivity}). This Context will be used to save the Preference values.
       *
       * @return The Context of this Preference.
       */
      public Context getContext() {
          return mContext;
      }
  
      /**
       * Returns the {@link SharedPreferences} where this Preference can read its
       * value(s). Usually, it's easier to use one of the helper read methods:
       * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)},
       * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)},
       * {@link #getPersistedString(String)}. To save values, see
       * {@link #getEditor()}.
       * <p>
       * In some cases, writes to the {@link #getEditor()} will not be committed
       * right away and hence not show up in the returned
       * {@link SharedPreferences}, this is intended behavior to improve
       * performance.
       *
       * @return the {@link SharedPreferences} where this Preference reads its value(s). If
       *         this preference isn't attached to a Preference hierarchy or if
       *         a {@link PreferenceDataStore} has been set, this method returns {@code null}.
       * @see #getEditor()
       * @see #setPreferenceDataStore(PreferenceDataStore)
       */
      public SharedPreferences getSharedPreferences() {
          if (mPreferenceManager == null || getPreferenceDataStore() != null) {
              return null;
          }
  
          return mPreferenceManager.getSharedPreferences();
      }
  
      /**
       * Returns an {@link SharedPreferences.Editor} where this Preference can
       * save its value(s). Usually it's easier to use one of the helper save
       * methods: {@link #persistBoolean(boolean)}, {@link #persistFloat(float)},
       * {@link #persistInt(int)}, {@link #persistLong(long)},
       * {@link #persistString(String)}. To read values, see
       * {@link #getSharedPreferences()}. If {@link #shouldCommit()} returns
       * true, it is this Preference's responsibility to commit.
       * <p>
       * In some cases, writes to this will not be committed right away and hence
       * not show up in the SharedPreferences, this is intended behavior to
       * improve performance.
       *
       * @return a {@link SharedPreferences.Editor} where this preference saves its value(s). If
       *         this preference isn't attached to a Preference hierarchy or if
       *         a {@link PreferenceDataStore} has been set, this method returns {@code null}.
       * @see #shouldCommit()
       * @see #getSharedPreferences()
       * @see #setPreferenceDataStore(PreferenceDataStore)
       */
      public SharedPreferences.Editor getEditor() {
          if (mPreferenceManager == null || getPreferenceDataStore() != null) {
              return null;
          }
  
          return mPreferenceManager.getEditor();
      }
  
      /**
       * Returns whether the {@link Preference} should commit its saved value(s) in
       * {@link #getEditor()}. This may return false in situations where batch
       * committing is being done (by the manager) to improve performance.
       *
       * <p>If this preference is using {@link PreferenceDataStore} this value is irrelevant.
       *
       * @return Whether the Preference should commit its saved value(s).
       * @see #getEditor()
       */
      public boolean shouldCommit() {
          if (mPreferenceManager == null) {
              return false;
          }
  
          return mPreferenceManager.shouldCommit();
      }
  
      /**
       * Compares Preference objects based on order (if set), otherwise alphabetically on the titles.
       *
       * @param another The Preference to compare to this one.
       * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>;
       *          greater than 0 if this Preference sorts after <var>another</var>.
       */
      @Override
      public int compareTo(Preference another) {
          if (mOrder != another.mOrder) {
              // Do order comparison
              return mOrder - another.mOrder;
          } else if (mTitle == another.mTitle) {
              // If titles are null or share same object comparison
              return 0;
          } else if (mTitle == null) {
              return 1;
          } else if (another.mTitle == null) {
              return -1;
          } else {
              // Do name comparison
              return CharSequences.compareToIgnoreCase(mTitle, another.mTitle);
          }
      }
  
      /**
       * Sets the internal change listener.
       *
       * @param listener The listener.
       * @see #notifyChanged()
       */
      @UnsupportedAppUsage
      final void setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener) {
          mListener = listener;
      }
  
      /**
       * Should be called when the data of this {@link Preference} has changed.
       */
      protected void notifyChanged() {
          if (mListener != null) {
              mListener.onPreferenceChange(this);
          }
      }
  
      /**
       * Should be called when a Preference has been
       * added/removed from this group, or the ordering should be
       * re-evaluated.
       */
      protected void notifyHierarchyChanged() {
          if (mListener != null) {
              mListener.onPreferenceHierarchyChange(this);
          }
      }
  
      /**
       * Gets the {@link PreferenceManager} that manages this Preference object's tree.
       *
       * @return The {@link PreferenceManager}.
       */
      public PreferenceManager getPreferenceManager() {
          return mPreferenceManager;
      }
  
      /**
       * Called when this Preference has been attached to a Preference hierarchy.
       * Make sure to call the super implementation.
       *
       * @param preferenceManager The PreferenceManager of the hierarchy.
       */
      protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
          mPreferenceManager = preferenceManager;
  
          mId = preferenceManager.getNextId();
  
          dispatchSetInitialValue();
      }
  
      /**
       * Called when the Preference hierarchy has been attached to the
       * {@link PreferenceActivity}. This can also be called when this
       * Preference has been attached to a group that was already attached
       * to the {@link PreferenceActivity}.
       */
      protected void onAttachedToActivity() {
          // At this point, the hierarchy that this preference is in is connected
          // with all other preferences.
          registerDependency();
      }
  
      /**
       * Assigns a {@link PreferenceGroup} as the parent of this Preference. Set {@code null} to
       * remove the current parent.
       *
       * @param parentGroup Parent preference group of this Preference or {@code null} if none.
       */
      void assignParent(@Nullable PreferenceGroup parentGroup) {
          mParentGroup = parentGroup;
      }
  
      private void registerDependency() {
  
          if (TextUtils.isEmpty(mDependencyKey)) return;
  
          Preference preference = findPreferenceInHierarchy(mDependencyKey);
          if (preference != null) {
              preference.registerDependent(this);
          } else {
              throw new IllegalStateException("Dependency \"" + mDependencyKey
                      + "\" not found for preference \"" + mKey + "\" (title: \"" + mTitle + "\"");
          }
      }
  
      private void unregisterDependency() {
          if (mDependencyKey != null) {
              final Preference oldDependency = findPreferenceInHierarchy(mDependencyKey);
              if (oldDependency != null) {
                  oldDependency.unregisterDependent(this);
              }
          }
      }
  
      /**
       * Finds a Preference in this hierarchy (the whole thing,
       * even above/below your {@link PreferenceScreen} screen break) with the given
       * key.
       * <p>
       * This only functions after we have been attached to a hierarchy.
       *
       * @param key The key of the Preference to find.
       * @return The Preference that uses the given key.
       */
      protected Preference findPreferenceInHierarchy(String key) {
          if (TextUtils.isEmpty(key) || mPreferenceManager == null) {
              return null;
          }
  
          return mPreferenceManager.findPreference(key);
      }
  
      /**
       * Adds a dependent Preference on this Preference so we can notify it.
       * Usually, the dependent Preference registers itself (it's good for it to
       * know it depends on something), so please use
       * {@link Preference#setDependency(String)} on the dependent Preference.
       *
       * @param dependent The dependent Preference that will be enabled/disabled
       *            according to the state of this Preference.
       */
      @UnsupportedAppUsage
      private void registerDependent(Preference dependent) {
          if (mDependents == null) {
              mDependents = new ArrayList<Preference>();
          }
  
          mDependents.add(dependent);
  
          dependent.onDependencyChanged(this, shouldDisableDependents());
      }
  
      /**
       * Removes a dependent Preference on this Preference.
       *
       * @param dependent The dependent Preference that will be enabled/disabled
       *            according to the state of this Preference.
       * @return Returns the same Preference object, for chaining multiple calls
       *         into a single statement.
       */
      private void unregisterDependent(Preference dependent) {
          if (mDependents != null) {
              mDependents.remove(dependent);
          }
      }
  
      /**
       * Notifies any listening dependents of a change that affects the
       * dependency.
       *
       * @param disableDependents Whether this Preference should disable
       *            its dependents.
       */
      public void notifyDependencyChange(boolean disableDependents) {
          final List<Preference> dependents = mDependents;
  
          if (dependents == null) {
              return;
          }
  
          final int dependentsCount = dependents.size();
          for (int i = 0; i < dependentsCount; i++) {
              dependents.get(i).onDependencyChanged(this, disableDependents);
          }
      }
  
      /**
       * Called when the dependency changes.
       *
       * @param dependency The Preference that this Preference depends on.
       * @param disableDependent Set true to disable this Preference.
       */
      public void onDependencyChanged(Preference dependency, boolean disableDependent) {
          if (mDependencyMet == disableDependent) {
              mDependencyMet = !disableDependent;
  
              // Enabled state can change dependent preferences' states, so notify
              notifyDependencyChange(shouldDisableDependents());
  
              notifyChanged();
          }
      }
  
      /**
       * Called when the implicit parent dependency changes.
       *
       * @param parent The Preference that this Preference depends on.
       * @param disableChild Set true to disable this Preference.
       */
      public void onParentChanged(Preference parent, boolean disableChild) {
          if (mParentDependencyMet == disableChild) {
              mParentDependencyMet = !disableChild;
  
              // Enabled state can change dependent preferences' states, so notify
              notifyDependencyChange(shouldDisableDependents());
  
              notifyChanged();
          }
      }
  
      /**
       * Checks whether this preference's dependents should currently be
       * disabled.
       *
       * @return True if the dependents should be disabled, otherwise false.
       */
      public boolean shouldDisableDependents() {
          return !isEnabled();
      }
  
      /**
       * Sets the key of a Preference that this Preference will depend on. If that
       * Preference is not set or is off, this Preference will be disabled.
       *
       * @param dependencyKey The key of the Preference that this depends on.
       */
      public void setDependency(String dependencyKey) {
          // Unregister the old dependency, if we had one
          unregisterDependency();
  
          // Register the new
          mDependencyKey = dependencyKey;
          registerDependency();
      }
  
      /**
       * Returns the key of the dependency on this Preference.
       *
       * @return The key of the dependency.
       * @see #setDependency(String)
       */
      public String getDependency() {
          return mDependencyKey;
      }
  
      /**
       * Returns the {@link PreferenceGroup} which is this Preference assigned to or {@code null} if
       * this preference is not assigned to any group or is a root Preference.
       *
       * @return the parent PreferenceGroup or {@code null} if not attached to any
       */
      @Nullable
      public PreferenceGroup getParent() {
          return mParentGroup;
      }
  
      /**
       * Called when this Preference is being removed from the hierarchy. You
       * should remove any references to this Preference that you know about. Make
       * sure to call through to the superclass implementation.
       */
      @CallSuper
      protected void onPrepareForRemoval() {
          unregisterDependency();
      }
  
      /**
       * Sets the default value for this Preference, which will be set either if
       * persistence is off or persistence is on and the preference is not found
       * in the persistent storage.
       *
       * @param defaultValue The default value.
       */
      public void setDefaultValue(Object defaultValue) {
          mDefaultValue = defaultValue;
      }
  
      private void dispatchSetInitialValue() {
          if (getPreferenceDataStore() != null) {
              onSetInitialValue(true, mDefaultValue);
              return;
          }
  
          // By now, we know if we are persistent.
          final boolean shouldPersist = shouldPersist();
          if (!shouldPersist || !getSharedPreferences().contains(mKey)) {
              if (mDefaultValue != null) {
                  onSetInitialValue(false, mDefaultValue);
              }
          } else {
              onSetInitialValue(true, null);
          }
      }
  
      /**
       * Implement this to set the initial value of the Preference.
       *
       * <p>If <var>restorePersistedValue</var> is true, you should restore the
       * Preference value from the {@link android.content.SharedPreferences}. If
       * <var>restorePersistedValue</var> is false, you should set the Preference
       * value to defaultValue that is given (and possibly store to SharedPreferences
       * if {@link #shouldPersist()} is true).
       *
       * <p>In case of using {@link PreferenceDataStore}, the <var>restorePersistedValue</var> is
       * always {@code true}. But the default value (if provided) is set.
       *
       * <p>This may not always be called. One example is if it should not persist
       * but there is no default value given.
       *
       * @param restorePersistedValue True to restore the persisted value;
       *            false to use the given <var>defaultValue</var>.
       * @param defaultValue The default value for this Preference. Only use this
       *            if <var>restorePersistedValue</var> is false.
       */
      protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
      }
  
      private void tryCommit(SharedPreferences.Editor editor) {
          if (mPreferenceManager.shouldCommit()) {
              try {
                  editor.apply();
              } catch (AbstractMethodError unused) {
                  // The app injected its own pre-Gingerbread
                  // SharedPreferences.Editor implementation without
                  // an apply method.
                  editor.commit();
              }
          }
      }
  
      /**
       * Attempts to persist a String if this Preference is persistent.
       *
       * @param value The value to persist.
       * @return True if this Preference is persistent. (This is not whether the
       *         value was persisted, since we may not necessarily commit if there
       *         will be a batch commit later.)
       * @see #getPersistedString(String)
       */
      protected boolean persistString(String value) {
          if (!shouldPersist()) {
              return false;
          }
  
          // Shouldn't store null
          if (TextUtils.equals(value, getPersistedString(null))) {
              // It's already there, so the same as persisting
              return true;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              dataStore.putString(mKey, value);
          } else {
              SharedPreferences.Editor editor = mPreferenceManager.getEditor();
              editor.putString(mKey, value);
              tryCommit(editor);
          }
          return true;
      }
  
      /**
       * Attempts to get a persisted String if this Preference is persistent.
       *
       * @param defaultReturnValue The default value to return if either this
       *            Preference is not persistent or this Preference is not present.
       * @return The value from the data store or the default return
       *         value.
       * @see #persistString(String)
       */
      protected String getPersistedString(String defaultReturnValue) {
          if (!shouldPersist()) {
              return defaultReturnValue;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              return dataStore.getString(mKey, defaultReturnValue);
          }
  
          return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
      }
  
      /**
       * Attempts to persist a set of Strings if this Preference is persistent.
       *
       * @param values The values to persist.
       * @return True if this Preference is persistent. (This is not whether the
       *         value was persisted, since we may not necessarily commit if there
       *         will be a batch commit later.)
       * @see #getPersistedStringSet(Set)
       */
      public boolean persistStringSet(Set<String>  values) {
          if (!shouldPersist()) {
              return false;
          }
  
          // Shouldn't store null
          if (values.equals(getPersistedStringSet(null))) {
              // It's already there, so the same as persisting
              return true;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              dataStore.putStringSet(mKey, values);
          } else {
              SharedPreferences.Editor editor = mPreferenceManager.getEditor();
              editor.putStringSet(mKey, values);
              tryCommit(editor);
          }
          return true;
      }
  
      /**
       * Attempts to get a persisted set of Strings if this Preference is persistent.
       *
       * @param defaultReturnValue The default value to return if either this
       *            Preference is not persistent or this Preference is not present.
       * @return The value from the data store or the default return
       *         value.
       * @see #persistStringSet(Set)
       */
      public Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
          if (!shouldPersist()) {
              return defaultReturnValue;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              return dataStore.getStringSet(mKey, defaultReturnValue);
          }
  
          return mPreferenceManager.getSharedPreferences().getStringSet(mKey, defaultReturnValue);
      }
  
      /**
       * Attempts to persist an int if this Preference is persistent.
       *
       * @param value The value to persist.
       * @return True if this Preference is persistent. (This is not whether the
       *         value was persisted, since we may not necessarily commit if there
       *         will be a batch commit later.)
       * @see #persistString(String)
       * @see #getPersistedInt(int)
       */
      protected boolean persistInt(int value) {
          if (!shouldPersist()) {
              return false;
          }
  
          if (value == getPersistedInt(~value)) {
              // It's already there, so the same as persisting
              return true;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              dataStore.putInt(mKey, value);
          } else {
              SharedPreferences.Editor editor = mPreferenceManager.getEditor();
              editor.putInt(mKey, value);
              tryCommit(editor);
          }
          return true;
      }
  
      /**
       * Attempts to get a persisted int if this Preference is persistent.
       *
       * @param defaultReturnValue The default value to return if either this
       *            Preference is not persistent or this Preference is not present.
       * @return The value from the data store or the default return
       *         value.
       * @see #getPersistedString(String)
       * @see #persistInt(int)
       */
      protected int getPersistedInt(int defaultReturnValue) {
          if (!shouldPersist()) {
              return defaultReturnValue;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              return dataStore.getInt(mKey, defaultReturnValue);
          }
  
          return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue);
      }
  
      /**
       * Attempts to persist a long if this Preference is persistent.
       *
       * @param value The value to persist.
       * @return True if this Preference is persistent. (This is not whether the
       *         value was persisted, since we may not necessarily commit if there
       *         will be a batch commit later.)
       * @see #persistString(String)
       * @see #getPersistedFloat(float)
       */
      protected boolean persistFloat(float value) {
          if (!shouldPersist()) {
              return false;
          }
  
          if (value == getPersistedFloat(Float.NaN)) {
              // It's already there, so the same as persisting
              return true;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              dataStore.putFloat(mKey, value);
          } else {
              SharedPreferences.Editor editor = mPreferenceManager.getEditor();
              editor.putFloat(mKey, value);
              tryCommit(editor);
          }
          return true;
      }
  
      /**
       * Attempts to get a persisted float if this Preference is persistent.
       *
       * @param defaultReturnValue The default value to return if either this
       *            Preference is not persistent or this Preference is not present.
       * @return The value from the data store or the default return
       *         value.
       * @see #getPersistedString(String)
       * @see #persistFloat(float)
       */
      protected float getPersistedFloat(float defaultReturnValue) {
          if (!shouldPersist()) {
              return defaultReturnValue;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              return dataStore.getFloat(mKey, defaultReturnValue);
          }
  
          return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue);
      }
  
      /**
       * Attempts to persist a long if this Preference is persistent.
       *
       * @param value The value to persist.
       * @return True if this Preference is persistent. (This is not whether the
       *         value was persisted, since we may not necessarily commit if there
       *         will be a batch commit later.)
       * @see #persistString(String)
       * @see #getPersistedLong(long)
       */
      protected boolean persistLong(long value) {
          if (!shouldPersist()) {
              return false;
          }
  
          if (value == getPersistedLong(~value)) {
              // It's already there, so the same as persisting
              return true;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              dataStore.putLong(mKey, value);
          } else {
              SharedPreferences.Editor editor = mPreferenceManager.getEditor();
              editor.putLong(mKey, value);
              tryCommit(editor);
          }
          return true;
      }
  
      /**
       * Attempts to get a persisted long if this Preference is persistent.
       *
       * @param defaultReturnValue The default value to return if either this
       *            Preference is not persistent or this Preference is not present.
       * @return The value from the data store or the default return
       *         value.
       * @see #getPersistedString(String)
       * @see #persistLong(long)
       */
      protected long getPersistedLong(long defaultReturnValue) {
          if (!shouldPersist()) {
              return defaultReturnValue;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              return dataStore.getLong(mKey, defaultReturnValue);
          }
  
          return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue);
      }
  
      /**
       * Attempts to persist a boolean if this Preference is persistent.
       *
       * @param value The value to persist.
       * @return True if this Preference is persistent. (This is not whether the
       *         value was persisted, since we may not necessarily commit if there
       *         will be a batch commit later.)
       * @see #persistString(String)
       * @see #getPersistedBoolean(boolean)
       */
      protected boolean persistBoolean(boolean value) {
          if (!shouldPersist()) {
              return false;
          }
  
          if (value == getPersistedBoolean(!value)) {
              // It's already there, so the same as persisting
              return true;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              dataStore.putBoolean(mKey, value);
          } else {
              SharedPreferences.Editor editor = mPreferenceManager.getEditor();
              editor.putBoolean(mKey, value);
              tryCommit(editor);
          }
          return true;
      }
  
      /**
       * Attempts to get a persisted boolean if this Preference is persistent.
       *
       * @param defaultReturnValue The default value to return if either this
       *            Preference is not persistent or this Preference is not present.
       * @return The value from the data store or the default return
       *         value.
       * @see #getPersistedString(String)
       * @see #persistBoolean(boolean)
       */
      protected boolean getPersistedBoolean(boolean defaultReturnValue) {
          if (!shouldPersist()) {
              return defaultReturnValue;
          }
  
          PreferenceDataStore dataStore = getPreferenceDataStore();
          if (dataStore != null) {
              return dataStore.getBoolean(mKey, defaultReturnValue);
          }
  
          return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
      }
  
      @Override
      public String toString() {
          return getFilterableStringBuilder().toString();
      }
  
      /**
       * Returns the text that will be used to filter this Preference depending on
       * user input.
       * <p>
       * If overridding and calling through to the superclass, make sure to prepend
       * your additions with a space.
       *
       * @return Text as a {@link StringBuilder} that will be used to filter this
       *         preference. By default, this is the title and summary
       *         (concatenated with a space).
       */
      StringBuilder getFilterableStringBuilder() {
          StringBuilder sb = new StringBuilder();
          CharSequence title = getTitle();
          if (!TextUtils.isEmpty(title)) {
              sb.append(title).append(' ');
          }
          CharSequence summary = getSummary();
          if (!TextUtils.isEmpty(summary)) {
              sb.append(summary).append(' ');
          }
          if (sb.length() > 0) {
              // Drop the last space
              sb.setLength(sb.length() - 1);
          }
          return sb;
      }
  
      /**
       * Store this Preference hierarchy's frozen state into the given container.
       *
       * @param container The Bundle in which to save the instance of this Preference.
       *
       * @see #restoreHierarchyState
       * @see #onSaveInstanceState
       */
      public void saveHierarchyState(Bundle container) {
          dispatchSaveInstanceState(container);
      }
  
      /**
       * Called by {@link #saveHierarchyState} to store the instance for this Preference and its
       * children. May be overridden to modify how the save happens for children. For example, some
       * Preference objects may want to not store an instance for their children.
       *
       * @param container The Bundle in which to save the instance of this Preference.
       *
       * @see #saveHierarchyState
       * @see #onSaveInstanceState
       */
      void dispatchSaveInstanceState(Bundle container) {
          if (hasKey()) {
              mBaseMethodCalled = false;
              Parcelable state = onSaveInstanceState();
              if (!mBaseMethodCalled) {
                  throw new IllegalStateException(
                          "Derived class did not call super.onSaveInstanceState()");
              }
              if (state != null) {
                  container.putParcelable(mKey, state);
              }
          }
      }
  
      /**
       * Hook allowing a Preference to generate a representation of its internal
       * state that can later be used to create a new instance with that same
       * state. This state should only contain information that is not persistent
       * or can be reconstructed later.
       *
       * @return A Parcelable object containing the current dynamic state of this Preference, or
       *         {@code null} if there is nothing interesting to save. The default implementation
       *         returns {@code null}.
       * @see #onRestoreInstanceState
       * @see #saveHierarchyState
       */
      protected Parcelable onSaveInstanceState() {
          mBaseMethodCalled = true;
          return BaseSavedState.EMPTY_STATE;
      }
  
      /**
       * Restore this Preference hierarchy's previously saved state from the given container.
       *
       * @param container The Bundle that holds the previously saved state.
       *
       * @see #saveHierarchyState
       * @see #onRestoreInstanceState
       */
      public void restoreHierarchyState(Bundle container) {
          dispatchRestoreInstanceState(container);
      }
  
      /**
       * Called by {@link #restoreHierarchyState} to retrieve the saved state for this
       * Preference and its children. May be overridden to modify how restoring
       * happens to the children of a Preference. For example, some Preference objects may
       * not want to save state for their children.
       *
       * @param container The Bundle that holds the previously saved state.
       * @see #restoreHierarchyState
       * @see #onRestoreInstanceState
       */
      void dispatchRestoreInstanceState(Bundle container) {
          if (hasKey()) {
              Parcelable state = container.getParcelable(mKey);
              if (state != null) {
                  mBaseMethodCalled = false;
                  onRestoreInstanceState(state);
                  if (!mBaseMethodCalled) {
                      throw new IllegalStateException(
                              "Derived class did not call super.onRestoreInstanceState()");
                  }
              }
          }
      }
  
      /**
       * Hook allowing a Preference to re-apply a representation of its internal state that had
       * previously been generated by {@link #onSaveInstanceState}. This function will never be called
       * with a {@code null} state.
       *
       * @param state The saved state that had previously been returned by
       *            {@link #onSaveInstanceState}.
       * @see #onSaveInstanceState
       * @see #restoreHierarchyState
       */
      protected void onRestoreInstanceState(Parcelable state) {
          mBaseMethodCalled = true;
          if (state != BaseSavedState.EMPTY_STATE && state != null) {
              throw new IllegalArgumentException("Wrong state class -- expecting Preference State");
          }
      }
  
      /**
       * A base class for managing the instance state of a {@link Preference}.
       *
       * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
       *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
       *      Preference Library</a> for consistent behavior across all devices.
       *      For more information on using the AndroidX Preference Library see
       *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
       */
      @Deprecated
      public static class BaseSavedState extends AbsSavedState {
          public BaseSavedState(Parcel source) {
              super(source);
          }
  
          public BaseSavedState(Parcelable superState) {
              super(superState);
          }
  
          public static final @android.annotation.NonNull Parcelable.Creator<BaseSavedState> CREATOR =
                  new Parcelable.Creator<BaseSavedState>() {
                      public BaseSavedState createFromParcel(Parcel in) {
                          return new BaseSavedState(in);
                      }
  
                      public BaseSavedState[] newArray(int size) {
                          return new BaseSavedState[size];
                      }
                  };
      }
  
  }
  