Loading src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceAdapter.java +19 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,9 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; public class AudioSharingDeviceAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { Loading @@ -35,7 +38,7 @@ public class AudioSharingDeviceAdapter extends RecyclerView.Adapter<RecyclerView private static final String TAG = "AudioSharingDeviceAdapter"; private final Context mContext; private final List<AudioSharingDeviceItem> mDevices; private List<AudioSharingDeviceItem> mDevices; private final OnClickListener mOnClickListener; private final ActionType mType; Loading Loading @@ -113,6 +116,21 @@ public class AudioSharingDeviceAdapter extends RecyclerView.Adapter<RecyclerView return mDevices.size(); } /** Updates the data set and notify the change. */ public void updateItems(@NonNull List<AudioSharingDeviceItem> items) { if (mDevices.size() != items.size()) { List<AudioSharingDeviceItem> oldItems = new ArrayList<>(mDevices); oldItems.removeAll(items); if (oldItems.isEmpty()) { Log.d(TAG, "Skip updateItems, no change"); return; } } mDevices = ImmutableList.copyOf(items); Log.d(TAG, "updateItems, items = " + mDevices); notifyDataSetChanged(); } public interface OnClickListener { /** Called when an item has been clicked. */ void onClick(AudioSharingDeviceItem item); Loading src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java +15 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.Objects; public final class AudioSharingDeviceItem implements Parcelable { private final String mName; Loading Loading @@ -80,4 +83,16 @@ public final class AudioSharingDeviceItem implements Parcelable { public String toString() { return "AudioSharingDeviceItem groupId = " + mGroupId + ", isActive = " + mIsActive; } @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof AudioSharingDeviceItem other)) return false; return mName.equals(other.getName()) && mGroupId == other.getGroupId() && mIsActive == other.isActive(); } @Override public int hashCode() { return Objects.hash(mName, mGroupId, mIsActive); } } src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java +49 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; import java.util.List; import javax.annotation.CheckReturnValue; public class AudioSharingDialogFactory { Loading @@ -50,6 +52,53 @@ public class AudioSharingDialogFactory { return new AudioSharingDialogFactory.DialogBuilder(context); } /** * Updates the title of the dialog custom title. * * @param dialog The dialog to update * @param titleText The text to be used for the title.. */ public static void updateTitle(@NonNull AlertDialog dialog, @NonNull CharSequence titleText) { TextView title = dialog.findViewById(R.id.title_text); if (title != null) { title.setText(titleText); title.setVisibility(View.VISIBLE); } } /** * Updates the custom message of the dialog custom body. * * @param dialog The dialog to update * @param message The text to be used for the custom message body. */ public static void updateCustomMessage(@NonNull AlertDialog dialog, @NonNull CharSequence message) { TextView subTitle = dialog.findViewById(R.id.description_text); if (subTitle != null) { subTitle.setText(message); subTitle.setVisibility(View.VISIBLE); } } /** * Updates the custom device actions of the dialog custom body. * * @param dialog The dialog to update * @param deviceItems device items to build dialog actions. */ public static void updateCustomDeviceActions( @NonNull AlertDialog dialog, @NonNull List<AudioSharingDeviceItem> deviceItems) { RecyclerView recyclerView = dialog.findViewById(R.id.device_btn_list); if (recyclerView != null && recyclerView.getVisibility() == View.VISIBLE) { RecyclerView.Adapter adapter = recyclerView.getAdapter(); if (adapter instanceof AudioSharingDeviceAdapter) { ((AudioSharingDeviceAdapter) adapter).updateItems(deviceItems); } } } /** Builder class with configurable options for the dialog to be shown for audio sharing. */ public static class DialogBuilder { private Context mContext; Loading src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java +47 −68 Original line number Diff line number Diff line Loading @@ -33,21 +33,19 @@ import androidx.lifecycle.Lifecycle; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.flags.Flags; import com.android.settingslib.utils.ThreadUtils; import com.google.common.collect.ImmutableList; import java.util.List; import java.util.Locale; public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFragment { private static final String TAG = "AudioSharingDisconnectDialog"; private static final String BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS = "bundle_key_device_to_disconnect_items"; private static final String BUNDLE_KEY_NEW_DEVICE_NAME = "bundle_key_new_device_name"; // The host creates an instance of this dialog fragment must implement this interface to receive // event callbacks. Loading Loading @@ -80,8 +78,10 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag * @param newDevice The latest connected device triggered this dialog. * @param listener The callback to handle the user action on this dialog. * @param eventData The eventData to log with for dialog onClick events. * * @return whether the dialog is shown */ public static void show( public static boolean show( @Nullable Fragment host, @NonNull List<AudioSharingDeviceItem> deviceItems, @NonNull CachedBluetoothDevice newDevice, Loading @@ -89,60 +89,46 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag @NonNull Pair<Integer, Object>[] eventData) { if (host == null) { Log.d(TAG, "Fail to show dialog, host is null"); return; return false; } if (!BluetoothUtils.isAudioSharingUIAvailable(host.getContext())) { Log.d(TAG, "Fail to show dialog, feature disabled"); return false; } if (BluetoothUtils.isAudioSharingUIAvailable(host.getContext())) { final FragmentManager manager; try { manager = host.getChildFragmentManager(); } catch (IllegalStateException e) { Log.d(TAG, "Fail to show dialog: " + e.getMessage()); return; return false; } Lifecycle.State currentState = host.getLifecycle().getCurrentState(); if (!currentState.isAtLeast(Lifecycle.State.STARTED)) { Log.d(TAG, "Fail to show dialog with state: " + currentState); return; return false; } AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); if (dialog != null) { int newGroupId = BluetoothUtils.getGroupId(newDevice); if (sNewDevice != null && newGroupId == BluetoothUtils.getGroupId(sNewDevice)) { Log.d( TAG, String.format( Locale.US, "Dialog is showing for the same device group %d, " + "update the content.", newGroupId)); sListener = listener; sNewDevice = newDevice; sEventData = eventData; return; AudioSharingUtils.postOnMainThread( host.getContext(), () -> { AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); if (dialog != null) { Log.d(TAG, "Dialog is showing, update the content."); updateDialog(ImmutableList.copyOf(deviceItems), dialog); } else { Log.d( TAG, String.format( Locale.US, "Dialog is showing for new device group %d, " + "dismiss current dialog.", newGroupId)); dialog.dismiss(); logDialogAutoDismiss(dialog); } } sListener = listener; sNewDevice = newDevice; sEventData = eventData; Log.d(TAG, "Show up the dialog."); final Bundle bundle = new Bundle(); bundle.putParcelableList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems); bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDevice.getName()); bundle.putParcelableList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems); AudioSharingDisconnectDialogFragment dialogFrag = new AudioSharingDisconnectDialogFragment(); dialogFrag.setArguments(bundle); dialogFrag.show(manager, TAG); } }); return true; } /** Return the tag of {@link AudioSharingDisconnectDialogFragment} dialog. */ Loading Loading @@ -226,16 +212,9 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag } } private static void logDialogAutoDismiss(AlertDialog dialog) { var unused = ThreadUtils.postOnBackgroundThread( () -> FeatureFactory.getFeatureFactory() .getMetricsFeatureProvider() .action( dialog.getContext(), SettingsEnums .ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS, SettingsEnums .DIALOG_AUDIO_SHARING_SWITCH_DEVICE)); private static void updateDialog( @NonNull List<AudioSharingDeviceItem> deviceItems, @NonNull AlertDialog dialog) { AudioSharingDialogFactory.updateCustomDeviceActions(dialog, deviceItems); } } src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java +42 −32 Original line number Diff line number Diff line Loading @@ -77,8 +77,10 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { * @param newDevice The latest connected device triggered this dialog. * @param listener The callback to handle the user action on this dialog. * @param eventData The eventData to log with for dialog onClick events. * * @return whether the dialog is shown */ public static void show( public static boolean show( @Nullable Fragment host, @NonNull List<AudioSharingDeviceItem> deviceItems, @NonNull CachedBluetoothDevice newDevice, Loading @@ -86,25 +88,32 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { @NonNull Pair<Integer, Object>[] eventData) { if (host == null) { Log.d(TAG, "Fail to show dialog, host is null"); return; return false; } if (!BluetoothUtils.isAudioSharingUIAvailable(host.getContext())) { Log.d(TAG, "Fail to show dialog, feature disabled"); return false; } if (BluetoothUtils.isAudioSharingUIAvailable(host.getContext())) { final FragmentManager manager; try { manager = host.getChildFragmentManager(); } catch (IllegalStateException e) { Log.d(TAG, "Fail to show dialog: " + e.getMessage()); return; return false; } Lifecycle.State currentState = host.getLifecycle().getCurrentState(); if (!currentState.isAtLeast(Lifecycle.State.STARTED)) { Log.d(TAG, "Fail to show dialog with state: " + currentState); return; return false; } sListener = listener; sNewDevice = newDevice; sEventData = eventData; AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); AudioSharingUtils.postOnMainThread( host.getContext(), () -> { AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); if (dialog != null) { Log.d(TAG, "Dialog is showing, update the content."); updateDialog(deviceItems, newDevice.getName(), dialog); Loading @@ -118,7 +127,8 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { dialogFrag.setArguments(bundle); dialogFrag.show(manager, TAG); } } }); return true; } /** Return the tag of {@link AudioSharingJoinDialogFragment} dialog. */ Loading Loading
src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceAdapter.java +19 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,9 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; public class AudioSharingDeviceAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { Loading @@ -35,7 +38,7 @@ public class AudioSharingDeviceAdapter extends RecyclerView.Adapter<RecyclerView private static final String TAG = "AudioSharingDeviceAdapter"; private final Context mContext; private final List<AudioSharingDeviceItem> mDevices; private List<AudioSharingDeviceItem> mDevices; private final OnClickListener mOnClickListener; private final ActionType mType; Loading Loading @@ -113,6 +116,21 @@ public class AudioSharingDeviceAdapter extends RecyclerView.Adapter<RecyclerView return mDevices.size(); } /** Updates the data set and notify the change. */ public void updateItems(@NonNull List<AudioSharingDeviceItem> items) { if (mDevices.size() != items.size()) { List<AudioSharingDeviceItem> oldItems = new ArrayList<>(mDevices); oldItems.removeAll(items); if (oldItems.isEmpty()) { Log.d(TAG, "Skip updateItems, no change"); return; } } mDevices = ImmutableList.copyOf(items); Log.d(TAG, "updateItems, items = " + mDevices); notifyDataSetChanged(); } public interface OnClickListener { /** Called when an item has been clicked. */ void onClick(AudioSharingDeviceItem item); Loading
src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java +15 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.Objects; public final class AudioSharingDeviceItem implements Parcelable { private final String mName; Loading Loading @@ -80,4 +83,16 @@ public final class AudioSharingDeviceItem implements Parcelable { public String toString() { return "AudioSharingDeviceItem groupId = " + mGroupId + ", isActive = " + mIsActive; } @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof AudioSharingDeviceItem other)) return false; return mName.equals(other.getName()) && mGroupId == other.getGroupId() && mIsActive == other.isActive(); } @Override public int hashCode() { return Objects.hash(mName, mGroupId, mIsActive); } }
src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java +49 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; import java.util.List; import javax.annotation.CheckReturnValue; public class AudioSharingDialogFactory { Loading @@ -50,6 +52,53 @@ public class AudioSharingDialogFactory { return new AudioSharingDialogFactory.DialogBuilder(context); } /** * Updates the title of the dialog custom title. * * @param dialog The dialog to update * @param titleText The text to be used for the title.. */ public static void updateTitle(@NonNull AlertDialog dialog, @NonNull CharSequence titleText) { TextView title = dialog.findViewById(R.id.title_text); if (title != null) { title.setText(titleText); title.setVisibility(View.VISIBLE); } } /** * Updates the custom message of the dialog custom body. * * @param dialog The dialog to update * @param message The text to be used for the custom message body. */ public static void updateCustomMessage(@NonNull AlertDialog dialog, @NonNull CharSequence message) { TextView subTitle = dialog.findViewById(R.id.description_text); if (subTitle != null) { subTitle.setText(message); subTitle.setVisibility(View.VISIBLE); } } /** * Updates the custom device actions of the dialog custom body. * * @param dialog The dialog to update * @param deviceItems device items to build dialog actions. */ public static void updateCustomDeviceActions( @NonNull AlertDialog dialog, @NonNull List<AudioSharingDeviceItem> deviceItems) { RecyclerView recyclerView = dialog.findViewById(R.id.device_btn_list); if (recyclerView != null && recyclerView.getVisibility() == View.VISIBLE) { RecyclerView.Adapter adapter = recyclerView.getAdapter(); if (adapter instanceof AudioSharingDeviceAdapter) { ((AudioSharingDeviceAdapter) adapter).updateItems(deviceItems); } } } /** Builder class with configurable options for the dialog to be shown for audio sharing. */ public static class DialogBuilder { private Context mContext; Loading
src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java +47 −68 Original line number Diff line number Diff line Loading @@ -33,21 +33,19 @@ import androidx.lifecycle.Lifecycle; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.flags.Flags; import com.android.settingslib.utils.ThreadUtils; import com.google.common.collect.ImmutableList; import java.util.List; import java.util.Locale; public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFragment { private static final String TAG = "AudioSharingDisconnectDialog"; private static final String BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS = "bundle_key_device_to_disconnect_items"; private static final String BUNDLE_KEY_NEW_DEVICE_NAME = "bundle_key_new_device_name"; // The host creates an instance of this dialog fragment must implement this interface to receive // event callbacks. Loading Loading @@ -80,8 +78,10 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag * @param newDevice The latest connected device triggered this dialog. * @param listener The callback to handle the user action on this dialog. * @param eventData The eventData to log with for dialog onClick events. * * @return whether the dialog is shown */ public static void show( public static boolean show( @Nullable Fragment host, @NonNull List<AudioSharingDeviceItem> deviceItems, @NonNull CachedBluetoothDevice newDevice, Loading @@ -89,60 +89,46 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag @NonNull Pair<Integer, Object>[] eventData) { if (host == null) { Log.d(TAG, "Fail to show dialog, host is null"); return; return false; } if (!BluetoothUtils.isAudioSharingUIAvailable(host.getContext())) { Log.d(TAG, "Fail to show dialog, feature disabled"); return false; } if (BluetoothUtils.isAudioSharingUIAvailable(host.getContext())) { final FragmentManager manager; try { manager = host.getChildFragmentManager(); } catch (IllegalStateException e) { Log.d(TAG, "Fail to show dialog: " + e.getMessage()); return; return false; } Lifecycle.State currentState = host.getLifecycle().getCurrentState(); if (!currentState.isAtLeast(Lifecycle.State.STARTED)) { Log.d(TAG, "Fail to show dialog with state: " + currentState); return; return false; } AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); if (dialog != null) { int newGroupId = BluetoothUtils.getGroupId(newDevice); if (sNewDevice != null && newGroupId == BluetoothUtils.getGroupId(sNewDevice)) { Log.d( TAG, String.format( Locale.US, "Dialog is showing for the same device group %d, " + "update the content.", newGroupId)); sListener = listener; sNewDevice = newDevice; sEventData = eventData; return; AudioSharingUtils.postOnMainThread( host.getContext(), () -> { AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); if (dialog != null) { Log.d(TAG, "Dialog is showing, update the content."); updateDialog(ImmutableList.copyOf(deviceItems), dialog); } else { Log.d( TAG, String.format( Locale.US, "Dialog is showing for new device group %d, " + "dismiss current dialog.", newGroupId)); dialog.dismiss(); logDialogAutoDismiss(dialog); } } sListener = listener; sNewDevice = newDevice; sEventData = eventData; Log.d(TAG, "Show up the dialog."); final Bundle bundle = new Bundle(); bundle.putParcelableList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems); bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDevice.getName()); bundle.putParcelableList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems); AudioSharingDisconnectDialogFragment dialogFrag = new AudioSharingDisconnectDialogFragment(); dialogFrag.setArguments(bundle); dialogFrag.show(manager, TAG); } }); return true; } /** Return the tag of {@link AudioSharingDisconnectDialogFragment} dialog. */ Loading Loading @@ -226,16 +212,9 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag } } private static void logDialogAutoDismiss(AlertDialog dialog) { var unused = ThreadUtils.postOnBackgroundThread( () -> FeatureFactory.getFeatureFactory() .getMetricsFeatureProvider() .action( dialog.getContext(), SettingsEnums .ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS, SettingsEnums .DIALOG_AUDIO_SHARING_SWITCH_DEVICE)); private static void updateDialog( @NonNull List<AudioSharingDeviceItem> deviceItems, @NonNull AlertDialog dialog) { AudioSharingDialogFactory.updateCustomDeviceActions(dialog, deviceItems); } }
src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java +42 −32 Original line number Diff line number Diff line Loading @@ -77,8 +77,10 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { * @param newDevice The latest connected device triggered this dialog. * @param listener The callback to handle the user action on this dialog. * @param eventData The eventData to log with for dialog onClick events. * * @return whether the dialog is shown */ public static void show( public static boolean show( @Nullable Fragment host, @NonNull List<AudioSharingDeviceItem> deviceItems, @NonNull CachedBluetoothDevice newDevice, Loading @@ -86,25 +88,32 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { @NonNull Pair<Integer, Object>[] eventData) { if (host == null) { Log.d(TAG, "Fail to show dialog, host is null"); return; return false; } if (!BluetoothUtils.isAudioSharingUIAvailable(host.getContext())) { Log.d(TAG, "Fail to show dialog, feature disabled"); return false; } if (BluetoothUtils.isAudioSharingUIAvailable(host.getContext())) { final FragmentManager manager; try { manager = host.getChildFragmentManager(); } catch (IllegalStateException e) { Log.d(TAG, "Fail to show dialog: " + e.getMessage()); return; return false; } Lifecycle.State currentState = host.getLifecycle().getCurrentState(); if (!currentState.isAtLeast(Lifecycle.State.STARTED)) { Log.d(TAG, "Fail to show dialog with state: " + currentState); return; return false; } sListener = listener; sNewDevice = newDevice; sEventData = eventData; AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); AudioSharingUtils.postOnMainThread( host.getContext(), () -> { AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); if (dialog != null) { Log.d(TAG, "Dialog is showing, update the content."); updateDialog(deviceItems, newDevice.getName(), dialog); Loading @@ -118,7 +127,8 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { dialogFrag.setArguments(bundle); dialogFrag.show(manager, TAG); } } }); return true; } /** Return the tag of {@link AudioSharingJoinDialogFragment} dialog. */ Loading