Merge "Check isSimHardwareVisible earlier" into main
diff --git a/res/layout/privatespace_advancing_screen.xml b/res/layout/privatespace_advancing_screen.xml
index cebb6fa..5b69593 100644
--- a/res/layout/privatespace_advancing_screen.xml
+++ b/res/layout/privatespace_advancing_screen.xml
@@ -20,7 +20,6 @@
android:id="@+id/privatesapce_autoadvance_screen"
android:layout_width="match_parent"
android:layout_height="match_parent"
- app:sucHeaderText="@string/privatespace_lock_protected_title"
android:icon="@drawable/ic_privatespace_icon">
<LinearLayout style="@style/SudContentFrame"
android:layout_width="match_parent"
@@ -34,26 +33,14 @@
android:contentDescription="@null"
android:src="@drawable/privatespace_setup_flow_placeholder"/>
- <LinearLayout
- android:id="@+id/setup_progress"
+ <TextView
+ android:id="@+id/createMessage"
+ style="@style/PrivateSpaceSetupTextFontStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="bottom|right"
- android:layout_margin="16dp"
- android:orientation="horizontal">
- <ProgressBar
- android:id="@+id/progressBar_cyclic"
- style="?android:attr/progressBarStyleSmall"
- android:layout_width="20dp"
- android:layout_height="20dp"
- android:layout_gravity="center"/>
- <TextView
- android:id="@+id/createMessage"
- style="@style/PrivateSpaceSetupTextFontStyle"
- android:textSize="14sp"
- android:text="@string/privatespace_setting_up_text"
- android:layout_margin="8dp"/>
- </LinearLayout>
+ android:textSize="14sp"
+ android:text="@string/privatespace_setting_up_text"
+ android:layout_marginBottom="24dp"/>
</LinearLayout>
</com.google.android.setupdesign.GlifLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e4e5750..1bd943c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1278,12 +1278,12 @@
<string name="privatespace_apps_permission_text">Private Space apps won\u2019t appear in permission manager, privacy dashboard, and other settings when Private Space is locked</string>
<!-- Text shown at the bottom in Private Space auto advancing screens. [CHAR LIMIT=60] -->
<string name="privatespace_setting_up_text">Setting up Private Space\u2026</string>
- <!-- Title for Private Space setup in auto advancing screen informing private space is protected by a lock. [CHAR LIMIT=60] -->
- <string name="privatespace_lock_protected_title">Private Space is protected by a lock</string>
<!-- Title for Private Space setup in auto advancing screen informing private space is hidden when locked. [CHAR LIMIT=NONE] -->
<string name="privatespace_apps_hidden_title">Usage info for Private Space apps is hidden when it\u2019s locked</string>
<!-- Title for Private Space setup in auto advancing screen informing private space can be accessed from apps list. [CHAR LIMIT=60] -->
<string name="privatespace_access_from_apps_title">Access Private Space from your apps list</string>
+ <!-- Title for Private Space setup in auto advancing screen informing some system apps are already installed in Private Space. [CHAR LIMIT=NONE] -->
+ <string name="privatespace_system_apps_installed_title">Some system apps are already installed in Private Space</string>
<!-- Title for Private Space creation error screen. [CHAR LIMIT=60] -->
<string name="privatespace_error_screen_title">Couldn\u2019t set up Private Space</string>
<!-- Summary for the Private Space creation error screen. [CHAR LIMIT=60] -->
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java
index a68117a..5998e30 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java
@@ -22,10 +22,12 @@
public final class AudioSharingDeviceItem implements Parcelable {
private final String mName;
private final int mGroupId;
+ private final boolean mIsActive;
- public AudioSharingDeviceItem(String name, int groupId) {
+ public AudioSharingDeviceItem(String name, int groupId, boolean isActive) {
mName = name;
mGroupId = groupId;
+ mIsActive = isActive;
}
public String getName() {
@@ -36,15 +38,21 @@
return mGroupId;
}
+ public boolean isActive() {
+ return mIsActive;
+ }
+
public AudioSharingDeviceItem(Parcel in) {
mName = in.readString();
mGroupId = in.readInt();
+ mIsActive = in.readBoolean();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
dest.writeInt(mGroupId);
+ dest.writeBoolean(mIsActive);
}
@Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
index 4ece70e..a0d44ff 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
@@ -120,6 +120,9 @@
/** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
public static AudioSharingDeviceItem buildAudioSharingDeviceItem(
CachedBluetoothDevice cachedDevice) {
- return new AudioSharingDeviceItem(cachedDevice.getName(), cachedDevice.getGroupId());
+ return new AudioSharingDeviceItem(
+ cachedDevice.getName(),
+ cachedDevice.getGroupId(),
+ BluetoothUtils.isActiveLeAudioDevice(cachedDevice));
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java
index 0577f70..47f70c7 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java
@@ -28,9 +28,25 @@
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.flags.Flags;
+import java.util.ArrayList;
+
/** Provides a dialog to choose the active device for calls and alarms. */
public class CallsAndAlarmsDialogFragment extends InstrumentedDialogFragment {
private static final String TAG = "CallsAndAlarmsDialog";
+ private static final String BUNDLE_KEY_DEVICE_ITEMS = "bundle_key_device_items";
+
+ // The host creates an instance of this dialog fragment must implement this interface to receive
+ // event callbacks.
+ public interface DialogEventListener {
+ /**
+ * Called when users click the device item to set active for calls and alarms in the dialog.
+ *
+ * @param item The device item clicked.
+ */
+ void onItemClick(AudioSharingDeviceItem item);
+ }
+
+ private static DialogEventListener sListener;
@Override
public int getMetricsCategory() {
@@ -41,28 +57,43 @@
* Display the {@link CallsAndAlarmsDialogFragment} dialog.
*
* @param host The Fragment this dialog will be hosted.
+ * @param deviceItems The connected device items in audio sharing session.
+ * @param listener The callback to handle the user action on this dialog.
*/
- public static void show(Fragment host) {
+ public static void show(
+ Fragment host,
+ ArrayList<AudioSharingDeviceItem> deviceItems,
+ DialogEventListener listener) {
if (!Flags.enableLeAudioSharing()) return;
final FragmentManager manager = host.getChildFragmentManager();
+ sListener = listener;
if (manager.findFragmentByTag(TAG) == null) {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
final CallsAndAlarmsDialogFragment dialog = new CallsAndAlarmsDialogFragment();
+ dialog.setArguments(bundle);
dialog.show(manager, TAG);
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- // TODO: use real device names
- String[] choices = {"Buds 1", "Buds 2"};
+ Bundle arguments = requireArguments();
+ ArrayList<AudioSharingDeviceItem> deviceItems =
+ arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS);
+ int checkedItem = -1;
+ // deviceItems is ordered. The active device is put in the first place if it does exist
+ if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) checkedItem = 0;
+ String[] choices =
+ deviceItems.stream().map(AudioSharingDeviceItem::getName).toArray(String[]::new);
AlertDialog.Builder builder =
new AlertDialog.Builder(getActivity())
.setTitle(R.string.calls_and_alarms_device_title)
.setSingleChoiceItems(
choices,
- 0, // TODO: set to current active device.
+ checkedItem,
(dialog, which) -> {
- // TODO: set device to active device for calls and alarms.
+ sListener.onItemClick(deviceItems.get(which));
});
return builder.create();
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java
index 44e75ec..a7d18e7 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java
@@ -16,23 +16,42 @@
package com.android.settings.connecteddevice.audiosharing;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
+import com.android.settings.bluetooth.Utils;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/** PreferenceController to control the dialog to choose the active device for calls and alarms */
-public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferenceController {
+public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferenceController
+ implements BluetoothCallback, DefaultLifecycleObserver {
private static final String TAG = "CallsAndAlarmsPreferenceController";
-
private static final String PREF_KEY = "calls_and_alarms";
+
+ private final LocalBluetoothManager mLocalBtManager;
private DashboardFragment mFragment;
+ Map<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
+ private ArrayList<AudioSharingDeviceItem> mDeviceItemsInSharingSession = new ArrayList<>();
public CallsAndAlarmsPreferenceController(Context context) {
super(context, PREF_KEY);
+ mLocalBtManager = Utils.getLocalBtManager(mContext);
}
@Override
@@ -43,17 +62,60 @@
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
+ updateDeviceItemsInSharingSession();
+ // mDeviceItemsInSharingSession is ordered. The active device is the first place if exits.
+ if (!mDeviceItemsInSharingSession.isEmpty()
+ && mDeviceItemsInSharingSession.get(0).isActive()) {
+ mPreference.setSummary(mDeviceItemsInSharingSession.get(0).getName());
+ } else {
+ mPreference.setSummary("");
+ }
mPreference.setOnPreferenceClickListener(
preference -> {
- if (mFragment != null) {
- CallsAndAlarmsDialogFragment.show(mFragment);
- } else {
+ if (mFragment == null) {
Log.w(TAG, "Dialog fail to show due to null host.");
+ return true;
+ }
+ updateDeviceItemsInSharingSession();
+ if (mDeviceItemsInSharingSession.size() >= 2) {
+ CallsAndAlarmsDialogFragment.show(
+ mFragment,
+ mDeviceItemsInSharingSession,
+ (AudioSharingDeviceItem item) -> {
+ for (CachedBluetoothDevice device :
+ mGroupedConnectedDevices.get(item.getGroupId())) {
+ device.setActive();
+ }
+ });
}
return true;
});
}
+ @Override
+ public void onStart(@NonNull LifecycleOwner owner) {
+ if (mLocalBtManager != null) {
+ mLocalBtManager.getEventManager().registerCallback(this);
+ }
+ }
+
+ @Override
+ public void onStop(@NonNull LifecycleOwner owner) {
+ if (mLocalBtManager != null) {
+ mLocalBtManager.getEventManager().unregisterCallback(this);
+ }
+ }
+
+ @Override
+ public void onActiveDeviceChanged(
+ @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ if (bluetoothProfile != BluetoothProfile.LE_AUDIO) {
+ Log.d(TAG, "Ignore onActiveDeviceChanged, not LE_AUDIO profile");
+ return;
+ }
+ mPreference.setSummary(activeDevice == null ? "" : activeDevice.getName());
+ }
+
/**
* Initialize the controller.
*
@@ -62,4 +124,12 @@
public void init(DashboardFragment fragment) {
this.mFragment = fragment;
}
+
+ private void updateDeviceItemsInSharingSession() {
+ mGroupedConnectedDevices =
+ AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
+ mDeviceItemsInSharingSession =
+ AudioSharingUtils.buildOrderedDeviceItemsInSharingSession(
+ mGroupedConnectedDevices, mLocalBtManager);
+ }
}
diff --git a/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java b/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java
index 3b59166..1061498 100644
--- a/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java
+++ b/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java
@@ -19,6 +19,11 @@
import static com.android.settings.privatespace.PrivateSpaceSetupActivity.ACCOUNT_LOGIN_ACTION;
import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
@@ -51,16 +56,17 @@
private static final String TAG = "AutoAdvanceFragment";
private static final String TITLE_INDEX = "title_index";
private static final int DELAY_BETWEEN_SCREENS = 5000; // 5 seconds in millis
+ private static final int ANIMATION_DURATION_MILLIS = 500;
private GlifLayout mRootView;
private Handler mHandler;
private int mScreenTitleIndex;
private static final List<Pair<Integer, Integer>> HEADER_IMAGE_PAIRS =
ImmutableList.of(
- new Pair(R.string.privatespace_lock_protected_title,
- R.drawable.privatespace_setup_flow_placeholder),
new Pair(R.string.privatespace_apps_hidden_title,
R.drawable.privatespace_setup_flow_placeholder),
new Pair(R.string.privatespace_access_from_apps_title,
+ R.drawable.privatespace_setup_flow_placeholder),
+ new Pair(R.string.privatespace_system_apps_installed_title,
R.drawable.privatespace_setup_flow_placeholder));
private Runnable mUpdateScreenResources =
@@ -69,7 +75,7 @@
public void run() {
if (getActivity() != null) {
if (++mScreenTitleIndex < HEADER_IMAGE_PAIRS.size()) {
- updateHeaderAndImage();
+ startFadeOutAnimation();
mHandler.postDelayed(mUpdateScreenResources, DELAY_BETWEEN_SCREENS);
} else {
PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer
@@ -148,5 +154,32 @@
mRootView.setHeaderText(HEADER_IMAGE_PAIRS.get(mScreenTitleIndex).first);
((ImageView) mRootView.findViewById(R.id.placeholder_image))
.setImageResource(HEADER_IMAGE_PAIRS.get(mScreenTitleIndex).second);
+ startFadeInAnimation();
+ }
+
+ private void startFadeInAnimation() {
+ ValueAnimator textView = ObjectAnimator.ofFloat(
+ mRootView.getHeaderTextView(), View.ALPHA, 0f, 1f);
+ ValueAnimator imageView = ObjectAnimator.ofFloat(
+ mRootView.findViewById(R.id.placeholder_image), View.ALPHA, 0, 1f);
+ AnimatorSet fadeIn = new AnimatorSet();
+ fadeIn.playTogether(textView, imageView);
+ fadeIn.setDuration(ANIMATION_DURATION_MILLIS).start();
+ }
+
+ private void startFadeOutAnimation() {
+ AnimatorSet fadeOut = new AnimatorSet();
+ ValueAnimator textView = ObjectAnimator.ofFloat(
+ mRootView.getHeaderTextView(), View.ALPHA, 1f, 0f);
+ ValueAnimator imageView = ObjectAnimator.ofFloat(
+ mRootView.findViewById(R.id.placeholder_image), View.ALPHA, 1f, 0f);
+ fadeOut.playTogether(textView, imageView);
+ fadeOut.setDuration(ANIMATION_DURATION_MILLIS).start();
+ fadeOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ updateHeaderAndImage();
+ }
+ });
}
}