- * Furthermore, the preferences shown will follow the visual style of system - * preferences. It is easy to create a hierarchy of preferences (that can be - * shown on multiple screens) via XML. For these reasons, it is recommended to - * use this fragment (as a superclass) to deal with preferences in applications. - *
- * A {@link PreferenceScreen} object should be at the top of the preference - * hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy - * denote a screen break--that is the preferences contained within subsequent - * {@link PreferenceScreen} should be shown on another screen. The preference - * framework handles this by calling {@link #onNavigateToScreen(PreferenceScreen)}. - *
- * The preference hierarchy can be formed in multiple ways: - *
- * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The - * root element should be a {@link PreferenceScreen}. Subsequent elements can point - * to actual {@link Preference} subclasses. As mentioned above, subsequent - * {@link PreferenceScreen} in the hierarchy will result in the screen break. - *
- * To specify an object hierarchy rooted with {@link PreferenceScreen}, use - * {@link #setPreferenceScreen(PreferenceScreen)}. - *
- * As a convenience, this fragment implements a click listener for any - * preference in the current hierarchy, see - * {@link #onPreferenceTreeClick(Preference)}. - * - *
For information about using {@code PreferenceFragment}, - * read the Settings - * guide.
- *The following sample code shows a simple preference fragment that is - * populated from a resource. The resource it loads is:
- * - * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml preferences} - * - *The fragment implementation itself simply populates the preferences - * when created. Note that the preferences framework takes care of loading - * the current values out of the app preferences and writing them when changed:
- * - * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/java/com/example/android/supportpreference/FragmentSupportPreferencesCompat.java - * support_fragment_compat} - * - * @see Preference - * @see PreferenceScreen - */ -@SuppressWarnings({"WeakerAccess", "unused", "HandlerLeak", "JavaDoc", "RestrictedApi"}) -public abstract class PreferenceController extends RestoreViewOnCreateController implements - PreferenceManager.OnDisplayPreferenceDialogListener, - DialogPreference.TargetFragment { - - /** - * Fragment argument used to specify the tag of the desired root - * {@link androidx.preference.PreferenceScreen} object. - */ - public static final String ARG_PREFERENCE_ROOT = - "androidx.preference.PreferenceFragmentCompat.PREFERENCE_ROOT"; - - private static final String PREFERENCES_TAG = "android:preferences"; - - private static final String DIALOG_FRAGMENT_TAG = - "androidx.preference.PreferenceFragment.DIALOG"; - - private PreferenceManager mPreferenceManager; - RecyclerView mList; - private boolean mHavePrefs; - private boolean mInitDone; - - private Context mStyledContext; - - private int mLayoutResId = R.layout.preference_list_fragment; - - private DividerDecoration mDividerDecoration = null; - - private static final int MSG_BIND_PREFERENCES = 1; - private Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - - case MSG_BIND_PREFERENCES: - bindPreferences(); - break; - } - } - }; - - final private Runnable mRequestFocus = new Runnable() { - @Override - public void run() { - mList.focusableViewAvailable(mList); - } - }; - - private Runnable mSelectPreferenceRunnable; - - /** - * Interface that PreferenceFragment's containing activity should - * implement to be able to process preference items that wish to - * switch to a specified fragment. - */ - public interface OnPreferenceStartFragmentCallback { - /** - * Called when the user has clicked on a Preference that has - * a fragment class name associated with it. The implementation - * should instantiate and switch to an instance of the given - * fragment. - * @param caller The fragment requesting navigation. - * @param pref The preference requesting the fragment. - * @return true if the fragment creation has been handled - */ - boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref); - } - - /** - * Interface that PreferenceFragment's containing activity should - * implement to be able to process preference items that wish to - * switch to a new screen of preferences. - */ - public interface OnPreferenceStartScreenCallback { - /** - * Called when the user has clicked on a PreferenceScreen item in order to navigate to a new - * screen of preferences. - * @param caller The fragment requesting navigation. - * @param pref The preference screen to navigate to. - * @return true if the screen navigation has been handled - */ - boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref); - } - - public interface OnPreferenceDisplayDialogCallback { - - /** - * @param caller The fragment containing the preference requesting the dialog. - * @param pref The preference requesting the dialog. - * @return true if the dialog creation has been handled. - */ - boolean onPreferenceDisplayDialog(PreferenceController caller, Preference pref); - } - - /** - * Convenience constructor for use when no arguments are needed. - */ - public PreferenceController() { - super(null); - } - - /** - * Constructor that takes arguments that need to be retained across restarts. - * - * @param args Any arguments that need to be retained. - */ - public PreferenceController(@Nullable Bundle args) { - super(args); - } - - /** - * Called during {@link #onCreate(Bundle)} to supply the preferences for this fragment. - * Subclasses are expected to call {@link #setPreferenceScreen(PreferenceScreen)} either - * directly or via helper methods such as {@link #addPreferencesFromResource(int)}. - * - * @param savedInstanceState If the fragment is being re-created from - * a previous saved state, this is the state. - * @param rootKey If non-null, this preference fragment should be rooted at the - * {@link androidx.preference.PreferenceScreen} with this key. - */ - public abstract void onCreatePreferences(Bundle savedInstanceState, String rootKey); - - @Override - @NonNull - public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, - @Nullable Bundle savedInstanceState) { - mInitDone = false; - mHavePrefs = false; - - final TypedValue tv = new TypedValue(); - getActivity().getTheme().resolveAttribute(R.attr.preferenceTheme, tv, true); - int theme = tv.resourceId; - if (theme == 0) { - // Fallback to default theme. - theme = R.style.PreferenceThemeOverlay; - } - mStyledContext = new ContextThemeWrapper(getActivity(), theme); - - mPreferenceManager = new PreferenceManager(mStyledContext); - final String rootKey = getArgs().getString(ARG_PREFERENCE_ROOT); - onCreatePreferences(savedInstanceState, rootKey); - - TypedArray a = mStyledContext.obtainStyledAttributes(null, - R.styleable.PreferenceFragmentCompat, - R.attr.preferenceFragmentCompatStyle, - 0); - - mLayoutResId = a.getResourceId(R.styleable.PreferenceFragmentCompat_android_layout, - mLayoutResId); - - mDividerDecoration = new DividerDecoration(); - final Drawable divider = a.getDrawable( - R.styleable.PreferenceFragmentCompat_android_divider); - final int dividerHeight = a.getDimensionPixelSize( - R.styleable.PreferenceFragmentCompat_android_dividerHeight, -1); - final boolean allowDividerAfterLastItem = a.getBoolean( - R.styleable.PreferenceFragmentCompat_allowDividerAfterLastItem, true); - - a.recycle(); - - final LayoutInflater themedInflater = inflater.cloneInContext(mStyledContext); - - final View view = themedInflater.inflate(mLayoutResId, container, false); - - final View rawListContainer = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER); - if (!(rawListContainer instanceof ViewGroup)) { - throw new RuntimeException("Content has view with id attribute " - + "'android.R.id.list_container' that is not a ViewGroup class"); - } - - final ViewGroup listContainer = (ViewGroup) rawListContainer; - - final RecyclerView listView = onCreateRecyclerView(themedInflater, listContainer, - savedInstanceState); - if (listView == null) { - throw new RuntimeException("Could not create RecyclerView"); - } - - mList = listView; - - listView.addItemDecoration(mDividerDecoration); - setDivider(divider); - if (dividerHeight != -1) { - setDividerHeight(dividerHeight); - } - mDividerDecoration.setAllowDividerAfterLastItem(allowDividerAfterLastItem); - - // If mList isn't present in the view hierarchy, add it. mList is automatically inflated - // on an Auto device so don't need to add it. - if (mList.getParent() == null) { - listContainer.addView(mList); - } - mHandler.post(mRequestFocus); - - onViewCreated(view, savedInstanceState); - - return view; - } - - /** - * Sets the drawable that will be drawn between each item in the list. - *- * Note: If the drawable does not have an intrinsic - * height, you should also call {@link #setDividerHeight(int)}. - * - * @param divider the drawable to use - * @attr ref R.styleable#PreferenceFragmentCompat_android_divider - */ - public void setDivider(Drawable divider) { - if (mDividerDecoration != null) { - mDividerDecoration.setDivider(divider); - } - } - - /** - * Sets the height of the divider that will be drawn between each item in the list. Calling - * this will override the intrinsic height as set by {@link #setDivider(Drawable)} - * - * @param height The new height of the divider in pixels. - * @attr ref R.styleable#PreferenceFragmentCompat_android_dividerHeight - */ - public void setDividerHeight(int height) { - if (mDividerDecoration != null) { - mDividerDecoration.setDividerHeight(height); - } - } - - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - if (savedInstanceState != null) { - Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG); - if (container != null) { - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - if (preferenceScreen != null) { - preferenceScreen.restoreHierarchyState(container); - } - } - } - - if (mHavePrefs) { - bindPreferences(); - if (mSelectPreferenceRunnable != null) { - mSelectPreferenceRunnable.run(); - mSelectPreferenceRunnable = null; - } - } - - mInitDone = true; - } - - @Override - public void onAttach(@NonNull View view) { - super.onAttach(view); - mPreferenceManager.setOnDisplayPreferenceDialogListener(this); - } - - @Override - public void onDetach(@NonNull View view) { - super.onDetach(view); - mPreferenceManager.setOnDisplayPreferenceDialogListener(null); - } - - @Override - public void onDestroyView(@NonNull View view) { - mHandler.removeCallbacks(mRequestFocus); - mHandler.removeMessages(MSG_BIND_PREFERENCES); - if (mHavePrefs) { - unbindPreferences(); - } - mList = null; - mPreferenceManager = null; - mStyledContext = null; - mDividerDecoration = null; - - super.onDestroyView(view); - } - - @Override - protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { - super.onSaveViewState(view, outState); - - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - if (preferenceScreen != null) { - Bundle container = new Bundle(); - preferenceScreen.saveHierarchyState(container); - outState.putBundle(PREFERENCES_TAG, container); - } - } - - /** - * Returns the {@link PreferenceManager} used by this fragment. - * @return The {@link PreferenceManager}. - */ - public PreferenceManager getPreferenceManager() { - return mPreferenceManager; - } - - /** - * Sets the root of the preference hierarchy that this fragment is showing. - * - * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. - */ - public void setPreferenceScreen(PreferenceScreen preferenceScreen) { - if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) { - onUnbindPreferences(); - mHavePrefs = true; - if (mInitDone) { - postBindPreferences(); - } - } - } - - /** - * Gets the root of the preference hierarchy that this fragment is showing. - * - * @return The {@link PreferenceScreen} that is the root of the preference - * hierarchy. - */ - public PreferenceScreen getPreferenceScreen() { - return mPreferenceManager.getPreferenceScreen(); - } - - /** - * Inflates the given XML resource and adds the preference hierarchy to the current - * preference hierarchy. - * - * @param preferencesResId The XML resource ID to inflate. - */ - public void addPreferencesFromResource(@XmlRes int preferencesResId) { - requirePreferenceManager(); - - setPreferenceScreen(mPreferenceManager.inflateFromResource(mStyledContext, - preferencesResId, getPreferenceScreen())); - } - - /** - * Inflates the given XML resource and replaces the current preference hierarchy (if any) with - * the preference hierarchy rooted at {@code key}. - * - * @param preferencesResId The XML resource ID to inflate. - * @param key The preference key of the {@link androidx.preference.PreferenceScreen} - * to use as the root of the preference hierarchy, or null to use the root - * {@link androidx.preference.PreferenceScreen}. - */ - public void setPreferencesFromResource(@XmlRes int preferencesResId, @Nullable String key) { - requirePreferenceManager(); - - final PreferenceScreen xmlRoot = mPreferenceManager.inflateFromResource(mStyledContext, - preferencesResId, null); - - final Preference root; - if (key != null) { - root = xmlRoot.findPreference(key); - if (!(root instanceof PreferenceScreen)) { - throw new IllegalArgumentException("Preference object with key " + key - + " is not a PreferenceScreen"); - } - } else { - root = xmlRoot; - } - - setPreferenceScreen((PreferenceScreen) root); - } - - /** - * Finds a {@link Preference} based on its key. - * - * @param key The key of the preference to retrieve. - * @return The {@link Preference} with the key, or null. - * @see androidx.preference.PreferenceGroup#findPreference(CharSequence) - */ - @Override - public Preference findPreference(CharSequence key) { - if (mPreferenceManager == null) { - return null; - } - return mPreferenceManager.findPreference(key); - } - - private void requirePreferenceManager() { - if (mPreferenceManager == null) { - throw new RuntimeException("This should be called after super.onCreate."); - } - } - - private void postBindPreferences() { - if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return; - mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); - } - - private void bindPreferences() { - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - if (preferenceScreen != null) { - getListView().setAdapter(onCreateAdapter(preferenceScreen)); - preferenceScreen.onAttached(); - } - onBindPreferences(); - } - - private void unbindPreferences() { - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - if (preferenceScreen != null) { - preferenceScreen.onDetached(); - } - onUnbindPreferences(); - } - - /** @hide */ - @RestrictTo(LIBRARY_GROUP) - protected void onBindPreferences() { - } - - /** @hide */ - @RestrictTo(LIBRARY_GROUP) - protected void onUnbindPreferences() { - } - - public final RecyclerView getListView() { - return mList; - } - - /** - * Creates the {@link RecyclerView} used to display the preferences. - * Subclasses may override this to return a customized - * {@link RecyclerView}. - * @param inflater The LayoutInflater object that can be used to inflate the - * {@link RecyclerView}. - * @param parent The parent {@link android.view.View} that the RecyclerView will be attached to. - * This method should not add the view itself, but this can be used to generate - * the LayoutParams of the view. - * @param savedInstanceState If non-null, this view is being re-constructed from a previous - * saved state as given here - * @return A new RecyclerView object to be placed into the view hierarchy - */ - public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, - Bundle savedInstanceState) { - // If device detected is Auto, use Auto's custom layout that contains a custom ViewGroup - // wrapping a RecyclerView - if (mStyledContext.getPackageManager().hasSystemFeature(PackageManager - .FEATURE_AUTOMOTIVE)) { - RecyclerView recyclerView = parent.findViewById(R.id.recycler_view); - if (recyclerView != null) { - return recyclerView; - } - } - RecyclerView recyclerView = (RecyclerView) inflater - .inflate(R.layout.preference_recyclerview, parent, false); - - recyclerView.setLayoutManager(onCreateLayoutManager()); - recyclerView.setAccessibilityDelegateCompat( - new PreferenceRecyclerViewAccessibilityDelegate(recyclerView)); - - return recyclerView; - } - - /** - * Called from {@link #onCreateRecyclerView} to create the - * {@link RecyclerView.LayoutManager} for the created - * {@link RecyclerView}. - * @return A new {@link RecyclerView.LayoutManager} instance. - */ - public RecyclerView.LayoutManager onCreateLayoutManager() { - return new LinearLayoutManager(getActivity()); - } - - /** - * Creates the root adapter. - * - * @param preferenceScreen Preference screen object to create the adapter for. - * @return An adapter that contains the preferences contained in this {@link PreferenceScreen}. - */ - protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { - return new PreferenceGroupAdapter(preferenceScreen); - } - - /** - * Called when a preference in the tree requests to display a dialog. Subclasses should - * override this method to display custom dialogs or to handle dialogs for custom preference - * classes. - * - * @param preference The Preference object requesting the dialog. - */ - @Override - public void onDisplayPreferenceDialog(Preference preference) { - boolean handled = false; - if (getCallbackFragment() instanceof OnPreferenceDisplayDialogCallback) { - handled = ((OnPreferenceDisplayDialogCallback) getCallbackFragment()) - .onPreferenceDisplayDialog(this, preference); - } - if (!handled && getActivity() instanceof OnPreferenceDisplayDialogCallback) { - handled = ((OnPreferenceDisplayDialogCallback) getActivity()) - .onPreferenceDisplayDialog(this, preference); - } - - if (handled) { - return; - } - - // check if dialog is already showing - if (getRouter().getControllerWithTag(DIALOG_FRAGMENT_TAG) != null) { - return; - } - - final PreferenceDialogController f; - if (preference instanceof EditTextPreference) { - f = EditTextPreferenceDialogController.newInstance(preference.getKey()); - } else if (preference instanceof ListPreference) { - f = ListPreferenceDialogController.newInstance(preference.getKey()); - } else if (preference instanceof MultiSelectListPreference) { - f = MultiSelectListPreferenceDialogController.newInstance(preference.getKey()); - } else { - throw new IllegalArgumentException("Tried to display dialog for unknown " + - "preference type. Did you forget to override onDisplayPreferenceDialog()?"); - } - f.setTargetController(this); - f.showDialog(getRouter(), DIALOG_FRAGMENT_TAG); - } - - /** - * Basically a wrapper for getParentFragment which is v17+. Used by the leanback preference lib. - * @return Fragment to possibly use as a callback - * @hide - */ - @RestrictTo(LIBRARY_GROUP) - public Fragment getCallbackFragment() { - return null; - } - - public void scrollToPreference(final String key) { - scrollToPreferenceInternal(null, key); - } - - public void scrollToPreference(final Preference preference) { - scrollToPreferenceInternal(preference, null); - } - - private void scrollToPreferenceInternal(final Preference preference, final String key) { - final Runnable r = new Runnable() { - @Override - public void run() { - final RecyclerView.Adapter adapter = mList.getAdapter(); - if (!(adapter instanceof - PreferenceGroup.PreferencePositionCallback)) { - if (adapter != null) { - throw new IllegalStateException("Adapter must implement " - + "PreferencePositionCallback"); - } else { - // Adapter was set to null, so don't scroll I guess? - return; - } - } - final int position; - if (preference != null) { - position = ((PreferenceGroup.PreferencePositionCallback) adapter) - .getPreferenceAdapterPosition(preference); - } else { - position = ((PreferenceGroup.PreferencePositionCallback) adapter) - .getPreferenceAdapterPosition(key); - } - if (position != RecyclerView.NO_POSITION) { - mList.scrollToPosition(position); - } else { - // Item not found, wait for an update and try again - adapter.registerAdapterDataObserver( - new ScrollToPreferenceObserver(adapter, mList, preference, key)); - } - } - }; - if (mList == null) { - mSelectPreferenceRunnable = r; - } else { - r.run(); - } - } - - private static class ScrollToPreferenceObserver extends RecyclerView.AdapterDataObserver { - private final RecyclerView.Adapter mAdapter; - private final RecyclerView mList; - private final Preference mPreference; - private final String mKey; - - public ScrollToPreferenceObserver(RecyclerView.Adapter adapter, RecyclerView list, - Preference preference, String key) { - mAdapter = adapter; - mList = list; - mPreference = preference; - mKey = key; - } - - private void scrollToPreference() { - mAdapter.unregisterAdapterDataObserver(this); - final int position; - if (mPreference != null) { - position = ((PreferenceGroup.PreferencePositionCallback) mAdapter) - .getPreferenceAdapterPosition(mPreference); - } else { - position = ((PreferenceGroup.PreferencePositionCallback) mAdapter) - .getPreferenceAdapterPosition(mKey); - } - if (position != RecyclerView.NO_POSITION) { - mList.scrollToPosition(position); - } - } - - @Override - public void onChanged() { - scrollToPreference(); - } - - @Override - public void onItemRangeChanged(int positionStart, int itemCount) { - scrollToPreference(); - } - - @Override - public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { - scrollToPreference(); - } - - @Override - public void onItemRangeInserted(int positionStart, int itemCount) { - scrollToPreference(); - } - - @Override - public void onItemRangeRemoved(int positionStart, int itemCount) { - scrollToPreference(); - } - - @Override - public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { - scrollToPreference(); - } - } - - private class DividerDecoration extends RecyclerView.ItemDecoration { - - private Drawable mDivider; - private int mDividerHeight; - private boolean mAllowDividerAfterLastItem = true; - - DividerDecoration() { - } - - @Override - public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { - if (mDivider == null) { - return; - } - final int childCount = parent.getChildCount(); - final int width = parent.getWidth(); - for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) { - final View view = parent.getChildAt(childViewIndex); - if (shouldDrawDividerBelow(view, parent)) { - int top = (int) view.getY() + view.getHeight(); - mDivider.setBounds(0, top, width, top + mDividerHeight); - mDivider.draw(c); - } - } - } - - @Override - public void getItemOffsets(Rect outRect, View view, RecyclerView parent, - RecyclerView.State state) { - if (shouldDrawDividerBelow(view, parent)) { - outRect.bottom = mDividerHeight; - } - } - - private boolean shouldDrawDividerBelow(View view, RecyclerView parent) { - final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view); - final boolean dividerAllowedBelow = holder instanceof PreferenceViewHolder - && ((PreferenceViewHolder) holder).isDividerAllowedBelow(); - if (!dividerAllowedBelow) { - return false; - } - boolean nextAllowed = mAllowDividerAfterLastItem; - int index = parent.indexOfChild(view); - if (index < parent.getChildCount() - 1) { - final View nextView = parent.getChildAt(index + 1); - final RecyclerView.ViewHolder nextHolder = parent.getChildViewHolder(nextView); - nextAllowed = nextHolder instanceof PreferenceViewHolder - && ((PreferenceViewHolder) nextHolder).isDividerAllowedAbove(); - } - return nextAllowed; - } - - public void setDivider(Drawable divider) { - if (divider != null) { - mDividerHeight = divider.getIntrinsicHeight(); - } else { - mDividerHeight = 0; - } - mDivider = divider; - mList.invalidateItemDecorations(); - } - - public void setDividerHeight(int dividerHeight) { - mDividerHeight = dividerHeight; - mList.invalidateItemDecorations(); - } - - public void setAllowDividerAfterLastItem(boolean allowDividerAfterLastItem) { - mAllowDividerAfterLastItem = allowDividerAfterLastItem; - } - } -} diff --git a/j2k-preference/src/main/java/androidx/preference/PreferenceDialogController.java b/j2k-preference/src/main/java/androidx/preference/PreferenceDialogController.java deleted file mode 100644 index c50b606d91..0000000000 --- a/j2k-preference/src/main/java/androidx/preference/PreferenceDialogController.java +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (C) 2015 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 androidx.preference; - -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.*; -import android.widget.TextView; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RestrictTo; -import androidx.appcompat.app.AlertDialog; -import com.bluelinelabs.conductor.Controller; -import com.bluelinelabs.conductor.RestoreViewOnCreateController; -import com.bluelinelabs.conductor.Router; -import com.bluelinelabs.conductor.RouterTransaction; -import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler; - -import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; - -/** - * Abstract base class which presents a dialog associated with a - * {@link androidx.preference.DialogPreference}. Since the preference object may - * not be available during fragment re-creation, the necessary information for displaying the dialog - * is read once during the initial call to {@link #onCreate(Bundle)} and saved/restored in the saved - * instance state. Custom subclasses should also follow this pattern. - */ -public abstract class PreferenceDialogController extends RestoreViewOnCreateController implements - DialogInterface.OnClickListener { - - protected static final String ARG_KEY = "key"; - - private static final String SAVE_DIALOG_STATE_TAG = "android:savedDialogState"; - private static final String SAVE_STATE_TITLE = "PreferenceDialogController.title"; - private static final String SAVE_STATE_POSITIVE_TEXT = "PreferenceDialogController.positiveText"; - private static final String SAVE_STATE_NEGATIVE_TEXT = "PreferenceDialogController.negativeText"; - private static final String SAVE_STATE_MESSAGE = "PreferenceDialogController.message"; - private static final String SAVE_STATE_LAYOUT = "PreferenceDialogController.layout"; - private static final String SAVE_STATE_ICON = "PreferenceDialogController.icon"; - - private DialogPreference mPreference; - - private CharSequence mDialogTitle; - private CharSequence mPositiveButtonText; - private CharSequence mNegativeButtonText; - private CharSequence mDialogMessage; - private @LayoutRes int mDialogLayoutRes; - - private BitmapDrawable mDialogIcon; - - /** Which button was clicked. */ - private int mWhichButtonClicked; - - private Dialog dialog; - private boolean dismissed; - - @NonNull - @Override - final protected View onCreateView(@NonNull LayoutInflater inflater, - @NonNull ViewGroup container, - @Nullable Bundle savedViewState) { - - onCreate(savedViewState); - - dialog = onCreateDialog(savedViewState); - //noinspection ConstantConditions - dialog.setOwnerActivity(getActivity()); - dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - PreferenceDialogController.this.dismissDialog(); - } - }); - if (savedViewState != null) { - Bundle dialogState = savedViewState.getBundle(SAVE_DIALOG_STATE_TAG); - if (dialogState != null) { - dialog.onRestoreInstanceState(dialogState); - } - } - return new View(getActivity());//stub view - } - - public void onCreate(Bundle savedInstanceState) { - final Controller rawController = getTargetController(); - if (!(rawController instanceof DialogPreference.TargetFragment)) { - throw new IllegalStateException("Target controller must implement TargetFragment" + - " interface"); - } - - final DialogPreference.TargetFragment controller = - (DialogPreference.TargetFragment) rawController; - - final String key = getArgs().getString(ARG_KEY); - if (savedInstanceState == null) { - mPreference = (DialogPreference) controller.findPreference(key); - mDialogTitle = mPreference.getDialogTitle(); - mPositiveButtonText = mPreference.getPositiveButtonText(); - mNegativeButtonText = mPreference.getNegativeButtonText(); - mDialogMessage = mPreference.getDialogMessage(); - mDialogLayoutRes = mPreference.getDialogLayoutResource(); - - final Drawable icon = mPreference.getDialogIcon(); - if (icon == null || icon instanceof BitmapDrawable) { - mDialogIcon = (BitmapDrawable) icon; - } else { - final Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), - icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(bitmap); - icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - icon.draw(canvas); - mDialogIcon = new BitmapDrawable(getResources(), bitmap); - } - } - } - - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putCharSequence(SAVE_STATE_TITLE, mDialogTitle); - outState.putCharSequence(SAVE_STATE_POSITIVE_TEXT, mPositiveButtonText); - outState.putCharSequence(SAVE_STATE_NEGATIVE_TEXT, mNegativeButtonText); - outState.putCharSequence(SAVE_STATE_MESSAGE, mDialogMessage); - outState.putInt(SAVE_STATE_LAYOUT, mDialogLayoutRes); - if (mDialogIcon != null) { - outState.putParcelable(SAVE_STATE_ICON, mDialogIcon.getBitmap()); - } - } - - @Override - protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - - mDialogTitle = savedInstanceState.getCharSequence(SAVE_STATE_TITLE); - mPositiveButtonText = savedInstanceState.getCharSequence(SAVE_STATE_POSITIVE_TEXT); - mNegativeButtonText = savedInstanceState.getCharSequence(SAVE_STATE_NEGATIVE_TEXT); - mDialogMessage = savedInstanceState.getCharSequence(SAVE_STATE_MESSAGE); - mDialogLayoutRes = savedInstanceState.getInt(SAVE_STATE_LAYOUT, 0); - final Bitmap bitmap = savedInstanceState.getParcelable(SAVE_STATE_ICON); - if (bitmap != null) { - mDialogIcon = new BitmapDrawable(getResources(), bitmap); - } - } - - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Context context = getActivity(); - mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE; - - final AlertDialog.Builder builder = new AlertDialog.Builder(context) - .setTitle(mDialogTitle) - .setIcon(mDialogIcon) - .setPositiveButton(mPositiveButtonText, this) - .setNegativeButton(mNegativeButtonText, this); - - View contentView = onCreateDialogView(context); - if (contentView != null) { - onBindDialogView(contentView); - builder.setView(contentView); - } else { - builder.setMessage(mDialogMessage); - } - - onPrepareDialogBuilder(builder); - - // Create the dialog - final Dialog dialog = builder.create(); - if (needInputMethod()) { - requestInputMethod(dialog); - } - - return dialog; - } - - @Override - protected void onSaveViewState(@NonNull View view, @NonNull Bundle outState) { - super.onSaveViewState(view, outState); - Bundle dialogState = dialog.onSaveInstanceState(); - outState.putBundle(SAVE_DIALOG_STATE_TAG, dialogState); - } - - @Override - protected void onAttach(@NonNull View view) { - super.onAttach(view); - dialog.show(); - } - - @Override - protected void onDetach(@NonNull View view) { - super.onDetach(view); - dialog.hide(); - } - - @Override - protected void onDestroyView(@NonNull View view) { - super.onDestroyView(view); - dialog.setOnDismissListener(null); - dialog.dismiss(); - dialog = null; - mPreference = null; - } - - /** - * Display the dialog, create a transaction and pushing the controller. - * - * @param router The router on which the transaction will be applied - */ - public void showDialog(@NonNull Router router) { - showDialog(router, null); - } - - /** - * Display the dialog, create a transaction and pushing the controller. - * - * @param router The router on which the transaction will be applied - * @param tag The tag for this controller - */ - public void showDialog(@NonNull Router router, @Nullable String tag) { - dismissed = false; - router.pushController(RouterTransaction.with(this) - .pushChangeHandler(new SimpleSwapChangeHandler(false)) - .popChangeHandler(new SimpleSwapChangeHandler(false)) - .tag(tag)); - } - - /** - * Dismiss the dialog and pop this controller - */ - public void dismissDialog() { - if (dismissed) { - return; - } - onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE); - getRouter().popController(this); - dismissed = true; - } - - @Nullable - protected Dialog getDialog() { - return dialog; - } - - /** - * Get the preference that requested this dialog. Available after {@link #onCreate(Bundle)} has - * been called on the {@link PreferenceFragmentCompat} which launched this dialog. - * - * @return The {@link DialogPreference} associated with this - * dialog. - */ - public DialogPreference getPreference() { - if (mPreference == null) { - final String key = getArgs().getString(ARG_KEY); - final DialogPreference.TargetFragment controller = - (DialogPreference.TargetFragment) getTargetController(); - mPreference = (DialogPreference) controller.findPreference(key); - } - return mPreference; - } - - /** - * Prepares the dialog builder to be shown when the preference is clicked. - * Use this to set custom properties on the dialog. - *
- * Do not {@link AlertDialog.Builder#create()} or - * {@link AlertDialog.Builder#show()}. - */ - protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { - } - - /** - * Returns whether the preference needs to display a soft input method when the dialog - * is displayed. Default is false. Subclasses should override this method if they need - * the soft input method brought up automatically. - * - * @hide - */ - @RestrictTo(LIBRARY_GROUP) - protected boolean needInputMethod() { - return false; - } - - /** - * Sets the required flags on the dialog window to enable input method window to show up. - */ - private void requestInputMethod(Dialog dialog) { - Window window = dialog.getWindow(); - window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - } - - /** - * Creates the content view for the dialog (if a custom content view is - * required). By default, it inflates the dialog layout resource if it is - * set. - * - * @return The content View for the dialog. - * @see DialogPreference#setLayoutResource(int) - */ - protected View onCreateDialogView(Context context) { - final int resId = mDialogLayoutRes; - if (resId == 0) { - return null; - } - - LayoutInflater inflater = LayoutInflater.from(context); - return inflater.inflate(resId, null); - } - - /** - * Binds views in the content View of the dialog to data. - *
- * Make sure to call through to the superclass implementation. - * - * @param view The content View of the dialog, if it is custom. - */ - protected void onBindDialogView(View view) { - View dialogMessageView = view.findViewById(android.R.id.message); - - if (dialogMessageView != null) { - final CharSequence message = mDialogMessage; - int newVisibility = View.GONE; - - if (!TextUtils.isEmpty(message)) { - if (dialogMessageView instanceof TextView) { - ((TextView) dialogMessageView).setText(message); - } - - newVisibility = View.VISIBLE; - } - - if (dialogMessageView.getVisibility() != newVisibility) { - dialogMessageView.setVisibility(newVisibility); - } - } - } - - @Override - public void onClick(DialogInterface dialog, int which) { - mWhichButtonClicked = which; - } - - public abstract void onDialogClosed(boolean positiveResult); -} diff --git a/settings.gradle b/settings.gradle index f340a35e17..e7b4def49c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':j2k-preference' +include ':app'