1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.common; 18 19 import android.app.AlertDialog; 20 import android.app.Dialog; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.os.Bundle; 24 import android.os.Parcelable; 25 import android.text.TextUtils; 26 27 import androidx.annotation.NonNull; 28 import androidx.annotation.Nullable; 29 import androidx.annotation.StringRes; 30 import androidx.fragment.app.DialogFragment; 31 32 /** 33 * Common dialog that can be used across the settings app to ask the user to confirm their desired 34 * action. 35 */ 36 public class ConfirmationDialogFragment extends DialogFragment implements 37 DialogInterface.OnClickListener { 38 39 /** Builder to help construct {@link ConfirmationDialogFragment}. */ 40 public static class Builder { 41 42 private final Context mContext; 43 private Bundle mArgs; 44 private String mTitle; 45 private String mMessage; 46 private String mPosLabel; 47 private String mNegLabel; 48 private ConfirmListener mConfirmListener; 49 private RejectListener mRejectListener; 50 Builder(Context context)51 public Builder(Context context) { 52 mContext = context; 53 } 54 55 /** Sets the title. */ setTitle(String title)56 public Builder setTitle(String title) { 57 mTitle = title; 58 return this; 59 } 60 61 /** Sets the title. */ setTitle(@tringRes int title)62 public Builder setTitle(@StringRes int title) { 63 mTitle = mContext.getString(title); 64 return this; 65 } 66 67 /** Sets the message. */ setMessage(String message)68 public Builder setMessage(String message) { 69 mMessage = message; 70 return this; 71 } 72 73 /** Sets the message. */ setMessage(@tringRes int message)74 public Builder setMessage(@StringRes int message) { 75 mMessage = mContext.getString(message); 76 return this; 77 } 78 79 /** Sets the positive button label. */ setPositiveButton(String label, ConfirmListener confirmListener)80 public Builder setPositiveButton(String label, ConfirmListener confirmListener) { 81 mPosLabel = label; 82 mConfirmListener = confirmListener; 83 return this; 84 } 85 86 /** Sets the positive button label. */ setPositiveButton(@tringRes int label, ConfirmListener confirmListener)87 public Builder setPositiveButton(@StringRes int label, ConfirmListener confirmListener) { 88 mPosLabel = mContext.getString(label); 89 mConfirmListener = confirmListener; 90 return this; 91 } 92 93 /** Sets the negative button label. */ setNegativeButton(String label, RejectListener rejectListener)94 public Builder setNegativeButton(String label, RejectListener rejectListener) { 95 mNegLabel = label; 96 mRejectListener = rejectListener; 97 return this; 98 } 99 100 /** Sets the negative button label. */ setNegativeButton(@tringRes int label, RejectListener rejectListener)101 public Builder setNegativeButton(@StringRes int label, RejectListener rejectListener) { 102 mNegLabel = mContext.getString(label); 103 mRejectListener = rejectListener; 104 return this; 105 } 106 107 /** Adds an argument string to the argument bundle. */ addArgumentString(String argumentKey, String argument)108 public Builder addArgumentString(String argumentKey, String argument) { 109 if (mArgs == null) { 110 mArgs = new Bundle(); 111 } 112 mArgs.putString(argumentKey, argument); 113 return this; 114 } 115 116 /** Adds an argument boolean to the argument bundle. */ addArgumentBoolean(String argumentKey, boolean argument)117 public Builder addArgumentBoolean(String argumentKey, boolean argument) { 118 if (mArgs == null) { 119 mArgs = new Bundle(); 120 } 121 mArgs.putBoolean(argumentKey, argument); 122 return this; 123 } 124 125 /** Adds an argument Parcelable to the argument bundle. */ addArgumentParcelable(String argumentKey, Parcelable argument)126 public Builder addArgumentParcelable(String argumentKey, Parcelable argument) { 127 if (mArgs == null) { 128 mArgs = new Bundle(); 129 } 130 mArgs.putParcelable(argumentKey, argument); 131 return this; 132 } 133 134 /** Constructs the {@link ConfirmationDialogFragment}. */ build()135 public ConfirmationDialogFragment build() { 136 return ConfirmationDialogFragment.init(this); 137 } 138 } 139 140 /** Identifier used to launch the dialog fragment. */ 141 public static final String TAG = "ConfirmationDialogFragment"; 142 143 // Argument keys are prefixed with TAG in order to reduce the changes of collision with user 144 // provided arguments. 145 private static final String ALL_ARGUMENTS_KEY = TAG + "_all_arguments"; 146 private static final String ARGUMENTS_KEY = TAG + "_arguments"; 147 private static final String TITLE_KEY = TAG + "_title"; 148 private static final String MESSAGE_KEY = TAG + "_message"; 149 private static final String POSITIVE_KEY = TAG + "_positive"; 150 private static final String NEGATIVE_KEY = TAG + "_negative"; 151 152 private String mTitle; 153 private String mMessage; 154 private String mPosLabel; 155 private String mNegLabel; 156 private ConfirmListener mConfirmListener; 157 private RejectListener mRejectListener; 158 159 /** Constructs the dialog fragment from the arguments provided in the {@link Builder} */ init(Builder builder)160 private static ConfirmationDialogFragment init(Builder builder) { 161 ConfirmationDialogFragment dialogFragment = new ConfirmationDialogFragment(); 162 Bundle args = new Bundle(); 163 args.putBundle(ARGUMENTS_KEY, builder.mArgs); 164 args.putString(TITLE_KEY, builder.mTitle); 165 args.putString(MESSAGE_KEY, builder.mMessage); 166 args.putString(POSITIVE_KEY, builder.mPosLabel); 167 args.putString(NEGATIVE_KEY, builder.mNegLabel); 168 dialogFragment.setArguments(args); 169 dialogFragment.setConfirmListener(builder.mConfirmListener); 170 dialogFragment.setRejectListener(builder.mRejectListener); 171 return dialogFragment; 172 } 173 174 /** 175 * Since it is possible for the listeners to be unregistered on configuration change, provide a 176 * way to reattach the listeners. 177 */ resetListeners(@ullable ConfirmationDialogFragment dialogFragment, @Nullable ConfirmListener confirmListener, @Nullable RejectListener rejectListener)178 public static void resetListeners(@Nullable ConfirmationDialogFragment dialogFragment, 179 @Nullable ConfirmListener confirmListener, @Nullable RejectListener rejectListener) { 180 if (dialogFragment != null) { 181 dialogFragment.setConfirmListener(confirmListener); 182 dialogFragment.setRejectListener(rejectListener); 183 } 184 } 185 186 /** Sets the listener which listens to a click on the positive button. */ setConfirmListener(ConfirmListener confirmListener)187 private void setConfirmListener(ConfirmListener confirmListener) { 188 mConfirmListener = confirmListener; 189 } 190 191 /** Gets the listener which listens to a click on the positive button */ 192 @Nullable getConfirmListener()193 public ConfirmListener getConfirmListener() { 194 return mConfirmListener; 195 } 196 197 /** Sets the listener which listens to a click on the negative button. */ setRejectListener(RejectListener rejectListener)198 private void setRejectListener(RejectListener rejectListener) { 199 mRejectListener = rejectListener; 200 } 201 202 /** Gets the listener which listens to a click on the negative button. */ 203 @Nullable getRejectListener()204 public RejectListener getRejectListener() { 205 return mRejectListener; 206 } 207 208 @Override onCreate(@ullable Bundle savedInstanceState)209 public void onCreate(@Nullable Bundle savedInstanceState) { 210 super.onCreate(savedInstanceState); 211 212 Bundle args = getArguments(); 213 if (savedInstanceState != null) { 214 args = savedInstanceState.getBundle(ALL_ARGUMENTS_KEY); 215 } 216 217 if (args != null) { 218 mTitle = getArguments().getString(TITLE_KEY); 219 mMessage = getArguments().getString(MESSAGE_KEY); 220 mPosLabel = getArguments().getString(POSITIVE_KEY); 221 mNegLabel = getArguments().getString(NEGATIVE_KEY); 222 } 223 } 224 225 @Override onSaveInstanceState(@onNull Bundle outState)226 public void onSaveInstanceState(@NonNull Bundle outState) { 227 super.onSaveInstanceState(outState); 228 outState.putBundle(ALL_ARGUMENTS_KEY, getArguments()); 229 } 230 231 @NonNull 232 @Override onCreateDialog(@ullable Bundle savedInstanceState)233 public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { 234 AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 235 if (!TextUtils.isEmpty(mTitle)) { 236 builder.setTitle(mTitle); 237 } 238 if (!TextUtils.isEmpty(mMessage)) { 239 builder.setMessage(mMessage); 240 } 241 if (!TextUtils.isEmpty(mPosLabel)) { 242 builder.setPositiveButton(mPosLabel, this); 243 } 244 if (!TextUtils.isEmpty(mNegLabel)) { 245 builder.setNegativeButton(mNegLabel, this); 246 } 247 return builder.create(); 248 } 249 250 @Override onClick(DialogInterface dialog, int which)251 public void onClick(DialogInterface dialog, int which) { 252 if (which == DialogInterface.BUTTON_POSITIVE) { 253 if (mConfirmListener != null) { 254 mConfirmListener.onConfirm(getArguments().getBundle(ARGUMENTS_KEY)); 255 } 256 } else if (which == DialogInterface.BUTTON_NEGATIVE) { 257 if (mRejectListener != null) { 258 mRejectListener.onReject(getArguments().getBundle(ARGUMENTS_KEY)); 259 } 260 } 261 } 262 263 /** Listens to the confirmation action. */ 264 public interface ConfirmListener { 265 /** 266 * Defines the action to take on confirm. The bundle will contain the arguments added when 267 * constructing the dialog through with {@link Builder#addArgumentString(String, String)}. 268 */ onConfirm(@ullable Bundle arguments)269 void onConfirm(@Nullable Bundle arguments); 270 } 271 272 /** Listens to the rejection action. */ 273 public interface RejectListener { 274 /** 275 * Defines the action to take on reject. The bundle will contain the arguments added when 276 * constructing the dialog through with {@link Builder#addArgumentString(String, String)}. 277 */ onReject(@ullable Bundle arguments)278 void onReject(@Nullable Bundle arguments); 279 } 280 } 281