1 /*
2  * Copyright (C) 2023 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.app.Dialog;
20 import android.app.settings.SettingsEnums;
21 import android.os.Bundle;
22 import android.util.Log;
23 import android.util.Pair;
24 
25 import androidx.annotation.NonNull;
26 import androidx.annotation.Nullable;
27 import androidx.annotation.VisibleForTesting;
28 import androidx.appcompat.app.AlertDialog;
29 import androidx.fragment.app.Fragment;
30 import androidx.fragment.app.FragmentManager;
31 
32 import com.android.settings.R;
33 import com.android.settings.bluetooth.Utils;
34 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
35 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
36 
37 import java.util.List;
38 
39 public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment {
40     private static final String TAG = "AudioSharingJoinDialog";
41 
42     private static final String BUNDLE_KEY_DEVICE_ITEMS = "bundle_key_device_items";
43     private static final String BUNDLE_KEY_NEW_DEVICE_NAME = "bundle_key_new_device_name";
44 
45     // The host creates an instance of this dialog fragment must implement this interface to receive
46     // event callbacks.
47     public interface DialogEventListener {
48         /** Called when users click the share audio button in the dialog. */
onShareClick()49         void onShareClick();
50 
51         /** Called when users click the cancel button in the dialog. */
onCancelClick()52         void onCancelClick();
53     }
54 
55     @Nullable private static DialogEventListener sListener;
56     @Nullable private static CachedBluetoothDevice sNewDevice;
57     private static Pair<Integer, Object>[] sEventData = new Pair[0];
58 
59     @Override
getMetricsCategory()60     public int getMetricsCategory() {
61         return AudioSharingUtils.isBroadcasting(Utils.getLocalBtManager(getContext()))
62                 ? SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE
63                 : SettingsEnums.DIALOG_START_AUDIO_SHARING;
64     }
65 
66     /**
67      * Display the {@link AudioSharingJoinDialogFragment} dialog.
68      *
69      * <p>If the dialog is showing, update the dialog message and event listener.
70      *
71      * @param host The Fragment this dialog will be hosted.
72      * @param deviceItems The existing connected device items eligible for audio sharing.
73      * @param newDevice The latest connected device triggered this dialog.
74      * @param listener The callback to handle the user action on this dialog.
75      * @param eventData The eventData to log with for dialog onClick events.
76      */
show( @onNull Fragment host, @NonNull List<AudioSharingDeviceItem> deviceItems, @NonNull CachedBluetoothDevice newDevice, @NonNull DialogEventListener listener, @NonNull Pair<Integer, Object>[] eventData)77     public static void show(
78             @NonNull Fragment host,
79             @NonNull List<AudioSharingDeviceItem> deviceItems,
80             @NonNull CachedBluetoothDevice newDevice,
81             @NonNull DialogEventListener listener,
82             @NonNull Pair<Integer, Object>[] eventData) {
83         if (!AudioSharingUtils.isFeatureEnabled()) return;
84         final FragmentManager manager = host.getChildFragmentManager();
85         sListener = listener;
86         sNewDevice = newDevice;
87         sEventData = eventData;
88         AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
89         if (dialog != null) {
90             Log.d(TAG, "Dialog is showing, update the content.");
91             updateDialog(deviceItems, newDevice.getName(), dialog);
92         } else {
93             Log.d(TAG, "Show up the dialog.");
94             final Bundle bundle = new Bundle();
95             bundle.putParcelableList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
96             bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDevice.getName());
97             final AudioSharingJoinDialogFragment dialogFrag = new AudioSharingJoinDialogFragment();
98             dialogFrag.setArguments(bundle);
99             dialogFrag.show(manager, TAG);
100         }
101     }
102 
103     /** Return the tag of {@link AudioSharingJoinDialogFragment} dialog. */
tag()104     public static @NonNull String tag() {
105         return TAG;
106     }
107 
108     /** Get the latest connected device which triggers the dialog. */
getDevice()109     public @Nullable CachedBluetoothDevice getDevice() {
110         return sNewDevice;
111     }
112 
113     /** Test only: get the {@link DialogEventListener} passed to the dialog. */
114     @VisibleForTesting
115     @Nullable
getListener()116     DialogEventListener getListener() {
117         return sListener;
118     }
119 
120     /** Test only: get the event data passed to the dialog. */
121     @VisibleForTesting
122     @NonNull
getEventData()123     Pair<Integer, Object>[] getEventData() {
124         return sEventData;
125     }
126 
127     @Override
128     @NonNull
onCreateDialog(@ullable Bundle savedInstanceState)129     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
130         Bundle arguments = requireArguments();
131         List<AudioSharingDeviceItem> deviceItems =
132                 arguments.getParcelable(BUNDLE_KEY_DEVICE_ITEMS, List.class);
133         String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME);
134         AlertDialog dialog =
135                 AudioSharingDialogFactory.newBuilder(getActivity())
136                         .setTitle(R.string.audio_sharing_share_dialog_title)
137                         .setTitleIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
138                         .setIsCustomBodyEnabled(true)
139                         .setCustomMessage(R.string.audio_sharing_dialog_share_content)
140                         .setCustomPositiveButton(
141                                 R.string.audio_sharing_share_button_label,
142                                 v -> {
143                                     if (sListener != null) {
144                                         sListener.onShareClick();
145                                         mMetricsFeatureProvider.action(
146                                                 getContext(),
147                                                 SettingsEnums
148                                                 .ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED,
149                                                 sEventData);
150                                     }
151                                     dismiss();
152                                 })
153                         .setCustomNegativeButton(
154                                 R.string.audio_sharing_no_thanks_button_label,
155                                 v -> {
156                                     if (sListener != null) {
157                                         sListener.onCancelClick();
158                                         mMetricsFeatureProvider.action(
159                                                 getContext(),
160                                                 SettingsEnums
161                                                 .ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED,
162                                                 sEventData);
163                                     }
164                                     dismiss();
165                                 })
166                         .build();
167         if (deviceItems == null) {
168             Log.d(TAG, "Fail to create dialog: null deviceItems");
169         } else {
170             updateDialog(deviceItems, newDeviceName, dialog);
171         }
172         dialog.show();
173         AudioSharingDialogHelper.updateMessageStyle(dialog);
174         return dialog;
175     }
176 
updateDialog( List<AudioSharingDeviceItem> deviceItems, String newDeviceName, @NonNull AlertDialog dialog)177     private static void updateDialog(
178             List<AudioSharingDeviceItem> deviceItems,
179             String newDeviceName,
180             @NonNull AlertDialog dialog) {
181         // Only dialog message can be updated when the dialog is showing.
182         // Thus we put the device name for sharing as the dialog message.
183         if (deviceItems.isEmpty()) {
184             dialog.setMessage(newDeviceName);
185         } else {
186             dialog.setMessage(
187                     dialog.getContext()
188                             .getString(
189                                     R.string.audio_sharing_share_dialog_subtitle,
190                                     deviceItems.get(0).getName(),
191                                     newDeviceName));
192         }
193     }
194 }
195