• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.audiostreams;
18 
19 import android.app.settings.SettingsEnums;
20 import android.bluetooth.BluetoothLeBroadcastMetadata;
21 import android.content.Context;
22 import android.content.res.Resources;
23 import android.graphics.Bitmap;
24 import android.graphics.drawable.Drawable;
25 import android.os.Bundle;
26 import android.util.Log;
27 import android.view.LayoutInflater;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.widget.ImageView;
31 import android.widget.TextView;
32 
33 import androidx.annotation.NonNull;
34 import androidx.annotation.Nullable;
35 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
36 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
37 
38 import com.android.settings.R;
39 import com.android.settings.bluetooth.Utils;
40 import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider;
41 import com.android.settings.core.InstrumentedFragment;
42 import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
43 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
44 import com.android.settingslib.qrcode.QrCodeGenerator;
45 import com.android.settingslib.utils.ThreadUtils;
46 import com.android.settings.overlay.FeatureFactory;
47 
48 import com.google.zxing.WriterException;
49 
50 import java.nio.charset.StandardCharsets;
51 import java.util.List;
52 import java.util.Optional;
53 
54 public class AudioStreamsQrCodeFragment extends InstrumentedFragment {
55     private static final String TAG = "AudioStreamsQrCodeFragment";
56 
57     AudioSharingFeatureProvider audioSharingFeatureProvider =
58             FeatureFactory.getFeatureFactory().getAudioSharingFeatureProvider();
59 
60     @Override
getMetricsCategory()61     public int getMetricsCategory() {
62         return SettingsEnums.AUDIO_STREAM_QR_CODE;
63     }
64 
65     @Override
onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)66     public final View onCreateView(
67             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
68         return inflater.inflate(R.layout.bluetooth_audio_streams_qr_code, container, false);
69     }
70 
71     @Override
onViewCreated(@onNull View view, @Nullable Bundle savedInstanceState)72     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
73         super.onViewCreated(view, savedInstanceState);
74         // Collapse or expand the app bar based on orientation for better display the qr code image.
75         AudioStreamsHelper.configureAppBarByOrientation(getActivity());
76         var unused =
77                 ThreadUtils.postOnBackgroundThread(
78                         () -> {
79                             BluetoothLeBroadcastMetadata broadcastMetadata = getBroadcastMetadata();
80                             if (broadcastMetadata == null) {
81                                 return;
82                             }
83                             Drawable drawable =
84                                     getQrCodeDrawable(broadcastMetadata, getActivity())
85                                             .orElse(null);
86                             if (drawable == null) {
87                                 return;
88                             }
89 
90                             ThreadUtils.postOnMainThread(
91                                     () -> {
92                                         audioSharingFeatureProvider.setQrCode(
93                                                 this,
94                                                 view,
95                                                 R.id.qrcode_view,
96                                                 drawable,
97                                                 BluetoothLeBroadcastMetadataExt.INSTANCE
98                                                         .toQrCodeString(broadcastMetadata));
99                                         if (broadcastMetadata.getBroadcastCode() != null) {
100                                             String password =
101                                                     new String(
102                                                             broadcastMetadata.getBroadcastCode(),
103                                                             StandardCharsets.UTF_8);
104                                             String passwordText =
105                                                     getString(
106                                                             R.string
107                                                                     .audio_streams_qr_code_page_password,
108                                                             password);
109                                             ((TextView) view.requireViewById(R.id.password))
110                                                     .setText(passwordText);
111                                         }
112                                         TextView summaryView =
113                                                 view.requireViewById(android.R.id.summary);
114                                         String summary =
115                                                 getString(
116                                                         R.string
117                                                                 .audio_streams_qr_code_page_description,
118                                                         broadcastMetadata.getBroadcastName());
119                                         summaryView.setText(summary);
120                                     });
121                         });
122     }
123 
124     /** Gets an optional drawable from metadata. */
getQrCodeDrawable( @ullable BluetoothLeBroadcastMetadata metadata, Context context)125     public static Optional<Drawable> getQrCodeDrawable(
126             @Nullable BluetoothLeBroadcastMetadata metadata,
127             Context context) {
128         if (metadata == null) {
129             Log.d(TAG, "getQrCodeDrawable: broadcastMetadata is empty!");
130             return Optional.empty();
131         }
132         String metadataStr = BluetoothLeBroadcastMetadataExt.INSTANCE.toQrCodeString(metadata);
133         if (metadataStr.isEmpty()) {
134             Log.d(TAG, "getQrCodeDrawable: metadataStr is empty!");
135             return Optional.empty();
136         }
137         Log.d(TAG, "getQrCodeDrawable: metadata : " + metadata);
138         try {
139             Resources resources = context.getResources();
140             int qrcodeSize = resources.getDimensionPixelSize(R.dimen.audio_streams_qrcode_size);
141             int margin = resources.getDimensionPixelSize(R.dimen.audio_streams_qrcode_margin);
142             Bitmap bitmap = QrCodeGenerator.encodeQrCode(metadataStr, qrcodeSize, margin);
143             RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(resources, bitmap);
144             drawable.setCornerRadius(resources.getDimensionPixelSize(
145                     R.dimen.audio_streams_qrcode_preview_radius));
146             return Optional.of(drawable);
147         } catch (WriterException e) {
148             Log.d(
149                     TAG,
150                     "getQrCodeDrawable: broadcastMetadata "
151                             + metadata
152                             + " qrCode generation exception "
153                             + e);
154         }
155 
156         return Optional.empty();
157     }
158 
159     @Nullable
getBroadcastMetadata()160     private BluetoothLeBroadcastMetadata getBroadcastMetadata() {
161         LocalBluetoothLeBroadcast localBluetoothLeBroadcast =
162                 Utils.getLocalBtManager(getActivity())
163                         .getProfileManager()
164                         .getLeAudioBroadcastProfile();
165         if (localBluetoothLeBroadcast == null) {
166             Log.d(TAG, "getBroadcastMetadataQrCode: localBluetoothLeBroadcast is null!");
167             return null;
168         }
169 
170         List<BluetoothLeBroadcastMetadata> metadata =
171                 localBluetoothLeBroadcast.getAllBroadcastMetadata();
172         if (metadata.isEmpty()) {
173             Log.d(TAG, "getBroadcastMetadataQrCode: metadata is null!");
174             return null;
175         }
176 
177         return metadata.get(0);
178     }
179 }
180