• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.bluetooth;
18 
19 import android.app.settings.SettingsEnums;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothProfile;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.provider.DeviceConfig;
25 import android.provider.Settings;
26 import android.text.TextUtils;
27 import android.util.Log;
28 import android.widget.Toast;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.VisibleForTesting;
32 import androidx.appcompat.app.AlertDialog;
33 
34 import com.android.settings.R;
35 import com.android.settings.core.SettingsUIDeviceConfig;
36 import com.android.settings.overlay.FeatureFactory;
37 import com.android.settingslib.bluetooth.BluetoothUtils;
38 import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener;
39 import com.android.settingslib.bluetooth.LocalBluetoothManager;
40 import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
41 
42 /**
43  * Utils is a helper class that contains constants for various
44  * Android resource IDs, debug logging flags, and static methods
45  * for creating dialogs.
46  */
47 public final class Utils {
48 
49     private static final String TAG = "BluetoothUtils";
50 
51     static final boolean V = BluetoothUtils.V; // verbose logging
52     static final boolean D = BluetoothUtils.D;  // regular logging
53 
Utils()54     private Utils() {
55     }
56 
getConnectionStateSummary(int connectionState)57     public static int getConnectionStateSummary(int connectionState) {
58         switch (connectionState) {
59             case BluetoothProfile.STATE_CONNECTED:
60                 return R.string.bluetooth_connected;
61             case BluetoothProfile.STATE_CONNECTING:
62                 return R.string.bluetooth_connecting;
63             case BluetoothProfile.STATE_DISCONNECTED:
64                 return R.string.bluetooth_disconnected;
65             case BluetoothProfile.STATE_DISCONNECTING:
66                 return R.string.bluetooth_disconnecting;
67             default:
68                 return 0;
69         }
70     }
71 
72     // Create (or recycle existing) and show disconnect dialog.
showDisconnectDialog(Context context, AlertDialog dialog, DialogInterface.OnClickListener disconnectListener, CharSequence title, CharSequence message)73     static AlertDialog showDisconnectDialog(Context context,
74             AlertDialog dialog,
75             DialogInterface.OnClickListener disconnectListener,
76             CharSequence title, CharSequence message) {
77         if (dialog == null) {
78             dialog = new AlertDialog.Builder(context)
79                     .setPositiveButton(android.R.string.ok, disconnectListener)
80                     .setNegativeButton(android.R.string.cancel, null)
81                     .create();
82         } else {
83             if (dialog.isShowing()) {
84                 dialog.dismiss();
85             }
86             // use disconnectListener for the correct profile(s)
87             CharSequence okText = context.getText(android.R.string.ok);
88             dialog.setButton(DialogInterface.BUTTON_POSITIVE,
89                     okText, disconnectListener);
90         }
91         dialog.setTitle(title);
92         dialog.setMessage(message);
93         dialog.show();
94         return dialog;
95     }
96 
97     @VisibleForTesting
showConnectingError(Context context, String name, LocalBluetoothManager manager)98     static void showConnectingError(Context context, String name, LocalBluetoothManager manager) {
99         FeatureFactory.getFactory(context).getMetricsFeatureProvider().visible(context,
100                 SettingsEnums.PAGE_UNKNOWN, SettingsEnums.ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR,
101                 0);
102         showError(context, name, R.string.bluetooth_connecting_error_message, manager);
103     }
104 
showError(Context context, String name, int messageResId)105     static void showError(Context context, String name, int messageResId) {
106         showError(context, name, messageResId, getLocalBtManager(context));
107     }
108 
showError(Context context, String name, int messageResId, LocalBluetoothManager manager)109     private static void showError(Context context, String name, int messageResId,
110             LocalBluetoothManager manager) {
111         String message = context.getString(messageResId, name);
112         Context activity = manager.getForegroundActivity();
113         if (manager.isForegroundActivity()) {
114             try {
115                 new AlertDialog.Builder(activity)
116                         .setTitle(R.string.bluetooth_error_title)
117                         .setMessage(message)
118                         .setPositiveButton(android.R.string.ok, null)
119                         .show();
120             } catch (Exception e) {
121                 Log.e(TAG, "Cannot show error dialog.", e);
122             }
123         } else {
124             Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
125         }
126     }
127 
getLocalBtManager(Context context)128     public static LocalBluetoothManager getLocalBtManager(Context context) {
129         return LocalBluetoothManager.getInstance(context, mOnInitCallback);
130     }
131 
createRemoteName(Context context, BluetoothDevice device)132     public static String createRemoteName(Context context, BluetoothDevice device) {
133         String mRemoteName = device != null ? device.getAlias() : null;
134 
135         if (mRemoteName == null) {
136             mRemoteName = context.getString(R.string.unknown);
137         }
138         return mRemoteName;
139     }
140 
141     private static final ErrorListener mErrorListener = new ErrorListener() {
142         @Override
143         public void onShowError(Context context, String name, int messageResId) {
144             showError(context, name, messageResId);
145         }
146     };
147 
148     private static final BluetoothManagerCallback mOnInitCallback = new BluetoothManagerCallback() {
149         @Override
150         public void onBluetoothManagerInitialized(Context appContext,
151                 LocalBluetoothManager bluetoothManager) {
152             BluetoothUtils.setErrorListener(mErrorListener);
153         }
154     };
155 
isBluetoothScanningEnabled(Context context)156     public static boolean isBluetoothScanningEnabled(Context context) {
157         return Settings.Global.getInt(context.getContentResolver(),
158                 Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0) == 1;
159     }
160 
161     /**
162      * Check if the Bluetooth device supports advanced details header
163      *
164      * @param bluetoothDevice the BluetoothDevice to get metadata
165      * @return true if it supports advanced details header, false otherwise.
166      */
isAdvancedDetailsHeader(@onNull BluetoothDevice bluetoothDevice)167     public static boolean isAdvancedDetailsHeader(@NonNull BluetoothDevice bluetoothDevice) {
168         final boolean advancedEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
169                 SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, true);
170         if (!advancedEnabled) {
171             Log.d(TAG, "isAdvancedDetailsHeader: advancedEnabled is false");
172             return false;
173         }
174         // The metadata is for Android R
175         final boolean untetheredHeadset = BluetoothUtils.getBooleanMetaData(bluetoothDevice,
176                 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET);
177         if (untetheredHeadset) {
178             Log.d(TAG, "isAdvancedDetailsHeader: untetheredHeadset is true");
179             return true;
180         }
181         // The metadata is for Android S
182         final String deviceType = BluetoothUtils.getStringMetaData(bluetoothDevice,
183                 BluetoothDevice.METADATA_DEVICE_TYPE);
184         if (TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)
185                 || TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_WATCH)
186                 || TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_DEFAULT)) {
187             Log.d(TAG, "isAdvancedDetailsHeader: deviceType is " + deviceType);
188             return true;
189         }
190         return false;
191     }
192 }
193