1 /*
2  * Copyright (C) 2024 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.settings.connecteddevice.audiosharing;
18 
19 import android.content.Context;
20 import android.content.DialogInterface;
21 import android.view.LayoutInflater;
22 import android.view.View;
23 import android.widget.Button;
24 import android.widget.ImageView;
25 import android.widget.TextView;
26 
27 import androidx.annotation.DrawableRes;
28 import androidx.annotation.NonNull;
29 import androidx.annotation.StringRes;
30 import androidx.appcompat.app.AlertDialog;
31 import androidx.recyclerview.widget.LinearLayoutManager;
32 import androidx.recyclerview.widget.RecyclerView;
33 
34 import com.android.settings.R;
35 
36 import javax.annotation.CheckReturnValue;
37 
38 public class AudioSharingDialogFactory {
39     private static final String TAG = "AudioSharingDialogFactory";
40 
41     /**
42      * Initializes a builder for the dialog to be shown for audio sharing.
43      *
44      * @param context The {@link Context} that will be used to create the dialog.
45      * @return A configurable builder for the dialog.
46      */
47     @NonNull
newBuilder(@onNull Context context)48     public static AudioSharingDialogFactory.DialogBuilder newBuilder(@NonNull Context context) {
49         return new AudioSharingDialogFactory.DialogBuilder(context);
50     }
51 
52     /** Builder class with configurable options for the dialog to be shown for audio sharing. */
53     public static class DialogBuilder {
54         private Context mContext;
55         private AlertDialog.Builder mBuilder;
56         private View mCustomTitle;
57         private View mCustomBody;
58         private boolean mIsCustomBodyEnabled;
59 
60         /**
61          * Private constructor for the dialog builder class. Should not be invoked directly;
62          * instead, use {@link AudioSharingDialogFactory#newBuilder(Context)}.
63          *
64          * @param context The {@link Context} that will be used to create the dialog.
65          */
DialogBuilder(@onNull Context context)66         private DialogBuilder(@NonNull Context context) {
67             mContext = context;
68             mBuilder = new AlertDialog.Builder(context);
69             LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext());
70             mCustomTitle =
71                     inflater.inflate(R.layout.dialog_custom_title_audio_sharing, /* root= */ null);
72             mCustomBody =
73                     inflater.inflate(R.layout.dialog_custom_body_audio_sharing, /* parent= */ null);
74         }
75 
76         /**
77          * Sets title of the dialog custom title.
78          *
79          * @param titleRes Resource ID of the string to be used for the dialog title.
80          * @return This builder.
81          */
82         @NonNull
setTitle(@tringRes int titleRes)83         public AudioSharingDialogFactory.DialogBuilder setTitle(@StringRes int titleRes) {
84             TextView title = mCustomTitle.findViewById(R.id.title_text);
85             title.setText(titleRes);
86             return this;
87         }
88 
89         /**
90          * Sets title of the dialog custom title.
91          *
92          * @param titleText The text to be used for the title.
93          * @return This builder.
94          */
95         @NonNull
setTitle(@onNull CharSequence titleText)96         public AudioSharingDialogFactory.DialogBuilder setTitle(@NonNull CharSequence titleText) {
97             TextView title = mCustomTitle.findViewById(R.id.title_text);
98             title.setText(titleText);
99             return this;
100         }
101 
102         /**
103          * Sets the title icon of the dialog custom title.
104          *
105          * @param iconRes The text to be used for the title.
106          * @return This builder.
107          */
108         @NonNull
setTitleIcon(@rawableRes int iconRes)109         public AudioSharingDialogFactory.DialogBuilder setTitleIcon(@DrawableRes int iconRes) {
110             ImageView icon = mCustomTitle.findViewById(R.id.title_icon);
111             icon.setImageResource(iconRes);
112             return this;
113         }
114 
115         /**
116          * Sets the message body of the dialog.
117          *
118          * @param messageRes Resource ID of the string to be used for the message body.
119          * @return This builder.
120          */
121         @NonNull
setMessage(@tringRes int messageRes)122         public AudioSharingDialogFactory.DialogBuilder setMessage(@StringRes int messageRes) {
123             mBuilder.setMessage(messageRes);
124             return this;
125         }
126 
127         /**
128          * Sets the message body of the dialog.
129          *
130          * @param message The text to be used for the message body.
131          * @return This builder.
132          */
133         @NonNull
setMessage(@onNull CharSequence message)134         public AudioSharingDialogFactory.DialogBuilder setMessage(@NonNull CharSequence message) {
135             mBuilder.setMessage(message);
136             return this;
137         }
138 
139         /** Whether to use custom body. */
140         @NonNull
setIsCustomBodyEnabled( boolean isCustomBodyEnabled)141         public AudioSharingDialogFactory.DialogBuilder setIsCustomBodyEnabled(
142                 boolean isCustomBodyEnabled) {
143             mIsCustomBodyEnabled = isCustomBodyEnabled;
144             return this;
145         }
146 
147         /**
148          * Sets the custom image of the dialog custom body.
149          *
150          * @param iconRes The text to be used for the title.
151          * @return This builder.
152          */
153         @NonNull
setCustomImage(@rawableRes int iconRes)154         public AudioSharingDialogFactory.DialogBuilder setCustomImage(@DrawableRes int iconRes) {
155             ImageView image = mCustomBody.findViewById(R.id.description_image);
156             image.setImageResource(iconRes);
157             image.setVisibility(View.VISIBLE);
158             return this;
159         }
160 
161         /**
162          * Sets the custom message of the dialog custom body.
163          *
164          * @param messageRes Resource ID of the string to be used for the message body.
165          * @return This builder.
166          */
167         @NonNull
setCustomMessage(@tringRes int messageRes)168         public AudioSharingDialogFactory.DialogBuilder setCustomMessage(@StringRes int messageRes) {
169             TextView subTitle = mCustomBody.findViewById(R.id.description_text);
170             subTitle.setText(messageRes);
171             subTitle.setVisibility(View.VISIBLE);
172             return this;
173         }
174 
175         /**
176          * Sets the custom message of the dialog custom body.
177          *
178          * @param message The text to be used for the custom message body.
179          * @return This builder.
180          */
181         @NonNull
setCustomMessage( @onNull CharSequence message)182         public AudioSharingDialogFactory.DialogBuilder setCustomMessage(
183                 @NonNull CharSequence message) {
184             TextView subTitle = mCustomBody.findViewById(R.id.description_text);
185             subTitle.setText(message);
186             subTitle.setVisibility(View.VISIBLE);
187             return this;
188         }
189 
190         /**
191          * Sets the custom device actions of the dialog custom body.
192          *
193          * @param adapter The adapter for device items to build dialog actions.
194          * @return This builder.
195          */
196         @NonNull
setCustomDeviceActions( @onNull AudioSharingDeviceAdapter adapter)197         public AudioSharingDialogFactory.DialogBuilder setCustomDeviceActions(
198                 @NonNull AudioSharingDeviceAdapter adapter) {
199             RecyclerView recyclerView = mCustomBody.findViewById(R.id.device_btn_list);
200             recyclerView.setAdapter(adapter);
201             recyclerView.setLayoutManager(
202                     new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false));
203             recyclerView.setVisibility(View.VISIBLE);
204             return this;
205         }
206 
207         /**
208          * Sets the positive button label and listener for the dialog.
209          *
210          * @param labelRes Resource ID of the string to be used for the positive button label.
211          * @param listener The listener to be invoked when the positive button is pressed.
212          * @return This builder.
213          */
214         @NonNull
setPositiveButton( @tringRes int labelRes, @NonNull DialogInterface.OnClickListener listener)215         public AudioSharingDialogFactory.DialogBuilder setPositiveButton(
216                 @StringRes int labelRes, @NonNull DialogInterface.OnClickListener listener) {
217             mBuilder.setPositiveButton(labelRes, listener);
218             return this;
219         }
220 
221         /**
222          * Sets the positive button label and listener for the dialog.
223          *
224          * @param label The text to be used for the positive button label.
225          * @param listener The listener to be invoked when the positive button is pressed.
226          * @return This builder.
227          */
228         @NonNull
setPositiveButton( @onNull CharSequence label, @NonNull DialogInterface.OnClickListener listener)229         public AudioSharingDialogFactory.DialogBuilder setPositiveButton(
230                 @NonNull CharSequence label, @NonNull DialogInterface.OnClickListener listener) {
231             mBuilder.setPositiveButton(label, listener);
232             return this;
233         }
234 
235         /**
236          * Sets the custom positive button label and listener for the dialog custom body.
237          *
238          * @param labelRes Resource ID of the string to be used for the positive button label.
239          * @param listener The listener to be invoked when the positive button is pressed.
240          * @return This builder.
241          */
242         @NonNull
setCustomPositiveButton( @tringRes int labelRes, @NonNull View.OnClickListener listener)243         public AudioSharingDialogFactory.DialogBuilder setCustomPositiveButton(
244                 @StringRes int labelRes, @NonNull View.OnClickListener listener) {
245             Button positiveBtn = mCustomBody.findViewById(R.id.positive_btn);
246             positiveBtn.setText(labelRes);
247             positiveBtn.setOnClickListener(listener);
248             positiveBtn.setVisibility(View.VISIBLE);
249             return this;
250         }
251 
252         /**
253          * Sets the custom positive button label and listener for the dialog custom body.
254          *
255          * @param label The text to be used for the positive button label.
256          * @param listener The listener to be invoked when the positive button is pressed.
257          * @return This builder.
258          */
259         @NonNull
setCustomPositiveButton( @onNull CharSequence label, @NonNull View.OnClickListener listener)260         public AudioSharingDialogFactory.DialogBuilder setCustomPositiveButton(
261                 @NonNull CharSequence label, @NonNull View.OnClickListener listener) {
262             Button positiveBtn = mCustomBody.findViewById(R.id.positive_btn);
263             positiveBtn.setText(label);
264             positiveBtn.setOnClickListener(listener);
265             positiveBtn.setVisibility(View.VISIBLE);
266             return this;
267         }
268 
269         /**
270          * Sets the negative button label and listener for the dialog.
271          *
272          * @param labelRes Resource ID of the string to be used for the negative button label.
273          * @param listener The listener to be invoked when the negative button is pressed.
274          * @return This builder.
275          */
276         @NonNull
setNegativeButton( @tringRes int labelRes, @NonNull DialogInterface.OnClickListener listener)277         public AudioSharingDialogFactory.DialogBuilder setNegativeButton(
278                 @StringRes int labelRes, @NonNull DialogInterface.OnClickListener listener) {
279             mBuilder.setNegativeButton(labelRes, listener);
280             return this;
281         }
282 
283         /**
284          * Sets the negative button label and listener for the dialog.
285          *
286          * @param label The text to be used for the negative button label.
287          * @param listener The listener to be invoked when the negative button is pressed.
288          * @return This builder.
289          */
290         @NonNull
setNegativeButton( @onNull CharSequence label, @NonNull DialogInterface.OnClickListener listener)291         public AudioSharingDialogFactory.DialogBuilder setNegativeButton(
292                 @NonNull CharSequence label, @NonNull DialogInterface.OnClickListener listener) {
293             mBuilder.setNegativeButton(label, listener);
294             return this;
295         }
296 
297         /**
298          * Sets the custom negative button label and listener for the dialog custom body.
299          *
300          * @param labelRes Resource ID of the string to be used for the negative button label.
301          * @param listener The listener to be invoked when the negative button is pressed.
302          * @return This builder.
303          */
304         @NonNull
setCustomNegativeButton( @tringRes int labelRes, @NonNull View.OnClickListener listener)305         public AudioSharingDialogFactory.DialogBuilder setCustomNegativeButton(
306                 @StringRes int labelRes, @NonNull View.OnClickListener listener) {
307             Button negativeBtn = mCustomBody.findViewById(R.id.negative_btn);
308             negativeBtn.setText(labelRes);
309             negativeBtn.setOnClickListener(listener);
310             negativeBtn.setVisibility(View.VISIBLE);
311             return this;
312         }
313 
314         /**
315          * Sets the custom negative button label and listener for the dialog custom body.
316          *
317          * @param label The text to be used for the negative button label.
318          * @param listener The listener to be invoked when the negative button is pressed.
319          * @return This builder.
320          */
321         @NonNull
setCustomNegativeButton( @onNull CharSequence label, @NonNull View.OnClickListener listener)322         public AudioSharingDialogFactory.DialogBuilder setCustomNegativeButton(
323                 @NonNull CharSequence label, @NonNull View.OnClickListener listener) {
324             Button negativeBtn = mCustomBody.findViewById(R.id.negative_btn);
325             negativeBtn.setText(label);
326             negativeBtn.setOnClickListener(listener);
327             negativeBtn.setVisibility(View.VISIBLE);
328             return this;
329         }
330 
331         /**
332          * Builds a dialog with the current configs.
333          *
334          * @return The dialog to be shown for audio sharing.
335          */
336         @NonNull
337         @CheckReturnValue
build()338         public AlertDialog build() {
339             if (mIsCustomBodyEnabled) {
340                 mBuilder.setView(mCustomBody);
341             }
342             final AlertDialog dialog =
343                     mBuilder.setCustomTitle(mCustomTitle).setCancelable(false).create();
344             dialog.setCanceledOnTouchOutside(false);
345             return dialog;
346         }
347     }
348 
AudioSharingDialogFactory()349     private AudioSharingDialogFactory() {}
350 }
351