Merge "Remove unnecessary custom shadow of android.system.Os" into main
diff --git a/res/layout/user_dictionary_add_word_fullscreen.xml b/res/layout/user_dictionary_add_word_fullscreen.xml
index 42bd197..cf73d411 100644
--- a/res/layout/user_dictionary_add_word_fullscreen.xml
+++ b/res/layout/user_dictionary_add_word_fullscreen.xml
@@ -17,31 +17,49 @@
     android:id="@+id/user_dict_settings_add_dialog_top"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical" >
-
-    <EditText
-        android:id="@+id/user_dictionary_add_word_text"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="fill_horizontal|center_vertical"
-        android:layout_marginBottom="8dip"
-        android:layout_marginStart="8dip"
-        android:layout_marginTop="8dip"
-        android:hint="@string/user_dict_settings_add_word_hint"
-        android:imeOptions="flagNoFullscreen"
-        android:inputType="textNoSuggestions"
-        android:maxLength="@integer/maximum_user_dictionary_word_length" >
-
-        <requestFocus />
-    </EditText>
+    android:paddingStart="@dimen/settingslib_listPreferredItemPaddingStart"
+    android:paddingLeft="@dimen/settingslib_listPreferredItemPaddingStart"
+    android:paddingEnd="@dimen/settingslib_listPreferredItemPaddingStart"
+    android:paddingRight="@dimen/settingslib_listPreferredItemPaddingStart"
+    android:orientation="vertical">
 
     <GridLayout
         android:id="@+id/user_dictionary_add_word_grid"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="8dip"
-        android:layout_marginStart="8dip"
-        android:columnCount="2" >
+        android:columnCount="2">
+
+        <TextView
+            android:id="@+id/user_dictionary_add_word_label"
+            style="?android:attr/textAppearanceSmall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start|center_vertical"
+            android:text="@string/user_dict_settings_add_word_option_name"
+            android:textColor="?android:attr/textColorPrimary" />
+
+        <EditText
+            android:id="@+id/user_dictionary_add_word_text"
+            android:layout_height="wrap_content"
+            android:layout_gravity="fill_horizontal|center_vertical"
+            android:layout_marginBottom="8dip"
+            android:layout_marginStart="8dip"
+            android:layout_marginTop="8dip"
+            android:minHeight="@dimen/min_tap_target_size"
+            android:hint="@string/user_dict_settings_add_word_hint"
+            android:imeOptions="flagNoFullscreen"
+            android:inputType="textNoSuggestions"
+            android:maxLength="@integer/maximum_user_dictionary_word_length">
+
+            <requestFocus />
+        </EditText>
+    </GridLayout>
+
+    <GridLayout
+        android:id="@+id/user_dictionary_add_shortcut_grid"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:columnCount="2">
 
         <TextView
             android:id="@+id/user_dictionary_add_shortcut_label"
@@ -49,7 +67,8 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="start|center_vertical"
-            android:text="@string/user_dict_settings_add_shortcut_option_name" />
+            android:text="@string/user_dict_settings_add_shortcut_option_name"
+            android:textColor="?android:attr/textColorPrimary" />
 
         <EditText
             android:id="@+id/user_dictionary_add_shortcut"
@@ -58,6 +77,7 @@
             android:layout_marginBottom="8dip"
             android:layout_marginStart="8dip"
             android:layout_marginTop="8dip"
+            android:minHeight="@dimen/min_tap_target_size"
             android:hint="@string/user_dict_settings_add_shortcut_hint"
             android:imeOptions="flagNoFullscreen"
             android:inputType="textNoSuggestions"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bd9ae3b..491cd0a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -41,9 +41,13 @@
     <!-- [CHAR LIMIT=NONE] Device Info screen. Okay we get it, stop pressing, you already have it on -->
     <string name="show_dev_already">No need, you are already a developer.</string>
 
-    <!-- [CHAR LIMIT=NONE] Toast message when user attemps to launch developer otions before enabling it. -->
+    <!-- [CHAR LIMIT=NONE] Toast message when user attempts to launch developer options before enabling it. -->
     <string name="dev_settings_disabled_warning">Please enable developer options first.</string>
 
+    <!-- [CHAR LIMIT=NONE] Toast message when non-admin user attempts to launch developer options. -->
+    <string name="dev_settings_available_to_admin_only_warning">Only the admin users can access developer settings.</string>
+
+
     <!-- Category headings in left-pane header menu --> <skip />
     <!-- Settings main menu category heading. System (Updates, data, accessibility, about phone). [CHAR LIMIT=40] -->
     <string name="header_category_system">System</string>
@@ -4057,8 +4061,6 @@
     <string name="archiving_succeeded">Archived <xliff:g id="package_label" example="Translate">%1$s</xliff:g></string>
     <!-- Toast message when restoring an app failed. -->
     <string name="restoring_failed">Restoring failed</string>
-    <!-- Toast message when restoring an app succeeded. -->
-    <string name="restoring_succeeded">Restored <xliff:g id="package_label" example="Translate">%1$s</xliff:g></string>
     <!-- Toast message when restoring an app has started. -->
     <string name="restoring_in_progress">Restoring <xliff:g id="package_label" example="Translate">%1$s</xliff:g></string>
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java
index 2f56f77..3396b8b 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java
@@ -37,22 +37,20 @@
 import com.android.settings.bluetooth.Utils;
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.VolumeControlProfile;
-import com.android.settingslib.utils.ThreadUtils;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
 public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePreferenceController
         implements DevicePreferenceCallback {
-    private static final boolean DEBUG = BluetoothUtils.D;
-
     private static final String TAG = "AudioSharingDeviceVolumeGroupController";
     private static final String KEY = "audio_sharing_device_volume_group";
 
@@ -63,8 +61,43 @@
     private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
     private FragmentManager mFragmentManager;
     private PreferenceGroup mPreferenceGroup;
-    private Map<Preference, BluetoothVolumeControl.Callback> mCallbackMap =
-            new HashMap<Preference, BluetoothVolumeControl.Callback>();
+    private List<AudioSharingDeviceVolumePreference> mVolumePreferences = new ArrayList<>();
+    private Map<Integer, Integer> mValueMap = new HashMap<Integer, Integer>();
+
+    private BluetoothVolumeControl.Callback mVolumeControlCallback =
+            new BluetoothVolumeControl.Callback() {
+                @Override
+                public void onVolumeOffsetChanged(
+                        @NonNull BluetoothDevice device, int volumeOffset) {}
+
+                @Override
+                public void onDeviceVolumeChanged(
+                        @NonNull BluetoothDevice device,
+                        @IntRange(from = -255, to = 255) int volume) {
+                    CachedBluetoothDevice cachedDevice =
+                            mLocalBtManager.getCachedDeviceManager().findDevice(device);
+                    if (cachedDevice == null) return;
+                    mValueMap.put(cachedDevice.getGroupId(), volume);
+                    for (AudioSharingDeviceVolumePreference preference : mVolumePreferences) {
+                        if (preference.getCachedDevice() != null
+                                && preference.getCachedDevice().getGroupId()
+                                        == cachedDevice.getGroupId()) {
+                            // If the callback return invalid volume, try to
+                            // get the volume from AudioManager.STREAM_MUSIC
+                            int finalVolume = getAudioVolumeIfNeeded(volume);
+                            Log.d(
+                                    TAG,
+                                    "onDeviceVolumeChanged: set volume to "
+                                            + finalVolume
+                                            + " for "
+                                            + device.getAnonymizedAddress());
+                            mContext.getMainExecutor()
+                                    .execute(() -> preference.setProgress(finalVolume));
+                            break;
+                        }
+                    }
+                }
+            };
 
     private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
             new BluetoothLeBroadcastAssistant.Callback() {
@@ -176,6 +209,10 @@
         }
         mAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
         mBluetoothDeviceUpdater.registerCallback();
+        if (mVolumeControl != null) {
+            Log.d(TAG, "onStart() Registered volume control callback");
+            mVolumeControl.registerCallback(mExecutor, mVolumeControlCallback);
+        }
     }
 
     @Override
@@ -191,17 +228,16 @@
         }
         mAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
         mBluetoothDeviceUpdater.unregisterCallback();
+        if (mVolumeControl != null) {
+            Log.d(TAG, "onStop() Unregistered volume control callback");
+            mVolumeControl.unregisterCallback(mVolumeControlCallback);
+            mValueMap.clear();
+        }
     }
 
     @Override
     public void onDestroy(@NonNull LifecycleOwner owner) {
-        for (var entry : mCallbackMap.entrySet()) {
-            if (DEBUG) {
-                Log.d(TAG, "onDestroy: unregister callback for " + entry.getKey());
-            }
-            mVolumeControl.unregisterCallback(entry.getValue());
-        }
-        mCallbackMap.clear();
+        mVolumePreferences.clear();
     }
 
     @Override
@@ -228,14 +264,22 @@
             mPreferenceGroup.setVisible(true);
         }
         mPreferenceGroup.addPreference(preference);
-        if (mVolumeControl != null && preference instanceof AudioSharingDeviceVolumePreference) {
-            BluetoothVolumeControl.Callback callback =
-                    buildVcCallback((AudioSharingDeviceVolumePreference) preference);
-            mCallbackMap.put(preference, callback);
-            if (DEBUG) {
-                Log.d(TAG, "onDeviceAdded: register callback for " + preference);
-            }
-            mVolumeControl.registerCallback(mExecutor, callback);
+        if (preference instanceof AudioSharingDeviceVolumePreference) {
+            var volumePref = (AudioSharingDeviceVolumePreference) preference;
+            mVolumePreferences.add(volumePref);
+            if (volumePref.getProgress() > 0) return;
+            CachedBluetoothDevice device = volumePref.getCachedDevice();
+            if (device == null) return;
+            int volume = mValueMap.getOrDefault(device.getGroupId(), -1);
+            // If the volume is invalid, try to get the volume from AudioManager.STREAM_MUSIC
+            int finalVolume = getAudioVolumeIfNeeded(volume);
+            Log.d(
+                    TAG,
+                    "onDeviceAdded: set volume to "
+                            + finalVolume
+                            + " for "
+                            + device.getDevice().getAnonymizedAddress());
+            mContext.getMainExecutor().execute(() -> volumePref.setProgress(finalVolume));
         }
     }
 
@@ -245,12 +289,18 @@
         if (mPreferenceGroup.getPreferenceCount() == 0) {
             mPreferenceGroup.setVisible(false);
         }
-        if (mVolumeControl != null && mCallbackMap.containsKey(preference)) {
-            if (DEBUG) {
-                Log.d(TAG, "onDeviceRemoved: unregister callback for " + preference);
+        if (preference instanceof AudioSharingDeviceVolumePreference) {
+            var volumePref = (AudioSharingDeviceVolumePreference) preference;
+            if (mVolumePreferences.contains(volumePref)) {
+                mVolumePreferences.remove(volumePref);
             }
-            mVolumeControl.unregisterCallback(mCallbackMap.get(preference));
-            mCallbackMap.remove(preference);
+            CachedBluetoothDevice device = volumePref.getCachedDevice();
+            Log.d(
+                    TAG,
+                    "onDeviceRemoved: "
+                            + (device == null
+                                    ? "null"
+                                    : device.getDevice().getAnonymizedAddress()));
         }
     }
 
@@ -278,39 +328,6 @@
                         fragment.getMetricsCategory());
     }
 
-    private BluetoothVolumeControl.Callback buildVcCallback(
-            AudioSharingDeviceVolumePreference preference) {
-        return new BluetoothVolumeControl.Callback() {
-            @Override
-            public void onVolumeOffsetChanged(BluetoothDevice device, int volumeOffset) {}
-
-            @Override
-            public void onDeviceVolumeChanged(
-                    @NonNull BluetoothDevice device,
-                    @IntRange(from = -255, to = 255) int volume) {
-                CachedBluetoothDevice cachedDevice =
-                        mLocalBtManager.getCachedDeviceManager().findDevice(device);
-                if (cachedDevice == null) return;
-                if (preference.getCachedDevice() != null
-                        && preference.getCachedDevice().getGroupId() == cachedDevice.getGroupId()) {
-                    // If the callback return invalid volume, try to get the volume from
-                    // AudioManager.STREAM_MUSIC
-                    int finalVolume = getAudioVolumeIfNeeded(volume);
-                    Log.d(
-                            TAG,
-                            "onDeviceVolumeChanged: set volume to "
-                                    + finalVolume
-                                    + " for "
-                                    + device.getAnonymizedAddress());
-                    ThreadUtils.postOnMainThread(
-                            () -> {
-                                preference.setProgress(finalVolume);
-                            });
-                }
-            }
-        };
-    }
-
     private int getAudioVolumeIfNeeded(int volume) {
         if (volume >= 0) return volume;
         try {
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index e841eac..ff465a3 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -233,7 +233,14 @@
             return;
         }
         Context context = requireContext();
-        if (!DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)) {
+        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
+
+        if (!um.isAdminUser()) {
+            Toast.makeText(context, R.string.dev_settings_available_to_admin_only_warning,
+                            Toast.LENGTH_SHORT)
+                    .show();
+            finish();
+        } else if (!DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)) {
             Toast.makeText(context, R.string.dev_settings_disabled_warning, Toast.LENGTH_SHORT)
                     .show();
             finish();
diff --git a/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt b/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt
index c47fdac..6596529 100644
--- a/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppRestoreButton.kt
@@ -73,12 +73,6 @@
         )
         try {
             packageInstaller.requestUnarchive(app.packageName, pendingIntent.intentSender)
-            val appLabel = userPackageManager.getApplicationLabel(app)
-            Toast.makeText(
-                context,
-                context.getString(R.string.restoring_in_progress, appLabel),
-                Toast.LENGTH_SHORT
-            ).show()
         } catch (e: Exception) {
             Log.e(LOG_TAG, "Request unarchive failed", e)
             Toast.makeText(
@@ -92,23 +86,11 @@
     private fun onReceive(intent: Intent, app: ApplicationInfo) {
         when (val unarchiveStatus =
             intent.getIntExtra(PackageInstaller.EXTRA_UNARCHIVE_STATUS, Int.MIN_VALUE)) {
-            PackageInstaller.STATUS_PENDING_USER_ACTION -> {
-                Log.e(
-                    LOG_TAG,
-                    "Request unarchiving failed for $packageName with code $unarchiveStatus"
-                )
-                Toast.makeText(
-                    context,
-                    context.getString(R.string.restoring_failed),
-                    Toast.LENGTH_SHORT
-                ).show()
-            }
-
-            PackageInstaller.STATUS_SUCCESS -> {
+            PackageInstaller.UNARCHIVAL_OK -> {
                 val appLabel = userPackageManager.getApplicationLabel(app)
                 Toast.makeText(
                     context,
-                    context.getString(R.string.restoring_succeeded, appLabel),
+                    context.getString(R.string.restoring_in_progress, appLabel),
                     Toast.LENGTH_SHORT
                 ).show()
             }