Removing custom androidx pref + start of vector icons
Before Width: | Height: | Size: 248 B |
Before Width: | Height: | Size: 291 B |
Before Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 204 B |
Before Width: | Height: | Size: 186 B |
Before Width: | Height: | Size: 291 B |
Before Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 291 B |
Before Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 580 B |
Before Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 740 B |
Before Width: | Height: | Size: 872 B |
Before Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 580 B |
Before Width: | Height: | Size: 872 B |
Before Width: | Height: | Size: 1.2 KiB |
@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24.0"
|
|
||||||
android:viewportHeight="24.0"
|
|
||||||
android:tint="?attr/colorControlNormal">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M18,4l-4,4h3v7c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2V8c0,-2.21 -1.79,-4 -4,-4S5,5.79 5,8v7H2l4,4 4,-4H7V8c0,-1.1 0.9,-2 2,-2s2,0.9 2,2v7c0,2.21 1.79,4 4,4s4,-1.79 4,-4V8h3l-4,-4z"/>
|
|
||||||
</vector>
|
|
5
app/src/main/res/drawable/ic_swap_calls_white_24dp.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M18,4l-4,4h3v7c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2V8c0,-2.21 -1.79,-4 -4,-4S5,5.79 5,8v7H2l4,4 4,-4H7V8c0,-1.1 0.9,-2 2,-2s2,0.9 2,2v7c0,2.21 1.79,4 4,4s4,-1.79 4,-4V8h3l-4,-4z"/>
|
||||||
|
</vector>
|
@ -25,13 +25,13 @@
|
|||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_migrate"
|
android:id="@+id/action_migrate"
|
||||||
android:icon="@drawable/baseline_swap_calls_white_24"
|
android:icon="@drawable/ic_swap_calls_white_24dp"
|
||||||
android:title="@string/label_migration"
|
android:title="@string/label_migration"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_hide_title"
|
android:id="@+id/action_hide_title"
|
||||||
android:icon="@drawable/baseline_swap_calls_white_24"
|
android:icon="@drawable/ic_swap_calls_white_24dp"
|
||||||
android:title="@string/label_hide_title"
|
android:title="@string/label_hide_title"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
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
@ -1,25 +0,0 @@
|
|||||||
# 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
|
|
@ -1,3 +0,0 @@
|
|||||||
<manifest package="androidx.preference.conductor">
|
|
||||||
|
|
||||||
</manifest>
|
|
@ -1,107 +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.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,120 +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.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,813 +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.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.
|
|
||||||
* <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', ':j2k-preference'
|
include ':app'
|
||||||
|