Added Local fork of the preferences controller
Because it didnt work correctly and I dont wanna upload the changes to git
This commit is contained in:
parent
7f7acfc55a
commit
2681ff3c9a
@ -112,7 +112,6 @@ dependencies {
|
|||||||
implementation 'com.github.inorichi:junrar-android:634c1f5'
|
implementation 'com.github.inorichi:junrar-android:634c1f5'
|
||||||
|
|
||||||
// Android support library
|
// Android support library
|
||||||
implementation 'com.android.support:preference-v7:28.0.0'
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation 'com.google.android.material:material:1.0.0'
|
implementation 'com.google.android.material:material:1.0.0'
|
||||||
@ -219,7 +218,7 @@ dependencies {
|
|||||||
implementation ("com.bluelinelabs:conductor-support:2.1.5") {
|
implementation ("com.bluelinelabs:conductor-support:2.1.5") {
|
||||||
exclude group: "com.android.support"
|
exclude group: "com.android.support"
|
||||||
}
|
}
|
||||||
implementation 'com.github.inorichi:conductor-support-preference:78e2344'
|
implementation project(":j2k-preference")
|
||||||
|
|
||||||
// RxBindings
|
// RxBindings
|
||||||
final rxbindings_version = '1.0.1'
|
final rxbindings_version = '1.0.1'
|
||||||
|
29
j2k-preference/build.gradle
Normal file
29
j2k-preference/build.gradle
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 29
|
||||||
|
buildToolsVersion '29.0.2'
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 16
|
||||||
|
targetSdkVersion 29
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
encoding = 'UTF-8'
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
|
compileOnly 'com.bluelinelabs:conductor:2.1.5'
|
||||||
|
}
|
25
j2k-preference/proguard-rules.pro
vendored
Normal file
25
j2k-preference/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in C:\Users\len\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
3
j2k-preference/src/main/AndroidManifest.xml
Normal file
3
j2k-preference/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<manifest package="androidx.preference.conductor">
|
||||||
|
|
||||||
|
</manifest>
|
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* 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.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RestrictTo;
|
||||||
|
|
||||||
|
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
|
||||||
|
|
||||||
|
public class EditTextPreferenceDialogController extends PreferenceDialogController {
|
||||||
|
|
||||||
|
private static final String SAVE_STATE_TEXT = "EditTextPreferenceDialogController.text";
|
||||||
|
|
||||||
|
private EditText mEditText;
|
||||||
|
|
||||||
|
private CharSequence mText;
|
||||||
|
|
||||||
|
public static EditTextPreferenceDialogController newInstance(String key) {
|
||||||
|
EditTextPreferenceDialogController
|
||||||
|
controller = new EditTextPreferenceDialogController();
|
||||||
|
controller.getArgs().putString(ARG_KEY, key);
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
mText = getEditTextPreference().getText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putCharSequence(SAVE_STATE_TEXT, mText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
|
mText = savedInstanceState.getCharSequence(SAVE_STATE_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindDialogView(View view) {
|
||||||
|
super.onBindDialogView(view);
|
||||||
|
|
||||||
|
mEditText = view.findViewById(android.R.id.edit);
|
||||||
|
mEditText.requestFocus();
|
||||||
|
|
||||||
|
if (mEditText == null) {
|
||||||
|
throw new IllegalStateException("Dialog view must contain an EditText with id" +
|
||||||
|
" @android:id/edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
mEditText.setText(mText);
|
||||||
|
// Place cursor at the end
|
||||||
|
mEditText.setSelection(mEditText.getText().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroyView(@NonNull View view) {
|
||||||
|
super.onDestroyView(view);
|
||||||
|
mEditText = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditTextPreference getEditTextPreference() {
|
||||||
|
return (EditTextPreference) getPreference();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@RestrictTo(LIBRARY_GROUP)
|
||||||
|
@Override
|
||||||
|
protected boolean needInputMethod() {
|
||||||
|
// We want the input method to show, if possible, when dialog is displayed
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDialogClosed(boolean positiveResult) {
|
||||||
|
if (positiveResult) {
|
||||||
|
String value = mEditText.getText().toString();
|
||||||
|
if (getEditTextPreference().callChangeListener(value)) {
|
||||||
|
getEditTextPreference().setText(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
public class ListPreferenceDialogController extends PreferenceDialogController {
|
||||||
|
|
||||||
|
private static final String SAVE_STATE_INDEX = "ListPreferenceDialogController.index";
|
||||||
|
private static final String SAVE_STATE_ENTRIES = "ListPreferenceDialogController.entries";
|
||||||
|
private static final String SAVE_STATE_ENTRY_VALUES =
|
||||||
|
"ListPreferenceDialogController.entryValues";
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
private int mClickedDialogEntryIndex;
|
||||||
|
private CharSequence[] mEntries;
|
||||||
|
private CharSequence[] mEntryValues;
|
||||||
|
|
||||||
|
public static ListPreferenceDialogController newInstance(String key) {
|
||||||
|
ListPreferenceDialogController controller =
|
||||||
|
new ListPreferenceDialogController();
|
||||||
|
controller.getArgs().putString(ARG_KEY, key);
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
final ListPreference preference = getListPreference();
|
||||||
|
|
||||||
|
if (preference.getEntries() == null || preference.getEntryValues() == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"ListPreference requires an entries array and an entryValues array.");
|
||||||
|
}
|
||||||
|
|
||||||
|
mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue());
|
||||||
|
mEntries = preference.getEntries();
|
||||||
|
mEntryValues = preference.getEntryValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex);
|
||||||
|
outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
|
||||||
|
outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
|
|
||||||
|
mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0);
|
||||||
|
mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
|
||||||
|
mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListPreference getListPreference() {
|
||||||
|
return (ListPreference) getPreference();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||||
|
super.onPrepareDialogBuilder(builder);
|
||||||
|
|
||||||
|
builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
mClickedDialogEntryIndex = which;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clicking on an item simulates the positive button
|
||||||
|
* click, and dismisses the dialog.
|
||||||
|
*/
|
||||||
|
ListPreferenceDialogController.this.onClick(dialog,
|
||||||
|
DialogInterface.BUTTON_POSITIVE);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The typical interaction for list-based dialogs is to have
|
||||||
|
* click-on-an-item dismiss the dialog instead of the user having to
|
||||||
|
* press 'Ok'.
|
||||||
|
*/
|
||||||
|
builder.setPositiveButton(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDialogClosed(boolean positiveResult) {
|
||||||
|
final ListPreference preference = getListPreference();
|
||||||
|
if (positiveResult && mClickedDialogEntryIndex >= 0) {
|
||||||
|
String value = mEntryValues[mClickedDialogEntryIndex].toString();
|
||||||
|
if (preference.callChangeListener(value)) {
|
||||||
|
preference.setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.preference.MultiSelectListPreference;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@SuppressWarnings("RestrictedApi")
|
||||||
|
public class MultiSelectListPreferenceDialogController extends PreferenceDialogController {
|
||||||
|
|
||||||
|
private static final String SAVE_STATE_VALUES =
|
||||||
|
"MultiSelectListPreferenceDialogController.values";
|
||||||
|
private static final String SAVE_STATE_CHANGED =
|
||||||
|
"MultiSelectListPreferenceDialogController.changed";
|
||||||
|
private static final String SAVE_STATE_ENTRIES =
|
||||||
|
"MultiSelectListPreferenceDialogController.entries";
|
||||||
|
private static final String SAVE_STATE_ENTRY_VALUES =
|
||||||
|
"MultiSelectListPreferenceDialogController.entryValues";
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
Set<String> mNewValues = new HashSet<>();
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
boolean mPreferenceChanged;
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
CharSequence[] mEntries;
|
||||||
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||||
|
CharSequence[] mEntryValues;
|
||||||
|
|
||||||
|
public static MultiSelectListPreferenceDialogController newInstance(String key) {
|
||||||
|
MultiSelectListPreferenceDialogController controller =
|
||||||
|
new MultiSelectListPreferenceDialogController();
|
||||||
|
controller.getArgs().putString(ARG_KEY, key);
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
final MultiSelectListPreference preference = getListPreference();
|
||||||
|
|
||||||
|
if (preference.getEntries() == null || preference.getEntryValues() == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"MultiSelectListPreference requires an entries array and " +
|
||||||
|
"an entryValues array.");
|
||||||
|
}
|
||||||
|
|
||||||
|
mNewValues.clear();
|
||||||
|
mNewValues.addAll(preference.getValues());
|
||||||
|
mPreferenceChanged = false;
|
||||||
|
mEntries = preference.getEntries();
|
||||||
|
mEntryValues = preference.getEntryValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putStringArrayList(SAVE_STATE_VALUES, new ArrayList<>(mNewValues));
|
||||||
|
outState.putBoolean(SAVE_STATE_CHANGED, mPreferenceChanged);
|
||||||
|
outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
|
||||||
|
outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
|
mNewValues.clear();
|
||||||
|
mNewValues.addAll(savedInstanceState.getStringArrayList(SAVE_STATE_VALUES));
|
||||||
|
mPreferenceChanged = savedInstanceState.getBoolean(SAVE_STATE_CHANGED, false);
|
||||||
|
mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
|
||||||
|
mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MultiSelectListPreference getListPreference() {
|
||||||
|
return (MultiSelectListPreference) getPreference();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||||
|
super.onPrepareDialogBuilder(builder);
|
||||||
|
|
||||||
|
final int entryCount = mEntryValues.length;
|
||||||
|
final boolean[] checkedItems = new boolean[entryCount];
|
||||||
|
for (int i = 0; i < entryCount; i++) {
|
||||||
|
checkedItems[i] = mNewValues.contains(mEntryValues[i].toString());
|
||||||
|
}
|
||||||
|
builder.setMultiChoiceItems(mEntries, checkedItems,
|
||||||
|
new DialogInterface.OnMultiChoiceClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
|
||||||
|
if (isChecked) {
|
||||||
|
mPreferenceChanged |= mNewValues.add(
|
||||||
|
mEntryValues[which].toString());
|
||||||
|
} else {
|
||||||
|
mPreferenceChanged |= mNewValues.remove(
|
||||||
|
mEntryValues[which].toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDialogClosed(boolean positiveResult) {
|
||||||
|
final MultiSelectListPreference preference = getListPreference();
|
||||||
|
if (positiveResult && mPreferenceChanged) {
|
||||||
|
final Set<String> values = mNewValues;
|
||||||
|
if (preference.callChangeListener(values)) {
|
||||||
|
preference.setValues(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mPreferenceChanged = false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,813 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.ContextThemeWrapper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RestrictTo;
|
||||||
|
import androidx.annotation.XmlRes;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.preference.MultiSelectListPreference;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.bluelinelabs.conductor.RestoreViewOnCreateController;
|
||||||
|
|
||||||
|
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a hierarchy of {@link Preference} objects as
|
||||||
|
* lists. These preferences will
|
||||||
|
* automatically save to {@link android.content.SharedPreferences} as the user interacts with
|
||||||
|
* them. To retrieve an instance of {@link android.content.SharedPreferences} that the
|
||||||
|
* preference hierarchy in this fragment will use, call
|
||||||
|
* {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)}
|
||||||
|
* with a context in the same package as this fragment.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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)}.
|
||||||
|
* <p>
|
||||||
|
* The preference hierarchy can be formed in multiple ways:
|
||||||
|
* <li> From an XML file specifying the hierarchy
|
||||||
|
* <li> From different {@link android.app.Activity Activities} that each specify its own
|
||||||
|
* preferences in an XML file via {@link android.app.Activity} meta-data
|
||||||
|
* <li> From an object hierarchy rooted with {@link PreferenceScreen}
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* To specify an object hierarchy rooted with {@link PreferenceScreen}, use
|
||||||
|
* {@link #setPreferenceScreen(PreferenceScreen)}.
|
||||||
|
* <p>
|
||||||
|
* As a convenience, this fragment implements a click listener for any
|
||||||
|
* preference in the current hierarchy, see
|
||||||
|
* {@link #onPreferenceTreeClick(Preference)}.
|
||||||
|
*
|
||||||
|
* <div class="special reference">
|
||||||
|
* <h3>Developer Guides</h3>
|
||||||
|
* <p>For information about using {@code PreferenceFragment},
|
||||||
|
* read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
|
||||||
|
* guide.</p>
|
||||||
|
* </div>
|
||||||
|
*
|
||||||
|
* <a name="SampleCode"></a>
|
||||||
|
* <h3>Sample Code</h3>
|
||||||
|
*
|
||||||
|
* <p>The following sample code shows a simple preference fragment that is
|
||||||
|
* populated from a resource. The resource it loads is:</p>
|
||||||
|
*
|
||||||
|
* {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml preferences}
|
||||||
|
*
|
||||||
|
* <p>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:</p>
|
||||||
|
*
|
||||||
|
* {@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.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note:</strong> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,363 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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);
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
include ':app'
|
include ':app', ':j2k-preference'
|
||||||
|
Loading…
Reference in New Issue
Block a user