/* * Copyright 2018 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. * * This file is part of and modified for Gadgetbridge. */ package nodomain.freeyourgadget.gadgetbridge.util.dialogs; 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.View; import android.view.Window; import android.view.WindowManager; import android.widget.TextView; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import androidx.preference.DialogPreference; import com.google.android.material.dialog.MaterialAlertDialogBuilder; /** * Abstract base class which presents a dialog associated with a {@link 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 MaterialPreferenceDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { protected static final String ARG_KEY = "key"; private static final String SAVE_STATE_TITLE = "PreferenceDialogFragment.title"; private static final String SAVE_STATE_POSITIVE_TEXT = "PreferenceDialogFragment.positiveText"; private static final String SAVE_STATE_NEGATIVE_TEXT = "PreferenceDialogFragment.negativeText"; private static final String SAVE_STATE_MESSAGE = "PreferenceDialogFragment.message"; private static final String SAVE_STATE_LAYOUT = "PreferenceDialogFragment.layout"; private static final String SAVE_STATE_ICON = "PreferenceDialogFragment.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; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Fragment rawFragment = getTargetFragment(); if (!(rawFragment instanceof DialogPreference.TargetFragment)) { throw new IllegalStateException("Target fragment must implement TargetFragment" + " interface"); } final DialogPreference.TargetFragment fragment = (DialogPreference.TargetFragment) rawFragment; final String key = getArguments().getString(ARG_KEY); if (savedInstanceState == null) { mPreference = fragment.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); } } else { 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); } } } @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 public @NonNull Dialog onCreateDialog(Bundle savedInstanceState) { final Context context = getActivity(); mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE; final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(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; } /** * 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 = getArguments().getString(ARG_KEY); final DialogPreference.TargetFragment fragment = (DialogPreference.TargetFragment) getTargetFragment(); mPreference = fragment.findPreference(key); } return mPreference; } /** * Prepares the dialog builder to be shown when the preference is clicked. * Use this to set custom properties on the dialog. * *

Do not {@link MaterialAlertDialogBuilder#create()} or {@link MaterialAlertDialogBuilder#show()}. */ protected void onPrepareDialogBuilder(MaterialAlertDialogBuilder 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. * *

Note: If your application targets P or above, ensure your subclass manually requests * focus (ideally in {@link #onBindDialogView(View)}) for the input field in order to * correctly attach the input method to the field. * * @hide */ 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; } return getLayoutInflater().inflate(resId, null); } /** * Binds views in the content view of the dialog to data. * *

Make sure to call through to the superclass implementation. * * @param view The content view of the dialog, if it is custom */ protected void onBindDialogView(View view) { View dialogMessageView = view.findViewById(android.R.id.message); if (dialogMessageView != null) { final CharSequence message = mDialogMessage; int newVisibility = View.GONE; if (!TextUtils.isEmpty(message)) { if (dialogMessageView instanceof TextView) { ((TextView) dialogMessageView).setText(message); } newVisibility = View.VISIBLE; } if (dialogMessageView.getVisibility() != newVisibility) { dialogMessageView.setVisibility(newVisibility); } } } @Override public void onClick(DialogInterface dialog, int which) { mWhichButtonClicked = which; } @Override public void onDismiss(@NonNull DialogInterface dialog) { super.onDismiss(dialog); onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE); } public abstract void onDialogClosed(boolean positiveResult); }