Merge changes from topic "talkback_dialog" into main
* changes: Show turn off talkback dialog if needed in find an audio stream page. Show turn off talkback dialog if needed in `AudioStreamConfirmDialog`.
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsDashboardFragment.KEY_BROADCAST_METADATA;
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsHelper.getEnabledScreenReaderServices;
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsHelper.setAccessibilityServiceOff;
|
||||
import static com.android.settingslib.bluetooth.BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA;
|
||||
|
||||
import android.app.Activity;
|
||||
@@ -41,6 +43,7 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
||||
private static final String TAG = "AudioStreamConfirmDialog";
|
||||
@@ -86,6 +89,8 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
||||
case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_FEATURE_UNSUPPORTED ->
|
||||
getUnsupportedDialog();
|
||||
case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE -> getNoLeDeviceDialog();
|
||||
case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_TURN_OFF_TALKBACK ->
|
||||
getTurnOffTalkbackDialog();
|
||||
case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN -> getConfirmDialog();
|
||||
default -> getErrorDialog();
|
||||
};
|
||||
@@ -168,6 +173,36 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
||||
.build();
|
||||
}
|
||||
|
||||
private Dialog getTurnOffTalkbackDialog() {
|
||||
return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
|
||||
.setTitle(getString(R.string.audio_streams_dialog_turn_off_talkback_title))
|
||||
.setSubTitle2(getString(R.string.audio_streams_dialog_turn_off_talkback_subtitle))
|
||||
.setLeftButtonText(getString(R.string.cancel))
|
||||
.setLeftButtonOnClickListener(
|
||||
unused -> {
|
||||
dismiss();
|
||||
if (mActivity != null) {
|
||||
mActivity.finish();
|
||||
}
|
||||
})
|
||||
.setRightButtonText(
|
||||
getString(R.string.audio_streams_dialog_turn_off_talkback_button))
|
||||
.setRightButtonOnClickListener(
|
||||
dialog -> {
|
||||
var unused = ThreadUtils.postOnBackgroundThread(() -> {
|
||||
var enabledScreenReader = getEnabledScreenReaderServices(mContext);
|
||||
if (!enabledScreenReader.isEmpty()) {
|
||||
setAccessibilityServiceOff(mContext, enabledScreenReader);
|
||||
}
|
||||
});
|
||||
dismiss();
|
||||
if (mActivity != null) {
|
||||
mActivity.finish();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private Dialog getNoLeDeviceDialog() {
|
||||
return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
|
||||
.setTitle(getString(R.string.audio_streams_dialog_no_le_device_title))
|
||||
@@ -234,6 +269,9 @@ public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
||||
if (!hasConnectedDevice) {
|
||||
return SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE;
|
||||
}
|
||||
if (!getEnabledScreenReaderServices(mContext).isEmpty()) {
|
||||
return SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_TURN_OFF_TALKBACK;
|
||||
}
|
||||
return hasMetadata
|
||||
? SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN
|
||||
: SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_DATA_ERROR;
|
||||
|
||||
@@ -68,7 +68,10 @@ public class AudioStreamsDialogFragment extends InstrumentedDialogFragment {
|
||||
* @param dialogBuilder The builder for constructing the dialog.
|
||||
* @param dialogId The dialog settings enum for logging
|
||||
*/
|
||||
public static void show(Fragment host, DialogBuilder dialogBuilder, int dialogId) {
|
||||
public static void show(@Nullable Fragment host, DialogBuilder dialogBuilder, int dialogId) {
|
||||
if (host == null) {
|
||||
return;
|
||||
}
|
||||
if (!host.isAdded()) {
|
||||
Log.w(TAG, "The host fragment is not added to the activity!");
|
||||
return;
|
||||
@@ -77,7 +80,10 @@ public class AudioStreamsDialogFragment extends InstrumentedDialogFragment {
|
||||
(new AudioStreamsDialogFragment(dialogBuilder, dialogId)).show(manager, TAG);
|
||||
}
|
||||
|
||||
static void dismissAll(Fragment host) {
|
||||
static void dismissAll(@Nullable Fragment host) {
|
||||
if (host == null) {
|
||||
return;
|
||||
}
|
||||
if (!host.isAdded()) {
|
||||
Log.w(TAG, "The host fragment is not added to the activity!");
|
||||
return;
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_ID;
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_TITLE;
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
|
||||
import static com.android.settingslib.accessibility.AccessibilityUtils.setAccessibilityServiceState;
|
||||
import static com.android.settingslib.bluetooth.BluetoothUtils.isAudioSharingHysteresisModeFixAvailable;
|
||||
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState;
|
||||
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED;
|
||||
@@ -30,15 +31,18 @@ import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothLeAudioContentMetadata;
|
||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
@@ -56,9 +60,12 @@ import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -399,4 +406,55 @@ public class AudioStreamsHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a set of enabled screen reader services that are pre-installed.
|
||||
*
|
||||
* <p>This method checks the accessibility manager for enabled accessibility services
|
||||
* and filters them based on a list of pre-installed screen reader service component names
|
||||
* defined in the {@code config_preinstalled_screen_reader_services} resource array.</p>
|
||||
*
|
||||
* @param context The context.
|
||||
* @return A set of {@link ComponentName} objects representing the enabled pre-installed
|
||||
* screen reader services, or an empty set if no services are found, or if an error occurs.
|
||||
*/
|
||||
public static Set<ComponentName> getEnabledScreenReaderServices(Context context) {
|
||||
AccessibilityManager manager = context.getSystemService(AccessibilityManager.class);
|
||||
if (manager == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<String> screenReaderServices = new HashSet<>();
|
||||
Collections.addAll(screenReaderServices, context.getResources()
|
||||
.getStringArray(R.array.config_preinstalled_screen_reader_services));
|
||||
if (screenReaderServices.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<ComponentName> enabledScreenReaderServices = new HashSet<>();
|
||||
List<AccessibilityServiceInfo> enabledServices = manager.getEnabledAccessibilityServiceList(
|
||||
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
|
||||
for (AccessibilityServiceInfo service : enabledServices) {
|
||||
ComponentName componentName = service.getComponentName();
|
||||
if (screenReaderServices.contains(componentName.flattenToString())) {
|
||||
enabledScreenReaderServices.add(componentName);
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "getEnabledScreenReaderServices(): " + enabledScreenReaderServices);
|
||||
return enabledScreenReaderServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off the specified accessibility services.
|
||||
*
|
||||
* This method iterates through a set of ComponentName objects, each representing an
|
||||
* accessibility service, and disables them.
|
||||
*
|
||||
* @param context The application context.
|
||||
* @param services A set of ComponentName objects representing the services to disable.
|
||||
*/
|
||||
public static void setAccessibilityServiceOff(Context context, Set<ComponentName> services) {
|
||||
for (ComponentName service : services) {
|
||||
Log.d(TAG, "setScreenReaderOff(): " + service);
|
||||
setAccessibilityServiceState(context, service, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssista
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||
import android.bluetooth.BluetoothStatusCodes;
|
||||
|
||||
public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastAssistantCallback {
|
||||
private final AudioStreamsProgressCategoryController mCategoryController;
|
||||
@@ -44,6 +45,9 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA
|
||||
|
||||
@Override
|
||||
public void onSearchStartFailed(int reason) {
|
||||
if (reason == BluetoothStatusCodes.ERROR_ALREADY_IN_TARGET_STATE) {
|
||||
return;
|
||||
}
|
||||
super.onSearchStartFailed(reason);
|
||||
mCategoryController.showToast("Failed to start scanning. Try again.");
|
||||
mCategoryController.setScanning(false);
|
||||
@@ -57,6 +61,9 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA
|
||||
|
||||
@Override
|
||||
public void onSearchStopFailed(int reason) {
|
||||
if (reason == BluetoothStatusCodes.ERROR_ALREADY_IN_TARGET_STATE) {
|
||||
return;
|
||||
}
|
||||
super.onSearchStopFailed(reason);
|
||||
mCategoryController.showToast("Failed to stop scanning. Try again.");
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsHelper.getEnabledScreenReaderServices;
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsHelper.setAccessibilityServiceOff;
|
||||
import static com.android.settingslib.bluetooth.BluetoothUtils.isAudioSharingHysteresisModeFixAvailable;
|
||||
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED;
|
||||
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED;
|
||||
@@ -32,8 +34,10 @@ import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -59,6 +63,7 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -103,6 +108,9 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
}
|
||||
};
|
||||
|
||||
private final AccessibilityManager.AccessibilityServicesStateChangeListener
|
||||
mAccessibilityListener = manager -> init();
|
||||
|
||||
private final Comparator<AudioStreamPreference> mComparator =
|
||||
Comparator.<AudioStreamPreference, Boolean>comparing(
|
||||
p ->
|
||||
@@ -148,6 +156,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
private SourceOriginForLogging mSourceFromQrCodeOriginForLogging;
|
||||
@Nullable private AudioStreamsProgressCategoryPreference mCategoryPreference;
|
||||
@Nullable private Fragment mFragment;
|
||||
@Nullable AccessibilityManager mAccessibilityManager;
|
||||
|
||||
public AudioStreamsProgressCategoryController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
@@ -159,6 +168,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
mBroadcastAssistantCallback = new AudioStreamsProgressCategoryCallback(this);
|
||||
mHysteresisModeFixAvailable = BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(
|
||||
mContext);
|
||||
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,6 +187,10 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
if (mBluetoothManager != null) {
|
||||
mBluetoothManager.getEventManager().registerCallback(mBluetoothCallback);
|
||||
}
|
||||
if (mAccessibilityManager != null) {
|
||||
mAccessibilityManager.addAccessibilityServicesStateChangeListener(
|
||||
mExecutor, mAccessibilityListener);
|
||||
}
|
||||
mExecutor.execute(this::init);
|
||||
}
|
||||
|
||||
@@ -185,6 +199,10 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
if (mBluetoothManager != null) {
|
||||
mBluetoothManager.getEventManager().unregisterCallback(mBluetoothCallback);
|
||||
}
|
||||
if (mAccessibilityManager != null) {
|
||||
mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
|
||||
mAccessibilityListener);
|
||||
}
|
||||
mExecutor.execute(this::stopScanning);
|
||||
}
|
||||
|
||||
@@ -548,6 +566,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
boolean hasConnected =
|
||||
AudioStreamsHelper.getCachedBluetoothDeviceInSharingOrLeConnected(mBluetoothManager)
|
||||
.isPresent();
|
||||
Set<ComponentName> screenReaderServices = getEnabledScreenReaderServices(mContext);
|
||||
AudioSharingUtils.postOnMainThread(
|
||||
mContext,
|
||||
() -> {
|
||||
@@ -556,27 +575,27 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
mCategoryPreference.setVisible(hasConnected);
|
||||
}
|
||||
});
|
||||
if (hasConnected) {
|
||||
if (hasConnected && screenReaderServices.isEmpty()) {
|
||||
startScanning();
|
||||
AudioSharingUtils.postOnMainThread(
|
||||
mContext,
|
||||
() -> {
|
||||
if (mFragment != null) {
|
||||
AudioStreamsDialogFragment.dismissAll(mFragment);
|
||||
}
|
||||
});
|
||||
AudioSharingUtils.postOnMainThread(mContext,
|
||||
() -> AudioStreamsDialogFragment.dismissAll(mFragment));
|
||||
} else {
|
||||
stopScanning();
|
||||
AudioSharingUtils.postOnMainThread(
|
||||
mContext,
|
||||
() -> {
|
||||
if (mFragment != null) {
|
||||
AudioStreamsDialogFragment.show(
|
||||
mFragment,
|
||||
getNoLeDeviceDialog(),
|
||||
SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_NO_LE_DEVICE);
|
||||
}
|
||||
});
|
||||
if (!hasConnected) {
|
||||
AudioSharingUtils.postOnMainThread(
|
||||
mContext, () -> AudioStreamsDialogFragment.show(
|
||||
mFragment,
|
||||
getNoLeDeviceDialog(),
|
||||
SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_NO_LE_DEVICE)
|
||||
);
|
||||
} else if (!screenReaderServices.isEmpty()) {
|
||||
AudioSharingUtils.postOnMainThread(
|
||||
mContext, () -> AudioStreamsDialogFragment.show(
|
||||
mFragment,
|
||||
getTurnOffTalkbackDialog(screenReaderServices),
|
||||
SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_TURN_OFF_TALKBACK)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,6 +626,9 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
stateList.forEach(
|
||||
state -> handleSourcePaused(device, state)));
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "startScanning()");
|
||||
}
|
||||
mLeBroadcastAssistant.startSearchingForSources(emptyList());
|
||||
mMediaControlHelper.start();
|
||||
});
|
||||
@@ -741,4 +763,31 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
private AudioStreamsDialogFragment.DialogBuilder getTurnOffTalkbackDialog(
|
||||
Set<ComponentName> enabledScreenReader) {
|
||||
return new AudioStreamsDialogFragment.DialogBuilder(mContext)
|
||||
.setTitle(mContext.getString(R.string.audio_streams_dialog_turn_off_talkback_title))
|
||||
.setSubTitle2(mContext.getString(
|
||||
R.string.audio_streams_dialog_turn_off_talkback_subtitle))
|
||||
.setLeftButtonText(mContext.getString(R.string.cancel))
|
||||
.setLeftButtonOnClickListener(dialog -> {
|
||||
dialog.dismiss();
|
||||
if (mFragment != null && mFragment.getActivity() != null) {
|
||||
// Navigate back
|
||||
mFragment.getActivity().finish();
|
||||
}
|
||||
})
|
||||
.setRightButtonText(
|
||||
mContext.getString(R.string.audio_streams_dialog_turn_off_talkback_button))
|
||||
.setRightButtonOnClickListener(
|
||||
dialog -> {
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
if (!enabledScreenReader.isEmpty()) {
|
||||
setAccessibilityServiceOff(mContext, enabledScreenReader);
|
||||
}
|
||||
});
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user