1 /*
2  * Copyright 2016, 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.managedprovisioning.common;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.DialogFragment;
22 import android.os.Bundle;
23 
24 import androidx.annotation.StringRes;
25 
26 import com.android.managedprovisioning.util.LazyStringResource;
27 
28 /**
29  * Utility class wrapping a {@link AlertDialog} in a {@link DialogFragment}
30  * <p> In order to properly handle Dialog lifecycle we follow the practice of wrapping of them
31  * in a Dialog Fragment.
32  * <p> If buttons are to be used (enabled by setting a button message), the creator {@link Activity}
33  * must implement {@link SimpleDialogListener}.
34  */
35 public class SimpleDialog extends DialogFragment {
36     private static final String TITLE = "title";
37     private static final String MESSAGE = "message";
38     private static final String LOCALIZED_MESSAGE = "localized_message";
39     private static final String NEGATIVE_BUTTON_MESSAGE = "negativeButtonMessage";
40     private static final String POSITIVE_BUTTON_MESSAGE = "positiveButtonMessage";
41 
42     /**
43      * Use the {@link Builder} instead. Keeping the constructor public only because
44      * a {@link DialogFragment} must have an empty constructor that is public.
45      */
SimpleDialog()46     public SimpleDialog() {
47     }
48 
49     @Override
onCreateDialog(Bundle savedInstanceState)50     public AlertDialog onCreateDialog(Bundle savedInstanceState) {
51         var context = getContext();
52         final SimpleDialogListener dialogListener = (SimpleDialogListener) getActivity();
53 
54         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
55 
56         Bundle args = getArguments();
57         if (args.containsKey(TITLE)) {
58             builder.setTitle(LazyStringResource.of(args.getBundle(TITLE)).value(context));
59         }
60 
61         if (args.containsKey(MESSAGE)) {
62             builder.setMessage(LazyStringResource.of(args.getBundle(MESSAGE)).value(context));
63         }
64 
65         if (args.containsKey(NEGATIVE_BUTTON_MESSAGE)) {
66             builder.setNegativeButton(
67                     LazyStringResource.of(args.getBundle(NEGATIVE_BUTTON_MESSAGE)).value(context),
68                     (dialog, which) -> dialogListener.onNegativeButtonClick(SimpleDialog.this));
69         }
70 
71         if (args.containsKey(POSITIVE_BUTTON_MESSAGE)) {
72             builder.setPositiveButton(
73                     LazyStringResource.of(args.getBundle(POSITIVE_BUTTON_MESSAGE)).value(context),
74                     (dialog, which) -> dialogListener.onPositiveButtonClick(SimpleDialog.this));
75         }
76 
77         return builder.create();
78     }
79 
80     /**
81      * Throws an exception informing of a lack of a handler for a dialog button click
82      * <p> Useful when implementing {@link SimpleDialogListener}
83      */
throwButtonClickHandlerNotImplemented(DialogFragment dialog)84     public static void throwButtonClickHandlerNotImplemented(DialogFragment dialog) {
85         throw new IllegalArgumentException("Button click handler not implemented for dialog: "
86                 + dialog.getTag());
87     }
88 
89     /**
90      * A builder for SimpleDialog
91      */
92     public static class Builder implements DialogBuilder {
93         private LazyStringResource mTitle;
94         private LazyStringResource mMessage;
95         private LazyStringResource mNegativeButtonMessage;
96         private LazyStringResource mPositiveButtonMessage;
97         private Boolean mCancelable;
98 
99         /**
100          * Sets the title
101          *
102          * @param title Title resource.
103          */
setTitle(LazyStringResource title)104         public Builder setTitle(LazyStringResource title) {
105             this.mTitle = title;
106             return this;
107         }
108 
109         /**
110          * Sets the title
111          *
112          * @param titleId Title resource id.
113          */
setTitle(@tringRes int titleId)114         public Builder setTitle(@StringRes int titleId) {
115             return setTitle(LazyStringResource.of(titleId));
116         }
117 
118         /**
119          * Sets the message
120          *
121          * @param message Message resource.
122          */
setMessage(LazyStringResource message)123         public Builder setMessage(LazyStringResource message) {
124             this.mMessage = message;
125             return this;
126         }
127 
128         /**
129          * Sets the message
130          *
131          * @param messageId Message resource id.
132          */
setMessage(@tringRes int messageId)133         public Builder setMessage(@StringRes int messageId) {
134             return setMessage(LazyStringResource.of(messageId));
135         }
136 
137         /**
138          * Sets a message for the button.
139          *
140          * <p>Makes the button appear (without setting a button message, a button is not displayed)
141          *
142          * <p>Callback must be handled by a creator {@link Activity}, which must implement {@link
143          * SimpleDialogListener}.
144          *
145          * @param negativeButtonMessage Message resource.
146          */
setNegativeButtonMessage(LazyStringResource negativeButtonMessage)147         public Builder setNegativeButtonMessage(LazyStringResource negativeButtonMessage) {
148             this.mNegativeButtonMessage = negativeButtonMessage;
149             return this;
150         }
151 
152         /**
153          * Sets a message for the button.
154          *
155          * <p>Makes the button appear (without setting a button message, a button is not displayed)
156          *
157          * <p>Callback must be handled by a creator {@link Activity}, which must implement {@link
158          * SimpleDialogListener}.
159          *
160          * @param negativeButtonMessageId Message resource id.
161          */
setNegativeButtonMessage(@tringRes int negativeButtonMessageId)162         public Builder setNegativeButtonMessage(@StringRes int negativeButtonMessageId) {
163             return setNegativeButtonMessage(LazyStringResource.of(negativeButtonMessageId));
164         }
165 
166         /**
167          * Sets a message for the button.
168          *
169          * <p>Makes the button appear (without setting a button message, a button is not displayed)
170          *
171          * <p>Callback must be handled by a creator {@link Activity}, which must implement {@link
172          * SimpleDialogListener}.
173          *
174          * @param positiveButtonMessage Message resource.
175          */
setPositiveButtonMessage(LazyStringResource positiveButtonMessage)176         public Builder setPositiveButtonMessage(LazyStringResource positiveButtonMessage) {
177             this.mPositiveButtonMessage = positiveButtonMessage;
178             return this;
179         }
180 
181         /**
182          * Sets a message for the button.
183          *
184          * <p>Makes the button appear (without setting a button message, a button is not displayed)
185          *
186          * <p>Callback must be handled by a creator {@link Activity}, which must implement {@link
187          * SimpleDialogListener}.
188          *
189          * @param positiveButtonMessageId Message resource id.
190          */
setPositiveButtonMessage(@tringRes int positiveButtonMessageId)191         public Builder setPositiveButtonMessage(@StringRes int positiveButtonMessageId) {
192             return setPositiveButtonMessage(LazyStringResource.of(positiveButtonMessageId));
193         }
194 
195         /** Sets whether the dialog is cancelable or not. Default is true. */
setCancelable(boolean cancelable)196         public Builder setCancelable(boolean cancelable) {
197             mCancelable = cancelable;
198             return this;
199         }
200 
201         /** Creates an {@link SimpleDialog} with the arguments supplied to this builder. */
202         @Override
build()203         public SimpleDialog build() {
204             SimpleDialog instance = new SimpleDialog();
205             Bundle args = new Bundle();
206 
207             if (mTitle != null) {
208                 args.putBundle(TITLE, mTitle.toBundle());
209             }
210 
211             if (mMessage != null) {
212                 args.putBundle(MESSAGE, mMessage.toBundle());
213             }
214 
215             if (mNegativeButtonMessage != null) {
216                 args.putBundle(NEGATIVE_BUTTON_MESSAGE, mNegativeButtonMessage.toBundle());
217             }
218 
219             if (mPositiveButtonMessage != null) {
220                 args.putBundle(POSITIVE_BUTTON_MESSAGE, mPositiveButtonMessage.toBundle());
221             }
222 
223             if (mCancelable != null) {
224                 instance.setCancelable(mCancelable);
225             }
226 
227             instance.setArguments(args);
228             return instance;
229         }
230     }
231 
232     /**
233      * Interface for handling callbacks from {@link SimpleDialog} buttons.
234      *
235      * <p>If multiple dialogs are used in a context of a single {@link Activity},
236      * a consumer of the interface can differentiate between dialogs using
237      * e.g. a {@link DialogFragment#getTag()}, or {@link DialogFragment#getArguments()}.
238      */
239     public interface SimpleDialogListener {
240         /**
241          * Called when a user clicks on the positive dialog button.
242          * <p> To be implemented by a host {@link Activity} object.
243          * @param dialog {@link DialogFragment} where the click happened.
244          */
onPositiveButtonClick(DialogFragment dialog)245         void onPositiveButtonClick(DialogFragment dialog);
246 
247         /**
248          * Called when a user clicks on the negative dialog button.
249          * <p> To be implemented by a host {@link Activity} object.
250          * @param dialog {@link DialogFragment} where the click happened.
251          */
onNegativeButtonClick(DialogFragment dialog)252         void onNegativeButtonClick(DialogFragment dialog);
253     }
254 }
255