Merge "[Audiosharing] Update media devices section." into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 85a443e..f88c55d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1741,6 +1741,8 @@
android:targetActivity=".spa.SpaBridgeActivity">
<meta-data android:name="com.android.settings.spa.DESTINATION"
android:value="TogglePermissionAppList/UseFullScreenIntent"/>
+ <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+ android:value="@string/menu_key_apps"/>
</activity-alias>
<activity-alias android:name="AppManageFullScreenIntent"
@@ -1753,6 +1755,8 @@
</intent-filter>
<meta-data android:name="com.android.settings.spa.DESTINATION"
android:value="TogglePermissionAppInfoPage/UseFullScreenIntent"/>
+ <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+ android:value="@string/menu_key_apps"/>
</activity-alias>
<activity android:name=".applications.InstalledAppOpenByDefaultActivity"
@@ -4967,6 +4971,8 @@
</intent-filter>
<meta-data android:name="com.android.settings.spa.DESTINATION"
android:value="UsageStats"/>
+ <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+ android:value="@string/menu_key_apps"/>
</activity-alias>
<!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6ed0d82..71a7b30 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4786,6 +4786,10 @@
<string name="accessibility_toggle_large_pointer_icon_title">Large mouse pointer</string>
<!-- Summary for the accessibility preference for enabling/disabling large icons for mouse/trackpad pointers. [CHAR LIMIT=60] -->
<string name="accessibility_toggle_large_pointer_icon_summary">Make the mouse pointer more noticeable</string>
+ <!-- Title for the accessibility preference for forcing all apps to use dark theme. [CHAR LIMIT=35] -->
+ <string name="accessibility_force_invert_title">Make all apps dark</string>
+ <!-- Summary for the accessibility preference for forcing all apps to use dark theme. [CHAR LIMIT=100] -->
+ <string name="accessibility_force_invert_summary">Applies to apps without their own dark theme. Some apps may have display issues, like inverted colors.</string>
<!-- Title for the accessibility preference for disabling animations. [CHAR LIMIT=35] -->
<string name="accessibility_disable_animations">Remove animations</string>
<!-- Summary for the accessibility preference for disabling animations. [CHAR LIMIT=60] -->
@@ -6071,10 +6075,6 @@
<!-- Backup and reset Settings screen --><skip />
<!-- Backup and reset settings menu and activity title -->
<string name="privacy_settings_title">Backup</string>
- <!-- Summary for the Backup settings when it is turned on. -->
- <string name="backup_summary_state_on">On</string>
- <!-- Summary for the Backup settings when it is turned off. -->
- <string name="backup_summary_state_off">Off</string>
<!-- Backup section title -->
<string name="backup_section_title">Backup & restore</string>
<!-- Personal data section title -->
diff --git a/res/xml/accessibility_color_and_motion.xml b/res/xml/accessibility_color_and_motion.xml
index 3b2e78d..a500b72 100644
--- a/res/xml/accessibility_color_and_motion.xml
+++ b/res/xml/accessibility_color_and_motion.xml
@@ -50,6 +50,14 @@
settings:searchable="false"/>
<SwitchPreferenceCompat
+ android:icon="@drawable/ic_dark_ui"
+ android:key="toggle_force_invert"
+ android:persistent="false"
+ android:summary="@string/accessibility_force_invert_summary"
+ android:title="@string/accessibility_force_invert_title"
+ settings:controller="com.android.settings.accessibility.ToggleForceInvertPreferenceController"/>
+
+ <SwitchPreferenceCompat
android:icon="@drawable/ic_accessibility_animation"
android:key="toggle_disable_animations"
android:persistent="false"
diff --git a/res/xml/bluetooth_audio_streams.xml b/res/xml/bluetooth_audio_streams.xml
index b419eaa..95ee710 100644
--- a/res/xml/bluetooth_audio_streams.xml
+++ b/res/xml/bluetooth_audio_streams.xml
@@ -34,6 +34,7 @@
<com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryPreference
android:key="audio_streams_nearby_category"
- android:title="@string/audio_streams_pref_title" />
+ android:title="@string/audio_streams_pref_title"
+ settings:controller="com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController" />
</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/accessibility/ColorAndMotionFragment.java b/src/com/android/settings/accessibility/ColorAndMotionFragment.java
index 7933eb1..28c5335 100644
--- a/src/com/android/settings/accessibility/ColorAndMotionFragment.java
+++ b/src/com/android/settings/accessibility/ColorAndMotionFragment.java
@@ -21,11 +21,13 @@
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
+import android.view.accessibility.Flags;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.TwoStatePreference;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -47,6 +49,8 @@
private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN = "daltonizer_preference";
private static final String TOGGLE_DISABLE_ANIMATIONS = "toggle_disable_animations";
private static final String TOGGLE_LARGE_POINTER_ICON = "toggle_large_pointer_icon";
+ @VisibleForTesting
+ static final String TOGGLE_FORCE_INVERT = "toggle_force_invert";
private Preference mDisplayDaltonizerPreferenceScreen;
private TwoStatePreference mToggleDisableAnimationsPreference;
@@ -70,6 +74,9 @@
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+ if (Flags.forceInvertColor()) {
+ mShortcutFeatureKeys.add(ToggleForceInvertPreferenceController.SETTINGS_KEY);
+ }
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
mSettingsContentObserver.registerKeysToObserverCallback(mShortcutFeatureKeys,
diff --git a/src/com/android/settings/accessibility/ToggleForceInvertPreferenceController.java b/src/com/android/settings/accessibility/ToggleForceInvertPreferenceController.java
new file mode 100644
index 0000000..3f2cc13
--- /dev/null
+++ b/src/com/android/settings/accessibility/ToggleForceInvertPreferenceController.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.view.accessibility.Flags;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/** A toggle preference controller for force invert (force dark). */
+public class ToggleForceInvertPreferenceController extends TogglePreferenceController {
+
+ public static final String SETTINGS_KEY =
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED;
+
+ @VisibleForTesting
+ static final int ON = 1;
+ @VisibleForTesting
+ static final int OFF = 0;
+
+ public ToggleForceInvertPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return Settings.Secure.getInt(mContext.getContentResolver(), SETTINGS_KEY, OFF) != OFF;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ return Settings.Secure.putInt(mContext.getContentResolver(),
+ SETTINGS_KEY, isChecked ? ON : OFF);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return Flags.forceInvertColor() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_accessibility;
+ }
+}
diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java
index 6f8597e..2c41be4 100644
--- a/src/com/android/settings/applications/AppInfoBase.java
+++ b/src/com/android/settings/applications/AppInfoBase.java
@@ -144,10 +144,14 @@
if (mAppEntry != null) {
// Get application info again to refresh changed properties of application
try {
- mPackageInfo = mPm.getPackageInfoAsUser(mAppEntry.info.packageName,
- PackageManager.MATCH_DISABLED_COMPONENTS |
- PackageManager.GET_SIGNING_CERTIFICATES |
- PackageManager.GET_PERMISSIONS, mUserId);
+ mPackageInfo = mPm.getPackageInfoAsUser(
+ mAppEntry.info.packageName,
+ PackageManager.PackageInfoFlags.of(
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.GET_SIGNING_CERTIFICATES
+ | PackageManager.GET_PERMISSIONS
+ | PackageManager.MATCH_ARCHIVED_PACKAGES),
+ mUserId);
} catch (NameNotFoundException e) {
Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
}
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
index e771ff4..82d55f3 100644
--- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -731,10 +731,12 @@
try {
mPackageInfo = activity.getPackageManager().getPackageInfo(
mAppEntry.info.packageName,
- PackageManager.MATCH_DISABLED_COMPONENTS |
- PackageManager.MATCH_ANY_USER |
- PackageManager.GET_SIGNATURES |
- PackageManager.GET_PERMISSIONS);
+ PackageManager.PackageInfoFlags.of(
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_ANY_USER
+ | PackageManager.GET_SIGNATURES
+ | PackageManager.GET_PERMISSIONS
+ | PackageManager.MATCH_ARCHIVED_PACKAGES));
} catch (NameNotFoundException e) {
Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
}
diff --git a/src/com/android/settings/backup/BackupSettingsHelper.java b/src/com/android/settings/backup/BackupSettingsHelper.java
index b55172e..258f54d 100644
--- a/src/com/android/settings/backup/BackupSettingsHelper.java
+++ b/src/com/android/settings/backup/BackupSettingsHelper.java
@@ -24,7 +24,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
@@ -51,24 +50,6 @@
}
/**
- * If there is only one profile, show whether the backup is on or off.
- * Otherwise, show nothing.
- */
- public String getSummary() {
- UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (userManager.getUserProfiles().size() == 1) {
- try {
- int resId = mBackupManager.isBackupEnabled()
- ? R.string.backup_summary_state_on : R.string.backup_summary_state_off;
- return mContext.getText(resId).toString();
- } catch (RemoteException e) {
- Log.e(TAG, "Error getting isBackupEnabled", e);
- }
- }
- return null;
- }
-
- /**
* Returns an intent to launch backup settings from backup transport if the intent was provided
* by the transport. Otherwise returns the intent to launch the default backup settings screen.
*
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index 483a74b..9329cc29 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -27,6 +27,8 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
@@ -39,6 +41,7 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -54,6 +57,7 @@
public class AudioSharingDevicePreferenceController extends BasePreferenceController
implements DefaultLifecycleObserver, DevicePreferenceCallback, BluetoothCallback {
+ private static final boolean DEBUG = BluetoothUtils.D;
private static final String TAG = "AudioSharingDevicePrefController";
private static final String KEY = "audio_sharing_device_list";
@@ -372,19 +376,31 @@
+ " non le audio device");
return;
}
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Start handling onProfileConnectionStateChanged for "
+ + cachedDevice.getDevice().getAnonymizedAddress());
+ }
if (!isLeAudioSupported) {
// Handle connected ineligible (non LE audio) remote device
if (isBroadcasting()) {
// Show stop audio sharing dialog when an ineligible (non LE audio) remote device
// connected during a sharing session.
+ closeOpeningDialogs();
AudioSharingStopDialogFragment.show(
mFragment,
cachedDevice.getName(),
- () -> {
- mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId());
- });
+ () -> mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId()));
+ } else {
+ // Do nothing for ineligible (non LE audio) remote device when no sharing session.
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Ignore onProfileConnectionStateChanged for non LE audio without"
+ + " sharing session");
+ }
}
- // Do nothing for ineligible (non LE audio) remote device when no sharing session.
} else {
Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
@@ -398,6 +414,7 @@
// Show audio sharing switch dialog when the third eligible (LE audio) remote device
// connected during a sharing session.
if (deviceItemsInSharingSession.size() >= 2) {
+ closeOpeningDialogs();
AudioSharingDisconnectDialogFragment.show(
mFragment,
deviceItemsInSharingSession,
@@ -421,6 +438,7 @@
} else {
// Show audio sharing join dialog when the first or second eligible (LE audio)
// remote device connected during a sharing session.
+ closeOpeningDialogs();
AudioSharingJoinDialogFragment.show(
mFragment,
deviceItemsInSharingSession,
@@ -447,6 +465,7 @@
// Show audio sharing join dialog when the second eligible (LE audio) remote device
// connect and no sharing session.
if (deviceItems.size() == 1) {
+ closeOpeningDialogs();
AudioSharingJoinDialogFragment.show(
mFragment,
deviceItems,
@@ -524,4 +543,15 @@
mAssistant.addSource(sink, broadcastMetadata, /* isGroupOp= */ false);
}
}
+
+ private void closeOpeningDialogs() {
+ if (mFragment == null) return;
+ List<Fragment> fragments = mFragment.getChildFragmentManager().getFragments();
+ for (Fragment fragment : fragments) {
+ if (fragment instanceof DialogFragment) {
+ Log.d(TAG, "Remove staled opening dialog " + fragment.getTag());
+ ((DialogFragment) fragment).dismiss();
+ }
+ }
+ }
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
index 365aed5..461c230 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
@@ -77,15 +77,12 @@
if (!AudioSharingUtils.isFeatureEnabled()) return;
final FragmentManager manager = host.getChildFragmentManager();
sListener = listener;
- if (manager.findFragmentByTag(TAG) == null) {
- final Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems);
- bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
- AudioSharingDisconnectDialogFragment dialog =
- new AudioSharingDisconnectDialogFragment();
- dialog.setArguments(bundle);
- dialog.show(manager, TAG);
- }
+ final Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems);
+ bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
+ AudioSharingDisconnectDialogFragment dialog = new AudioSharingDisconnectDialogFragment();
+ dialog.setArguments(bundle);
+ dialog.show(manager, TAG);
}
@Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
index 589c955..2d7b4c4 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
@@ -71,14 +71,12 @@
if (!AudioSharingUtils.isFeatureEnabled()) 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);
- bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
- final AudioSharingJoinDialogFragment dialog = new AudioSharingJoinDialogFragment();
- dialog.setArguments(bundle);
- dialog.show(manager, TAG);
- }
+ final Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
+ bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
+ final AudioSharingJoinDialogFragment dialog = new AudioSharingJoinDialogFragment();
+ dialog.setArguments(bundle);
+ dialog.show(manager, TAG);
}
@Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
index 13416aa..31125de 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
@@ -56,13 +56,11 @@
if (!AudioSharingUtils.isFeatureEnabled()) return;
final FragmentManager manager = host.getChildFragmentManager();
sListener = listener;
- if (manager.findFragmentByTag(TAG) == null) {
- final Bundle bundle = new Bundle();
- bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
- AudioSharingStopDialogFragment dialog = new AudioSharingStopDialogFragment();
- dialog.setArguments(bundle);
- dialog.show(manager, TAG);
- }
+ final Bundle bundle = new Bundle();
+ bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDeviceName);
+ AudioSharingStopDialogFragment dialog = new AudioSharingStopDialogFragment();
+ dialog.setArguments(bundle);
+ dialog.show(manager, TAG);
}
@Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
index 6809c7d..8f701a3 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.util.AttributeSet;
+import androidx.annotation.Nullable;
+
import com.android.settings.R;
import com.android.settingslib.widget.TwoTargetPreference;
@@ -27,25 +29,35 @@
* {@link TwoTargetPreference}.
*/
public class AudioStreamPreference extends TwoTargetPreference {
- private boolean mShowLock = true;
+ private boolean mIsConnected = false;
/**
- * Sets whether to display the lock icon.
+ * Update preference UI based on connection status
*
- * @param showLock Should show / hide the lock icon
+ * @param isConnected Is this streams connected
*/
- public void setShowLock(boolean showLock) {
- mShowLock = showLock;
+ public void setIsConnected(
+ boolean isConnected, @Nullable OnPreferenceClickListener onPreferenceClickListener) {
+ if (mIsConnected == isConnected
+ && getOnPreferenceClickListener() == onPreferenceClickListener) {
+ // Nothing to update.
+ return;
+ }
+ mIsConnected = isConnected;
+ setSummary(isConnected ? "Listening now" : "");
+ setOrder(isConnected ? 0 : 1);
+ setOnPreferenceClickListener(onPreferenceClickListener);
notifyChanged();
}
- public AudioStreamPreference(Context context, AttributeSet attrs) {
+ public AudioStreamPreference(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
+ setIcon(R.drawable.ic_bt_audio_sharing);
}
@Override
protected boolean shouldHideSecondTarget() {
- return !mShowLock;
+ return mIsConnected;
}
@Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java
new file mode 100644
index 0000000..788b253
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsBroadcastAssistantCallback.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.audiosharing.audiostreams;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.util.Log;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+
+import java.util.Locale;
+
+public class AudioStreamsBroadcastAssistantCallback
+ implements BluetoothLeBroadcastAssistant.Callback {
+
+ private static final String TAG = "AudioStreamsBroadcastAssistantCallback";
+ private static final boolean DEBUG = BluetoothUtils.D;
+
+ private AudioStreamsProgressCategoryController mCategoryController;
+
+ public AudioStreamsBroadcastAssistantCallback(
+ AudioStreamsProgressCategoryController audioStreamsProgressCategoryController) {
+ mCategoryController = audioStreamsProgressCategoryController;
+ }
+
+ @Override
+ public void onReceiveStateChanged(
+ BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onReceiveStateChanged() sink : "
+ + sink.getAddress()
+ + " sourceId: "
+ + sourceId
+ + " state: "
+ + state);
+ }
+ }
+
+ @Override
+ public void onSearchStartFailed(int reason) {
+ Log.w(TAG, "onSearchStartFailed() reason : " + reason);
+ mCategoryController.showToast(
+ String.format(Locale.US, "Failed to start scanning, reason %d", reason));
+ }
+
+ @Override
+ public void onSearchStarted(int reason) {
+ if (mCategoryController == null) {
+ Log.w(TAG, "onSearchStarted() : mCategoryController is null!");
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onSearchStarted() reason : " + reason);
+ }
+ mCategoryController.setScanning(true);
+ }
+
+ @Override
+ public void onSearchStopFailed(int reason) {
+ Log.w(TAG, "onSearchStopFailed() reason : " + reason);
+ mCategoryController.showToast(
+ String.format(Locale.US, "Failed to stop scanning, reason %d", reason));
+ }
+
+ @Override
+ public void onSearchStopped(int reason) {
+ if (mCategoryController == null) {
+ Log.w(TAG, "onSearchStopped() : mCategoryController is null!");
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onSearchStopped() reason : " + reason);
+ }
+ mCategoryController.setScanning(false);
+ }
+
+ @Override
+ public void onSourceAddFailed(
+ BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) {}
+
+ @Override
+ public void onSourceAdded(BluetoothDevice sink, int sourceId, int reason) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onSourceAdded() sink : "
+ + sink.getAddress()
+ + " sourceId: "
+ + sourceId
+ + " reason: "
+ + reason);
+ }
+ }
+
+ @Override
+ public void onSourceFound(BluetoothLeBroadcastMetadata source) {
+ if (mCategoryController == null) {
+ Log.w(TAG, "onSourceFound() : mCategoryController is null!");
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onSourceFound() broadcastId : " + source.getBroadcastId());
+ }
+ mCategoryController.addSourceFound(source);
+ }
+
+ @Override
+ public void onSourceLost(int broadcastId) {
+ if (DEBUG) {
+ Log.d(TAG, "onSourceLost() broadcastId : " + broadcastId);
+ }
+ mCategoryController.removeSourceLost(broadcastId);
+ }
+
+ @Override
+ public void onSourceModified(BluetoothDevice sink, int sourceId, int reason) {}
+
+ @Override
+ public void onSourceModifyFailed(BluetoothDevice sink, int sourceId, int reason) {}
+
+ @Override
+ public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {}
+
+ @Override
+ public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {}
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
new file mode 100644
index 0000000..fef1e7b
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.audiosharing.audiostreams;
+
+import static java.util.Collections.emptyList;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudioContentMetadata;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
+import com.android.settings.core.BasePreferenceController;
+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.LocalBluetoothProfileManager;
+import com.android.settingslib.utils.ThreadUtils;
+
+import com.google.common.base.Strings;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.stream.Stream;
+
+import javax.annotation.Nullable;
+
+public class AudioStreamsProgressCategoryController extends BasePreferenceController
+ implements DefaultLifecycleObserver {
+ private static final String TAG = "AudioStreamsProgressCategoryController";
+ private static final boolean DEBUG = BluetoothUtils.D;
+
+ private final Executor mExecutor;
+ private final AudioStreamsBroadcastAssistantCallback mBroadcastAssistantCallback;
+ private final LocalBluetoothManager mBluetoothManager;
+ private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
+ private final ConcurrentHashMap<Integer, AudioStreamPreference> mBroadcastIdToPreferenceMap =
+ new ConcurrentHashMap<>();
+ private @Nullable AudioStreamsProgressCategoryPreference mCategoryPreference;
+
+ public AudioStreamsProgressCategoryController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mExecutor = Executors.newSingleThreadExecutor();
+ mBluetoothManager = Utils.getLocalBtManager(mContext);
+ mLeBroadcastAssistant = getLeBroadcastAssistant(mBluetoothManager);
+ mBroadcastAssistantCallback = new AudioStreamsBroadcastAssistantCallback(this);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mCategoryPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onStart(@NonNull LifecycleOwner owner) {
+ if (mLeBroadcastAssistant == null) {
+ Log.w(TAG, "onStart(): LeBroadcastAssistant is null!");
+ return;
+ }
+ mBroadcastIdToPreferenceMap.clear();
+ if (mCategoryPreference != null) {
+ mCategoryPreference.removeAll();
+ }
+ mExecutor.execute(
+ () -> {
+ mLeBroadcastAssistant.registerServiceCallBack(
+ mExecutor, mBroadcastAssistantCallback);
+ if (DEBUG) {
+ Log.d(TAG, "scanAudioStreamsStart()");
+ }
+ mLeBroadcastAssistant.startSearchingForSources(emptyList());
+ // Display currently connected streams
+ var unused =
+ ThreadUtils.postOnBackgroundThread(
+ () -> {
+ for (var sink :
+ getActiveSinksOnAssistant(mBluetoothManager)) {
+ mLeBroadcastAssistant
+ .getAllSources(sink)
+ .forEach(this::addSourceConnected);
+ }
+ });
+ });
+ }
+
+ @Override
+ public void onStop(@NonNull LifecycleOwner owner) {
+ if (mLeBroadcastAssistant == null) {
+ Log.w(TAG, "onStop(): LeBroadcastAssistant is null!");
+ return;
+ }
+ mExecutor.execute(
+ () -> {
+ if (mLeBroadcastAssistant.isSearchInProgress()) {
+ if (DEBUG) {
+ Log.d(TAG, "scanAudioStreamsStop()");
+ }
+ mLeBroadcastAssistant.stopSearchingForSources();
+ }
+ mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
+ });
+ }
+
+ void setScanning(boolean isScanning) {
+ ThreadUtils.postOnMainThread(
+ () -> {
+ if (mCategoryPreference != null) mCategoryPreference.setProgress(isScanning);
+ });
+ }
+
+ void addSourceFound(BluetoothLeBroadcastMetadata source) {
+ Preference.OnPreferenceClickListener onClickListener =
+ preference -> {
+ if (DEBUG) {
+ Log.d(TAG, "preferenceClicked(): attempt to join broadcast");
+ }
+
+ // TODO(chelseahao): add source to sink
+ return true;
+ };
+ mBroadcastIdToPreferenceMap.computeIfAbsent(
+ source.getBroadcastId(),
+ k -> {
+ var p = createPreference(source, onClickListener);
+ ThreadUtils.postOnMainThread(
+ () -> {
+ if (mCategoryPreference != null) {
+ mCategoryPreference.addPreference(p);
+ }
+ });
+ return p;
+ });
+ }
+
+ void removeSourceLost(int broadcastId) {
+ var toRemove = mBroadcastIdToPreferenceMap.remove(broadcastId);
+ if (toRemove != null) {
+ ThreadUtils.postOnMainThread(
+ () -> {
+ if (mCategoryPreference != null) {
+ mCategoryPreference.removePreference(toRemove);
+ }
+ });
+ }
+ // TODO(chelseahao): remove source from sink
+ }
+
+ private void addSourceConnected(BluetoothLeBroadcastReceiveState state) {
+ mBroadcastIdToPreferenceMap.compute(
+ state.getBroadcastId(),
+ (k, v) -> {
+ if (v == null) {
+ // Create a new preference as the source has not been added.
+ var p = createPreference(state);
+ ThreadUtils.postOnMainThread(
+ () -> {
+ if (mCategoryPreference != null) {
+ mCategoryPreference.addPreference(p);
+ }
+ });
+ return p;
+ } else {
+ // This source has been added either by scanning, or it's currently
+ // connected to another active sink. Update its connection status to true
+ // if needed.
+ ThreadUtils.postOnMainThread(() -> v.setIsConnected(true, null));
+ return v;
+ }
+ });
+ }
+
+ private AudioStreamPreference createPreference(
+ BluetoothLeBroadcastMetadata source,
+ Preference.OnPreferenceClickListener onPreferenceClickListener) {
+ AudioStreamPreference preference = new AudioStreamPreference(mContext, /* attrs= */ null);
+ preference.setTitle(
+ source.getSubgroups().stream()
+ .map(s -> s.getContentMetadata().getProgramInfo())
+ .filter(i -> !Strings.isNullOrEmpty(i))
+ .findFirst()
+ .orElse("Broadcast Id: " + source.getBroadcastId()));
+ preference.setIsConnected(false, onPreferenceClickListener);
+ return preference;
+ }
+
+ private AudioStreamPreference createPreference(BluetoothLeBroadcastReceiveState state) {
+ AudioStreamPreference preference = new AudioStreamPreference(mContext, /* attrs= */ null);
+ preference.setTitle(
+ state.getSubgroupMetadata().stream()
+ .map(BluetoothLeAudioContentMetadata::getProgramInfo)
+ .filter(i -> !Strings.isNullOrEmpty(i))
+ .findFirst()
+ .orElse("Broadcast Id: " + state.getBroadcastId()));
+ preference.setIsConnected(true, null);
+ return preference;
+ }
+
+ private static List<BluetoothDevice> getActiveSinksOnAssistant(LocalBluetoothManager manager) {
+ if (manager == null) {
+ Log.w(TAG, "getActiveSinksOnAssistant(): LocalBluetoothManager is null!");
+ return emptyList();
+ }
+ return AudioSharingUtils.getActiveSinkOnAssistant(manager)
+ .map(
+ cachedBluetoothDevice ->
+ Stream.concat(
+ Stream.of(cachedBluetoothDevice.getDevice()),
+ cachedBluetoothDevice.getMemberDevice().stream()
+ .map(CachedBluetoothDevice::getDevice))
+ .toList())
+ .orElse(emptyList());
+ }
+
+ private static @Nullable LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant(
+ LocalBluetoothManager manager) {
+ if (manager == null) {
+ Log.w(TAG, "getLeBroadcastAssistant(): LocalBluetoothManager is null!");
+ return null;
+ }
+
+ LocalBluetoothProfileManager profileManager = manager.getProfileManager();
+ if (profileManager == null) {
+ Log.w(TAG, "getLeBroadcastAssistant(): LocalBluetoothProfileManager is null!");
+ return null;
+ }
+
+ return profileManager.getLeAudioBroadcastAssistantProfile();
+ }
+
+ void showToast(String msg) {
+ AudioSharingUtils.toastMessage(mContext, msg);
+ }
+}
diff --git a/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt b/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt
index a71bc8f..3bfb24a 100644
--- a/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt
+++ b/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt
@@ -19,10 +19,13 @@
import android.content.Context
import android.content.pm.PackageManager
import android.os.UserHandle
+import android.util.IconDrawableFactory
import androidx.preference.Preference
-import com.android.settingslib.Utils
-class AppPreferenceRepository(private val context: Context) {
+class AppPreferenceRepository(
+ private val context: Context,
+ private val iconDrawableFactory: IconDrawableFactory = IconDrawableFactory.newInstance(context),
+) {
private val packageManager = context.packageManager
fun loadAppPreferences(uids: List<Int>): List<Preference> = uids.flatMap { uid ->
@@ -38,7 +41,7 @@
private fun getPreference(packageName: String, userId: Int): Preference? = try {
val app = packageManager.getApplicationInfoAsUser(packageName, 0, userId)
Preference(context).apply {
- icon = Utils.getBadgedIcon(context, app)
+ icon = iconDrawableFactory.getBadgedIcon(app)
title = app.loadLabel(packageManager)
isSelectable = false
}
diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java
index 879f134..9545276 100644
--- a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java
+++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java
@@ -83,6 +83,7 @@
mFinalSelectedLayout = mLayout;
mKeyboardLayouts = mIm.getKeyboardLayoutListForInputDevice(
mInputDeviceIdentifier, mUserId, mInputMethodInfo, mInputMethodSubtype);
+ NewKeyboardSettingsUtils.sortKeyboardLayoutsByLabel(mKeyboardLayouts);
parent.getActivity().setTitle(mTitle);
}
diff --git a/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java b/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java
index f3dc0a3..a927165 100644
--- a/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java
+++ b/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java
@@ -27,6 +27,8 @@
import android.view.inputmethod.InputMethodSubtype;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
/**
@@ -123,4 +125,11 @@
InputDeviceIdentifier identifier, InputMethodInfo info, InputMethodSubtype subtype) {
return inputManager.getKeyboardLayoutForInputDevice(identifier, userId, info, subtype);
}
+
+ static void sortKeyboardLayoutsByLabel(KeyboardLayout[] keyboardLayouts) {
+ Arrays.sort(
+ keyboardLayouts,
+ Comparator.comparing(KeyboardLayout::getLabel)
+ );
+ }
}
diff --git a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
index b4e768c..a17144f 100644
--- a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
+++ b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
@@ -19,7 +19,19 @@
import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+import android.Manifest;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.service.euicc.EuiccService;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
@@ -29,12 +41,12 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.settings.network.MobileNetworkRepository;
import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.mobile.dataservice.DataServiceUtils;
-import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
-import com.android.settingslib.mobile.dataservice.UiccInfoEntity;
+
+import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@@ -47,6 +59,9 @@
private MobileNetworkRepository mMobileNetworkRepository;
private List<SubscriptionInfoEntity> mSubscriptionInfoEntityList = new ArrayList<>();
private SubscriptionInfoEntity mSubscriptionInfoEntity;
+ private static int sQueryFlag =
+ PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DIRECT_BOOT_AUTO
+ | PackageManager.GET_RESOLVED_FILTER;
public ConvertToEsimPreferenceController(Context context, String key, Lifecycle lifecycle,
LifecycleOwner lifecycleOwner, int subId) {
@@ -83,11 +98,14 @@
@Override
public int getAvailabilityStatus(int subId) {
- return CONDITIONALLY_UNAVAILABLE;
// TODO(b/262195754): Need the intent to enabled the feature.
-// return mSubscriptionInfoEntity != null && mSubscriptionInfoEntity.isActiveSubscriptionId
-// && !mSubscriptionInfoEntity.isEmbedded ? AVAILABLE
-// : CONDITIONALLY_UNAVAILABLE;
+ if (findConversionSupportComponent()) {
+ return mSubscriptionInfoEntity != null && mSubscriptionInfoEntity.isActiveSubscriptionId
+ && !mSubscriptionInfoEntity.isEmbedded && isActiveSubscription(subId)
+ ? AVAILABLE
+ : CONDITIONALLY_UNAVAILABLE;
+ }
+ return CONDITIONALLY_UNAVAILABLE;
}
@VisibleForTesting
@@ -104,6 +122,9 @@
return false;
}
// Send intent to launch LPA
+ Intent intent = new Intent(EuiccManager.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION);
+ intent.putExtra("subId", mSubId);
+ mContext.startActivity(intent);
return true;
}
@@ -115,12 +136,73 @@
@Override
public void onActiveSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList) {
// TODO(b/262195754): Need the intent to enabled the feature.
-// mSubscriptionInfoEntityList = subInfoEntityList;
-// mSubscriptionInfoEntityList.forEach(entity -> {
-// if (Integer.parseInt(entity.subId) == mSubId) {
-// mSubscriptionInfoEntity = entity;
-// update();
-// }
-// });
+ mSubscriptionInfoEntityList = subInfoEntityList;
+ mSubscriptionInfoEntityList.forEach(entity -> {
+ if (Integer.parseInt(entity.subId) == mSubId) {
+ mSubscriptionInfoEntity = entity;
+ update();
+ }
+ });
+ }
+
+ private boolean isActiveSubscription(int subId) {
+ SubscriptionManager subscriptionManager = mContext.getSystemService(
+ SubscriptionManager.class);
+ SubscriptionInfo subInfo = subscriptionManager.getActiveSubscriptionInfo(subId);
+ if (subInfo == null) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean findConversionSupportComponent() {
+ Intent intent = new Intent(EuiccService.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION);
+ PackageManager packageManager = mContext.getPackageManager();
+ List<ResolveInfo> resolveInfoList = packageManager
+ .queryIntentActivities(intent, sQueryFlag);
+ if (resolveInfoList == null || resolveInfoList.isEmpty()) {
+ return false;
+ }
+ for (ResolveInfo resolveInfo : resolveInfoList) {
+ if (!isValidEuiccComponent(packageManager, resolveInfo)) {
+ continue;
+ } else {
+ return true;
+ }
+ }
+ return true;
+ }
+
+ private boolean isValidEuiccComponent(
+ PackageManager packageManager, @NotNull ResolveInfo resolveInfo) {
+ ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(resolveInfo);
+ String packageName = new ComponentName(componentInfo.packageName, componentInfo.name)
+ .getPackageName();
+
+ // Verify that the app is privileged (via granting of a privileged permission).
+ if (packageManager.checkPermission(
+ Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS, packageName)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+
+ // Verify that only the system can access the component.
+ final String permission;
+ if (componentInfo instanceof ServiceInfo) {
+ permission = ((ServiceInfo) componentInfo).permission;
+ } else if (componentInfo instanceof ActivityInfo) {
+ permission = ((ActivityInfo) componentInfo).permission;
+ } else {
+ return false;
+ }
+ if (!TextUtils.equals(permission, Manifest.permission.BIND_EUICC_SERVICE)) {
+ return false;
+ }
+
+ // Verify that the component declares a priority.
+ if (resolveInfo.filter == null || resolveInfo.filter.getPriority() == 0) {
+ return false;
+ }
+ return true;
}
}
diff --git a/src/com/android/settings/spa/SpaAppBridgeActivity.kt b/src/com/android/settings/spa/SpaAppBridgeActivity.kt
index 1a77442..a68d220 100644
--- a/src/com/android/settings/spa/SpaAppBridgeActivity.kt
+++ b/src/com/android/settings/spa/SpaAppBridgeActivity.kt
@@ -20,7 +20,6 @@
import android.content.Intent
import android.os.Bundle
import android.os.UserHandle
-import com.android.settings.spa.SpaBridgeActivity.Companion.getDestination
import com.android.settings.spa.SpaBridgeActivity.Companion.startSpaActivityFromBridge
/**
@@ -33,11 +32,7 @@
class SpaAppBridgeActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- getDestination()?.let { destinationPrefix ->
- getDestinationForApp(destinationPrefix, intent)?.let { destination ->
- startSpaActivityFromBridge(destination)
- }
- }
+ startSpaActivityFromBridge { getDestinationForApp(it, intent) }
finish()
}
diff --git a/src/com/android/settings/spa/SpaBridgeActivity.kt b/src/com/android/settings/spa/SpaBridgeActivity.kt
index 0e239ae..61d8f51 100644
--- a/src/com/android/settings/spa/SpaBridgeActivity.kt
+++ b/src/com/android/settings/spa/SpaBridgeActivity.kt
@@ -18,12 +18,10 @@
import android.app.Activity
import android.content.Intent
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ComponentInfoFlags
import android.os.Bundle
-import androidx.annotation.VisibleForTesting
import com.android.settings.activityembedding.ActivityEmbeddingUtils
import com.android.settings.activityembedding.EmbeddedDeepLinkUtils.tryStartMultiPaneDeepLink
+import com.android.settings.spa.SpaDestination.Companion.getDestination
import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL
import com.android.settingslib.spa.framework.util.appendSpaParams
@@ -37,29 +35,23 @@
class SpaBridgeActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- getDestination()?.let { destination ->
- startSpaActivityFromBridge(destination)
- }
+ startSpaActivityFromBridge()
finish()
}
companion object {
- fun Activity.startSpaActivityFromBridge(destination: String) {
+ fun Activity.startSpaActivityFromBridge(destinationFactory: (String) -> String? = { it }) {
+ val (destination, highlightMenuKey) = getDestination(destinationFactory) ?: return
val intent = Intent(this, SpaActivity::class.java)
- .appendSpaParams(destination = destination)
- .appendSpaParams(sessionName = SESSION_EXTERNAL)
+ .appendSpaParams(
+ destination = destination,
+ sessionName = SESSION_EXTERNAL,
+ )
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this) ||
- !tryStartMultiPaneDeepLink(intent)) {
+ !tryStartMultiPaneDeepLink(intent, highlightMenuKey)
+ ) {
startActivity(intent)
}
}
-
- fun Activity.getDestination(): String? =
- packageManager.getActivityInfo(
- componentName, ComponentInfoFlags.of(PackageManager.GET_META_DATA.toLong())
- ).metaData.getString(META_DATA_KEY_DESTINATION)
-
- @VisibleForTesting
- const val META_DATA_KEY_DESTINATION = "com.android.settings.spa.DESTINATION"
}
}
diff --git a/src/com/android/settings/spa/SpaDestination.kt b/src/com/android/settings/spa/SpaDestination.kt
new file mode 100644
index 0000000..bdec1d8
--- /dev/null
+++ b/src/com/android/settings/spa/SpaDestination.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa
+
+import android.app.Activity
+import android.content.pm.PackageManager
+import androidx.annotation.VisibleForTesting
+import com.android.settings.SettingsActivity.META_DATA_KEY_HIGHLIGHT_MENU_KEY
+
+data class SpaDestination(
+ val destination: String,
+ val highlightMenuKey: String?,
+) {
+ companion object {
+ fun Activity.getDestination(
+ destinationFactory: (String) -> String? = { it },
+ ): SpaDestination? {
+ val metaData = packageManager.getActivityInfo(
+ componentName,
+ PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA.toLong())
+ ).metaData
+ val destination = metaData.getString(META_DATA_KEY_DESTINATION)
+ if (destination.isNullOrBlank()) return null
+ val finalDestination = destinationFactory(destination)
+ if (finalDestination.isNullOrBlank()) return null
+ return SpaDestination(
+ destination = finalDestination,
+ highlightMenuKey = metaData.getString(META_DATA_KEY_HIGHLIGHT_MENU_KEY),
+ )
+ }
+
+ @VisibleForTesting
+ const val META_DATA_KEY_DESTINATION = "com.android.settings.spa.DESTINATION"
+ }
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index 6882963..85e59de 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -119,16 +119,19 @@
@Composable
private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) {
- val packageInfo = packageInfoPresenter.flow.collectAsStateWithLifecycle().value ?:return
- val app = checkNotNull(packageInfo.applicationInfo)
+ val packageInfoState = packageInfoPresenter.flow.collectAsStateWithLifecycle()
val featureFlags: FeatureFlags = FeatureFlagsImpl()
RegularScaffold(
title = stringResource(R.string.application_info_label),
actions = {
- if (featureFlags.archiving()) TopBarAppLaunchButton(packageInfoPresenter, app)
- AppInfoSettingsMoreOptions(packageInfoPresenter, app)
+ packageInfoState.value?.applicationInfo?.let { app ->
+ if (featureFlags.archiving()) TopBarAppLaunchButton(packageInfoPresenter, app)
+ AppInfoSettingsMoreOptions(packageInfoPresenter, app)
+ }
}
) {
+ val packageInfo = packageInfoState.value ?: return@RegularScaffold
+ val app = packageInfo.applicationInfo ?: return@RegularScaffold
val appInfoProvider = remember(packageInfo) { AppInfoProvider(packageInfo) }
appInfoProvider.AppInfo()
diff --git a/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt b/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
index 6b3535b..5f6f097 100644
--- a/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
@@ -23,8 +23,10 @@
import android.os.UserHandle
import android.os.UserManager
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAdd
@@ -33,6 +35,9 @@
import com.android.settingslib.spaprivileged.model.app.hasFlag
import com.android.settingslib.spaprivileged.model.app.isActiveAdmin
import com.android.settingslib.spaprivileged.model.app.userHandle
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) {
private val context = packageInfoPresenter.context
@@ -43,7 +48,7 @@
@Composable
fun getActionButton(app: ApplicationInfo): ActionButton? {
if (app.isSystemApp || app.isInstantApp) return null
- return uninstallButton(app = app, enabled = isUninstallButtonEnabled(app))
+ return uninstallButton(app)
}
/** Gets whether a package can be uninstalled. */
@@ -90,11 +95,15 @@
overlayManager.getOverlayInfo(packageName, userHandle)?.isEnabled == true
@Composable
- private fun uninstallButton(app: ApplicationInfo, enabled: Boolean) = ActionButton(
+ private fun uninstallButton(app: ApplicationInfo) = ActionButton(
text = if (isCloneApp(app)) context.getString(R.string.delete) else
context.getString(R.string.uninstall_text),
imageVector = ImageVector.vectorResource(R.drawable.ic_settings_delete),
- enabled = enabled,
+ enabled = remember(app) {
+ flow {
+ emit(isUninstallButtonEnabled(app))
+ }.flowOn(Dispatchers.Default)
+ }.collectAsStateWithLifecycle(false).value,
) { onUninstallClicked(app) }
private fun onUninstallClicked(app: ApplicationInfo) {
diff --git a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
index 8d0f0bb..a6bd8f0 100644
--- a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
+++ b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager
import android.os.UserHandle
import android.util.Log
+import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settings.spa.app.startUninstallActivity
@@ -40,6 +41,7 @@
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@@ -65,18 +67,42 @@
val userContext by lazy { context.asUser(userHandle) }
val userPackageManager: PackageManager by lazy { userContext.packageManager }
- val flow: StateFlow<PackageInfo?> = merge(
- flowOf(null), // kick an initial value
- context.broadcastReceiverAsUserFlow(
- intentFilter = IntentFilter().apply {
- addAction(Intent.ACTION_PACKAGE_CHANGED)
- addAction(Intent.ACTION_PACKAGE_REPLACED)
- addAction(Intent.ACTION_PACKAGE_RESTARTED)
- addDataScheme("package")
- },
- userHandle = userHandle,
- ),
- ).map { getPackageInfo() }
+ private val appChangeFlow = context.broadcastReceiverAsUserFlow(
+ intentFilter = IntentFilter().apply {
+ // App enabled / disabled
+ addAction(Intent.ACTION_PACKAGE_CHANGED)
+
+ // App archived
+ addAction(Intent.ACTION_PACKAGE_REMOVED)
+
+ // App updated / the updates are uninstalled (system app)
+ addAction(Intent.ACTION_PACKAGE_REPLACED)
+
+ // App force-stopped
+ addAction(Intent.ACTION_PACKAGE_RESTARTED)
+
+ addDataScheme("package")
+ },
+ userHandle = userHandle,
+ ).filter(::isInterestedAppChange).filter(::isForThisApp)
+
+ @VisibleForTesting
+ fun isInterestedAppChange(intent: Intent) = when {
+ intent.action != Intent.ACTION_PACKAGE_REMOVED -> true
+
+ // filter out the fully removed case, in which the page will be closed, so no need to
+ // refresh
+ intent.getBooleanExtra(Intent.EXTRA_DATA_REMOVED, false) -> false
+
+ // filter out the updates are uninstalled (system app), which will followed by a replacing
+ // broadcast, we can refresh at that time
+ intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) -> false
+
+ else -> true // App archived
+ }
+
+ val flow: StateFlow<PackageInfo?> = merge(flowOf(null), appChangeFlow)
+ .map { getPackageInfo() }
.stateIn(coroutineScope + Dispatchers.Default, SharingStarted.Eagerly, null)
/**
@@ -89,12 +115,14 @@
}
val navController = LocalNavController.current
DisposableBroadcastReceiverAsUser(intentFilter, userHandle) { intent ->
- if (packageName == intent.data?.schemeSpecificPart) {
+ if (isForThisApp(intent)) {
navController.navigateBack()
}
}
}
+ private fun isForThisApp(intent: Intent) = packageName == intent.data?.schemeSpecificPart
+
/** Enables this package. */
fun enable() {
logAction(SettingsEnums.ACTION_SETTINGS_ENABLE_APP)
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index 6d3c748..95a78c7 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -50,7 +50,7 @@
"src/**/*.kt",
],
- test_suites: ["general-tests"],
+ // test_suites attribute is not needed. This module will be configured in ATP GCL file.
static_libs: [
"Robolectric_shadows_androidx_fragment_upstream",
diff --git a/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java
index a30dbff..d3f5457 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java
@@ -16,10 +16,16 @@
package com.android.settings.accessibility;
+import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.core.app.ApplicationProvider;
@@ -27,6 +33,7 @@
import com.android.settings.testutils.XmlTestUtils;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -37,6 +44,9 @@
@RunWith(RobolectricTestRunner.class)
public class ColorAndMotionFragmentTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private final Context mContext = ApplicationProvider.getApplicationContext();
private ColorAndMotionFragment mFragment;
@@ -63,6 +73,20 @@
}
@Test
+ @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR)
+ public void forceInvertEnabled_getNonIndexableKeys_existInXmlLayout() {
+ final List<String> niks = ColorAndMotionFragment.SEARCH_INDEX_DATA_PROVIDER
+ .getNonIndexableKeys(mContext);
+ final List<String> keys =
+ XmlTestUtils.getKeysFromPreferenceXml(mContext,
+ R.xml.accessibility_color_and_motion);
+
+ assertThat(niks).doesNotContain(ColorAndMotionFragment.TOGGLE_FORCE_INVERT);
+ assertThat(keys).containsAtLeastElementsIn(niks);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_FORCE_INVERT_COLOR)
public void getNonIndexableKeys_existInXmlLayout() {
final List<String> niks = ColorAndMotionFragment.SEARCH_INDEX_DATA_PROVIDER
.getNonIndexableKeys(mContext);
@@ -70,6 +94,7 @@
XmlTestUtils.getKeysFromPreferenceXml(mContext,
R.xml.accessibility_color_and_motion);
+ assertThat(niks).contains(ColorAndMotionFragment.TOGGLE_FORCE_INVERT);
assertThat(keys).containsAtLeastElementsIn(niks);
}
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleForceInvertPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleForceInvertPreferenceControllerTest.java
new file mode 100644
index 0000000..d8197ef
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleForceInvertPreferenceControllerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link ToggleForceInvertPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class ToggleForceInvertPreferenceControllerTest {
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private ToggleForceInvertPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mController = new ToggleForceInvertPreferenceController(
+ mContext,
+ ColorAndMotionFragment.TOGGLE_FORCE_INVERT
+ );
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_FORCE_INVERT_COLOR)
+ public void flagOff_getAvailabilityStatus_shouldReturnUnsupported() {
+ assertThat(mController.getAvailabilityStatus())
+ .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR)
+ public void flagOn_getAvailabilityStatus_shouldReturnAvailable() {
+ assertThat(mController.getAvailabilityStatus())
+ .isEqualTo(BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void settingOff_reflectsCorrectValue() {
+ setEnabled(false);
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void settingOn_reflectsCorrectValue() {
+ setEnabled(true);
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void onCheck_settingChanges() {
+ setEnabled(false);
+
+ mController.setChecked(true);
+ assertThat(isEnabled()).isTrue();
+
+ mController.setChecked(false);
+ assertThat(isEnabled()).isFalse();
+ }
+
+ private boolean isEnabled() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, /* def=*/ -1) == ON;
+ }
+
+ private void setEnabled(boolean enabled) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, enabled ? ON : OFF);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java b/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java
index b6ef32c..17f17c1 100644
--- a/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java
+++ b/tests/robotests/src/com/android/settings/backup/BackupSettingsHelperTest.java
@@ -34,7 +34,6 @@
import android.content.res.Resources;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import com.android.settings.R;
@@ -48,8 +47,6 @@
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowUserManager;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = BackupSettingsHelperTest.ShadowBackupManagerStub.class)
@@ -72,46 +69,12 @@
@Mock
private static IBackupManager mBackupManager;
- private ShadowUserManager mUserManager;
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application.getApplicationContext());
when(mBackupManager.getCurrentTransport()).thenReturn("test_transport");
mBackupSettingsHelper = new BackupSettingsHelper(mContext);
- mUserManager = Shadow.extract(mContext.getSystemService(Context.USER_SERVICE));
- }
-
- @Test
- public void testGetSummary_backupEnabledOnlyOneProfile_showsOn() throws Exception {
- mUserManager.addUserProfile(new UserHandle(0));
- when(mBackupManager.isBackupEnabled()).thenReturn(true);
-
- String backupSummary = mBackupSettingsHelper.getSummary();
-
- assertThat(backupSummary).isEqualTo(mContext.getString(R.string.backup_summary_state_on));
- }
-
- @Test
- public void testGetSummary_backupDisabledOnlyOneProfile_showsOff() throws Exception {
- mUserManager.addUserProfile(new UserHandle(0));
- when(mBackupManager.isBackupEnabled()).thenReturn(false);
-
- String backupSummary = mBackupSettingsHelper.getSummary();
-
- assertThat(backupSummary).isEqualTo(mContext.getString(R.string.backup_summary_state_off));
- }
-
- @Test
- public void testGetSummary_TwoProfiles_returnsNull() throws Exception {
- mUserManager.addUserProfile(new UserHandle(0));
- mUserManager.addUserProfile(new UserHandle(10));
- when(mBackupManager.isBackupEnabled()).thenReturn(true);
-
- String backupSummary = mBackupSettingsHelper.getSummary();
-
- assertThat(backupSummary).isNull();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/biometrics/BiometricSettingsProviderTest.kt b/tests/robotests/src/com/android/settings/biometrics/BiometricSettingsProviderTest.kt
index cdad338..152258d 100644
--- a/tests/robotests/src/com/android/settings/biometrics/BiometricSettingsProviderTest.kt
+++ b/tests/robotests/src/com/android/settings/biometrics/BiometricSettingsProviderTest.kt
@@ -26,7 +26,6 @@
import com.android.settings.flags.Flags
import com.google.common.truth.Truth.assertThat
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,7 +45,9 @@
@Before
fun setUp() {
+ whenever(context.resources).thenReturn(resources)
provider = BiometricSettingsProvider()
+ provider.attachInfo(context, null)
}
@Test(expected = UnsupportedOperationException::class)
@@ -74,16 +75,14 @@
provider.update(Uri.EMPTY, null, null, null)
}
- @Ignore("b/313342682")
@Test
@RequiresFlagsEnabled(Flags.FLAG_BIOMETRIC_SETTINGS_PROVIDER)
fun getSuggestionState_shouldQueryFeatureProvider() {
val expectedValue = false
setSupportFaceEnroll(expectedValue)
- val bundle =
- provider.call(BiometricSettingsProvider.GET_SUW_FACE_ENABLED, null, Bundle())
- assertThat(bundle!!.getString(BiometricSettingsProvider.SUW_FACE_ENABLED))
+ val bundle = provider.call(BiometricSettingsProvider.GET_SUW_FACE_ENABLED, null, Bundle())
+ assertThat(bundle!!.getBoolean(BiometricSettingsProvider.SUW_FACE_ENABLED))
.isEqualTo(expectedValue)
}
diff --git a/tests/robotests/src/com/android/settings/development/SelectDebugAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/SelectDebugAppPreferenceControllerTest.java
index a998ddc..94fce17 100644
--- a/tests/robotests/src/com/android/settings/development/SelectDebugAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/SelectDebugAppPreferenceControllerTest.java
@@ -36,6 +36,7 @@
import androidx.preference.PreferenceScreen;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -71,6 +72,7 @@
mController.displayPreference(mPreferenceScreen);
}
+ @Ignore("b/314919354")
@Test
public void handlePreferenceTreeClick_preferenceClicked_launchActivity() {
final Intent activityStartIntent = new Intent(mContext, AppPicker.class);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
index 118c3ed..df330a3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
@@ -35,6 +35,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -128,6 +129,7 @@
assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
}
+ @Ignore("b/314921894")
@Test
public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob()
throws InterruptedException {
diff --git a/tests/robotests/src/com/android/settings/shortcut/CreateShortcutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/shortcut/CreateShortcutPreferenceControllerTest.java
index 3705ba8..9727dd1 100644
--- a/tests/robotests/src/com/android/settings/shortcut/CreateShortcutPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/shortcut/CreateShortcutPreferenceControllerTest.java
@@ -116,6 +116,7 @@
.isEqualTo(SHORTCUT_ID_PREFIX + intent.getComponent().flattenToShortString());
}
+ @Ignore("b/314924127")
@Test
public void queryShortcuts_shouldOnlyIncludeSystemApp() {
final ResolveInfo ri1 = new ResolveInfo();
@@ -139,6 +140,7 @@
assertThat(info.get(0).activityInfo).isEqualTo(ri2.activityInfo);
}
+ @Ignore("b/314924127")
@Test
public void queryShortcuts_shouldSortBasedOnPriority() {
final ResolveInfo ri1 = new ResolveInfo();
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index 57f4e17..84c8586 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -64,6 +64,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -200,6 +201,7 @@
.registerIntentToUri(eq(FakeToggleController.INTENT_FILTER), eq(INTENT_SLICE_URI));
}
+ @Ignore("b/314925256")
@Test
public void loadSlice_registersBackgroundListener() {
SliceTestUtils.insertSliceToDb(mContext, KEY);
diff --git a/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java b/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java
index f510994..16df9ba 100644
--- a/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java
+++ b/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java
@@ -29,6 +29,7 @@
import com.android.settingslib.search.SearchIndexableRaw;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -64,6 +65,7 @@
assertThat(value.intentAction).isEqualTo(ACTION_SUPPORT_SETTINGS);
}
+ @Ignore("b/314927625")
@Test
public void shouldHandleIntentAction() {
PackageManager packageManager = mContext.getPackageManager();
diff --git a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
index 8f43268..635343e 100644
--- a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
+++ b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java
@@ -13,6 +13,7 @@
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -39,6 +40,7 @@
mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider(mContext);
}
+ @Ignore("b/314929422")
@Test
public void onResume_noActionIfActivityDoesNotExist() {
// Pretend we are an activity that is starting up
diff --git a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
index 6e6fad8..321fcf5 100644
--- a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
@@ -27,6 +27,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -60,6 +61,7 @@
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_DEMO_MODE, 0);
}
+ @Ignore("b/314930928")
@Test
public void isAvailable_systemUser() {
mShadowUserManager.setIsAdminUser(true);
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
index 5a39368..82aff4e 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
@@ -50,6 +50,7 @@
import com.google.android.setupcompat.util.WizardManagerHelper;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -89,6 +90,7 @@
WifiDialogActivity mActivity;
+ @Ignore("b/314867581")
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java b/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java
index 8e52210..64bf525 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java
@@ -45,6 +45,7 @@
import com.android.settings.R;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -55,6 +56,7 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+@Ignore("b/314867581")
@RunWith(RobolectricTestRunner.class)
public class WifiNoInternetDialogTest {
diff --git a/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
index d8605de..73e4811 100644
--- a/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
@@ -29,6 +29,7 @@
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,6 +40,7 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+@Ignore("b/314867581")
@RunWith(RobolectricTestRunner.class)
public class WifiDppConfiguratorActivityTest {
diff --git a/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java
index 67d4678..c099060 100644
--- a/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/dpp/WifiDppEnrolleeActivityTest.java
@@ -28,6 +28,7 @@
import com.android.settingslib.wifi.WifiRestrictionsCache;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,6 +38,7 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+@Ignore("b/314867581")
@RunWith(RobolectricTestRunner.class)
public class WifiDppEnrolleeActivityTest {
diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
index 3e6c64b..fc0b946 100644
--- a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
@@ -47,6 +47,7 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+@Ignore("b/314867581")
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowInteractionJankMonitor.class)
public class SavedAccessPointsWifiSettings2Test {
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt
index c7371ee..05fce8f 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt
@@ -21,6 +21,7 @@
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.UserHandle
+import android.util.IconDrawableFactory
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
@@ -43,7 +44,9 @@
on { packageManager } doReturn packageManager
}
- private val repository = AppPreferenceRepository(context)
+ private val mockIconDrawableFactory = mock<IconDrawableFactory>()
+
+ private val repository = AppPreferenceRepository(context, mockIconDrawableFactory)
@Test
fun loadAppPreferences_packageNotFound_returnEmpty() {
@@ -61,9 +64,11 @@
@Test
fun loadAppPreferences_packageFound_returnPreference() {
val app = mock<ApplicationInfo> {
- on { loadUnbadgedIcon(any()) } doReturn UNBADGED_ICON
on { loadLabel(any()) } doReturn LABEL
}
+ mockIconDrawableFactory.stub {
+ on { getBadgedIcon(app) } doReturn UNBADGED_ICON
+ }
packageManager.stub {
on {
getApplicationInfoAsUser(PACKAGE_NAME, 0, UserHandle.getUserId(UID))
diff --git a/tests/spa_unit/src/com/android/settings/spa/SpaBridgeActivityTest.kt b/tests/spa_unit/src/com/android/settings/spa/SpaBridgeActivityTest.kt
deleted file mode 100644
index 48fa823..0000000
--- a/tests/spa_unit/src/com/android/settings/spa/SpaBridgeActivityTest.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.spa
-
-import android.app.Activity
-import android.content.ComponentName
-import android.content.pm.ActivityInfo
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ComponentInfoFlags
-import androidx.core.os.bundleOf
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settings.spa.SpaBridgeActivity.Companion.META_DATA_KEY_DESTINATION
-import com.android.settings.spa.SpaBridgeActivity.Companion.getDestination
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.any
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.mock
-
-@RunWith(AndroidJUnit4::class)
-class SpaBridgeActivityTest {
- private val mockPackageManager = mock<PackageManager> {
- on { getActivityInfo(eq(COMPONENT_NAME), any<ComponentInfoFlags>()) } doReturn
- ActivityInfo().apply {
- metaData = bundleOf(META_DATA_KEY_DESTINATION to DESTINATION)
- }
- }
-
- private val activity = mock<Activity> {
- on { componentName } doReturn COMPONENT_NAME
- on { packageManager } doReturn mockPackageManager
- }
-
- @Test
- fun getDestination() {
- val destination = activity.getDestination()
-
- assertThat(destination).isEqualTo(DESTINATION)
- }
-
- private companion object {
- const val PACKAGE_NAME = "package.name"
- const val ACTIVITY_NAME = "ActivityName"
- val COMPONENT_NAME = ComponentName(PACKAGE_NAME, ACTIVITY_NAME)
- const val DESTINATION = "Destination"
- }
-}
diff --git a/tests/spa_unit/src/com/android/settings/spa/SpaDestinationTest.kt b/tests/spa_unit/src/com/android/settings/spa/SpaDestinationTest.kt
new file mode 100644
index 0000000..0b9eb22
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/SpaDestinationTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa
+
+import android.app.Activity
+import android.content.ComponentName
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.os.Bundle
+import androidx.core.os.bundleOf
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.SettingsActivity.META_DATA_KEY_HIGHLIGHT_MENU_KEY
+import com.android.settings.spa.SpaDestination.Companion.META_DATA_KEY_DESTINATION
+import com.android.settings.spa.SpaDestination.Companion.getDestination
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+class SpaDestinationTest {
+ private var activityMetadata: Bundle = bundleOf()
+
+ private val mockPackageManager = mock<PackageManager> {
+ on {
+ getActivityInfo(
+ eq(COMPONENT_NAME),
+ any<PackageManager.ComponentInfoFlags>()
+ )
+ } doAnswer {
+ ActivityInfo().apply { metaData = activityMetadata }
+ }
+ }
+
+ private val activity = mock<Activity> {
+ on { componentName } doReturn COMPONENT_NAME
+ on { packageManager } doReturn mockPackageManager
+ }
+
+ @Test
+ fun getDestination_noDestination_returnNull() {
+ activityMetadata = bundleOf()
+
+ val destination = activity.getDestination()
+
+ assertThat(destination).isNull()
+ }
+
+ @Test
+ fun getDestination_withoutHighlightMenuKey() {
+ activityMetadata = bundleOf(META_DATA_KEY_DESTINATION to DESTINATION)
+
+ val (destination, highlightMenuKey) = activity.getDestination()!!
+
+ assertThat(destination).isEqualTo(DESTINATION)
+ assertThat(highlightMenuKey).isNull()
+ }
+
+ @Test
+ fun getDestination_withHighlightMenuKey() {
+ activityMetadata = bundleOf(
+ META_DATA_KEY_DESTINATION to DESTINATION,
+ META_DATA_KEY_HIGHLIGHT_MENU_KEY to HIGHLIGHT_MENU_KEY,
+ )
+
+ val (destination, highlightMenuKey) = activity.getDestination()!!
+
+ assertThat(destination).isEqualTo(DESTINATION)
+ assertThat(highlightMenuKey).isEqualTo(HIGHLIGHT_MENU_KEY)
+ }
+
+ private companion object {
+ const val PACKAGE_NAME = "package.name"
+ const val ACTIVITY_NAME = "ActivityName"
+ val COMPONENT_NAME = ComponentName(PACKAGE_NAME, ACTIVITY_NAME)
+ const val DESTINATION = "Destination"
+ const val HIGHLIGHT_MENU_KEY = "apps"
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt
index ecb540c..d81bb1a 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt
@@ -20,7 +20,9 @@
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageInfo
import android.content.pm.PackageManager
+import android.net.Uri
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.testutils.FakeFeatureFactory
@@ -61,11 +63,57 @@
private val fakeFeatureFactory = FakeFeatureFactory()
private val metricsFeatureProvider = fakeFeatureFactory.metricsFeatureProvider
+ private val packageInfoPresenter =
+ PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
+
+ @Test
+ fun isInterestedAppChange_packageChanged_isInterested() {
+ val intent = Intent(Intent.ACTION_PACKAGE_CHANGED).apply {
+ data = Uri.parse("package:$PACKAGE_NAME")
+ }
+
+ val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
+
+ assertThat(isInterestedAppChange).isTrue()
+ }
+
+ @Test
+ fun isInterestedAppChange_fullyRemoved_notInterested() {
+ val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
+ data = Uri.parse("package:$PACKAGE_NAME")
+ putExtra(Intent.EXTRA_DATA_REMOVED, true)
+ }
+
+ val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
+
+ assertThat(isInterestedAppChange).isFalse()
+ }
+
+ @Test
+ fun isInterestedAppChange_removedBeforeReplacing_notInterested() {
+ val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
+ data = Uri.parse("package:$PACKAGE_NAME")
+ putExtra(Intent.EXTRA_REPLACING, true)
+ }
+
+ val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
+
+ assertThat(isInterestedAppChange).isFalse()
+ }
+
+ @Test
+ fun isInterestedAppChange_archived_interested() {
+ val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
+ data = Uri.parse("package:$PACKAGE_NAME")
+ }
+
+ val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
+
+ assertThat(isInterestedAppChange).isTrue()
+ }
+
@Test
fun enable() = runBlocking {
- val packageInfoPresenter =
- PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
packageInfoPresenter.enable()
delay(100)
@@ -77,9 +125,6 @@
@Test
fun disable() = runBlocking {
- val packageInfoPresenter =
- PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
packageInfoPresenter.disable()
delay(100)
@@ -91,9 +136,6 @@
@Test
fun startUninstallActivity() = runBlocking {
- val packageInfoPresenter =
- PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
packageInfoPresenter.startUninstallActivity()
verifyAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP)
@@ -109,9 +151,6 @@
@Test
fun clearInstantApp() = runBlocking {
- val packageInfoPresenter =
- PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
packageInfoPresenter.clearInstantApp()
delay(100)
@@ -121,9 +160,6 @@
@Test
fun forceStop() = runBlocking {
- val packageInfoPresenter =
- PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
packageInfoPresenter.forceStop()
delay(100)
@@ -133,9 +169,6 @@
@Test
fun logAction() = runBlocking {
- val packageInfoPresenter =
- PackageInfoPresenter(context, PACKAGE_NAME, USER_ID, TestScope(), packageManagers)
-
packageInfoPresenter.logAction(123)
verifyAction(123)
@@ -148,5 +181,6 @@
private companion object {
const val PACKAGE_NAME = "package.name"
const val USER_ID = 0
+ val PACKAGE_INFO = PackageInfo()
}
}