Snap for 10078820 from 17a0266b16 to udc-release

Change-Id: I9e5dab9eaa426371cdc64e4e517846863d1cafe8
This commit is contained in:
Android Build Coastguard Worker
2023-05-06 01:32:32 +00:00
21 changed files with 431 additions and 116 deletions

View File

@@ -17,9 +17,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z"
android:pathData="M140,840Q116,840 98,822Q80,804 80,780L80,300Q80,276 98,258Q116,240 140,240L320,240L320,140Q320,116 338,98Q356,80 380,80L580,80Q604,80 622,98Q640,116 640,140L640,240L820,240Q844,240 862,258Q880,276 880,300L880,780Q880,804 862,822Q844,840 820,840L140,840ZM380,240L580,240L580,140Q580,140 580,140Q580,140 580,140L380,140Q380,140 380,140Q380,140 380,140L380,240Z"
android:fillColor="?android:attr/colorPrimary"/>
</vector>

View File

@@ -0,0 +1,26 @@
<!--
~ 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.
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path android:fillColor="@android:color/white"
android:pathData="M167,840Q146,845 130.5,829.5Q115,814 120,793L160,602L358,800L167,840ZM358,800L160,602L618,144Q641,121 675,121Q709,121 732,144L816,228Q839,251 839,285Q839,319 816,342L358,800ZM675,200L261,614L346,699L760,285Q760,285 760,285Q760,285 760,285L675,200Q675,200 675,200Q675,200 675,200Z"/>
</vector>

View File

@@ -43,18 +43,22 @@ public final class CombinedProviderInfo {
private final List<CredentialProviderInfo> mCredentialProviderInfos;
private final @Nullable AutofillServiceInfo mAutofillServiceInfo;
private final boolean mIsDefaultAutofillProvider;
private final boolean mIsDefaultCredmanProvider;
private final boolean mIsPrimaryCredmanProvider;
/** Constructs an information instance from both autofill and credential provider. */
public CombinedProviderInfo(
@Nullable List<CredentialProviderInfo> cpis,
@Nullable AutofillServiceInfo asi,
boolean isDefaultAutofillProvider,
boolean isDefaultCredmanProvider) {
mCredentialProviderInfos = new ArrayList<>(cpis);
boolean IsPrimaryCredmanProvider) {
if (cpis == null) {
mCredentialProviderInfos = new ArrayList<>();
} else {
mCredentialProviderInfos = new ArrayList<>(cpis);
}
mAutofillServiceInfo = asi;
mIsDefaultAutofillProvider = isDefaultAutofillProvider;
mIsDefaultCredmanProvider = isDefaultCredmanProvider;
mIsPrimaryCredmanProvider = IsPrimaryCredmanProvider;
}
/** Returns the credential provider info. */
@@ -149,8 +153,8 @@ public final class CombinedProviderInfo {
}
/** Returns whether the provider is the default credman provider. */
public boolean isDefaultCredmanProvider() {
return mIsDefaultCredmanProvider;
public boolean isPrimaryCredmanProvider() {
return mIsPrimaryCredmanProvider;
}
/** Returns the settings subtitle. */
@@ -192,7 +196,13 @@ public final class CombinedProviderInfo {
}
}
// TODO(280454916): Add logic here.
// If there is a primary cred man provider then return that.
for (CombinedProviderInfo cpi : providers) {
if (cpi.isPrimaryCredmanProvider()) {
return cpi;
}
}
return null;
}
@@ -250,14 +260,14 @@ public final class CombinedProviderInfo {
}
// Check if we have any enabled cred man services.
boolean isDefaultCredmanProvider = false;
if (!cpi.isEmpty()) {
isDefaultCredmanProvider = cpi.get(0).isEnabled();
boolean isPrimaryCredmanProvider = false;
if (cpi != null && !cpi.isEmpty()) {
isPrimaryCredmanProvider = cpi.get(0).isPrimary();
}
cmpi.add(
new CombinedProviderInfo(
cpi, selectedAsi, isDefaultAutofillProvider, isDefaultCredmanProvider));
cpi, selectedAsi, isDefaultAutofillProvider, isPrimaryCredmanProvider));
}
return cmpi;

View File

@@ -120,6 +120,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
mCredentialManager =
getCredentialManager(context, preferenceKey.equals("credentials_test"));
new SettingContentObserver(mHandler).register(context.getContentResolver());
mSettingsPackageMonitor.register(context, context.getMainLooper(), false);
}
private @Nullable CredentialManager getCredentialManager(Context context, boolean isTest) {
@@ -321,7 +322,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
mEnabledPackageNames.clear();
for (CredentialProviderInfo cpi : availableServices) {
if (cpi.isEnabled()) {
if (cpi.isEnabled() && !cpi.isPrimary()) {
mEnabledPackageNames.add(cpi.getServiceInfo().packageName);
}
}
@@ -560,15 +561,25 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
return;
}
List<String> enabledServices = getEnabledSettings();
// Get the existing primary providers since we don't touch them in
// this part of the UI we should just copy them over.
Set<String> primaryServices = new HashSet<>();
for (CredentialProviderInfo service : mServices) {
if (service.isPrimary()) {
primaryServices.add(service.getServiceInfo().getComponentName().flattenToString());
}
}
mCredentialManager.setEnabledProviders(
enabledServices,
new ArrayList<>(primaryServices),
getEnabledSettings(),
getUser(),
mExecutor,
new OutcomeReceiver<Void, SetEnabledProvidersException>() {
@Override
public void onResult(Void result) {
Log.i(TAG, "setEnabledProviders success");
updateFromExternal();
}
@Override

View File

@@ -47,7 +47,9 @@ import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.CandidateInfo;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DefaultCombinedPicker extends DefaultAppPickerFragment {
@@ -338,9 +340,9 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment {
return true;
}
private void setProviders(String autofillProvider, List<String> credManProviders) {
private void setProviders(String autofillProvider, List<String> primaryCredManProviders) {
if (TextUtils.isEmpty(autofillProvider)) {
if (credManProviders.size() > 0) {
if (primaryCredManProviders.size() > 0) {
autofillProvider =
CredentialManagerPreferenceController
.AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER;
@@ -350,12 +352,25 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment {
Settings.Secure.putStringForUser(
getContext().getContentResolver(), AUTOFILL_SETTING, autofillProvider, mUserId);
CredentialManager service = getCredentialProviderService();
final CredentialManager service = getCredentialProviderService();
if (service == null) {
return;
}
// Get the existing secondary providers since we don't touch them in
// this part of the UI we should just copy them over.
final List<String> credManProviders = new ArrayList<>();
for (CredentialProviderInfo cpi :
service.getCredentialProviderServices(
mUserId, CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)) {
if (cpi.isEnabled()) {
credManProviders.add(cpi.getServiceInfo().getComponentName().flattenToString());
}
}
service.setEnabledProviders(
primaryCredManProviders,
credManProviders,
mUserId,
ContextCompat.getMainExecutor(getContext()),

View File

@@ -135,12 +135,12 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
/** Provides Intent to setting activity for the specified autofill service. */
static final class AutofillSettingIntentProvider {
private final String mSelectedKey;
private final String mKey;
private final Context mContext;
private final int mUserId;
public AutofillSettingIntentProvider(Context context, int userId, String key) {
mSelectedKey = key;
mKey = key;
mContext = context;
mUserId = userId;
}
@@ -153,10 +153,9 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
for (ResolveInfo resolveInfo : resolveInfos) {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
final String flattenKey =
new ComponentName(serviceInfo.packageName, serviceInfo.name)
.flattenToString();
if (TextUtils.equals(mSelectedKey, flattenKey)) {
// If there are multiple autofill services then pick the first one.
if (mKey.startsWith(serviceInfo.packageName)) {
final String settingsActivity;
try {
settingsActivity =
@@ -164,7 +163,7 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
.getSettingsActivity();
} catch (SecurityException e) {
// Service does not declare the proper permission, ignore it.
Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e);
Log.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
return null;
}
if (TextUtils.isEmpty(settingsActivity)) {

View File

@@ -72,6 +72,7 @@ public class RequestPermissionActivity extends Activity implements
private int mRequest;
private AlertDialog mDialog;
private AlertDialog mRequestDialog;
private BroadcastReceiver mReceiver;
@@ -96,33 +97,35 @@ public class RequestPermissionActivity extends Activity implements
if (mRequest == REQUEST_DISABLE) {
switch (btState) {
case BluetoothAdapter.STATE_OFF:
case BluetoothAdapter.STATE_TURNING_OFF: {
case BluetoothAdapter.STATE_TURNING_OFF:
proceedAndFinish();
} break;
break;
case BluetoothAdapter.STATE_ON:
case BluetoothAdapter.STATE_TURNING_ON: {
RequestPermissionHelper.INSTANCE.requestDisable(this, mAppLabel,
() -> {
onDisableConfirmed();
return Unit.INSTANCE;
},
() -> {
cancelAndFinish();
return Unit.INSTANCE;
});
} break;
default: {
case BluetoothAdapter.STATE_TURNING_ON:
mRequestDialog =
RequestPermissionHelper.INSTANCE.requestDisable(this, mAppLabel,
() -> {
onDisableConfirmed();
return Unit.INSTANCE;
},
() -> {
cancelAndFinish();
return Unit.INSTANCE;
});
if (mRequestDialog != null) {
mRequestDialog.show();
}
break;
default:
Log.e(TAG, "Unknown adapter state: " + btState);
cancelAndFinish();
} break;
break;
}
} else {
switch (btState) {
case BluetoothAdapter.STATE_OFF:
case BluetoothAdapter.STATE_TURNING_OFF:
case BluetoothAdapter.STATE_TURNING_ON: {
case BluetoothAdapter.STATE_TURNING_ON:
/*
* Strictly speaking STATE_TURNING_ON belong with STATE_ON;
* however, BT may not be ready when the user clicks yes and we
@@ -131,20 +134,23 @@ public class RequestPermissionActivity extends Activity implements
* case via the broadcast receiver.
*/
// Start the helper activity to ask the user about enabling bt AND discovery
RequestPermissionHelper.INSTANCE.requestEnable(this, mAppLabel,
mRequest == REQUEST_ENABLE_DISCOVERABLE ? mTimeout : -1,
() -> {
onEnableConfirmed();
return Unit.INSTANCE;
},
() -> {
cancelAndFinish();
return Unit.INSTANCE;
});
} break;
case BluetoothAdapter.STATE_ON: {
// Show the helper dialog to ask the user about enabling bt AND discovery
mRequestDialog =
RequestPermissionHelper.INSTANCE.requestEnable(this, mAppLabel,
mRequest == REQUEST_ENABLE_DISCOVERABLE ? mTimeout : -1,
() -> {
onEnableConfirmed();
return Unit.INSTANCE;
},
() -> {
cancelAndFinish();
return Unit.INSTANCE;
});
if (mRequestDialog != null) {
mRequestDialog.show();
}
break;
case BluetoothAdapter.STATE_ON:
if (mRequest == REQUEST_ENABLE) {
// Nothing to do. Already enabled.
proceedAndFinish();
@@ -152,12 +158,11 @@ public class RequestPermissionActivity extends Activity implements
// Ask the user about enabling discovery mode
createDialog();
}
} break;
default: {
break;
default:
Log.e(TAG, "Unknown adapter state: " + btState);
cancelAndFinish();
} break;
break;
}
}
}
@@ -275,10 +280,6 @@ public class RequestPermissionActivity extends Activity implements
}
}
if (mDialog != null) {
mDialog.dismiss();
}
setResult(returnCode);
finish();
}
@@ -365,6 +366,14 @@ public class RequestPermissionActivity extends Activity implements
unregisterReceiver(mReceiver);
mReceiver = null;
}
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
mDialog = null;
}
if (mRequestDialog != null && mRequestDialog.isShowing()) {
mRequestDialog.dismiss();
mRequestDialog = null;
}
}
@Override

View File

@@ -30,20 +30,20 @@ object RequestPermissionHelper {
timeout: Int,
onAllow: () -> Unit,
onDeny: () -> Unit,
) {
): AlertDialog? {
if (context.resources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog)) {
// Don't even show the dialog if configured this way
onAllow()
return
return null
}
AlertDialog.Builder(context).apply {
return AlertDialog.Builder(context).apply {
setMessage(context.getEnableMessage(timeout, appLabel))
setPositiveButton(R.string.allow) { _, _ ->
if (context.isDisallowBluetooth()) onDeny() else onAllow()
}
setNegativeButton(R.string.deny) { _, _ -> onDeny() }
setOnCancelListener { onDeny() }
}.show()
}.create()
}
fun requestDisable(
@@ -51,18 +51,18 @@ object RequestPermissionHelper {
appLabel: CharSequence?,
onAllow: () -> Unit,
onDeny: () -> Unit,
) {
): AlertDialog? {
if (context.resources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog)) {
// Don't even show the dialog if configured this way
onAllow()
return
return null
}
AlertDialog.Builder(context).apply {
return AlertDialog.Builder(context).apply {
setMessage(context.getDisableMessage(appLabel))
setPositiveButton(R.string.allow) { _, _ -> onAllow() }
setNegativeButton(R.string.deny) { _, _ -> onDeny() }
setOnCancelListener { onDeny() }
}.show()
}.create()
}
}

View File

@@ -165,8 +165,7 @@ public class StylusDeviceUpdater implements InputManager.InputDeviceListener,
}
mUsiPreference.setKey(PREF_KEY);
mUsiPreference.setTitle(R.string.stylus_connected_devices_title);
// TODO(b/250909304): pending actual icon visD
mUsiPreference.setIcon(R.drawable.ic_edit);
mUsiPreference.setIcon(R.drawable.ic_stylus);
mUsiPreference.setOnPreferenceClickListener((Preference p) -> {
mMetricsFeatureProvider.logClickedPreference(p, mFragment.getMetricsCategory());
launchDeviceDetails();

View File

@@ -70,8 +70,7 @@ public class StylusUsiHeaderController extends BasePreferenceController implemen
ImageView iconView = mHeaderPreference.findViewById(R.id.entity_header_icon);
if (iconView != null) {
// TODO(b/250909304): get proper icon once VisD ready
iconView.setImageResource(R.drawable.ic_edit);
iconView.setImageResource(R.drawable.ic_stylus);
iconView.setContentDescription("Icon for stylus");
}
refresh();

View File

@@ -30,12 +30,12 @@ public class TrackpadReverseScrollingPreferenceController extends TogglePreferen
@Override
public boolean isChecked() {
return InputSettings.useTouchpadNaturalScrolling(mContext);
return !InputSettings.useTouchpadNaturalScrolling(mContext);
}
@Override
public boolean setChecked(boolean isChecked) {
InputSettings.setTouchpadNaturalScrolling(mContext, isChecked);
InputSettings.setTouchpadNaturalScrolling(mContext, !isChecked);
return true;
}

View File

@@ -22,14 +22,34 @@ import android.content.Intent
import android.os.RemoteException
import android.os.UserHandle
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settingslib.spa.framework.BrowseActivity
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.util.SESSION_BROWSE
import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL
import com.android.settingslib.spa.framework.util.appendSpaParams
import com.google.android.setupcompat.util.WizardManagerHelper
class SpaActivity : BrowseActivity() {
override fun isPageEnabled(page: SettingsPage) =
super.isPageEnabled(page) && !isSuwAndPageBlocked(page.sppName)
companion object {
private const val TAG = "SpaActivity"
/** The pages that blocked from SUW. */
private val SuwBlockedPages = setOf(AppInfoSettingsProvider.name)
@VisibleForTesting
fun Context.isSuwAndPageBlocked(name: String): Boolean =
if (name in SuwBlockedPages && !WizardManagerHelper.isDeviceProvisioned(this)) {
Log.w(TAG, "$name blocked before SUW completed.");
true
} else {
false
}
@JvmStatic
fun Context.startSpaActivity(destination: String) {
val intent = Intent(this, SpaActivity::class.java)

View File

@@ -19,12 +19,14 @@ package com.android.settings.spa.app.specialaccess
import android.Manifest
import android.app.AlarmManager
import android.app.compat.CompatChanges
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.PowerExemptionManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import com.android.settings.R
import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
@@ -85,6 +87,17 @@ class AlarmsAndRemindersAppListModel(
override fun setAllowed(record: AlarmsAndRemindersAppRecord, newAllowed: Boolean) {
record.controller.setAllowed(newAllowed)
logPermissionChange(newAllowed)
}
private fun logPermissionChange(newAllowed: Boolean) {
FeatureFactory.getFactory(context).metricsFeatureProvider.action(
SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_ALARMS_AND_REMINDERS_TOGGLE,
SettingsEnums.ALARMS_AND_REMINDERS,
"",
if (newAllowed) 1 else 0
)
}
private fun createRecord(

View File

@@ -18,9 +18,12 @@ package com.android.settings.spa.app.specialaccess
import android.Manifest
import android.app.AppOpsManager
import android.app.settings.SettingsEnums
import android.content.Context
import com.android.settings.R
import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
object AllFilesAccessAppListProvider : TogglePermissionAppListProvider {
@@ -35,4 +38,17 @@ class AllFilesAccessListModel(context: Context) : AppOpPermissionListModel(conte
override val appOp = AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE
override val permission = Manifest.permission.MANAGE_EXTERNAL_STORAGE
override val setModeByUid = true
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
super.setAllowed(record, newAllowed)
logPermissionChange(newAllowed)
}
private fun logPermissionChange(newAllowed: Boolean) {
val category = when {
newAllowed -> SettingsEnums.APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_ALLOW
else -> SettingsEnums.APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_DENY
}
FeatureFactory.getFactory(context).metricsFeatureProvider.action(context, category, "")
}
}

View File

@@ -18,9 +18,12 @@ package com.android.settings.spa.app.specialaccess
import android.Manifest
import android.app.AppOpsManager
import android.app.settings.SettingsEnums
import android.content.Context
import com.android.settings.R
import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
object DisplayOverOtherAppsAppListProvider : TogglePermissionAppListProvider {
@@ -34,4 +37,17 @@ class DisplayOverOtherAppsListModel(context: Context) : AppOpPermissionListModel
override val footerResId = R.string.allow_overlay_description
override val appOp = AppOpsManager.OP_SYSTEM_ALERT_WINDOW
override val permission = Manifest.permission.SYSTEM_ALERT_WINDOW
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
super.setAllowed(record, newAllowed)
logPermissionChange(newAllowed)
}
private fun logPermissionChange(newAllowed: Boolean) {
val category = when {
newAllowed -> SettingsEnums.APP_SPECIAL_PERMISSION_APPDRAW_ALLOW
else -> SettingsEnums.APP_SPECIAL_PERMISSION_APPDRAW_DENY
}
FeatureFactory.getFactory(context).metricsFeatureProvider.action(context, category, "")
}
}

View File

@@ -18,9 +18,12 @@ package com.android.settings.spa.app.specialaccess
import android.Manifest
import android.app.AppOpsManager
import android.app.settings.SettingsEnums
import android.content.Context
import com.android.settings.R
import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
object MediaManagementAppsAppListProvider : TogglePermissionAppListProvider {
@@ -35,4 +38,19 @@ class MediaManagementAppsListModel(context: Context) : AppOpPermissionListModel(
override val appOp = AppOpsManager.OP_MANAGE_MEDIA
override val permission = Manifest.permission.MANAGE_MEDIA
override val setModeByUid = true
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
super.setAllowed(record, newAllowed)
logPermissionChange(newAllowed)
}
private fun logPermissionChange(newAllowed: Boolean) {
FeatureFactory.getFactory(context).metricsFeatureProvider.action(
SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_MEDIA_MANAGEMENT_APPS_TOGGLE,
SettingsEnums.MEDIA_MANAGEMENT_APPS,
"",
if (newAllowed) 1 else 0
)
}
}

View File

@@ -18,9 +18,12 @@ package com.android.settings.spa.app.specialaccess
import android.Manifest
import android.app.AppOpsManager
import android.app.settings.SettingsEnums
import android.content.Context
import com.android.settings.R
import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
object ModifySystemSettingsAppListProvider : TogglePermissionAppListProvider {
@@ -34,4 +37,17 @@ class ModifySystemSettingsListModel(context: Context) : AppOpPermissionListModel
override val footerResId = R.string.write_settings_description
override val appOp = AppOpsManager.OP_WRITE_SETTINGS
override val permission = Manifest.permission.WRITE_SETTINGS
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
super.setAllowed(record, newAllowed)
logPermissionChange(newAllowed)
}
private fun logPermissionChange(newAllowed: Boolean) {
val category = when {
newAllowed -> SettingsEnums.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW
else -> SettingsEnums.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY
}
FeatureFactory.getFactory(context).metricsFeatureProvider.action(context, category, "")
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.bluetooth
import android.bluetooth.BluetoothAdapter
import android.content.Intent
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.android.controller.ActivityController
import org.robolectric.annotation.Config
import org.robolectric.shadow.api.Shadow
import org.robolectric.shadows.ShadowBluetoothAdapter
@RunWith(RobolectricTestRunner::class)
@Config(shadows = [ShadowAlertDialogCompat::class, ShadowBluetoothAdapter::class])
class RequestPermissionActivityTest {
private lateinit var activityController: ActivityController<RequestPermissionActivity>
private lateinit var bluetoothAdapter: ShadowBluetoothAdapter
@Before
fun setUp() {
bluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter())
}
@After
fun tearDown() {
activityController.pause().stop().destroy()
ShadowAlertDialogCompat.reset()
}
@Test
fun requestEnable_whenBluetoothIsOff_showConfirmDialog() {
bluetoothAdapter.setState(BluetoothAdapter.STATE_OFF)
createActivity(action = BluetoothAdapter.ACTION_REQUEST_ENABLE)
val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
val shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog)
assertThat(shadowDialog.message.toString())
.isEqualTo("An app wants to turn on Bluetooth")
}
@Test
fun requestEnable_whenBluetoothIsOn_doNothing() {
bluetoothAdapter.setState(BluetoothAdapter.STATE_ON)
createActivity(action = BluetoothAdapter.ACTION_REQUEST_ENABLE)
val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
assertThat(dialog).isNull()
}
@Test
fun requestDisable_whenBluetoothIsOff_doNothing() {
bluetoothAdapter.setState(BluetoothAdapter.STATE_OFF)
createActivity(action = BluetoothAdapter.ACTION_REQUEST_DISABLE)
val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
assertThat(dialog).isNull()
}
@Test
fun requestDisable_whenBluetoothIsOn_showConfirmDialog() {
bluetoothAdapter.setState(BluetoothAdapter.STATE_ON)
createActivity(action = BluetoothAdapter.ACTION_REQUEST_DISABLE)
val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
val shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog)
assertThat(shadowDialog.message.toString())
.isEqualTo("An app wants to turn off Bluetooth")
}
private fun createActivity(action: String) {
activityController =
ActivityController.of(RequestPermissionActivity(), Intent(action)).apply {
create()
start()
postCreate(null)
resume()
}
}
}

View File

@@ -49,13 +49,14 @@ class RequestPermissionHelperTest {
@Test
fun requestEnable_withAppLabelAndNoTimeout_hasCorrectMessage() {
val activity = activityController.get()
RequestPermissionHelper.requestEnable(
context = activity,
appLabel = "App Label",
timeout = -1,
onAllow = {},
onDeny = {},
)
)!!.show()
assertLatestMessageIs("App Label wants to turn on Bluetooth")
}
@@ -63,13 +64,14 @@ class RequestPermissionHelperTest {
@Test
fun requestEnable_withAppLabelAndZeroTimeout_hasCorrectMessage() {
val activity = activityController.get()
RequestPermissionHelper.requestEnable(
context = activity,
appLabel = "App Label",
timeout = 0,
onAllow = {},
onDeny = {},
)
)!!.show()
assertLatestMessageIs(
"App Label wants to turn on Bluetooth and make your phone visible to other devices. " +
@@ -80,13 +82,14 @@ class RequestPermissionHelperTest {
@Test
fun requestEnable_withAppLabelAndNormalTimeout_hasCorrectMessage() {
val activity = activityController.get()
RequestPermissionHelper.requestEnable(
context = activity,
appLabel = "App Label",
timeout = 120,
onAllow = {},
onDeny = {},
)
)!!.show()
assertLatestMessageIs(
"App Label wants to turn on Bluetooth and make your phone visible to other devices " +
@@ -97,13 +100,14 @@ class RequestPermissionHelperTest {
@Test
fun requestEnable_withNoAppLabelAndNoTimeout_hasCorrectMessage() {
val activity = activityController.get()
RequestPermissionHelper.requestEnable(
context = activity,
appLabel = null,
timeout = -1,
onAllow = {},
onDeny = {},
)
)!!.show()
assertLatestMessageIs("An app wants to turn on Bluetooth")
}
@@ -111,13 +115,14 @@ class RequestPermissionHelperTest {
@Test
fun requestEnable_withNoAppLabelAndZeroTimeout_hasCorrectMessage() {
val activity = activityController.get()
RequestPermissionHelper.requestEnable(
context = activity,
appLabel = null,
timeout = 0,
onAllow = {},
onDeny = {},
)
)!!.show()
assertLatestMessageIs(
"An app wants to turn on Bluetooth and make your phone visible to other devices. " +
@@ -128,13 +133,14 @@ class RequestPermissionHelperTest {
@Test
fun requestEnable_withNoAppLabelAndNormalTimeout_hasCorrectMessage() {
val activity = activityController.get()
RequestPermissionHelper.requestEnable(
context = activity,
appLabel = null,
timeout = 120,
onAllow = {},
onDeny = {},
)
)!!.show()
assertLatestMessageIs(
"An app wants to turn on Bluetooth and make your phone visible to other devices for " +
@@ -177,12 +183,13 @@ class RequestPermissionHelperTest {
@Test
fun requestDisable_withAppLabel_hasCorrectMessage() {
val activity = activityController.get()
RequestPermissionHelper.requestDisable(
context = activity,
appLabel = "App Label",
onAllow = {},
onDeny = {},
)
)!!.show()
assertLatestMessageIs("App Label wants to turn off Bluetooth")
}
@@ -190,12 +197,13 @@ class RequestPermissionHelperTest {
@Test
fun requestDisable_withNoAppLabel_hasCorrectMessage() {
val activity = activityController.get()
RequestPermissionHelper.requestDisable(
context = activity,
appLabel = null,
onAllow = {},
onDeny = {},
)
)!!.show()
assertLatestMessageIs("An app wants to turn off Bluetooth")
}

View File

@@ -61,22 +61,9 @@ public class TrackpadReverseScrollingPreferenceControllerTest {
}
@Test
public void setChecked_true_shouldReturn1() {
public void setChecked_true_shouldReturn0() {
mController.setChecked(true);
int result = Settings.System.getIntForUser(
mContext.getContentResolver(),
SETTING_KEY,
0,
UserHandle.USER_CURRENT);
assertThat(result).isEqualTo(1);
}
@Test
public void setChecked_false_shouldReturn0() {
mController.setChecked(false);
int result = Settings.System.getIntForUser(
mContext.getContentResolver(),
SETTING_KEY,
@@ -87,7 +74,20 @@ public class TrackpadReverseScrollingPreferenceControllerTest {
}
@Test
public void isChecked_providerPutInt1_returnTrue() {
public void setChecked_false_shouldReturn1() {
mController.setChecked(false);
int result = Settings.System.getIntForUser(
mContext.getContentResolver(),
SETTING_KEY,
0,
UserHandle.USER_CURRENT);
assertThat(result).isEqualTo(1);
}
@Test
public void isChecked_providerPutInt1_returnFalse() {
Settings.System.putIntForUser(
mContext.getContentResolver(),
SETTING_KEY,
@@ -96,11 +96,11 @@ public class TrackpadReverseScrollingPreferenceControllerTest {
boolean result = mController.isChecked();
assertThat(result).isTrue();
assertThat(result).isFalse();
}
@Test
public void isChecked_providerPutInt0_returnFalse() {
public void isChecked_providerPutInt0_returnTrue() {
Settings.System.putIntForUser(
mContext.getContentResolver(),
SETTING_KEY,
@@ -109,6 +109,6 @@ public class TrackpadReverseScrollingPreferenceControllerTest {
boolean result = mController.isChecked();
assertThat(result).isFalse();
assertThat(result).isTrue();
}
}

View File

@@ -21,33 +21,71 @@ import android.content.Intent
import android.net.Uri
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.spa.SpaActivity.Companion.isSuwAndPageBlocked
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.SpaActivity.Companion.startSpaActivityForApp
import com.android.settings.spa.app.AllAppListPageProvider
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settingslib.spa.framework.util.KEY_DESTINATION
import com.google.android.setupcompat.util.WizardManagerHelper
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class SpaActivityTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
private lateinit var mockSession: MockitoSession
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@Mock
private lateinit var context: Context
@Before
fun setUp() {
`when`(context.applicationContext.packageName).thenReturn("com.android.settings")
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(WizardManagerHelper::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(context.applicationContext).thenReturn(context)
}
@After
fun tearDown() {
mockSession.finishMocking()
}
@Test
fun isSuwAndPageBlocked_regularPage_notBlocked() {
val isBlocked = context.isSuwAndPageBlocked(AllAppListPageProvider.name)
assertThat(isBlocked).isFalse()
}
@Test
fun isSuwAndPageBlocked_blocklistedPageInSuw_blocked() {
whenever(WizardManagerHelper.isDeviceProvisioned(context)).thenReturn(false)
val isBlocked = context.isSuwAndPageBlocked(AppInfoSettingsProvider.name)
assertThat(isBlocked).isTrue()
}
@Test
fun isSuwAndPageBlocked_blocklistedPageNotInSuw_notBlocked() {
whenever(WizardManagerHelper.isDeviceProvisioned(context)).thenReturn(true)
val isBlocked = context.isSuwAndPageBlocked(AppInfoSettingsProvider.name)
assertThat(isBlocked).isFalse()
}
@Test