Snap for 10213992 from 6cb1f9401d to udc-qpr1-release

Change-Id: Ia57be4477bd96e96f09faf66f391608711f7360c
This commit is contained in:
Android Build Coastguard Worker
2023-05-27 05:28:39 +00:00
36 changed files with 770 additions and 880 deletions

View File

@@ -864,6 +864,7 @@
<activity
android:name="Settings$LongBackgroundTasksActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/long_background_tasks_label">
<intent-filter android:priority="1">
@@ -1545,6 +1546,7 @@
<activity
android:name="Settings$ManageApplicationsActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/applications_settings">
<intent-filter android:priority="1">
@@ -1607,6 +1609,7 @@
<activity
android:name="Settings$HighPowerApplicationsActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/high_power_apps">
<intent-filter android:priority="1">
@@ -1665,6 +1668,7 @@
This is for compatibility with old shortcuts. -->
<activity-alias android:name=".RunningServices"
android:label="@string/runningservices_settings_title"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:targetActivity="Settings$ManageApplicationsActivity">
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
@@ -1677,6 +1681,7 @@
This is for compatibility with old shortcuts. -->
<activity-alias android:name=".applications.StorageUse"
android:label="@string/storageuse_settings_title"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:targetActivity="Settings$ManageApplicationsActivity">
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
@@ -1743,6 +1748,7 @@
<!-- Provide direct entry into manage apps showing running services. -->
<activity android:name="Settings$RunningServicesActivity"
android:exported="true"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:label="@string/runningservices_settings_title">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -1761,6 +1767,7 @@
<!-- Provide direct entry into manage apps showing storage usage of apps. -->
<activity
android:name="Settings$StorageUseActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/storageuse_settings_title">
<intent-filter android:priority="1">
@@ -2105,6 +2112,7 @@
<activity
android:name="Settings$UsageAccessSettingsActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/usage_access_title">
<intent-filter android:priority="1">
@@ -3483,6 +3491,7 @@
<activity
android:name="Settings$TurnScreenOnSettingsActivity"
android:exported="true"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:label="@string/turn_screen_on_title">
<intent-filter android:priority="1">
<action android:name="android.settings.TURN_SCREEN_ON_SETTINGS" />
@@ -3662,6 +3671,7 @@
<activity android:name="Settings$NotificationAppListActivity"
android:label="@string/app_notifications_title"
android:icon="@drawable/ic_notifications"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.settings.ALL_APPS_NOTIFICATION_SETTINGS" />
@@ -3676,6 +3686,7 @@
<!-- Displays a list of apps available for cloning on the device -->
<activity android:name=".Settings$ClonedAppsListActivity"
android:label="@string/cloned_apps_dashboard_title"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.settings.MANAGE_CLONED_APPS_SETTINGS" />
@@ -3717,7 +3728,7 @@
<!-- Show channel-level notification settings (channel passed in as extras) -->
<activity android:name=".notification.app.ChannelPanelActivity"
android:label="@string/notification_channel_title"
android:theme="@style/Theme.Panel.Material"
android:theme="@style/Theme.Panel"
android:excludeFromRecents="true"
android:configChanges="keyboardHidden|screenSize"
android:exported="true">
@@ -3941,6 +3952,7 @@
<activity
android:name="Settings$OverlaySettingsActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/draw_overlay">
<intent-filter android:priority="1">
@@ -3978,6 +3990,7 @@
<activity
android:name="Settings$WriteSettingsActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/write_settings_title">
<intent-filter android:priority="1">
@@ -4011,6 +4024,7 @@
<activity
android:name="Settings$AlarmsAndRemindersActivity"
android:exported="true"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:label="@string/alarms_and_reminders_label">
<intent-filter android:priority="1">
<action android:name="android.settings.REQUEST_SCHEDULE_EXACT_ALARM" />
@@ -4041,6 +4055,7 @@
<activity
android:name="Settings$ManageExternalSourcesActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/install_other_apps">
<intent-filter android:priority="1">
@@ -4095,6 +4110,7 @@
<activity
android:name="Settings$ManageExternalStorageActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/manage_external_storage_title">
<intent-filter android:priority="1">
@@ -4126,6 +4142,7 @@
<activity
android:name="Settings$MediaManagementAppsActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:label="@string/media_management_apps_title">
<intent-filter android:priority="1">

View File

@@ -15,7 +15,10 @@
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid
android:color="?androidprv:attr/materialColorSurfaceContainer" />
<stroke
android:width="2dp"
android:color="?android:attr/colorAccent"/>

View File

@@ -18,7 +18,7 @@
android:shape="rectangle">
<stroke
android:width="1dp"
android:color="?android:attr/colorAccent"/>
android:color="@color/notification_importance_button_unselected"/>
<corners android:radius="@dimen/rect_button_radius" />
</shape>

View File

@@ -1,101 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<com.google.android.setupdesign.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/setup_wizard_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/sud_glif_blank_template"
style="?attr/fingerprint_layout_theme">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:clipToPadding="false"
android:clipChildren="false">
<!-- Both texts are kept as separate text views so it doesn't jump around in portrait.
See layouts/fingerprint_enroll_enrolling_base.xml. -->
<LinearLayout
android:id="@+id/layout_container"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_marginStart="?attr/sudMarginStart"
android:layout_marginEnd="@dimen/enroll_margin_end"
android:layout_marginBottom="@dimen/sud_content_frame_padding_bottom"
android:paddingStart="@dimen/enroll_padding_start"
android:paddingEnd="@dimen/enroll_padding_end"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:clipChildren="false"
android:clipToPadding="false">
<ImageView
android:id="@+id/sud_layout_icon"
style="@style/SudGlifIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitStart"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:src="@drawable/ic_lock" />
<TextView
android:id="@+id/suc_layout_title"
style="@style/SudGlifHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp" />
<TextView
style="@style/SudDescription.Glif"
android:id="@+id/sud_layout_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Space
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</ScrollView>
</LinearLayout>
</LinearLayout>
<include layout="@layout/udfps_enroll_view" />
</com.google.android.setupdesign.GlifLayout>

View File

@@ -15,7 +15,7 @@
~ limitations under the License.
-->
<com.google.android.setupdesign.GlifLayout
<com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -78,4 +78,4 @@
</LinearLayout>
</LinearLayout>
</com.google.android.setupdesign.GlifLayout>
</com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView>

View File

@@ -1056,8 +1056,10 @@
<string name="security_fingerprint_disclaimer_lockscreen_disabled_2">You can still use your fingerprint to authorize purchases and app access.</string>
<!-- Text shown in fingerprint enroll when we didn't observe progress for a few seconds. [CHAR LIMIT=100] -->
<string name="security_settings_fingerprint_enroll_lift_touch_again">Lift finger, then touch sensor again</string>
<!-- Dialog title during fingerprint enrollment to indicate bad sensor calibration. [CHAR LIMIT=100] -->
<string name="security_settings_fingerprint_bad_calibration_title">Can\u2019t use fingerprint sensor</string>
<!-- Text shown during fingerprint enrollment to indicate bad sensor calibration. [CHAR LIMIT=100] -->
<string name="security_settings_fingerprint_bad_calibration">Can\u2019t use fingerprint sensor. Visit a repair provider</string>
<string name="security_settings_fingerprint_bad_calibration">Visit a repair provider.</string>
<!-- Title for the section that has additional security settings. [CHAR LIMIT=60] -->
<string name="security_advanced_settings">More security settings</string>
<!-- String for the "More security settings" summary when a work profile is on the device. [CHAR_LIMIT=NONE] -->
@@ -9702,12 +9704,6 @@
<!-- [CHAR_LIMIT=60] Label for special access screen -->
<string name="special_access">Special app access</string>
<!-- Summary for special access settings [CHAR_LIMIT=NONE] -->
<plurals name="special_access_summary">
<item quantity="one">1 app can use unrestricted data</item>
<item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps can use unrestricted data</item>
</plurals>
<!-- Title for the See more preference item in Special app access settings [CHAR LIMIT=30] -->
<string name="special_access_more">See more</string>

View File

@@ -227,9 +227,6 @@
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
</style>
<style name="Theme.Panel.Material" parent="Theme.Panel" >
<item name="android:switchStyle">@style/Switch.SettingsLib</item>
</style>
<!-- Material theme for the pages containing TabLayout and ViewPager -->
<style name="Theme.TabTheme" parent="@style/Theme.MaterialComponents.DayNight">
<item name="colorPrimary">@*android:color/edge_effect_device_default_light</item>

View File

@@ -105,7 +105,6 @@
android:key="special_access"
android:fragment="com.android.settings.applications.specialaccess.SpecialAccessSettings"
android:title="@string/special_access"
android:order="20"
settings:controller="com.android.settings.applications.SpecialAppAccessPreferenceController"/>
android:order="20"/>
</PreferenceScreen>

View File

@@ -66,7 +66,6 @@ public class AppDashboardFragment extends DashboardFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
mAppsPreferenceController = use(AppsPreferenceController.class);
mAppsPreferenceController.setFragment(this /* fragment */);
getSettingsLifecycle().addObserver(mAppsPreferenceController);

View File

@@ -1,154 +0,0 @@
/*
* Copyright (C) 2017 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.applications;
import android.app.Application;
import android.content.Context;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.datausage.AppStateDataUsageBridge;
import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState;
import com.android.settings.datausage.DataSaverBackend;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.ArrayList;
public class SpecialAppAccessPreferenceController extends BasePreferenceController implements
AppStateBaseBridge.Callback, ApplicationsState.Callbacks, LifecycleObserver, OnStart,
OnStop, OnDestroy {
@VisibleForTesting
ApplicationsState.Session mSession;
private final ApplicationsState mApplicationsState;
private final AppStateDataUsageBridge mDataUsageBridge;
private final DataSaverBackend mDataSaverBackend;
private Preference mPreference;
private boolean mExtraLoaded;
public SpecialAppAccessPreferenceController(Context context, String key) {
super(context, key);
mApplicationsState = ApplicationsState.getInstance(
(Application) context.getApplicationContext());
mDataSaverBackend = new DataSaverBackend(context);
mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend);
}
public void setSession(Lifecycle lifecycle) {
mSession = mApplicationsState.newSession(this, lifecycle);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public void onStart() {
mDataUsageBridge.resume(true /* forceLoadAllApps */);
}
@Override
public void onStop() {
mDataUsageBridge.pause();
}
@Override
public void onDestroy() {
mDataUsageBridge.release();
}
@Override
public void updateState(Preference preference) {
updateSummary();
}
@Override
public void onExtraInfoUpdated() {
mExtraLoaded = true;
updateSummary();
}
private void updateSummary() {
if (!mExtraLoaded || mPreference == null) {
return;
}
final ArrayList<ApplicationsState.AppEntry> allApps = mSession.getAllApps();
int count = 0;
for (ApplicationsState.AppEntry entry : allApps) {
if (!ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry)) {
continue;
}
if (entry.extraInfo instanceof DataUsageState
&& ((DataUsageState) entry.extraInfo).isDataSaverAllowlisted) {
count++;
}
}
mPreference.setSummary(mContext.getResources().getQuantityString(
R.plurals.special_access_summary, count, count));
}
@Override
public void onRunningStateChanged(boolean running) {
}
@Override
public void onPackageListChanged() {
}
@Override
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
}
@Override
public void onPackageIconChanged() {
}
@Override
public void onPackageSizeChanged(String packageName) {
}
@Override
public void onAllSizesComputed() {
}
@Override
public void onLauncherInfoChanged() {
// when the value of the AppEntry.hasLauncherEntry was changed.
updateSummary();
}
@Override
public void onLoadEntriesCompleted() {
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (C) 2017 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.applications.specialaccess;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
public class DataSaverController extends BasePreferenceController {
public DataSaverController(Context context, String key) {
super(context, key);
}
@AvailabilityStatus
public int getAvailabilityStatus() {
return mContext.getResources().getBoolean(R.bool.config_show_data_saver)
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2017 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.applications.specialaccess
import android.content.Context
import android.net.NetworkPolicyManager
import android.os.UserHandle
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.core.BasePreferenceController
import com.android.settingslib.spa.framework.util.formatString
import com.android.settingslib.spaprivileged.model.app.AppListRepository
import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl
import com.google.common.annotations.VisibleForTesting
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class DataSaverController(context: Context, key: String) : BasePreferenceController(context, key) {
private lateinit var preference: Preference
@AvailabilityStatus
override fun getAvailabilityStatus(): Int = when {
mContext.resources.getBoolean(R.bool.config_show_data_saver) -> AVAILABLE
else -> UNSUPPORTED_ON_DEVICE
}
override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen)
preference = screen.findPreference(preferenceKey)!!
}
fun init(viewLifecycleOwner: LifecycleOwner) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
preference.summary = getUnrestrictedSummary(mContext)
}
}
}
companion object {
@VisibleForTesting
suspend fun getUnrestrictedSummary(
context: Context,
appListRepository: AppListRepository =
AppListRepositoryImpl(context.applicationContext),
) = context.formatString(
R.string.data_saver_unrestricted_summary,
"count" to getAllowCount(context.applicationContext, appListRepository),
)
private suspend fun getAllowCount(context: Context, appListRepository: AppListRepository) =
withContext(Dispatchers.IO) {
coroutineScope {
val appsDeferred = async {
appListRepository.loadAndFilterApps(
userId = UserHandle.myUserId(),
isSystemApp = false,
)
}
val uidsAllowed = NetworkPolicyManager.from(context)
.getUidsWithPolicy(NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND)
appsDeferred.await().count { app -> app.uid in uidsAllowed }
}
}
}
}

View File

@@ -21,6 +21,10 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGE_DE
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
@@ -46,6 +50,12 @@ public class SpecialAccessSettings extends DashboardFragment {
MANAGE_DEVICE_ADMIN_APPS, R.string.manage_device_admin);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
use(DataSaverController.class).init(getViewLifecycleOwner());
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.special_access;

View File

@@ -133,6 +133,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
protected long mChallenge;
protected boolean mFromSettingsSummary;
protected FooterBarMixin mFooterBarMixin;
protected boolean mShouldSetFooterBarBackground = true;
@Nullable
protected ScreenSizeFoldProvider mScreenSizeFoldProvider;
@Nullable
@@ -191,12 +192,14 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
super.onPostCreate(savedInstanceState);
initViews();
@SuppressLint("VisibleForTests")
final LinearLayout buttonContainer = mFooterBarMixin != null
? mFooterBarMixin.getButtonContainer()
: null;
if (buttonContainer != null) {
buttonContainer.setBackgroundColor(getBackgroundColor());
if (mShouldSetFooterBarBackground) {
@SuppressLint("VisibleForTests")
final LinearLayout buttonContainer = mFooterBarMixin != null
? mFooterBarMixin.getButtonContainer()
: null;
if (buttonContainer != null) {
buttonContainer.setBackgroundColor(getBackgroundColor());
}
}
}
@@ -331,7 +334,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
}
@ColorInt
private int getBackgroundColor() {
public int getBackgroundColor() {
final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
}

View File

@@ -32,10 +32,8 @@ import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
@@ -48,22 +46,16 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -79,25 +71,20 @@ import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.display.DisplayDensityUtils;
import com.android.settingslib.udfps.UdfpsOverlayParams;
import com.android.settingslib.udfps.UdfpsUtils;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieCompositionFactory;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.model.KeyPath;
import com.google.android.setupcompat.template.FooterActionButton;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.template.DescriptionMixin;
import com.google.android.setupdesign.template.HeaderMixin;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Locale;
/**
* Activity which handles the actual enrolling for fingerprint.
@@ -176,8 +163,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
@VisibleForTesting
@Nullable
UdfpsEnrollHelper mUdfpsEnrollHelper;
// TODO(b/260617060): Do not hard-code mScaleFactor, referring to AuthController.
private float mScaleFactor = 1.0f;
private ObjectAnimator mProgressAnim;
private TextView mErrorText;
private Interpolator mFastOutSlowInInterpolator;
@@ -206,7 +191,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
private boolean mHaveShownSfpsLeftEdgeLottie;
private boolean mHaveShownSfpsRightEdgeLottie;
private boolean mShouldShowLottie;
private UdfpsUtils mUdfpsUtils;
private ObjectAnimator mHelpAnimation;
private OrientationEventListener mOrientationEventListener;
@@ -251,88 +236,17 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
mAccessibilityManager = getSystemService(AccessibilityManager.class);
mIsAccessibilityEnabled = mAccessibilityManager.isEnabled();
mUdfpsUtils = new UdfpsUtils();
final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
listenOrientationEvent();
if (mCanAssumeUdfps) {
int rotation = getApplicationContext().getDisplay().getRotation();
final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate(
R.layout.udfps_enroll_enrolling, null, false);
final UdfpsEnrollView udfpsEnrollView = layout.findViewById(R.id.udfps_animation_view);
updateUdfpsEnrollView(udfpsEnrollView, props.get(0));
switch (rotation) {
case Surface.ROTATION_90:
final View sudContent = layout.findViewById(R.id.sud_layout_content);
if (sudContent != null) {
sudContent.setPadding(sudContent.getPaddingLeft(), 0,
sudContent.getPaddingRight(), sudContent.getPaddingBottom());
}
final UdfpsEnrollEnrollingView layout =
(UdfpsEnrollEnrollingView) getLayoutInflater().inflate(
R.layout.udfps_enroll_enrolling, null, false);
setUdfpsEnrollHelper();
layout.initView(props.get(0), mUdfpsEnrollHelper, mAccessibilityManager);
final LinearLayout layoutContainer = layout.findViewById(
R.id.layout_container);
final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
lp.setMarginEnd((int) getResources().getDimension(
R.dimen.rotation_90_enroll_margin_end));
layoutContainer.setPaddingRelative((int) getResources().getDimension(
R.dimen.rotation_90_enroll_padding_start), 0, isLayoutRtl
? 0 : (int) getResources().getDimension(
R.dimen.rotation_90_enroll_padding_end), 0);
layoutContainer.setLayoutParams(lp);
setOnHoverListener(true, layout, udfpsEnrollView);
setContentView(layout, lp);
break;
case Surface.ROTATION_0:
case Surface.ROTATION_180:
// In the portrait mode, layout_container's height is 0, so it's
// always shown at the bottom of the screen.
final FrameLayout portraitLayoutContainer = layout.findViewById(
R.id.layout_container);
// In the portrait mode, the title and lottie animation view may
// overlap when title needs three lines, so adding some paddings
// between them, and adjusting the fp progress view here accordingly.
final int layoutLottieAnimationPadding = (int) getResources()
.getDimension(R.dimen.udfps_lottie_padding_top);
portraitLayoutContainer.setPadding(0,
layoutLottieAnimationPadding, 0, 0);
final ImageView progressView = udfpsEnrollView.findViewById(
R.id.udfps_enroll_animation_fp_progress_view);
progressView.setPadding(0, -(layoutLottieAnimationPadding),
0, layoutLottieAnimationPadding);
final ImageView fingerprintView = udfpsEnrollView.findViewById(
R.id.udfps_enroll_animation_fp_view);
fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
0, layoutLottieAnimationPadding);
// TODO(b/260970216) Instead of hiding the description text view, we should
// make the header view scrollable if the text is too long.
// If description text view has overlap with udfps progress view, hide it.
View view = layout.getDescriptionTextView();
layout.getViewTreeObserver().addOnDrawListener(() -> {
if (view.getVisibility() == View.VISIBLE
&& hasOverlap(view, udfpsEnrollView)) {
view.setVisibility(View.GONE);
}
});
setOnHoverListener(false, layout, udfpsEnrollView);
setContentView(layout);
break;
case Surface.ROTATION_270:
default:
setOnHoverListener(true, layout, udfpsEnrollView);
setContentView(layout);
break;
}
setContentView(layout);
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
} else if (mCanAssumeSfps) {
setContentView(R.layout.sfps_enroll_enrolling);
@@ -372,22 +286,11 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
.build()
);
if (FeatureFlagUtils.isEnabled(getApplicationContext(),
FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
// Remove the space view and make the width of footer button container WRAP_CONTENT
// to avoid hiding the udfps view progress bar bottom.
final LinearLayout buttonContainer = mFooterBarMixin.getButtonContainer();
View spaceView = null;
for (int i = 0; i < buttonContainer.getChildCount(); i++) {
if (!(buttonContainer.getChildAt(i) instanceof FooterActionButton)) {
spaceView = buttonContainer.getChildAt(i);
break;
}
}
if (spaceView != null) {
spaceView.setVisibility(View.GONE);
buttonContainer.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
}
// If it's udfps, set the background color only for secondary button if necessary.
if (mCanAssumeUdfps) {
mShouldSetFooterBarBackground = false;
((UdfpsEnrollEnrollingView) getLayout()).setSecondaryButtonBackground(
getBackgroundColor());
}
final LayerDrawable fingerprintDrawable = mProgressBar != null
@@ -1230,30 +1133,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
}
}
private UdfpsEnrollView updateUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
FingerprintSensorPropertiesInternal udfpsProps) {
DisplayInfo displayInfo = new DisplayInfo();
getDisplay().getDisplayInfo(displayInfo);
mScaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
Rect udfpsBounds = udfpsProps.getLocation().getRect();
udfpsBounds.scale(mScaleFactor);
final Rect overlayBounds = new Rect(
0, /* left */
displayInfo.getNaturalHeight() / 2, /* top */
displayInfo.getNaturalWidth(), /* right */
displayInfo.getNaturalHeight() /* botom */);
UdfpsOverlayParams params = new UdfpsOverlayParams(
udfpsBounds,
overlayBounds,
displayInfo.getNaturalWidth(),
displayInfo.getNaturalHeight(),
mScaleFactor,
displayInfo.rotation);
udfpsEnrollView.setOverlayParams(params);
private void setUdfpsEnrollHelper() {
mUdfpsEnrollHelper = (UdfpsEnrollHelper) getSupportFragmentManager().findFragmentByTag(
FingerprintEnrollEnrolling.TAG_UDFPS_HELPER);
if (mUdfpsEnrollHelper == null) {
@@ -1263,57 +1143,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
.add(mUdfpsEnrollHelper, FingerprintEnrollEnrolling.TAG_UDFPS_HELPER)
.commitAllowingStateLoss();
}
udfpsEnrollView.setEnrollHelper(mUdfpsEnrollHelper);
return udfpsEnrollView;
}
private void setOnHoverListener(boolean isLandscape, GlifLayout enrollLayout,
UdfpsEnrollView udfpsEnrollView) {
if (!mIsAccessibilityEnabled) return;
final Context context = getApplicationContext();
final View.OnHoverListener onHoverListener = (v, event) -> {
// Map the touch to portrait mode if the device is in
// landscape mode.
final Point scaledTouch =
mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
event, udfpsEnrollView.getOverlayParams());
if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
udfpsEnrollView.getOverlayParams())) {
return false;
}
final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
mAccessibilityManager.isTouchExplorationEnabled(), context,
scaledTouch.x, scaledTouch.y, udfpsEnrollView.getOverlayParams());
if (theStr != null) {
v.announceForAccessibility(theStr);
}
return false;
};
enrollLayout.findManagedViewById(isLandscape ? R.id.sud_landscape_content_area
: R.id.sud_layout_content).setOnHoverListener(onHoverListener);
}
@VisibleForTesting boolean hasOverlap(View view1, View view2) {
int[] firstPosition = new int[2];
int[] secondPosition = new int[2];
view1.getLocationOnScreen(firstPosition);
view2.getLocationOnScreen(secondPosition);
// Rect constructor parameters: left, top, right, bottom
Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
firstPosition[0] + view1.getMeasuredWidth(),
firstPosition[1] + view1.getMeasuredHeight());
Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
secondPosition[0] + view2.getMeasuredWidth(),
secondPosition[1] + view2.getMeasuredHeight());
return rectView1.intersect(rectView2);
}
public static class IconTouchDialog extends InstrumentedDialogFragment {

View File

@@ -163,6 +163,8 @@ public class FingerprintErrorDialog extends InstrumentedDialogFragment {
switch (errMsgId) {
case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
return R.string.security_settings_fingerprint_enroll_error_dialog_title;
case FingerprintManager.FINGERPRINT_ERROR_BAD_CALIBRATION:
return R.string.security_settings_fingerprint_bad_calibration_title;
default:
return R.string
.security_settings_fingerprint_enroll_error_unable_to_process_dialog_title;

View File

@@ -0,0 +1,237 @@
/*
* 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.biometrics.fingerprint;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.ColorInt;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settingslib.udfps.UdfpsOverlayParams;
import com.android.settingslib.udfps.UdfpsUtils;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.view.BottomScrollView;
import java.util.Locale;
/**
* View for udfps enrolling.
*/
public class UdfpsEnrollEnrollingView extends GlifLayout {
private final UdfpsUtils mUdfpsUtils;
private final Context mContext;
// We don't need to listen to onConfigurationChanged() for mRotation here because
// FingerprintEnrollEnrolling is always recreated once the configuration is changed.
private final int mRotation;
private final boolean mIsLandscape;
private final boolean mShouldUseReverseLandscape;
private UdfpsEnrollView mUdfpsEnrollView;
private View mHeaderView;
private AccessibilityManager mAccessibilityManager;
public UdfpsEnrollEnrollingView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mRotation = mContext.getDisplay().getRotation();
mIsLandscape = mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
== View.LAYOUT_DIRECTION_RTL);
mShouldUseReverseLandscape = (mRotation == Surface.ROTATION_90 && isLayoutRtl)
|| (mRotation == Surface.ROTATION_270 && !isLayoutRtl);
mUdfpsUtils = new UdfpsUtils();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mHeaderView = findViewById(R.id.sud_landscape_header_area);
mUdfpsEnrollView = findViewById(R.id.udfps_animation_view);
}
void initView(FingerprintSensorPropertiesInternal udfpsProps,
UdfpsEnrollHelper udfpsEnrollHelper,
AccessibilityManager accessibilityManager) {
mAccessibilityManager = accessibilityManager;
initUdfpsEnrollView(mUdfpsEnrollView, udfpsProps, udfpsEnrollHelper);
if (!mIsLandscape) {
adjustPortraitPaddings();
} else if (mShouldUseReverseLandscape) {
swapHeaderAndContent();
}
setOnHoverListener();
}
void setSecondaryButtonBackground(@ColorInt int color) {
// Set the button background only when the button is not under udfps overlay to avoid UI
// overlap.
if (!mIsLandscape || mShouldUseReverseLandscape) {
return;
}
final Button secondaryButtonView =
getMixin(FooterBarMixin.class).getSecondaryButtonView();
secondaryButtonView.setBackgroundColor(color);
if (mRotation == Surface.ROTATION_90) {
secondaryButtonView.setGravity(Gravity.START);
} else {
secondaryButtonView.setGravity(Gravity.END);
}
mHeaderView.post(() -> {
secondaryButtonView.setLayoutParams(
new LinearLayout.LayoutParams(mHeaderView.getMeasuredWidth(),
ViewGroup.LayoutParams.WRAP_CONTENT));
});
}
private void initUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
FingerprintSensorPropertiesInternal udfpsProps,
UdfpsEnrollHelper udfpsEnrollHelper) {
DisplayInfo displayInfo = new DisplayInfo();
mContext.getDisplay().getDisplayInfo(displayInfo);
final float scaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
Rect udfpsBounds = udfpsProps.getLocation().getRect();
udfpsBounds.scale(scaleFactor);
final Rect overlayBounds = new Rect(
0, /* left */
displayInfo.getNaturalHeight() / 2, /* top */
displayInfo.getNaturalWidth(), /* right */
displayInfo.getNaturalHeight() /* botom */);
UdfpsOverlayParams params = new UdfpsOverlayParams(
udfpsBounds,
overlayBounds,
displayInfo.getNaturalWidth(),
displayInfo.getNaturalHeight(),
scaleFactor,
displayInfo.rotation);
udfpsEnrollView.setOverlayParams(params);
udfpsEnrollView.setEnrollHelper(udfpsEnrollHelper);
}
private void adjustPortraitPaddings() {
// In the portrait mode, layout_container's height is 0, so it's
// always shown at the bottom of the screen.
final FrameLayout portraitLayoutContainer = findViewById(R.id.layout_container);
// In the portrait mode, the title and lottie animation view may
// overlap when title needs three lines, so adding some paddings
// between them, and adjusting the fp progress view here accordingly.
final int layoutLottieAnimationPadding = (int) getResources()
.getDimension(R.dimen.udfps_lottie_padding_top);
portraitLayoutContainer.setPadding(0,
layoutLottieAnimationPadding, 0, 0);
final ImageView progressView = mUdfpsEnrollView.findViewById(
R.id.udfps_enroll_animation_fp_progress_view);
progressView.setPadding(0, -(layoutLottieAnimationPadding),
0, layoutLottieAnimationPadding);
final ImageView fingerprintView = mUdfpsEnrollView.findViewById(
R.id.udfps_enroll_animation_fp_view);
fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
0, layoutLottieAnimationPadding);
// TODO(b/260970216) Instead of hiding the description text view, we should
// make the header view scrollable if the text is too long.
// If description text view has overlap with udfps progress view, hide it.
final View descView = getDescriptionTextView();
getViewTreeObserver().addOnDrawListener(() -> {
if (descView.getVisibility() == View.VISIBLE
&& hasOverlap(descView, mUdfpsEnrollView)) {
descView.setVisibility(View.GONE);
}
});
}
private void setOnHoverListener() {
if (!mAccessibilityManager.isEnabled()) return;
final View.OnHoverListener onHoverListener = (v, event) -> {
// Map the touch to portrait mode if the device is in
// landscape mode.
final Point scaledTouch =
mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
event, mUdfpsEnrollView.getOverlayParams());
if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
mUdfpsEnrollView.getOverlayParams())) {
return false;
}
final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
mAccessibilityManager.isTouchExplorationEnabled(), mContext,
scaledTouch.x, scaledTouch.y, mUdfpsEnrollView.getOverlayParams());
if (theStr != null) {
v.announceForAccessibility(theStr);
}
return false;
};
findManagedViewById(mIsLandscape ? R.id.sud_landscape_content_area
: R.id.sud_layout_content).setOnHoverListener(onHoverListener);
}
private void swapHeaderAndContent() {
// Reverse header and body
ViewGroup parentView = (ViewGroup) mHeaderView.getParent();
parentView.removeView(mHeaderView);
parentView.addView(mHeaderView);
// Hide scroll indicators
BottomScrollView headerScrollView = mHeaderView.findViewById(R.id.sud_header_scroll_view);
headerScrollView.setScrollIndicators(0);
}
@VisibleForTesting
boolean hasOverlap(View view1, View view2) {
int[] firstPosition = new int[2];
int[] secondPosition = new int[2];
view1.getLocationOnScreen(firstPosition);
view2.getLocationOnScreen(secondPosition);
// Rect constructor parameters: left, top, right, bottom
Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
firstPosition[0] + view1.getMeasuredWidth(),
firstPosition[1] + view1.getMeasuredHeight());
Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
secondPosition[0] + view2.getMeasuredWidth(),
secondPosition[1] + view2.getMeasuredHeight());
return rectView1.intersect(rectView2);
}
}

View File

@@ -17,34 +17,22 @@ package com.android.settings.datausage
import android.app.settings.SettingsEnums
import android.content.Context
import android.net.NetworkPolicyManager
import android.os.Bundle
import android.os.UserHandle
import android.telephony.SubscriptionManager
import android.view.View
import android.widget.Switch
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.android.settings.R
import com.android.settings.SettingsActivity
import com.android.settings.SettingsPreferenceFragment
import com.android.settings.applications.specialaccess.DataSaverController
import com.android.settings.dashboard.DashboardFragment
import com.android.settings.search.BaseSearchIndexProvider
import com.android.settings.widget.SettingsMainSwitchBar
import com.android.settingslib.search.SearchIndexable
import com.android.settingslib.spa.framework.util.formatString
import com.android.settingslib.spaprivileged.model.app.AppListRepository
import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@SearchIndexable
class DataSaverSummary : SettingsPreferenceFragment() {
class DataSaverSummary : DashboardFragment() {
private lateinit var switchBar: SettingsMainSwitchBar
private lateinit var dataSaverBackend: DataSaverBackend
private lateinit var unrestrictedAccess: Preference
// Flag used to avoid infinite loop due if user switch it on/off too quick.
private var switching = false
@@ -57,8 +45,6 @@ class DataSaverSummary : SettingsPreferenceFragment() {
return
}
addPreferencesFromResource(R.xml.data_saver)
unrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS)!!
dataSaverBackend = DataSaverBackend(requireContext())
}
@@ -73,12 +59,14 @@ class DataSaverSummary : SettingsPreferenceFragment() {
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
use(DataSaverController::class.java).init(viewLifecycleOwner)
}
override fun onResume() {
super.onResume()
dataSaverBackend.addListener(dataSaverBackendListener)
viewLifecycleOwner.lifecycleScope.launch {
unrestrictedAccess.summary = getUnrestrictedSummary(requireContext())
}
}
override fun onPause() {
@@ -95,9 +83,10 @@ class DataSaverSummary : SettingsPreferenceFragment() {
}
}
override fun getPreferenceScreenResId() = R.xml.data_saver
override fun getMetricsCategory() = SettingsEnums.DATA_SAVER_SUMMARY
override fun getHelpResource() = R.string.help_url_data_saver
override fun getLogTag() = TAG
private val dataSaverBackendListener = object : DataSaverBackend.Listener {
override fun onDataSaverChanged(isDataSaving: Boolean) {
@@ -109,32 +98,7 @@ class DataSaverSummary : SettingsPreferenceFragment() {
}
companion object {
private const val KEY_UNRESTRICTED_ACCESS = "unrestricted_access"
@VisibleForTesting
suspend fun getUnrestrictedSummary(
context: Context,
appListRepository: AppListRepository =
AppListRepositoryImpl(context.applicationContext),
) = context.formatString(
R.string.data_saver_unrestricted_summary,
"count" to getAllowCount(context.applicationContext, appListRepository),
)
private suspend fun getAllowCount(context: Context, appListRepository: AppListRepository) =
withContext(Dispatchers.IO) {
coroutineScope {
val appsDeferred = async {
appListRepository.loadAndFilterApps(
userId = UserHandle.myUserId(),
isSystemApp = false,
)
}
val uidsAllowed = NetworkPolicyManager.from(context)
.getUidsWithPolicy(NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND)
appsDeferred.await().count { app -> app.uid in uidsAllowed }
}
}
private const val TAG = "DataSaverSummary"
private fun Context.isDataSaverVisible(): Boolean =
resources.getBoolean(R.bool.config_show_data_saver)

View File

@@ -354,13 +354,18 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
final NfcVerboseVendorLogPreferenceController nfcVerboseLogController =
getDevelopmentOptionsController(
NfcVerboseVendorLogPreferenceController.class);
final GraphicsDriverEnableAngleAsSystemDriverController enableAngleController =
getDevelopmentOptionsController(
GraphicsDriverEnableAngleAsSystemDriverController.class);
// If hardware offload isn't default value, we must reboot after disable
// developer options. Show a dialog for the user to confirm.
if ((a2dpController == null || a2dpController.isDefaultValue())
&& (leAudioController == null || leAudioController.isDefaultValue())
&& (nfcSnoopLogController == null || nfcSnoopLogController.isDefaultValue())
&& (nfcVerboseLogController == null
|| nfcVerboseLogController.isDefaultValue())) {
|| nfcVerboseLogController.isDefaultValue())
&& (enableAngleController == null
|| enableAngleController.isDefaultValue())) {
disableDeveloperOptions();
} else {
DisableDevSettingsDialogFragment.show(this /* host */);

View File

@@ -45,6 +45,10 @@ public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment
public static void show(DevelopmentSettingsDashboardFragment host) {
final DisableDevSettingsDialogFragment dialog = new DisableDevSettingsDialogFragment();
dialog.setTargetFragment(host, 0 /* requestCode */);
// We need to handle data changes and switch state based on which button user clicks,
// therefore we should enforce user to click one of the buttons
// by disallowing dialog dismiss through tapping outside of dialog bounds.
dialog.setCancelable(false);
final FragmentManager manager = host.getActivity().getSupportFragmentManager();
dialog.show(manager, TAG);
}

View File

@@ -81,6 +81,11 @@ public class GraphicsDriverEnableAngleAsSystemDriverController
this(context, fragment, new Injector());
}
private boolean isAngleSupported() {
return TextUtils.equals(
mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true");
}
@VisibleForTesting
GraphicsDriverEnableAngleAsSystemDriverController(
Context context, DevelopmentSettingsDashboardFragment fragment, Injector injector) {
@@ -118,38 +123,44 @@ public class GraphicsDriverEnableAngleAsSystemDriverController
this);
}
@Override
public void updateState(Preference preference) {
// set switch on if "persist.graphics.egl" is "angle" and angle is built in /vendor
// set switch off otherwise.
/** Return the default value of "persist.graphics.egl" */
public boolean isDefaultValue() {
if (!isAngleSupported()) {
return true;
}
final String currentGlesDriver =
mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver);
final boolean isAngleSupported =
TextUtils.equals(
mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true");
((SwitchPreference) mPreference).setChecked(isAngle && isAngleSupported);
((SwitchPreference) mPreference).setEnabled(isAngleSupported);
// default value of "persist.graphics.egl" is ""
return TextUtils.isEmpty(currentGlesDriver);
}
@Override
protected void onDeveloperOptionsSwitchEnabled() {
// only enable the switch if ro.gfx.angle.supported is true
// we use ro.gfx.angle.supported to indicate if ANGLE libs are installed under /vendor
final boolean isAngleSupported =
TextUtils.equals(
mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true");
((SwitchPreference) mPreference).setEnabled(isAngleSupported);
public void updateState(Preference preference) {
super.updateState(preference);
if (isAngleSupported()) {
// set switch on if "persist.graphics.egl" is "angle" and angle is built in /vendor
// set switch off otherwise.
final String currentGlesDriver =
mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver);
((SwitchPreference) mPreference).setChecked(isAngle);
} else {
mPreference.setEnabled(false);
((SwitchPreference) mPreference).setChecked(false);
}
}
@Override
protected void onDeveloperOptionsSwitchDisabled() {
// 1) set the persist.graphics.egl empty string
GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false);
// 2) reset the switch
((SwitchPreference) mPreference).setChecked(false);
// 3) disable switch
((SwitchPreference) mPreference).setEnabled(false);
// 1) disable the switch
super.onDeveloperOptionsSwitchDisabled();
if (isAngleSupported()) {
// 2) set the persist.graphics.egl empty string
GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false);
// 3) reset the switch
((SwitchPreference) mPreference).setChecked(false);
}
}
void toggleSwitchBack() {

View File

@@ -88,7 +88,7 @@ public class MobileNetworkRepository extends SubscriptionManager.OnSubscriptions
private List<MobileNetworkInfoEntity> mMobileNetworkInfoEntityList = new ArrayList<>();
private Context mContext;
private AirplaneModeObserver mAirplaneModeObserver;
private Uri mAirplaneModeSettingUri;
private DataRoamingObserver mDataRoamingObserver;
private MetricsFeatureProvider mMetricsFeatureProvider;
private Map<Integer, MobileDataContentObserver> mDataContentObserverMap = new HashMap<>();
private int mPhysicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
@@ -127,10 +127,13 @@ public class MobileNetworkRepository extends SubscriptionManager.OnSubscriptions
mUiccInfoDao = mMobileNetworkDatabase.mUiccInfoDao();
mMobileNetworkInfoDao = mMobileNetworkDatabase.mMobileNetworkInfoDao();
mAirplaneModeObserver = new AirplaneModeObserver(new Handler(Looper.getMainLooper()));
mAirplaneModeSettingUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
mDataRoamingObserver = new DataRoamingObserver(new Handler(Looper.getMainLooper()));
}
private class AirplaneModeObserver extends ContentObserver {
private Uri mAirplaneModeSettingUri =
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
AirplaneModeObserver(Handler handler) {
super(handler);
}
@@ -155,6 +158,46 @@ public class MobileNetworkRepository extends SubscriptionManager.OnSubscriptions
}
}
private class DataRoamingObserver extends ContentObserver {
private int mRegSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private String mBaseField = Settings.Global.DATA_ROAMING;
DataRoamingObserver(Handler handler) {
super(handler);
}
public void register(Context context, int subId) {
mRegSubId = subId;
String lastField = mBaseField;
createTelephonyManagerBySubId(subId);
TelephonyManager tm = mTelephonyManagerMap.get(subId);
if (tm.getSimCount() != 1) {
lastField += subId;
}
context.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(lastField), false, this);
}
public void unRegister(Context context) {
context.getContentResolver().unregisterContentObserver(this);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
TelephonyManager tm = mTelephonyManagerMap.get(mRegSubId);
if (tm == null) {
return;
}
sExecutor.execute(() -> {
insertMobileNetworkInfo(mContext, mRegSubId, tm);
});
boolean isDataRoamingEnabled = tm.isDataRoamingEnabled();
for (MobileNetworkCallback callback : sCallbacks) {
callback.onDataRoamingChanged(mRegSubId, isDataRoamingEnabled);
}
}
}
/**
* Register all callbacks and listener.
*
@@ -180,6 +223,7 @@ public class MobileNetworkRepository extends SubscriptionManager.OnSubscriptions
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
addRegisterBySubId(subId);
createTelephonyManagerBySubId(subId);
mDataRoamingObserver.register(mContext, subId);
}
}
@@ -251,6 +295,7 @@ public class MobileNetworkRepository extends SubscriptionManager.OnSubscriptions
if (sCallbacks.isEmpty()) {
mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
mAirplaneModeObserver.unRegister(mContext);
mDataRoamingObserver.unRegister(mContext);
mDataContentObserverMap.forEach((id, observer) -> {
observer.unRegister(mContext);
});
@@ -709,6 +754,12 @@ public class MobileNetworkRepository extends SubscriptionManager.OnSubscriptions
default void onAirplaneModeChanged(boolean enabled) {
}
/**
* Notify clients data roaming changed of subscription.
*/
default void onDataRoamingChanged(int subId, boolean enabled) {
}
default void onCallStateChanged(int state) {
}
}

View File

@@ -52,7 +52,6 @@ import com.android.settings.wifi.WifiPickerTrackerHelper;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
import com.android.settingslib.mobile.dataservice.UiccInfoEntity;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.utils.ThreadUtils;
@@ -441,8 +440,10 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
/** suppress full page if user is not admin */
@Override
protected boolean isPageSearchEnabled(Context context) {
return SubscriptionUtil.isSimHardwareVisible(context) &&
context.getSystemService(UserManager.class).isAdminUser();
boolean isAirplaneOff = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) == 0;
return isAirplaneOff && SubscriptionUtil.isSimHardwareVisible(context)
&& context.getSystemService(UserManager.class).isAdminUser();
}
};

View File

@@ -21,6 +21,7 @@ import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
@@ -34,6 +35,7 @@ import com.android.settings.network.telephony.TelephonyConstants.TelephonyManage
*/
public class PreferredNetworkModePreferenceController extends TelephonyBasePreferenceController
implements ListPreference.OnPreferenceChangeListener {
private static final String TAG = "PrefNetworkModeCtrl";
private CarrierConfigCache mCarrierConfigCache;
private TelephonyManager mTelephonyManager;
@@ -99,6 +101,10 @@ public class PreferredNetworkModePreferenceController extends TelephonyBasePrefe
}
private int getPreferredNetworkMode() {
if (mTelephonyManager == null) {
Log.w(TAG, "TelephonyManager is null");
return TelephonyManagerConstants.NETWORK_MODE_UNKNOWN;
}
return MobileNetworkUtils.getNetworkTypeFromRaf(
(int) mTelephonyManager.getAllowedNetworkTypesForReason(
TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));

View File

@@ -21,28 +21,23 @@ import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import android.content.Context;
import android.os.PersistableBundle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.network.GlobalSettingsChangeListener;
import com.android.settings.network.MobileNetworkRepository;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
import com.android.settingslib.mobile.dataservice.UiccInfoEntity;
import java.util.ArrayList;
import java.util.List;
@@ -52,7 +47,6 @@ import java.util.List;
*/
public class RoamingPreferenceController extends TelephonyTogglePreferenceController implements
LifecycleObserver, MobileNetworkRepository.MobileNetworkCallback {
private static final String TAG = "RoamingController";
private static final String DIALOG_TAG = "MobileDataDialog";
@@ -63,15 +57,6 @@ public class RoamingPreferenceController extends TelephonyTogglePreferenceContro
protected LifecycleOwner mLifecycleOwner;
private List<MobileNetworkInfoEntity> mMobileNetworkInfoEntityList = new ArrayList<>();
/**
* There're 2 listeners both activated at the same time.
* For project that access DATA_ROAMING, only first listener is functional.
* For project that access "DATA_ROAMING + subId", first listener will be stopped when receiving
* any onChange from second listener.
*/
private GlobalSettingsChangeListener mListener;
private GlobalSettingsChangeListener mListenerForSubId;
@VisibleForTesting
FragmentManager mFragmentManager;
MobileNetworkInfoEntity mMobileNetworkInfoEntity;
@@ -102,34 +87,11 @@ public class RoamingPreferenceController extends TelephonyTogglePreferenceContro
public void onStart() {
mMobileNetworkRepository.addRegister(mLifecycleOwner, this, mSubId);
mMobileNetworkRepository.updateEntity();
if (mListener == null) {
mListener = new GlobalSettingsChangeListener(mContext,
Settings.Global.DATA_ROAMING) {
public void onChanged(String field) {
updateState(mSwitchPreference);
}
};
}
stopMonitorSubIdSpecific();
if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
return;
}
mListenerForSubId = new GlobalSettingsChangeListener(mContext,
Settings.Global.DATA_ROAMING + mSubId) {
public void onChanged(String field) {
stopMonitor();
updateState(mSwitchPreference);
}
};
}
@OnLifecycleEvent(ON_STOP)
public void onStop() {
mMobileNetworkRepository.removeRegister(this);
stopMonitor();
stopMonitorSubIdSpecific();
}
@Override
@@ -219,20 +181,6 @@ public class RoamingPreferenceController extends TelephonyTogglePreferenceContro
dialogFragment.show(mFragmentManager, DIALOG_TAG);
}
private void stopMonitor() {
if (mListener != null) {
mListener.close();
mListener = null;
}
}
private void stopMonitorSubIdSpecific() {
if (mListenerForSubId != null) {
mListenerForSubId.close();
mListenerForSubId = null;
}
}
@VisibleForTesting
public void setMobileNetworkInfoEntity(MobileNetworkInfoEntity mobileNetworkInfoEntity) {
mMobileNetworkInfoEntity = mobileNetworkInfoEntity;
@@ -251,4 +199,13 @@ public class RoamingPreferenceController extends TelephonyTogglePreferenceContro
}
});
}
@Override
public void onDataRoamingChanged(int subId, boolean enabled) {
if (subId != mSubId) {
Log.d(TAG, "onDataRoamingChanged - wrong subId : " + subId + " / " + enabled);
return;
}
update();
}
}

View File

@@ -28,6 +28,7 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserManager;
import android.print.PrintJob;
import android.print.PrintJobId;
import android.print.PrintJobInfo;
@@ -45,6 +46,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.AsyncTaskLoader;
import androidx.loader.content.Loader;
@@ -92,6 +94,22 @@ public class PrintSettingsFragment extends ProfileSettingsPreferenceFragment
private PrintServicesController mPrintServicesController;
private Button mAddNewServiceButton;
@VisibleForTesting
boolean mIsUiRestricted;
public PrintSettingsFragment() {
super(UserManager.DISALLOW_PRINTING);
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.print_settings;
}
@Override
public int getMetricsCategory() {
@@ -107,12 +125,19 @@ public class PrintSettingsFragment extends ProfileSettingsPreferenceFragment
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = super.onCreateView(inflater, container, savedInstanceState);
addPreferencesFromResource(R.xml.print_settings);
mIsUiRestricted = isUiRestricted();
setupPreferences();
return root;
}
mActivePrintJobsCategory = (PreferenceCategory) findPreference(
PRINT_JOBS_CATEGORY);
mPrintServicesCategory = (PreferenceCategory) findPreference(
PRINT_SERVICES_CATEGORY);
@VisibleForTesting
void setupPreferences() {
if (mIsUiRestricted) {
return;
}
mActivePrintJobsCategory = (PreferenceCategory) findPreference(PRINT_JOBS_CATEGORY);
mPrintServicesCategory = (PreferenceCategory) findPreference(PRINT_SERVICES_CATEGORY);
getPreferenceScreen().removePreference(mActivePrintJobsCategory);
mPrintJobsController = new PrintJobsController();
@@ -120,20 +145,20 @@ public class PrintSettingsFragment extends ProfileSettingsPreferenceFragment
mPrintServicesController = new PrintServicesController();
getLoaderManager().initLoader(LOADER_ID_PRINT_SERVICES, null, mPrintServicesController);
return root;
}
@Override
public void onStart() {
super.onStart();
setHasOptionsMenu(true);
startSubSettingsIfNeeded();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setupEmptyViews();
}
@VisibleForTesting
void setupEmptyViews() {
if (mIsUiRestricted) {
return;
}
ViewGroup contentRoot = (ViewGroup) getListView().getParent();
View emptyView = getActivity().getLayoutInflater().inflate(
R.layout.empty_print_state, contentRoot, false);
@@ -152,6 +177,23 @@ public class PrintSettingsFragment extends ProfileSettingsPreferenceFragment
setEmptyView(emptyView);
}
@Override
public void onStart() {
super.onStart();
startSettings();
}
@VisibleForTesting
void startSettings() {
if (mIsUiRestricted) {
getPreferenceScreen().removeAll();
return;
}
setHasOptionsMenu(true);
startSubSettingsIfNeeded();
}
@Override
protected String getIntentActionString() {
return Settings.ACTION_PRINT_SETTINGS;

View File

@@ -27,13 +27,17 @@ import android.widget.AdapterView;
import android.widget.Spinner;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.dashboard.RestrictedDashboardFragment;
import com.android.settings.dashboard.profileselector.UserAdapter;
/**
* Base fragment class for per profile settings.
*/
public abstract class ProfileSettingsPreferenceFragment extends SettingsPreferenceFragment {
public abstract class ProfileSettingsPreferenceFragment extends RestrictedDashboardFragment {
public ProfileSettingsPreferenceFragment(String restrictionKey) {
super(restrictionKey);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {

View File

@@ -84,8 +84,10 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
WifiTetherSSIDPreferenceController mSSIDPreferenceController;
@VisibleForTesting
WifiTetherPasswordPreferenceController mPasswordPreferenceController;
private WifiTetherSecurityPreferenceController mSecurityPreferenceController;
private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController;
@VisibleForTesting
WifiTetherSecurityPreferenceController mSecurityPreferenceController;
@VisibleForTesting
WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController;
@VisibleForTesting
WifiTetherAutoOffPreferenceController mWifiTetherAutoOffPreferenceController;
@@ -276,15 +278,16 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
SoftApConfiguration buildNewConfig() {
SoftApConfiguration currentConfig = mWifiTetherViewModel.getSoftApConfiguration();
SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(currentConfig);
int securityType = (mWifiTetherViewModel.isSpeedFeatureAvailable())
? currentConfig.getSecurityType()
: mSecurityPreferenceController.getSecurityType();
configBuilder.setSsid(mSSIDPreferenceController.getSSID());
if (securityType != SoftApConfiguration.SECURITY_TYPE_OPEN) {
configBuilder.setPassphrase(
mPasswordPreferenceController.getPasswordValidated(securityType),
securityType);
}
int securityType =
mWifiTetherViewModel.isSpeedFeatureAvailable()
? currentConfig.getSecurityType()
: mSecurityPreferenceController.getSecurityType();
String passphrase =
securityType == SoftApConfiguration.SECURITY_TYPE_OPEN
? null
: mPasswordPreferenceController.getPasswordValidated(securityType);
configBuilder.setPassphrase(passphrase, securityType);
if (!mWifiTetherViewModel.isSpeedFeatureAvailable()) {
mMaxCompatibilityPrefController.setupMaximizeCompatibility(configBuilder);
}

View File

@@ -1,125 +0,0 @@
/*
* Copyright (C) 2017 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.applications;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.datausage.AppStateDataUsageBridge;
import com.android.settings.testutils.shadow.ShadowApplicationsState;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class, ShadowApplicationsState.class})
public class SpecialAppAccessPreferenceControllerTest {
private Context mContext;
@Mock
private ApplicationsState.Session mSession;
@Mock
private PreferenceScreen mScreen;
@Mock
private PackageManager mPackageManager;
private SpecialAppAccessPreferenceController mController;
private Preference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getApplicationContext()).thenReturn(mContext);
ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{0});
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(new ArrayList<ModuleInfo>()).when(mPackageManager).getInstalledModules(anyInt());
mController = new SpecialAppAccessPreferenceController(mContext, "test_key");
mPreference = new Preference(mContext);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.mSession = mSession;
}
@Test
public void getAvailabilityState_unsearchable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void updateState_shouldSetSummary() {
final ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
entry.hasLauncherEntry = true;
entry.info = new ApplicationInfo();
entry.extraInfo = new AppStateDataUsageBridge.DataUsageState(
true /* allowlisted */, false /* denylisted */);
apps.add(entry);
when(mSession.getAllApps()).thenReturn(apps);
mController.displayPreference(mScreen);
mController.onExtraInfoUpdated();
assertThat(mPreference.getSummary())
.isEqualTo(mContext.getResources().getQuantityString(
R.plurals.special_access_summary, 1, 1));
}
@Test
public void updateState_wrongExtraInfo_shouldNotIncludeInSummary() {
final ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
entry.hasLauncherEntry = true;
entry.info = new ApplicationInfo();
entry.extraInfo = new AppStateNotificationBridge.NotificationsSentState();
apps.add(entry);
when(mSession.getAllApps()).thenReturn(apps);
mController.displayPreference(mScreen);
mController.onExtraInfoUpdated();
assertThat(mPreference.getSummary())
.isEqualTo(mContext.getResources().getQuantityString(
R.plurals.special_access_summary, 0, 0));
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright (C) 2017 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.applications.specialaccess;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
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.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
public class DataSaverControllerTest {
private Context mContext;
private Resources mResources;
private DataSaverController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application.getApplicationContext());
mResources = spy(mContext.getResources());
when(mContext.getResources()).thenReturn(mResources);
mController = new DataSaverController(mContext, "key");
}
@Test
public void testDataSaver_byDefault_shouldBeShown() {
when(mResources.getBoolean(R.bool.config_show_data_saver)).thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
}
@Ignore
@Test
@Config(qualifiers = "mcc999")
public void testDataSaver_ifDisabledByCarrier_shouldNotBeShown() {
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void testDataSaver_ifDisabled_shouldNotBeShown() {
when(mResources.getBoolean(R.bool.config_show_data_saver)).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
}
}

View File

@@ -56,6 +56,7 @@ import android.os.Vibrator;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -314,11 +315,17 @@ public class FingerprintEnrollEnrollingTest {
@Test
public void fingerprintUdfpsOverlayEnrollment_descriptionViewGoneWithOverlap() {
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
doReturn(true).when(mActivity).hasOverlap(any(), any());
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
createActivity();
final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
final UdfpsEnrollEnrollingView defaultLayout = spy(
mActivity.findViewById(R.id.setup_wizard_layout));
doReturn(true).when(defaultLayout).hasOverlap(any(), any());
// Somehow spy doesn't work, and we need to call initView manually.
defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
mActivity.mUdfpsEnrollHelper,
mActivity.getSystemService(AccessibilityManager.class));
final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -328,11 +335,17 @@ public class FingerprintEnrollEnrollingTest {
@Test
public void fingerprintUdfpsOverlayEnrollment_descriptionViewVisibleWithoutOverlap() {
initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
doReturn(false).when(mActivity).hasOverlap(any(), any());
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
createActivity();
final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
final UdfpsEnrollEnrollingView defaultLayout = spy(
mActivity.findViewById(R.id.setup_wizard_layout));
doReturn(false).when(defaultLayout).hasOverlap(any(), any());
// Somehow spy doesn't work, and we need to call initView manually.
defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
mActivity.mUdfpsEnrollHelper,
mActivity.getSystemService(AccessibilityManager.class));
final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -578,7 +591,6 @@ public class FingerprintEnrollEnrollingTest {
mContext = spy(RuntimeEnvironment.application);
mActivity = spy(FingerprintEnrollEnrolling.class);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
when(mContext.getDisplay()).thenReturn(mMockDisplay);
when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);

View File

@@ -141,6 +141,7 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerTest {
@Test
public void onDeveloperOptionSwitchDisabled_shouldDisableAngleAsSystemDriver() {
ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
mController.onDeveloperOptionsSwitchDisabled();
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo("");
@@ -148,12 +149,14 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerTest {
@Test
public void onDeveloperOptionSwitchDisabled_preferenceShouldNotBeChecked() {
ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
mController.onDeveloperOptionsSwitchDisabled();
verify(mPreference).setChecked(false);
}
@Test
public void onDeveloperOptionsSwitchDisabled_preferenceShouldNotBeEnabled() {
ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true");
mController.onDeveloperOptionsSwitchDisabled();
verify(mPreference).setEnabled(false);
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.wifi.tether;
import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN;
import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
@@ -125,9 +126,13 @@ public class WifiTetherSettingsTest {
@Mock
private WifiTetherSSIDPreferenceController mSSIDPreferenceController;
@Mock
private WifiTetherSecurityPreferenceController mSecurityPreferenceController;
@Mock
private WifiTetherPasswordPreferenceController mPasswordPreferenceController;
@Mock
private WifiTetherAutoOffPreferenceController mWifiTetherAutoOffPreferenceController;
@Mock
private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController;
private WifiTetherSettings mSettings;
@@ -156,10 +161,13 @@ public class WifiTetherSettingsTest {
mSettings.mMainSwitchBar = mMainSwitchBar;
mSettings.mSSIDPreferenceController = mSSIDPreferenceController;
when(mSSIDPreferenceController.getSSID()).thenReturn(SSID);
mSettings.mSecurityPreferenceController = mSecurityPreferenceController;
when(mSecurityPreferenceController.getSecurityType()).thenReturn(SECURITY_TYPE_WPA3_SAE);
mSettings.mPasswordPreferenceController = mPasswordPreferenceController;
when(mPasswordPreferenceController.getPasswordValidated(anyInt())).thenReturn(PASSWORD);
mSettings.mWifiTetherAutoOffPreferenceController = mWifiTetherAutoOffPreferenceController;
when(mWifiTetherAutoOffPreferenceController.isEnabled()).thenReturn(true);
mSettings.mMaxCompatibilityPrefController = mMaxCompatibilityPrefController;
mSettings.mWifiTetherViewModel = mWifiTetherViewModel;
when(mSettings.findPreference(KEY_WIFI_HOTSPOT_SECURITY)).thenReturn(mWifiHotspotSecurity);
when(mSettings.findPreference(KEY_WIFI_HOTSPOT_SPEED)).thenReturn(mWifiHotspotSpeed);
@@ -359,6 +367,23 @@ public class WifiTetherSettingsTest {
assertThat(newConfig.getBand()).isEqualTo(currentConfig.getBand());
}
@Test
public void buildNewConfig_securityTypeChangeToOpen_setSecurityTypeCorrectly() {
SoftApConfiguration currentConfig = new SoftApConfiguration.Builder()
.setPassphrase(PASSWORD, SECURITY_TYPE_WPA3_SAE)
.setBand(BAND_2GHZ_5GHZ_6GHZ)
.build();
when(mWifiTetherViewModel.getSoftApConfiguration()).thenReturn(currentConfig);
when(mWifiTetherViewModel.isSpeedFeatureAvailable()).thenReturn(false);
doNothing().when(mMaxCompatibilityPrefController)
.setupMaximizeCompatibility(any(SoftApConfiguration.Builder.class));
when(mSecurityPreferenceController.getSecurityType()).thenReturn(SECURITY_TYPE_OPEN);
SoftApConfiguration newConfig = mSettings.buildNewConfig();
assertThat(newConfig.getSecurityType()).isEqualTo(SECURITY_TYPE_OPEN);
}
@Test
public void onRestartingChanged_restartingFalse_setLoadingFalse() {
doNothing().when(mSettings).setLoading(anyBoolean(), anyBoolean());

View File

@@ -14,15 +14,19 @@
* limitations under the License.
*/
package com.android.settings.datausage
package com.android.settings.applications.specialaccess
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.res.Resources
import android.net.NetworkPolicyManager
import android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.DataSaverSummary.Companion.getUnrestrictedSummary
import com.android.settings.R
import com.android.settings.applications.specialaccess.DataSaverController.Companion.getUnrestrictedSummary
import com.android.settings.core.BasePreferenceController.AVAILABLE
import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
import com.android.settingslib.spaprivileged.model.app.AppListRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -41,20 +45,41 @@ import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class DataSaverSummaryTest {
class DataSaverControllerTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Spy
private val resources: Resources = context.resources
@Mock
private lateinit var networkPolicyManager: NetworkPolicyManager
@Mock
private lateinit var dataSaverController: DataSaverController
@Before
fun setUp() {
whenever(context.applicationContext).thenReturn(context)
whenever(context.resources).thenReturn(resources)
whenever(NetworkPolicyManager.from(context)).thenReturn(networkPolicyManager)
dataSaverController = DataSaverController(context, "key")
}
@Test
fun getAvailabilityStatus_whenConfigOn_available() {
whenever(resources.getBoolean(R.bool.config_show_data_saver)).thenReturn(true)
assertThat(dataSaverController.availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
fun getAvailabilityStatus_whenConfigOff_unsupportedOnDevice() {
whenever(resources.getBoolean(R.bool.config_show_data_saver)).thenReturn(false)
assertThat(dataSaverController.availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE)
}
@Test

View File

@@ -57,7 +57,7 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
private GraphicsDriverEnableAngleAsSystemDriverController mController;
// Signal to wait for SystemProperty values changed
private class PropertyChangeSignal {
private static class PropertyChangeSignal {
private CountDownLatch mCountDownLatch;
private Runnable mCountDownJob;
@@ -217,23 +217,7 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
}
@Test
public void onDeveloperOptionSwitchEnabled_angleSupported_PreferenceShouldEnabled() {
when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
.thenReturn("true");
mController.onDeveloperOptionsSwitchEnabled();
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void onDeveloperOptionSwitchEnabled_angleNotSupported_PrefenceShouldDisabled() {
when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
.thenReturn("false");
mController.onDeveloperOptionsSwitchEnabled();
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void onDeveloperOptionSwitchDisabled_angleIsNotSystemGLESDriver() {
public void onDeveloperOptionSwitchDisabled_angleShouldNotBeSystemGLESDriver() {
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
@@ -242,6 +226,8 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
// Test that onDeveloperOptionSwitchDisabled,
// persist.graphics.egl updates to ""
when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
.thenReturn("true");
mController.onDeveloperOptionsSwitchDisabled();
propertyChangeSignal1.wait(100);
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
@@ -253,12 +239,16 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
@Test
public void onDeveloperOptionSwitchDisabled_PreferenceShouldNotBeChecked() {
when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
.thenReturn("true");
mController.onDeveloperOptionsSwitchDisabled();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void onDeveloperOptionSwitchDisabled_PreferenceShouldDisabled() {
when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
.thenReturn("true");
mController.onDeveloperOptionsSwitchDisabled();
assertThat(mPreference.isEnabled()).isFalse();
}

View File

@@ -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.print;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.os.Looper;
import android.view.View;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
public class PrintSettingsFragmentTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Spy
private final Context mContext = ApplicationProvider.getApplicationContext();
private PrintSettingsFragment mFragment;
private PreferenceManager mPreferenceManager;
private PreferenceScreen mPreferenceScreen;
@Before
public void setUp() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
mPreferenceManager = new PreferenceManager(mContext);
mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mFragment = spy(new PrintSettingsFragment());
doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
});
}
@Test
public void setupPreferences_uiIsRestricted_doNotAddPreferences() {
mFragment.mIsUiRestricted = true;
mFragment.setupPreferences();
verify(mFragment, never()).findPreference(any(CharSequence.class));
}
@Test
public void setupEmptyViews_uiIsRestricted_doNotSetEmptyView() {
mFragment.mIsUiRestricted = true;
mFragment.setupEmptyViews();
verify(mFragment, never()).setEmptyView(any(View.class));
}
@Test
public void startSettings_uiIsRestricted_removeAllPreferences() {
mFragment.mIsUiRestricted = true;
mFragment.startSettings();
assertThat(mPreferenceScreen.getPreferenceCount()).isEqualTo(0);
verify(mFragment, never()).setHasOptionsMenu(true);
}
}