Merge "Use shared lib for settings v2." into main
diff --git a/res/layout/accessibility_launch_activity_preference.xml b/res/layout/accessibility_launch_activity_preference.xml
index 6791206..f3841e2 100644
--- a/res/layout/accessibility_launch_activity_preference.xml
+++ b/res/layout/accessibility_launch_activity_preference.xml
@@ -21,8 +21,7 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingVertical="@dimen/settingslib_switchbar_margin"
- android:background="@android:color/transparent"
- android:importantForAccessibility="no">
+ android:background="@android:color/transparent">
<FrameLayout
android:layout_height="wrap_content"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 44a44e8..3d05af3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -641,7 +641,7 @@
</string>
<!-- Location settings footer link content description [CHAR LIMIT=NONE] -->
<string name="location_settings_footer_learn_more_content_description">
- Learn more about Location Settings.
+ Learn more about Location settings
</string>
<!-- Main Settings screen setting option title for the item to take you to the accounts screen [CHAR LIMIT=22] -->
@@ -12460,6 +12460,8 @@
<!-- Developer settings: select Grammatical gender dialog title [CHAR LIMIT=50]-->
<string name="grammatical_gender_dialog_title">Select Grammatical gender</string>
- <!-- Developer settings: Title for the screen allowing user to control Quarantined apps [CHAR LIMIT=50] -->
- <string name="quarantined_apps_title">Quarantined Apps</string>
+ <!-- Do not translate. Developer settings: Title for the screen allowing user to control Quarantined apps [CHAR LIMIT=50] -->
+ <string name="quarantined_apps_title" translatable="false">Quarantined Apps</string>
+ <!-- Do not translate. Developer settings: Button to unquarantine an app [CHAR LIMIT=20] -->
+ <string name="unquarantine_app_button" translatable="false">Unquarantine app</string>
</resources>
diff --git a/res/xml/data_usage_list.xml b/res/xml/data_usage_list.xml
index 791fc86..62456ed 100644
--- a/res/xml/data_usage_list.xml
+++ b/res/xml/data_usage_list.xml
@@ -22,7 +22,9 @@
android:title="@string/summary_placeholder">
<com.android.settings.datausage.ChartDataUsagePreference
- android:key="chart_data" />
+ android:key="chart_data"
+ settings:controller="com.android.settings.datausage.ChartDataUsagePreferenceController"
+ />
<Preference
android:key="non_carrier_data_usage_warning"
diff --git a/res/xml/terms_of_address.xml b/res/xml/terms_of_address.xml
new file mode 100644
index 0000000..a29a55c3
--- /dev/null
+++ b/res/xml/terms_of_address.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/terms_of_address_title">
+
+ <com.android.settingslib.widget.TopIntroPreference
+ android:title="@string/terms_of_address_intro_title"
+ android:persistent="false"/>
+
+ <com.android.settings.widget.TickButtonPreference
+ android:key="key_terms_of_address_not_specified"
+ android:title="@string/terms_of_address_not_specified"
+ settings:controller="com.android.settings.localepicker.TermsOfAddressNotSpecifiedController"/>
+
+ <com.android.settings.widget.TickButtonPreference
+ android:key="key_terms_of_address_feminine"
+ android:title="@string/terms_of_address_feminine"
+ settings:controller="com.android.settings.localepicker.TermsOfAddressFeminineController"/>
+
+ <com.android.settings.widget.TickButtonPreference
+ android:key="key_terms_of_address_masculine"
+ android:title="@string/terms_of_address_masculine"
+ settings:controller="com.android.settings.localepicker.TermsOfAddressMasculineController"/>
+
+ <com.android.settings.widget.TickButtonPreference
+ android:key="key_terms_of_address_neutral"
+ android:title="@string/terms_of_address_neutral"
+ settings:controller="com.android.settings.localepicker.TermsOfAddressNeutralController"/>
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java
index 2c157c4..b940dc8 100644
--- a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java
+++ b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java
@@ -16,6 +16,7 @@
package com.android.settings.applications.appcompat;
+import static android.os.UserHandle.getUserHandleForUid;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
@@ -26,8 +27,8 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.os.RemoteException;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
@@ -40,7 +41,6 @@
import com.google.common.annotations.VisibleForTesting;
-import java.util.List;
import java.util.Map;
/**
@@ -63,15 +63,12 @@
private final Context mContext;
private final IPackageManager mIPm;
/** Apps that have launcher entry defined in manifest */
- private final List<ResolveInfo> mInfoHasLauncherEntryList;
private final Map<Integer, String> mUserAspectRatioMap;
private final Map<Integer, CharSequence> mUserAspectRatioA11yMap;
public UserAspectRatioManager(@NonNull Context context) {
mContext = context;
mIPm = AppGlobals.getPackageManager();
- mInfoHasLauncherEntryList = mContext.getPackageManager().queryIntentActivities(
- UserAspectRatioManager.LAUNCHER_ENTRY_INTENT, PackageManager.GET_META_DATA);
mUserAspectRatioA11yMap = new ArrayMap<>();
mUserAspectRatioMap = getUserMinAspectRatioMapping();
}
@@ -159,9 +156,7 @@
Boolean appAllowsUserAspectRatioOverride = readComponentProperty(
mContext.getPackageManager(), app.packageName,
PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
- boolean hasLauncherEntry = mInfoHasLauncherEntryList.stream()
- .anyMatch(info -> info.activityInfo.packageName.equals(app.packageName));
- return !FALSE.equals(appAllowsUserAspectRatioOverride) && hasLauncherEntry;
+ return !FALSE.equals(appAllowsUserAspectRatioOverride) && hasLauncherEntry(app);
}
/**
@@ -178,6 +173,15 @@
DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN);
}
+ LauncherApps getLauncherApps() {
+ return mContext.getSystemService(LauncherApps.class);
+ }
+
+ private boolean hasLauncherEntry(@NonNull ApplicationInfo app) {
+ return !getLauncherApps().getActivityList(app.packageName, getUserHandleForUid(app.uid))
+ .isEmpty();
+ }
+
private static boolean getValueFromDeviceConfig(String name, boolean defaultValue) {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER, name, defaultValue);
}
@@ -267,9 +271,4 @@
}
return null;
}
-
- @VisibleForTesting
- void addInfoHasLauncherEntry(@NonNull ResolveInfo infoHasLauncherEntry) {
- mInfoHasLauncherEntryList.add(infoHasLauncherEntry);
- }
}
diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java
index c3be0aa..33307a3 100644
--- a/src/com/android/settings/datausage/AppDataUsage.java
+++ b/src/com/android/settings/datausage/AppDataUsage.java
@@ -31,6 +31,7 @@
import android.util.ArraySet;
import android.util.IconDrawableFactory;
import android.util.Log;
+import android.util.Range;
import android.view.View;
import android.widget.AdapterView;
@@ -472,7 +473,9 @@
List<NetworkCycleDataForUid> data) {
mUsageData = data;
mCycle.setOnItemSelectedListener(mCycleListener);
- mCycleAdapter.updateCycleList(data);
+ mCycleAdapter.updateCycleList(data.stream()
+ .map(cycle -> new Range<>(cycle.getStartTime(), cycle.getEndTime()))
+ .toList());
if (mSelectedCycle > 0L) {
final int numCycles = data.size();
int position = 0;
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreference.java b/src/com/android/settings/datausage/ChartDataUsagePreference.java
index fa467d2..e5a7307 100644
--- a/src/com/android/settings/datausage/ChartDataUsagePreference.java
+++ b/src/com/android/settings/datausage/ChartDataUsagePreference.java
@@ -26,15 +26,17 @@
import android.util.DataUnit;
import android.util.SparseIntArray;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.Utils;
+import com.android.settings.datausage.lib.NetworkCycleChartData;
+import com.android.settings.datausage.lib.NetworkUsageData;
import com.android.settings.widget.UsageView;
-import com.android.settingslib.net.NetworkCycleChartData;
-import com.android.settingslib.net.NetworkCycleData;
import java.util.ArrayList;
import java.util.Comparator;
@@ -51,8 +53,8 @@
private final int mWarningColor;
private final int mLimitColor;
- private Resources mResources;
- private NetworkPolicy mPolicy;
+ private final Resources mResources;
+ @Nullable private NetworkPolicy mPolicy;
private long mStart;
private long mEnd;
private NetworkCycleChartData mNetworkCycleChartData;
@@ -67,18 +69,16 @@
}
@Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- final UsageView chart = (UsageView) holder.findViewById(R.id.data_usage);
- if (mNetworkCycleChartData == null) {
- return;
- }
-
+ final UsageView chart = holder.itemView.requireViewById(R.id.data_usage);
final int top = getTop();
chart.clearPaths();
chart.configureGraph(toInt(mEnd - mStart), top);
- calcPoints(chart, mNetworkCycleChartData.getUsageBuckets());
- setupContentDescription(chart, mNetworkCycleChartData.getUsageBuckets());
+ if (mNetworkCycleChartData != null) {
+ calcPoints(chart, mNetworkCycleChartData.getDailyUsage());
+ setupContentDescription(chart, mNetworkCycleChartData.getDailyUsage());
+ }
chart.setBottomLabels(new CharSequence[] {
Utils.formatDateRange(getContext(), mStart, mStart),
Utils.formatDateRange(getContext(), mEnd, mEnd),
@@ -88,23 +88,21 @@
}
public int getTop() {
- final long totalData = mNetworkCycleChartData.getTotalUsage();
+ final long totalData =
+ mNetworkCycleChartData != null ? mNetworkCycleChartData.getTotal().getUsage() : 0;
final long policyMax =
mPolicy != null ? Math.max(mPolicy.limitBytes, mPolicy.warningBytes) : 0;
return (int) (Math.max(totalData, policyMax) / RESOLUTION);
}
@VisibleForTesting
- void calcPoints(UsageView chart, List<NetworkCycleData> usageSummary) {
- if (usageSummary == null) {
- return;
- }
+ void calcPoints(UsageView chart, @NonNull List<NetworkUsageData> usageSummary) {
final SparseIntArray points = new SparseIntArray();
points.put(0, 0);
final long now = System.currentTimeMillis();
long totalData = 0;
- for (NetworkCycleData data : usageSummary) {
+ for (NetworkUsageData data : usageSummary) {
final long startTime = data.getStartTime();
if (startTime > now) {
break;
@@ -112,7 +110,7 @@
final long endTime = data.getEndTime();
// increment by current bucket total
- totalData += data.getTotalUsage();
+ totalData += data.getUsage();
if (points.size() == 1) {
points.put(toInt(startTime - mStart) - 1, -1);
@@ -125,7 +123,8 @@
}
}
- private void setupContentDescription(UsageView chart, List<NetworkCycleData> usageSummary) {
+ private void setupContentDescription(
+ UsageView chart, @NonNull List<NetworkUsageData> usageSummary) {
final Context context = getContext();
final StringBuilder contentDescription = new StringBuilder();
final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH;
@@ -137,7 +136,7 @@
.getString(R.string.data_usage_chart_brief_content_description, startDate, endDate);
contentDescription.append(briefContentDescription);
- if (usageSummary == null || usageSummary.isEmpty()) {
+ if (usageSummary.isEmpty()) {
final String noDataContentDescription = mResources
.getString(R.string.data_usage_chart_no_data_content_description);
contentDescription.append(noDataContentDescription);
@@ -170,17 +169,17 @@
* Collect the date of the same percentage, e.g., Aug 2 to Aug 22: 0%; Aug 23: 2%.
*/
@VisibleForTesting
- List<DataUsageSummaryNode> getDensedStatsData(List<NetworkCycleData> usageSummary) {
+ List<DataUsageSummaryNode> getDensedStatsData(@NonNull List<NetworkUsageData> usageSummary) {
final List<DataUsageSummaryNode> dataUsageSummaryNodes = new ArrayList<>();
final long overallDataUsage = Math.max(1L, usageSummary.stream()
- .mapToLong(NetworkCycleData::getTotalUsage).sum());
+ .mapToLong(NetworkUsageData::getUsage).sum());
long cumulatedDataUsage = 0L;
- int cumulatedDataUsagePercentage = 0;
// Collect List of DataUsageSummaryNode for data usage percentage information.
- for (NetworkCycleData data : usageSummary) {
- cumulatedDataUsage += data.getTotalUsage();
- cumulatedDataUsagePercentage = (int) ((cumulatedDataUsage * 100) / overallDataUsage);
+ for (NetworkUsageData data : usageSummary) {
+ cumulatedDataUsage += data.getUsage();
+ int cumulatedDataUsagePercentage =
+ (int) ((cumulatedDataUsage * 100) / overallDataUsage);
final DataUsageSummaryNode node = new DataUsageSummaryNode(data.getStartTime(),
data.getEndTime(), cumulatedDataUsagePercentage);
@@ -268,8 +267,9 @@
}
if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) {
- chart.setDividerLoc((int) (policy.warningBytes / RESOLUTION));
- float weight = policy.warningBytes / RESOLUTION / (float) top;
+ int dividerLoc = (int) (policy.warningBytes / RESOLUTION);
+ chart.setDividerLoc(dividerLoc);
+ float weight = dividerLoc / (float) top;
float above = 1 - weight;
chart.setSideLabelWeights(above, weight);
middleVisibility = mWarningColor;
@@ -289,15 +289,21 @@
return new SpannableStringBuilder().append(label, new ForegroundColorSpan(mLimitColor), 0);
}
- public void setNetworkPolicy(NetworkPolicy policy) {
+ /** Sets network policy. */
+ public void setNetworkPolicy(@Nullable NetworkPolicy policy) {
mPolicy = policy;
notifyChanged();
}
+ /** Sets time. */
+ public void setTime(long start, long end) {
+ mStart = start;
+ mEnd = end;
+ notifyChanged();
+ }
+
public void setNetworkCycleData(NetworkCycleChartData data) {
mNetworkCycleChartData = data;
- mStart = data.getStartTime();
- mEnd = data.getEndTime();
notifyChanged();
}
}
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt b/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
new file mode 100644
index 0000000..0479be4
--- /dev/null
+++ b/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.datausage
+
+import android.content.Context
+import android.net.NetworkTemplate
+import androidx.annotation.OpenForTesting
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.PreferenceScreen
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.datausage.lib.INetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkCycleDataRepository
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+@OpenForTesting
+open class ChartDataUsagePreferenceController(context: Context, preferenceKey: String) :
+ BasePreferenceController(context, preferenceKey) {
+
+ private lateinit var repository: INetworkCycleDataRepository
+ private lateinit var preference: ChartDataUsagePreference
+ private lateinit var lifecycleScope: LifecycleCoroutineScope
+
+ open fun init(template: NetworkTemplate) {
+ this.repository = NetworkCycleDataRepository(mContext, template)
+ }
+
+ @VisibleForTesting
+ fun init(repository: INetworkCycleDataRepository) {
+ this.repository = repository
+ }
+
+ override fun getAvailabilityStatus() = AVAILABLE
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)!!
+ }
+
+ override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+ lifecycleScope = viewLifecycleOwner.lifecycleScope
+ }
+
+ /**
+ * Sets whether billing cycle modifiable.
+ *
+ * Don't bind warning / limit sweeps if not modifiable.
+ */
+ open fun setBillingCycleModifiable(isModifiable: Boolean) {
+ preference.setNetworkPolicy(
+ if (isModifiable) repository.getPolicy() else null
+ )
+ }
+
+ fun update(startTime: Long, endTime: Long) {
+ preference.setTime(startTime, endTime)
+ lifecycleScope.launch {
+ val chartData = withContext(Dispatchers.Default) {
+ repository.querySummary(startTime, endTime)
+ }
+ preference.setNetworkCycleData(chartData)
+ }
+ }
+}
diff --git a/src/com/android/settings/datausage/CycleAdapter.java b/src/com/android/settings/datausage/CycleAdapter.java
index 90a2035..7cff05e 100644
--- a/src/com/android/settings/datausage/CycleAdapter.java
+++ b/src/com/android/settings/datausage/CycleAdapter.java
@@ -14,9 +14,9 @@
package com.android.settings.datausage;
import android.content.Context;
+import android.util.Range;
import com.android.settings.Utils;
-import com.android.settingslib.net.NetworkCycleData;
import com.android.settingslib.widget.SettingsSpinnerAdapter;
import java.util.List;
@@ -62,15 +62,15 @@
* Rebuild list based on network data. Always selects the newest item,
* updating the inspection range on chartData.
*/
- public void updateCycleList(List<? extends NetworkCycleData> cycleData) {
+ public void updateCycleList(List<Range<Long>> cycleData) {
// stash away currently selected cycle to try restoring below
final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
mSpinner.getSelectedItem();
clear();
final Context context = getContext();
- for (NetworkCycleData data : cycleData) {
- add(new CycleAdapter.CycleItem(context, data.getStartTime(), data.getEndTime()));
+ for (Range<Long> cycle : cycleData) {
+ add(new CycleAdapter.CycleItem(context, cycle.getLower(), cycle.getUpper()));
}
// force pick the current cycle (first item)
diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java
deleted file mode 100644
index e7345ab..0000000
--- a/src/com/android/settings/datausage/DataUsageList.java
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2018 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.datausage;
-
-import android.app.Activity;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.Intent;
-import android.net.NetworkPolicy;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.telephony.SubscriptionManager;
-import android.util.EventLog;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.Lifecycle;
-import androidx.loader.app.LoaderManager.LoaderCallbacks;
-import androidx.loader.content.Loader;
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.datausage.lib.BillingCycleRepository;
-import com.android.settings.network.MobileDataEnabledListener;
-import com.android.settings.network.MobileNetworkRepository;
-import com.android.settings.widget.LoadingViewController;
-import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
-import com.android.settingslib.net.NetworkCycleChartData;
-import com.android.settingslib.net.NetworkCycleChartDataLoader;
-import com.android.settingslib.utils.ThreadUtils;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-import kotlin.Unit;
-
-/**
- * Panel showing data usage history across various networks, including options
- * to inspect based on usage cycle and control through {@link NetworkPolicy}.
- */
-public class DataUsageList extends DataUsageBaseFragment
- implements MobileDataEnabledListener.Client {
-
- static final String EXTRA_SUB_ID = "sub_id";
- static final String EXTRA_NETWORK_TEMPLATE = "network_template";
-
- private static final String TAG = "DataUsageList";
- private static final boolean LOGD = false;
-
- private static final String KEY_USAGE_AMOUNT = "usage_amount";
- private static final String KEY_CHART_DATA = "chart_data";
- private static final String KEY_TEMPLATE = "template";
- private static final String KEY_APP = "app";
-
- @VisibleForTesting
- static final int LOADER_CHART_DATA = 2;
-
- @VisibleForTesting
- MobileDataEnabledListener mDataStateListener;
-
- @VisibleForTesting
- NetworkTemplate mTemplate;
- @VisibleForTesting
- int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- @VisibleForTesting
- LoadingViewController mLoadingViewController;
-
- private ChartDataUsagePreference mChart;
-
- @Nullable
- private List<NetworkCycleChartData> mCycleData;
-
- // Spinner will keep the selected cycle even after paused, this only keeps the displayed cycle,
- // which need be cleared when resumed.
- private CycleAdapter.CycleItem mLastDisplayedCycle;
- private Preference mUsageAmount;
- private MobileNetworkRepository mMobileNetworkRepository;
- private SubscriptionInfoEntity mSubscriptionInfoEntity;
- private DataUsageListAppsController mDataUsageListAppsController;
- private BillingCycleRepository mBillingCycleRepository;
- @VisibleForTesting
- DataUsageListHeaderController mDataUsageListHeaderController;
-
- private boolean mIsBillingCycleModifiable = false;
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.DATA_USAGE_LIST;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (isGuestUser(getContext())) {
- Log.e(TAG, "This setting isn't available for guest user");
- EventLog.writeEvent(0x534e4554, "262741858", -1 /* UID */, "Guest user");
- finish();
- return;
- }
-
- final Activity activity = getActivity();
- mBillingCycleRepository = createBillingCycleRepository();
- if (!mBillingCycleRepository.isBandwidthControlEnabled()) {
- Log.w(TAG, "No bandwidth control; leaving");
- activity.finish();
- return;
- }
-
- mUsageAmount = findPreference(KEY_USAGE_AMOUNT);
- mChart = findPreference(KEY_CHART_DATA);
-
- processArgument();
- if (mTemplate == null) {
- Log.e(TAG, "No template; leaving");
- finish();
- return;
- }
- updateSubscriptionInfoEntity();
- mDataStateListener = new MobileDataEnabledListener(activity, this);
- mDataUsageListAppsController = use(DataUsageListAppsController.class);
- mDataUsageListAppsController.init(mTemplate);
- }
-
- @VisibleForTesting
- @NonNull
- BillingCycleRepository createBillingCycleRepository() {
- return new BillingCycleRepository(requireContext());
- }
-
- @Override
- public void onViewCreated(@NonNull View v, Bundle savedInstanceState) {
- super.onViewCreated(v, savedInstanceState);
-
- mDataUsageListHeaderController = new DataUsageListHeaderController(
- setPinnedHeaderView(R.layout.apps_filter_spinner),
- mTemplate,
- getMetricsCategory(),
- (cycle, position) -> {
- updateSelectedCycle(cycle, position);
- return Unit.INSTANCE;
- }
- );
-
- mLoadingViewController = new LoadingViewController(
- getView().findViewById(R.id.loading_container), getListView());
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mLoadingViewController.showLoadingViewDelayed();
- mDataStateListener.start(mSubId);
- mLastDisplayedCycle = null;
- updatePolicy();
-
- // kick off loader for network history
- // TODO: consider chaining two loaders together instead of reloading
- // network history when showing app detail.
- getLoaderManager().restartLoader(LOADER_CHART_DATA,
- buildArgs(mTemplate), mNetworkCycleDataCallbacks);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mDataStateListener.stop();
-
- getLoaderManager().destroyLoader(LOADER_CHART_DATA);
- }
-
- @Override
- protected int getPreferenceScreenResId() {
- return R.xml.data_usage_list;
- }
-
- @Override
- protected String getLogTag() {
- return TAG;
- }
-
- void processArgument() {
- final Bundle args = getArguments();
- if (args != null) {
- mSubId = args.getInt(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- mTemplate = args.getParcelable(EXTRA_NETWORK_TEMPLATE);
- }
- if (mTemplate == null && mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- final Intent intent = getIntent();
- mSubId = intent.getIntExtra(Settings.EXTRA_SUB_ID,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- mTemplate = intent.getParcelableExtra(Settings.EXTRA_NETWORK_TEMPLATE);
-
- if (mTemplate == null) {
- Optional<NetworkTemplate> mobileNetworkTemplateFromSim =
- DataUsageUtils.getMobileNetworkTemplateFromSubId(getContext(), getIntent());
- if (mobileNetworkTemplateFromSim.isPresent()) {
- mTemplate = mobileNetworkTemplateFromSim.get();
- }
- }
- }
- }
-
- @VisibleForTesting
- void updateSubscriptionInfoEntity() {
- mMobileNetworkRepository = MobileNetworkRepository.getInstance(getContext());
- ThreadUtils.postOnBackgroundThread(() -> {
- mSubscriptionInfoEntity = mMobileNetworkRepository.getSubInfoById(
- String.valueOf(mSubId));
- });
- }
-
- /**
- * Implementation of {@code MobileDataEnabledListener.Client}
- */
- public void onMobileDataEnabledChange() {
- updatePolicy();
- }
-
- private Bundle buildArgs(NetworkTemplate template) {
- final Bundle args = new Bundle();
- args.putParcelable(KEY_TEMPLATE, template);
- args.putParcelable(KEY_APP, null);
- return args;
- }
-
- /**
- * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
- * current {@link #mTemplate}.
- */
- @VisibleForTesting
- void updatePolicy() {
- mIsBillingCycleModifiable = isBillingCycleModifiable();
- if (mIsBillingCycleModifiable) {
- mChart.setNetworkPolicy(services.mPolicyEditor.getPolicy(mTemplate));
- } else {
- mChart.setNetworkPolicy(null); // don't bind warning / limit sweeps
- }
- updateConfigButtonVisibility();
- }
-
- @VisibleForTesting
- boolean isBillingCycleModifiable() {
- return mBillingCycleRepository.isModifiable(mSubId)
- && SubscriptionManager.from(requireContext())
- .getActiveSubscriptionInfo(mSubId) != null;
- }
-
- private void updateConfigButtonVisibility() {
- mDataUsageListHeaderController.setConfigButtonVisible(
- mIsBillingCycleModifiable && mCycleData != null);
- }
-
- /**
- * Updates the chart and detail data when initial loaded or selected cycle changed.
- */
- private void updateSelectedCycle(CycleAdapter.CycleItem cycle, int position) {
- // Avoid from updating UI after #onStop.
- if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
- return;
- }
-
- // Avoid from updating UI when async query still on-going.
- // This could happen when a request from #onMobileDataEnabledChange.
- if (mCycleData == null) {
- return;
- }
-
- if (Objects.equals(cycle, mLastDisplayedCycle)) {
- // Avoid duplicate update to avoid page flash.
- return;
- }
- mLastDisplayedCycle = cycle;
-
- if (LOGD) {
- Log.d(TAG, "showing cycle " + cycle + ", [start=" + cycle.start + ", end="
- + cycle.end + "]");
- }
-
- // update chart to show selected cycle, and update detail data
- // to match updated sweep bounds.
- NetworkCycleChartData cycleChartData = mCycleData.get(position);
- mChart.setNetworkCycleData(cycleChartData);
-
- updateDetailData(cycleChartData);
- }
-
- /**
- * Update details based on {@link #mChart} inspection range depending on
- * current mode. Updates {@link #mAdapter} with sorted list
- * of applications data usage.
- */
- private void updateDetailData(NetworkCycleChartData cycleChartData) {
- if (LOGD) Log.d(TAG, "updateDetailData()");
-
- // kick off loader for detailed stats
- mDataUsageListAppsController.update(
- mSubscriptionInfoEntity == null ? null : mSubscriptionInfoEntity.carrierId,
- cycleChartData.getStartTime(),
- cycleChartData.getEndTime()
- );
-
- final long totalBytes = cycleChartData.getTotalUsage();
- final CharSequence totalPhrase = DataUsageUtils.formatDataUsage(getActivity(), totalBytes);
- mUsageAmount.setTitle(getString(R.string.data_used_template, totalPhrase));
- }
-
- @VisibleForTesting
- final LoaderCallbacks<List<NetworkCycleChartData>> mNetworkCycleDataCallbacks =
- new LoaderCallbacks<>() {
- @Override
- @NonNull
- public Loader<List<NetworkCycleChartData>> onCreateLoader(int id, Bundle args) {
- return NetworkCycleChartDataLoader.builder(getContext())
- .setNetworkTemplate(mTemplate)
- .build();
- }
-
- @Override
- public void onLoadFinished(@NonNull Loader<List<NetworkCycleChartData>> loader,
- List<NetworkCycleChartData> data) {
- mLoadingViewController.showContent(false /* animate */);
- mCycleData = data;
- mDataUsageListHeaderController.updateCycleData(mCycleData);
- updateConfigButtonVisibility();
- mDataUsageListAppsController.setCycleData(mCycleData);
- }
-
- @Override
- public void onLoaderReset(@NonNull Loader<List<NetworkCycleChartData>> loader) {
- mCycleData = null;
- }
- };
-
- private static boolean isGuestUser(Context context) {
- if (context == null) return false;
- final UserManager userManager = context.getSystemService(UserManager.class);
- if (userManager == null) return false;
- return userManager.isGuestUser();
- }
-}
diff --git a/src/com/android/settings/datausage/DataUsageList.kt b/src/com/android/settings/datausage/DataUsageList.kt
new file mode 100644
index 0000000..9ac7161
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageList.kt
@@ -0,0 +1,229 @@
+/*
+ * 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.datausage
+
+import android.app.settings.SettingsEnums
+import android.net.NetworkPolicy
+import android.net.NetworkTemplate
+import android.os.Bundle
+import android.provider.Settings
+import android.telephony.SubscriptionManager
+import android.util.EventLog
+import android.util.Log
+import android.view.View
+import androidx.annotation.OpenForTesting
+import androidx.annotation.VisibleForTesting
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settings.datausage.lib.BillingCycleRepository
+import com.android.settings.datausage.lib.NetworkUsageData
+import com.android.settings.network.MobileDataEnabledListener
+import com.android.settings.network.MobileNetworkRepository
+import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.android.settingslib.utils.ThreadUtils
+import kotlin.jvm.optionals.getOrNull
+
+/**
+ * Panel showing data usage history across various networks, including options
+ * to inspect based on usage cycle and control through [NetworkPolicy].
+ */
+@OpenForTesting
+open class DataUsageList : DataUsageBaseFragment(), MobileDataEnabledListener.Client {
+ @VisibleForTesting
+ lateinit var dataStateListener: MobileDataEnabledListener
+
+ @JvmField
+ @VisibleForTesting
+ var template: NetworkTemplate? = null
+
+ @JvmField
+ @VisibleForTesting
+ var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+
+ // Spinner will keep the selected cycle even after paused, this only keeps the displayed cycle,
+ // which need be cleared when resumed.
+ private var lastDisplayedUsageData: NetworkUsageData? = null
+ private lateinit var usageAmount: Preference
+ private var subscriptionInfoEntity: SubscriptionInfoEntity? = null
+ private lateinit var dataUsageListAppsController: DataUsageListAppsController
+ private lateinit var chartDataUsagePreferenceController: ChartDataUsagePreferenceController
+ private lateinit var billingCycleRepository: BillingCycleRepository
+
+ @VisibleForTesting
+ var dataUsageListHeaderController: DataUsageListHeaderController? = null
+
+ override fun getMetricsCategory() = SettingsEnums.DATA_USAGE_LIST
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (requireContext().userManager.isGuestUser) {
+ Log.e(TAG, "This setting isn't available for guest user")
+ EventLog.writeEvent(0x534e4554, "262741858", -1 /* UID */, "Guest user")
+ finish()
+ return
+ }
+ billingCycleRepository = createBillingCycleRepository();
+ if (!billingCycleRepository.isBandwidthControlEnabled()) {
+ Log.w(TAG, "No bandwidth control; leaving")
+ finish()
+ return
+ }
+ usageAmount = findPreference(KEY_USAGE_AMOUNT)!!
+ processArgument()
+ val template = template
+ if (template == null) {
+ Log.e(TAG, "No template; leaving")
+ finish()
+ return
+ }
+ updateSubscriptionInfoEntity()
+ dataStateListener = MobileDataEnabledListener(activity, this)
+ dataUsageListAppsController = use(DataUsageListAppsController::class.java).apply {
+ init(template)
+ }
+ chartDataUsagePreferenceController = use(ChartDataUsagePreferenceController::class.java)
+ chartDataUsagePreferenceController.init(template)
+ }
+
+ @VisibleForTesting
+ open fun createBillingCycleRepository() = BillingCycleRepository(requireContext())
+
+ override fun onViewCreated(v: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(v, savedInstanceState)
+
+ val template = template ?: return
+ dataUsageListHeaderController = DataUsageListHeaderController(
+ setPinnedHeaderView(R.layout.apps_filter_spinner),
+ template,
+ metricsCategory,
+ viewLifecycleOwner,
+ ::onCyclesLoad,
+ ::updateSelectedCycle,
+ )
+ }
+
+ override fun onResume() {
+ super.onResume()
+ dataStateListener.start(subId)
+ lastDisplayedUsageData = null
+ updatePolicy()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ dataStateListener.stop()
+ }
+
+ override fun getPreferenceScreenResId() = R.xml.data_usage_list
+
+ override fun getLogTag() = TAG
+
+ fun processArgument() {
+ arguments?.let {
+ subId = it.getInt(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ template = it.getParcelable(EXTRA_NETWORK_TEMPLATE, NetworkTemplate::class.java)
+ }
+ if (template == null && subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ subId = intent.getIntExtra(
+ Settings.EXTRA_SUB_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ )
+ template = intent.getParcelableExtra(
+ Settings.EXTRA_NETWORK_TEMPLATE,
+ NetworkTemplate::class.java,
+ ) ?: DataUsageUtils.getMobileNetworkTemplateFromSubId(context, intent).getOrNull()
+ }
+ }
+
+ @VisibleForTesting
+ open fun updateSubscriptionInfoEntity() {
+ ThreadUtils.postOnBackgroundThread {
+ subscriptionInfoEntity =
+ MobileNetworkRepository.getInstance(context).getSubInfoById(subId.toString())
+ }
+ }
+
+ /**
+ * Implementation of `MobileDataEnabledListener.Client`
+ */
+ override fun onMobileDataEnabledChange() {
+ updatePolicy()
+ }
+
+ /** Update chart sweeps and cycle list to reflect [NetworkPolicy] for current [template]. */
+ @VisibleForTesting
+ fun updatePolicy() {
+ val isBillingCycleModifiable = isBillingCycleModifiable()
+ dataUsageListHeaderController?.setConfigButtonVisible(isBillingCycleModifiable)
+ chartDataUsagePreferenceController.setBillingCycleModifiable(isBillingCycleModifiable)
+ }
+
+ @VisibleForTesting
+ open fun isBillingCycleModifiable(): Boolean {
+ return (billingCycleRepository.isModifiable(subId) &&
+ requireContext().getSystemService(SubscriptionManager::class.java)!!
+ .getActiveSubscriptionInfo(subId) != null)
+ }
+
+ private fun onCyclesLoad(networkUsageData: List<NetworkUsageData>) {
+ dataUsageListAppsController.updateCycles(networkUsageData)
+ }
+
+ /**
+ * Updates the chart and detail data when initial loaded or selected cycle changed.
+ */
+ private fun updateSelectedCycle(usageData: NetworkUsageData) {
+ if (usageData == lastDisplayedUsageData) {
+ // Avoid duplicate update to avoid page flash.
+ return
+ }
+ lastDisplayedUsageData = usageData
+ Log.d(TAG, "showing cycle $usageData")
+
+ val totalPhrase = DataUsageUtils.formatDataUsage(requireContext(), usageData.usage)
+ usageAmount.title = getString(R.string.data_used_template, totalPhrase)
+
+ updateChart(usageData)
+ updateApps(usageData)
+ }
+
+ /** Updates chart to show selected cycle. */
+ private fun updateChart(usageData: NetworkUsageData) {
+ chartDataUsagePreferenceController.update(
+ startTime = usageData.startTime,
+ endTime = usageData.endTime,
+ )
+ }
+
+ /** Updates applications data usage. */
+ private fun updateApps(usageData: NetworkUsageData) {
+ dataUsageListAppsController.update(
+ carrierId = subscriptionInfoEntity?.carrierId,
+ startTime = usageData.startTime,
+ endTime = usageData.endTime,
+ )
+ }
+
+ companion object {
+ const val EXTRA_SUB_ID = "sub_id"
+ const val EXTRA_NETWORK_TEMPLATE = "network_template"
+
+ private const val TAG = "DataUsageList"
+ private const val KEY_USAGE_AMOUNT = "usage_amount"
+ }
+}
diff --git a/src/com/android/settings/datausage/DataUsageListAppsController.kt b/src/com/android/settings/datausage/DataUsageListAppsController.kt
index c324407..93623f4 100644
--- a/src/com/android/settings/datausage/DataUsageListAppsController.kt
+++ b/src/com/android/settings/datausage/DataUsageListAppsController.kt
@@ -31,8 +31,8 @@
import com.android.settings.core.BasePreferenceController
import com.android.settings.core.SubSettingLauncher
import com.android.settings.datausage.lib.AppDataUsageRepository
+import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settingslib.AppItem
-import com.android.settingslib.net.NetworkCycleChartData
import com.android.settingslib.net.UidDetailProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -48,7 +48,7 @@
private lateinit var preference: PreferenceGroup
private lateinit var lifecycleScope: LifecycleCoroutineScope
- private var cycleData: List<NetworkCycleChartData>? = null
+ private var cycleData: List<NetworkUsageData>? = null
open fun init(template: NetworkTemplate) {
this.template = template
@@ -70,7 +70,7 @@
lifecycleScope = viewLifecycleOwner.lifecycleScope
}
- fun setCycleData(cycleData: List<NetworkCycleChartData>?) {
+ fun updateCycles(cycleData: List<NetworkUsageData>) {
this.cycleData = cycleData
}
diff --git a/src/com/android/settings/datausage/DataUsageListHeaderController.kt b/src/com/android/settings/datausage/DataUsageListHeaderController.kt
index e295a4c..58fc3b5 100644
--- a/src/com/android/settings/datausage/DataUsageListHeaderController.kt
+++ b/src/com/android/settings/datausage/DataUsageListHeaderController.kt
@@ -18,25 +18,39 @@
import android.net.NetworkTemplate
import android.os.Bundle
+import android.util.Range
import android.view.View
import android.view.accessibility.AccessibilityEvent
import android.widget.AdapterView
import android.widget.Spinner
import androidx.annotation.OpenForTesting
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.R
import com.android.settings.core.SubSettingLauncher
-import com.android.settings.datausage.CycleAdapter.CycleItem
import com.android.settings.datausage.CycleAdapter.SpinnerInterface
-import com.android.settingslib.net.NetworkCycleChartData
+import com.android.settings.datausage.lib.INetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkUsageData
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
@OpenForTesting
open class DataUsageListHeaderController(
header: View,
template: NetworkTemplate,
sourceMetricsCategory: Int,
- private val onItemSelected: (cycleItem: CycleItem, position: Int) -> Unit,
+ viewLifecycleOwner: LifecycleOwner,
+ private val onCyclesLoad: (usageDataList: List<NetworkUsageData>) -> Unit,
+ private val onItemSelected: (usageData: NetworkUsageData) -> Unit,
+ private val repository: INetworkCycleDataRepository =
+ NetworkCycleDataRepository(header.context, template),
) {
private val context = header.context
+
private val configureButton: View = header.requireViewById(R.id.filter_settings)
private val cycleSpinner: Spinner = header.requireViewById(R.id.filter_spinner)
private val cycleAdapter = CycleAdapter(context, object : SpinnerInterface {
@@ -50,13 +64,12 @@
cycleSpinner.setSelection(position)
}
})
+ private var cycles: List<NetworkUsageData> = emptyList()
private val cycleListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (0 <= position && position < cycleAdapter.count) {
- cycleAdapter.getItem(position)?.let { cycleItem ->
- onItemSelected(cycleItem, position)
- }
+ cycles.getOrNull(position)?.let(onItemSelected)
}
}
@@ -80,24 +93,32 @@
cycleSpinner.visibility = View.GONE
cycleSpinner.accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun sendAccessibilityEvent(host: View, eventType: Int) {
- if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED) {
- // Ignore TYPE_VIEW_SELECTED or TalkBack will speak for it at onResume.
- return
- }
+ // Ignore TYPE_VIEW_SELECTED or TalkBack will speak for it at onResume.
+ if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED) return
super.sendAccessibilityEvent(host, eventType)
}
}
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ cycles = withContext(Dispatchers.Default) {
+ repository.loadCycles()
+ }
+ updateCycleData()
+ }
+ }
}
open fun setConfigButtonVisible(visible: Boolean) {
configureButton.visibility = if (visible) View.VISIBLE else View.GONE
}
- open fun updateCycleData(cycleData: List<NetworkCycleChartData>) {
+ private fun updateCycleData() {
cycleSpinner.onItemSelectedListener = cycleListener
// calculate policy cycles based on available data
// generate cycle list based on policy and available history
- cycleAdapter.updateCycleList(cycleData)
+ cycleAdapter.updateCycleList(cycles.map { Range(it.startTime, it.endTime) })
cycleSpinner.visibility = View.VISIBLE
+ onCyclesLoad(cycles)
}
}
diff --git a/src/com/android/settings/datausage/lib/NetworkCycleChartData.kt b/src/com/android/settings/datausage/lib/NetworkCycleChartData.kt
new file mode 100644
index 0000000..fd3c504
--- /dev/null
+++ b/src/com/android/settings/datausage/lib/NetworkCycleChartData.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.datausage.lib
+
+import kotlin.time.Duration.Companion.days
+
+/**
+ * Usage data in a billing cycle with daily data for plotting the usage chart.
+ */
+data class NetworkCycleChartData(
+ val total: NetworkUsageData,
+ val dailyUsage: List<NetworkUsageData>,
+) {
+ companion object {
+ val BUCKET_DURATION = 1.days
+ }
+}
diff --git a/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt b/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt
new file mode 100644
index 0000000..f10d506
--- /dev/null
+++ b/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.datausage.lib
+
+import android.app.usage.NetworkStats
+import android.app.usage.NetworkStatsManager
+import android.content.Context
+import android.net.NetworkPolicy
+import android.net.NetworkPolicyManager
+import android.net.NetworkTemplate
+import android.text.format.DateUtils
+import android.util.Log
+import android.util.Range
+import androidx.annotation.VisibleForTesting
+import com.android.settingslib.NetworkPolicyEditor
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.coroutineScope
+
+interface INetworkCycleDataRepository {
+ suspend fun loadCycles(): List<NetworkUsageData>
+ fun getPolicy(): NetworkPolicy?
+ suspend fun querySummary(startTime: Long, endTime: Long): NetworkCycleChartData?
+}
+
+class NetworkCycleDataRepository(
+ context: Context,
+ private val networkTemplate: NetworkTemplate,
+) : INetworkCycleDataRepository {
+ private val networkStatsManager = context.getSystemService(NetworkStatsManager::class.java)!!
+
+ private val policyManager = context.getSystemService(NetworkPolicyManager::class.java)!!
+
+ override suspend fun loadCycles(): List<NetworkUsageData> =
+ getCycles().queryUsage().filter { it.usage > 0 }
+
+ private fun getCycles(): List<Range<Long>> {
+ val policy = getPolicy() ?: return queryCyclesAsFourWeeks()
+ return policy.cycleIterator().asSequence().map {
+ Range(it.lower.toInstant().toEpochMilli(), it.upper.toInstant().toEpochMilli())
+ }.toList()
+ }
+
+ private fun queryCyclesAsFourWeeks(): List<Range<Long>> {
+ val timeRange = getTimeRange()
+ return reverseBucketRange(
+ startTime = timeRange.lower,
+ endTime = timeRange.upper,
+ bucketSize = DateUtils.WEEK_IN_MILLIS * 4,
+ )
+ }
+
+ @VisibleForTesting
+ fun getTimeRange(): Range<Long> = getTimeRangeOf(
+ networkStatsManager.queryDetailsForDevice(networkTemplate, Long.MIN_VALUE, Long.MAX_VALUE)
+ )
+
+ private fun getTimeRangeOf(stats: NetworkStats): Range<Long> {
+ var start = Long.MAX_VALUE
+ var end = Long.MIN_VALUE
+ val bucket = NetworkStats.Bucket()
+ while (stats.getNextBucket(bucket)) {
+ start = start.coerceAtMost(bucket.startTimeStamp)
+ end = end.coerceAtLeast(bucket.endTimeStamp)
+ }
+ return Range(start, end)
+ }
+
+ override fun getPolicy(): NetworkPolicy? =
+ with(NetworkPolicyEditor(policyManager)) {
+ read()
+ getPolicy(networkTemplate)
+ }
+
+ override suspend fun querySummary(startTime: Long, endTime: Long): NetworkCycleChartData? {
+ val usage = getUsage(startTime, endTime)
+ if (usage > 0L) {
+ return NetworkCycleChartData(
+ total = NetworkUsageData(startTime, endTime, usage),
+ dailyUsage = bucketRange(
+ startTime = startTime,
+ endTime = endTime,
+ bucketSize = NetworkCycleChartData.BUCKET_DURATION.inWholeMilliseconds,
+ ).queryUsage(),
+ )
+ }
+ return null
+ }
+
+ private suspend fun List<Range<Long>>.queryUsage(): List<NetworkUsageData> = coroutineScope {
+ map { range ->
+ async {
+ NetworkUsageData(
+ startTime = range.lower,
+ endTime = range.upper,
+ usage = getUsage(range.lower, range.upper),
+ )
+ }
+ }.awaitAll()
+ }
+
+ private fun bucketRange(startTime: Long, endTime: Long, bucketSize: Long): List<Range<Long>> {
+ val buckets = mutableListOf<Range<Long>>()
+ var currentStart = startTime
+ while (currentStart < endTime) {
+ val bucketEnd = currentStart + bucketSize
+ buckets += Range(currentStart, bucketEnd)
+ currentStart = bucketEnd
+ }
+ return buckets
+ }
+
+ private fun reverseBucketRange(
+ startTime: Long,
+ endTime: Long,
+ bucketSize: Long,
+ ): List<Range<Long>> {
+ val buckets = mutableListOf<Range<Long>>()
+ var currentEnd = endTime
+ while (currentEnd > startTime) {
+ val bucketStart = currentEnd - bucketSize
+ buckets += Range(bucketStart, currentEnd)
+ currentEnd = bucketStart
+ }
+ return buckets
+ }
+
+ private fun getUsage(start: Long, end: Long): Long = try {
+ networkStatsManager.querySummaryForDevice(networkTemplate, start, end).let {
+ it.rxBytes + it.txBytes
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Exception querying network detail.", e)
+ 0
+ }
+
+ companion object {
+ private const val TAG = "NetworkCycleDataRepository"
+ }
+}
diff --git a/src/com/android/settings/datausage/lib/NetworkUsageData.kt b/src/com/android/settings/datausage/lib/NetworkUsageData.kt
new file mode 100644
index 0000000..fc5db2b
--- /dev/null
+++ b/src/com/android/settings/datausage/lib/NetworkUsageData.kt
@@ -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.
+ */
+
+package com.android.settings.datausage.lib
+
+/**
+ * Base data structure representing usage data in a period.
+ */
+data class NetworkUsageData(
+ val startTime: Long,
+ val endTime: Long,
+ val usage: Long,
+)
diff --git a/src/com/android/settings/development/quarantine/QuarantinedAppsScreenController.java b/src/com/android/settings/development/quarantine/QuarantinedAppsScreenController.java
index e5373fd..c3cb38d 100644
--- a/src/com/android/settings/development/quarantine/QuarantinedAppsScreenController.java
+++ b/src/com/android/settings/development/quarantine/QuarantinedAppsScreenController.java
@@ -19,6 +19,7 @@
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.SuspendDialogInfo;
import android.os.UserHandle;
import androidx.annotation.VisibleForTesting;
@@ -181,8 +182,17 @@
private void setPackageQuarantined(String pkg, int uid, boolean quarantined) {
final PackageManager pm = mContext.createContextAsUser(
UserHandle.getUserHandleForUid(uid), 0).getPackageManager();
+ final SuspendDialogInfo dialogInfo;
+ if (quarantined) {
+ dialogInfo = new SuspendDialogInfo.Builder()
+ .setNeutralButtonText(R.string.unquarantine_app_button)
+ .setNeutralButtonAction(SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND)
+ .build();
+ } else {
+ dialogInfo = null;
+ }
pm.setPackagesSuspended(new String[] {pkg}, quarantined, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */,
+ null /* launcherExtras */, dialogInfo,
PackageManager.FLAG_SUSPEND_QUARANTINED);
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
index 4db4d3b..ea4fa3a 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
@@ -146,10 +146,15 @@
return false;
}
- private String getActionKey(String packageName) {
- final String actionKey = TextUtils.isEmpty(packageName)
- ? PACKAGE_NAME_NONE : packageName;
- return mAnomalyEventId == null ? actionKey : actionKey + "|" + mAnomalyEventId;
+ private boolean isAnomalyBatteryDiffEntry(BatteryDiffEntry entry) {
+ return mIsHighlightSlot && mAnomalyEntryKey != null
+ && mAnomalyEntryKey.equals(entry.getKey());
+ }
+
+ private String getActionKey(BatteryDiffEntry entry) {
+ final String actionKey = TextUtils.isEmpty(entry.getPackageName())
+ ? PACKAGE_NAME_NONE : entry.getPackageName();
+ return !isAnomalyBatteryDiffEntry(entry) ? actionKey : actionKey + "|" + mAnomalyEventId;
}
@Override
@@ -159,17 +164,16 @@
}
final PowerGaugePreference powerPref = (PowerGaugePreference) preference;
final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
- final String packageName = diffEntry.getPackageName();
mMetricsFeatureProvider.action(
/* attribution */ SettingsEnums.OPEN_BATTERY_USAGE,
/* action */ diffEntry.isSystemEntry()
? SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM
: SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
/* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
- getActionKey(packageName),
+ getActionKey(diffEntry),
(int) Math.round(diffEntry.getPercentage()));
Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s",
- diffEntry.getAppLabel(), diffEntry.getKey(), packageName));
+ diffEntry.getAppLabel(), diffEntry.getKey(), diffEntry.getPackageName()));
AdvancedPowerUsageDetail.startBatteryDetailPage(
mActivity, mFragment, diffEntry, powerPref.getPercentage(), mSlotTimestamp);
return true;
@@ -324,9 +328,7 @@
pref.setOrder(prefIndex);
pref.setSingleLineTitle(true);
// Updates App item preference style
- pref.setAnomalyHint(mIsHighlightSlot && mAnomalyEntryKey != null
- && mAnomalyEntryKey.equals(entry.getKey())
- ? mAnomalyHintString : null);
+ pref.setAnomalyHint(isAnomalyBatteryDiffEntry(entry) ? mAnomalyHintString : null);
// Sets the BatteryDiffEntry to preference for launching detailed page.
pref.setBatteryDiffEntry(entry);
pref.setSelectable(entry.validForRestriction());
diff --git a/src/com/android/settings/localepicker/TermsOfAddressBaseController.java b/src/com/android/settings/localepicker/TermsOfAddressBaseController.java
new file mode 100644
index 0000000..34c502f
--- /dev/null
+++ b/src/com/android/settings/localepicker/TermsOfAddressBaseController.java
@@ -0,0 +1,82 @@
+/**
+ * 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.localepicker;
+
+import android.app.GrammaticalInflectionManager;
+import android.content.Context;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.TickButtonPreference;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+public abstract class TermsOfAddressBaseController extends BasePreferenceController {
+
+ private PreferenceScreen mPreferenceScreen;
+ private MetricsFeatureProvider mMetricsFeatureProvider;
+ private TickButtonPreference mPreference;
+ private GrammaticalInflectionManager mGrammaticalInflectionManager;
+
+ public TermsOfAddressBaseController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
+ mGrammaticalInflectionManager = context.getSystemService(
+ GrammaticalInflectionManager.class);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreferenceScreen = screen;
+ mPreference = screen.findPreference(getPreferenceKey());
+ mPreference.setOnPreferenceClickListener(clickedPref -> {
+ mGrammaticalInflectionManager.setSystemWideGrammaticalGender(
+ getGrammaticalGenderType());
+ setSelected(mPreference);
+ mMetricsFeatureProvider.action(mContext, getMetricsActionKey());
+ return true;
+ });
+ updatePreferences();
+ }
+
+ private void setSelected(TickButtonPreference preference) {
+ for (int i = 1; i < mPreferenceScreen.getPreferenceCount(); i++) {
+ TickButtonPreference pref = (TickButtonPreference) mPreferenceScreen.getPreference(i);
+ pref.setSelected(pref.getKey().equals(preference.getKey()));
+ }
+ }
+
+ private void updatePreferences() {
+ if (mPreference == null) {
+ return;
+ }
+ mPreference.setSelected(
+ mGrammaticalInflectionManager.getSystemGrammaticalGender()
+ == getGrammaticalGenderType());
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ protected abstract int getMetricsActionKey();
+
+ protected abstract int getGrammaticalGenderType();
+}
diff --git a/src/com/android/settings/localepicker/TermsOfAddressFeminineController.java b/src/com/android/settings/localepicker/TermsOfAddressFeminineController.java
new file mode 100644
index 0000000..9a46872
--- /dev/null
+++ b/src/com/android/settings/localepicker/TermsOfAddressFeminineController.java
@@ -0,0 +1,45 @@
+/**
+ * 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.localepicker;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.res.Configuration;
+
+public class TermsOfAddressFeminineController extends TermsOfAddressBaseController {
+
+ private static final String KEY_FEMININE = "key_terms_of_address_feminine";
+
+ public TermsOfAddressFeminineController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_FEMININE;
+ }
+
+ @Override
+ protected int getMetricsActionKey() {
+ return SettingsEnums.ACTION_TERMS_OF_ADDRESS_FEMININE;
+ }
+
+ @Override
+ protected int getGrammaticalGenderType() {
+ return Configuration.GRAMMATICAL_GENDER_FEMININE;
+ }
+}
diff --git a/src/com/android/settings/localepicker/TermsOfAddressMasculineController.java b/src/com/android/settings/localepicker/TermsOfAddressMasculineController.java
new file mode 100644
index 0000000..cb39df0
--- /dev/null
+++ b/src/com/android/settings/localepicker/TermsOfAddressMasculineController.java
@@ -0,0 +1,45 @@
+/**
+ * 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.localepicker;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.res.Configuration;
+
+public class TermsOfAddressMasculineController extends TermsOfAddressBaseController {
+
+ private static final String KEY_MASCULINE = "key_terms_of_address_masculine";
+
+ public TermsOfAddressMasculineController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_MASCULINE;
+ }
+
+ @Override
+ protected int getMetricsActionKey() {
+ return SettingsEnums.ACTION_TERMS_OF_ADDRESS_MASCULINE;
+ }
+
+ @Override
+ protected int getGrammaticalGenderType() {
+ return Configuration.GRAMMATICAL_GENDER_MASCULINE;
+ }
+}
diff --git a/src/com/android/settings/localepicker/TermsOfAddressNeutralController.java b/src/com/android/settings/localepicker/TermsOfAddressNeutralController.java
new file mode 100644
index 0000000..e0693b5
--- /dev/null
+++ b/src/com/android/settings/localepicker/TermsOfAddressNeutralController.java
@@ -0,0 +1,45 @@
+/**
+ * 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.localepicker;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.res.Configuration;
+
+public class TermsOfAddressNeutralController extends TermsOfAddressBaseController {
+
+ private static final String KEY_NEUTRAL = "key_terms_of_address_neutral";
+
+ public TermsOfAddressNeutralController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_NEUTRAL;
+ }
+
+ @Override
+ protected int getMetricsActionKey() {
+ return SettingsEnums.ACTION_TERMS_OF_ADDRESS_NEUTRAL;
+ }
+
+ @Override
+ protected int getGrammaticalGenderType() {
+ return Configuration.GRAMMATICAL_GENDER_NEUTRAL;
+ }
+}
diff --git a/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedController.java b/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedController.java
new file mode 100644
index 0000000..4e2e450
--- /dev/null
+++ b/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedController.java
@@ -0,0 +1,45 @@
+/**
+ * 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.localepicker;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.res.Configuration;
+
+public class TermsOfAddressNotSpecifiedController extends TermsOfAddressBaseController {
+
+ private static final String KEY_NOT_SPECIFIED = "key_terms_of_address_not_specified";
+
+ public TermsOfAddressNotSpecifiedController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_NOT_SPECIFIED;
+ }
+
+ @Override
+ protected int getMetricsActionKey() {
+ return SettingsEnums.ACTION_TERMS_OF_ADDRESS_NOT_SPECIFIED;
+ }
+
+ @Override
+ protected int getGrammaticalGenderType() {
+ return Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+}
diff --git a/src/com/android/settings/notification/app/FullScreenIntentPermissionPreferenceController.kt b/src/com/android/settings/notification/app/FullScreenIntentPermissionPreferenceController.kt
index 2cc26f6..4349b4c 100644
--- a/src/com/android/settings/notification/app/FullScreenIntentPermissionPreferenceController.kt
+++ b/src/com/android/settings/notification/app/FullScreenIntentPermissionPreferenceController.kt
@@ -21,6 +21,7 @@
import android.app.AppOpsManager.OP_USE_FULL_SCREEN_INTENT
import android.content.AttributionSource
import android.content.Context
+import android.content.pm.PackageManager.NameNotFoundException
import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
import android.content.pm.PackageManager.GET_PERMISSIONS
import android.os.UserHandle
@@ -79,12 +80,16 @@
}
private fun isPermissionRequested(): Boolean {
- val packageInfo = packageManager.getPackageInfo(packageName, GET_PERMISSIONS)
+ try {
+ val packageInfo = packageManager.getPackageInfo(packageName, GET_PERMISSIONS)
- for (requestedPermission in packageInfo.requestedPermissions.orEmpty()) {
- if (USE_FULL_SCREEN_INTENT.equals(requestedPermission)) {
- return true
+ for (requestedPermission in packageInfo.requestedPermissions.orEmpty()) {
+ if (USE_FULL_SCREEN_INTENT.equals(requestedPermission)) {
+ return true
+ }
}
+ } catch (exception: NameNotFoundException) {
+ Log.e(TAG, "isPermissionRequested failed: $exception")
}
return false
diff --git a/src/com/android/settings/wifi/WifiDialog2.kt b/src/com/android/settings/wifi/WifiDialog2.kt
index 3e5f6fe..a0d215a 100644
--- a/src/com/android/settings/wifi/WifiDialog2.kt
+++ b/src/com/android/settings/wifi/WifiDialog2.kt
@@ -25,6 +25,7 @@
import android.widget.Button
import android.widget.ImageButton
import android.widget.TextView
+import androidx.annotation.OpenForTesting
import androidx.appcompat.app.AlertDialog
import com.android.settings.R
import com.android.settingslib.RestrictedLockUtils
@@ -34,7 +35,8 @@
/**
* Dialog for users to edit a Wi-Fi network.
*/
-class WifiDialog2 @JvmOverloads constructor(
+@OpenForTesting
+open class WifiDialog2 @JvmOverloads constructor(
context: Context,
private val listener: WifiDialog2Listener,
val wifiEntry: WifiEntry?,
diff --git a/tests/robotests/assets/exempt_slice_controller_not_in_xml b/tests/robotests/assets/exempt_slice_controller_not_in_xml
index 4a71a7b..88c716f 100644
--- a/tests/robotests/assets/exempt_slice_controller_not_in_xml
+++ b/tests/robotests/assets/exempt_slice_controller_not_in_xml
@@ -1,16 +1,17 @@
com.android.settings.accessibility.AccessibilitySlicePreferenceController
-com.android.settings.core.TogglePreferenceControllerTest$FakeToggle
+com.android.settings.accessibility.ReduceBrightColorsIntensityPreferenceController
+com.android.settings.accessibility.ReduceBrightColorsPersistencePreferenceController
com.android.settings.biometrics.face.FaceSettingsAttentionPreferenceController
+com.android.settings.core.TogglePreferenceControllerTest$FakeToggle
com.android.settings.display.DeviceStateAutoRotateSettingController
com.android.settings.display.SmartAutoRotatePreferenceController
com.android.settings.gestures.OneHandedMainSwitchPreferenceController
-com.android.settings.network.telephony.MmsMessagePreferenceController
com.android.settings.network.telephony.AutoDataSwitchPreferenceController
com.android.settings.network.telephony.Enhanced4gBasePreferenceController
-com.android.settings.testutils.FakeToggleController
-com.android.settings.testutils.FakeSliderController
+com.android.settings.network.telephony.MmsMessagePreferenceController
+com.android.settings.notification.RingVolumePreferenceController
com.android.settings.testutils.FakeInvalidSliderController
+com.android.settings.testutils.FakeSliderController
+com.android.settings.testutils.FakeToggleController
+com.android.settings.users.GuestTelephonyPreferenceController
com.android.settings.wifi.details2.WifiAutoConnectPreferenceController2
-com.android.settings.accessibility.ReduceBrightColorsIntensityPreferenceController
-com.android.settings.accessibility.ReduceBrightColorsPersistencePreferenceController
-
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
index 5f0f2b9..d381975 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
@@ -52,7 +52,9 @@
public class ConnectedDeviceDashboardFragmentTest {
private static final String KEY_NEARBY_DEVICES = "bt_nearby_slice";
private static final String KEY_DISCOVERABLE_FOOTER = "discoverable_footer";
- private static final String KEY_SEE_ALL = "previously_connected_devices_see_all";
+ private static final String KEY_SAVED_DEVICE_SEE_ALL = "previously_connected_devices_see_all";
+ private static final String KEY_FAST_PAIR_DEVICE_SEE_ALL = "fast_pair_devices_see_all";
+ private static final String KEY_FAST_PAIR_DEVICE_LIST = "fast_pair_devices";
private static final String KEY_ADD_BT_DEVICES = "add_bt_devices";
private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
@@ -92,7 +94,8 @@
.getNonIndexableKeys(mContext);
assertThat(niks).containsExactly(KEY_CONNECTED_DEVICES, KEY_AVAILABLE_DEVICES,
- KEY_NEARBY_DEVICES, KEY_DISCOVERABLE_FOOTER, KEY_SEE_ALL);
+ KEY_NEARBY_DEVICES, KEY_DISCOVERABLE_FOOTER, KEY_SAVED_DEVICE_SEE_ALL,
+ KEY_FAST_PAIR_DEVICE_SEE_ALL, KEY_FAST_PAIR_DEVICE_LIST);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/datausage/ChartDataUsagePreferenceTest.java b/tests/robotests/src/com/android/settings/datausage/ChartDataUsagePreferenceTest.java
index aab67be..9c93606 100644
--- a/tests/robotests/src/com/android/settings/datausage/ChartDataUsagePreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/ChartDataUsagePreferenceTest.java
@@ -31,9 +31,9 @@
import com.android.settings.R;
import com.android.settings.datausage.ChartDataUsagePreference.DataUsageSummaryNode;
+import com.android.settings.datausage.lib.NetworkCycleChartData;
+import com.android.settings.datausage.lib.NetworkUsageData;
import com.android.settings.widget.UsageView;
-import com.android.settingslib.net.NetworkCycleChartData;
-import com.android.settingslib.net.NetworkCycleData;
import org.junit.Before;
import org.junit.Test;
@@ -55,7 +55,7 @@
// Test bucket end date, 22 Mar 2018 00:00:00
private static final long TIMESTAMP_END = 1521676800000L;
- private List<NetworkCycleData> mNetworkCycleData;
+ private List<NetworkUsageData> mNetworkCycleData;
private NetworkCycleChartData mNetworkCycleChartData;
private ChartDataUsagePreference mPreference;
private Activity mActivity;
@@ -79,6 +79,9 @@
final ArgumentCaptor<SparseIntArray> pointsCaptor =
ArgumentCaptor.forClass(SparseIntArray.class);
createTestNetworkData();
+ mPreference.setTime(
+ mNetworkCycleChartData.getTotal().getStartTime(),
+ mNetworkCycleChartData.getTotal().getEndTime());
mPreference.setNetworkCycleData(mNetworkCycleChartData);
mPreference.calcPoints(usageView, mNetworkCycleData.subList(0, 5));
@@ -95,6 +98,9 @@
final ArgumentCaptor<SparseIntArray> pointsCaptor =
ArgumentCaptor.forClass(SparseIntArray.class);
createTestNetworkData();
+ mPreference.setTime(
+ mNetworkCycleChartData.getTotal().getStartTime(),
+ mNetworkCycleChartData.getTotal().getEndTime());
mPreference.setNetworkCycleData(mNetworkCycleChartData);
mPreference.calcPoints(usageView, mNetworkCycleData.subList(2, 7));
@@ -110,39 +116,62 @@
public void calcPoints_shouldNotDrawPointForFutureDate() {
final UsageView usageView = mock(UsageView.class);
final ArgumentCaptor<SparseIntArray> pointsCaptor =
- ArgumentCaptor.forClass(SparseIntArray.class);
+ ArgumentCaptor.forClass(SparseIntArray.class);
final long tonight = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(12);
mNetworkCycleData = new ArrayList<>();
// add test usage data for last 5 days
- mNetworkCycleData.add(createNetworkCycleData(
- tonight - TimeUnit.DAYS.toMillis(5), tonight - TimeUnit.DAYS.toMillis(4), 743823454L));
- mNetworkCycleData.add(createNetworkCycleData(
- tonight - TimeUnit.DAYS.toMillis(4), tonight - TimeUnit.DAYS.toMillis(3), 64396L));
- mNetworkCycleData.add(createNetworkCycleData(
- tonight - TimeUnit.DAYS.toMillis(3), tonight - TimeUnit.DAYS.toMillis(2), 2832L));
- mNetworkCycleData.add(createNetworkCycleData(
- tonight - TimeUnit.DAYS.toMillis(2), tonight - TimeUnit.DAYS.toMillis(1), 83849690L));
- mNetworkCycleData.add(createNetworkCycleData(
- tonight - TimeUnit.DAYS.toMillis(1), tonight, 1883657L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight - TimeUnit.DAYS.toMillis(5),
+ tonight - TimeUnit.DAYS.toMillis(4),
+ 743823454L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight - TimeUnit.DAYS.toMillis(4),
+ tonight - TimeUnit.DAYS.toMillis(3),
+ 64396L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight - TimeUnit.DAYS.toMillis(3),
+ tonight - TimeUnit.DAYS.toMillis(2),
+ 2832L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight - TimeUnit.DAYS.toMillis(2),
+ tonight - TimeUnit.DAYS.toMillis(1),
+ 83849690L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight - TimeUnit.DAYS.toMillis(1), tonight, 1883657L));
// add test usage data for next 5 days
- mNetworkCycleData.add(createNetworkCycleData(
- tonight, tonight + TimeUnit.DAYS.toMillis(1), 0L));
- mNetworkCycleData.add(createNetworkCycleData(
- tonight + TimeUnit.DAYS.toMillis(1), tonight + TimeUnit.DAYS.toMillis(2), 0L));
- mNetworkCycleData.add(createNetworkCycleData(
- tonight + TimeUnit.DAYS.toMillis(2), tonight + TimeUnit.DAYS.toMillis(3), 0L));
- mNetworkCycleData.add(createNetworkCycleData(
- tonight + TimeUnit.DAYS.toMillis(3), tonight + TimeUnit.DAYS.toMillis(4), 0L));
- mNetworkCycleData.add(createNetworkCycleData(
- tonight + TimeUnit.DAYS.toMillis(4), tonight + TimeUnit.DAYS.toMillis(5), 0L));
- mNetworkCycleData.add(createNetworkCycleData(
- tonight + TimeUnit.DAYS.toMillis(5), tonight + TimeUnit.DAYS.toMillis(6), 0L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight, tonight + TimeUnit.DAYS.toMillis(1), 0L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight + TimeUnit.DAYS.toMillis(1),
+ tonight + TimeUnit.DAYS.toMillis(2),
+ 0L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight + TimeUnit.DAYS.toMillis(2),
+ tonight + TimeUnit.DAYS.toMillis(3),
+ 0L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight + TimeUnit.DAYS.toMillis(3),
+ tonight + TimeUnit.DAYS.toMillis(4),
+ 0L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight + TimeUnit.DAYS.toMillis(4),
+ tonight + TimeUnit.DAYS.toMillis(5),
+ 0L));
+ mNetworkCycleData.add(new NetworkUsageData(
+ tonight + TimeUnit.DAYS.toMillis(5),
+ tonight + TimeUnit.DAYS.toMillis(6),
+ 0L));
- final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
- builder.setUsageBuckets(mNetworkCycleData)
- .setStartTime(tonight - TimeUnit.DAYS.toMillis(5))
- .setEndTime(tonight + TimeUnit.DAYS.toMillis(6));
- mNetworkCycleChartData = builder.build();
+ mNetworkCycleChartData = new NetworkCycleChartData(
+ new NetworkUsageData(
+ tonight - TimeUnit.DAYS.toMillis(5),
+ tonight + TimeUnit.DAYS.toMillis(6),
+ 0),
+ mNetworkCycleData
+ );
+ mPreference.setTime(
+ mNetworkCycleChartData.getTotal().getStartTime(),
+ mNetworkCycleChartData.getTotal().getEndTime());
mPreference.setNetworkCycleData(mNetworkCycleChartData);
mPreference.calcPoints(usageView, mNetworkCycleData);
@@ -170,6 +199,9 @@
final TextView labelStart = (TextView) mHolder.findViewById(R.id.label_start);
final TextView labelEnd = (TextView) mHolder.findViewById(R.id.label_end);
createTestNetworkData();
+ mPreference.setTime(
+ mNetworkCycleChartData.getTotal().getStartTime(),
+ mNetworkCycleChartData.getTotal().getEndTime());
mPreference.setNetworkCycleData(mNetworkCycleChartData);
mPreference.onBindViewHolder(mHolder);
@@ -198,38 +230,33 @@
private void createTestNetworkData() {
mNetworkCycleData = new ArrayList<>();
// create 10 arbitrary network data
- mNetworkCycleData.add(createNetworkCycleData(1521583200000L, 1521586800000L, 743823454L));
- mNetworkCycleData.add(createNetworkCycleData(1521586800000L, 1521590400000L, 64396L));
- mNetworkCycleData.add(createNetworkCycleData(1521590400000L, 1521655200000L, 2832L));
- mNetworkCycleData.add(createNetworkCycleData(1521655200000L, 1521658800000L, 83849690L));
- mNetworkCycleData.add(createNetworkCycleData(1521658800000L, 1521662400000L, 1883657L));
- mNetworkCycleData.add(createNetworkCycleData(1521662400000L, 1521666000000L, 705259L));
- mNetworkCycleData.add(createNetworkCycleData(1521666000000L, 1521669600000L, 216169L));
- mNetworkCycleData.add(createNetworkCycleData(1521669600000L, 1521673200000L, 6069175L));
- mNetworkCycleData.add(createNetworkCycleData(1521673200000L, 1521676800000L, 120389L));
- mNetworkCycleData.add(createNetworkCycleData(1521676800000L, 1521678800000L, 29947L));
+ mNetworkCycleData.add(new NetworkUsageData(1521583200000L, 1521586800000L, 743823454L));
+ mNetworkCycleData.add(new NetworkUsageData(1521586800000L, 1521590400000L, 64396L));
+ mNetworkCycleData.add(new NetworkUsageData(1521590400000L, 1521655200000L, 2832L));
+ mNetworkCycleData.add(new NetworkUsageData(1521655200000L, 1521658800000L, 83849690L));
+ mNetworkCycleData.add(new NetworkUsageData(1521658800000L, 1521662400000L, 1883657L));
+ mNetworkCycleData.add(new NetworkUsageData(1521662400000L, 1521666000000L, 705259L));
+ mNetworkCycleData.add(new NetworkUsageData(1521666000000L, 1521669600000L, 216169L));
+ mNetworkCycleData.add(new NetworkUsageData(1521669600000L, 1521673200000L, 6069175L));
+ mNetworkCycleData.add(new NetworkUsageData(1521673200000L, 1521676800000L, 120389L));
+ mNetworkCycleData.add(new NetworkUsageData(1521676800000L, 1521678800000L, 29947L));
- final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
- builder.setUsageBuckets(mNetworkCycleData)
- .setStartTime(TIMESTAMP_START)
- .setEndTime(TIMESTAMP_END);
- mNetworkCycleChartData = builder.build();
+ mNetworkCycleChartData = new NetworkCycleChartData(
+ new NetworkUsageData(TIMESTAMP_START, TIMESTAMP_END, 0),
+ mNetworkCycleData
+ );
}
private void createSomeSamePercentageNetworkData() {
mNetworkCycleData = new ArrayList<>();
- mNetworkCycleData.add(createNetworkCycleData(1521583200000L, 1521586800000L, 100));//33%
- mNetworkCycleData.add(createNetworkCycleData(1521586800000L, 1521590400000L, 1)); //33%
- mNetworkCycleData.add(createNetworkCycleData(1521590400000L, 1521655200000L, 0)); //33%
- mNetworkCycleData.add(createNetworkCycleData(1521655200000L, 1521658800000L, 0)); //33%
- mNetworkCycleData.add(createNetworkCycleData(1521658800000L, 1521662400000L, 200));//99%
- mNetworkCycleData.add(createNetworkCycleData(1521662400000L, 1521666000000L, 1)); //99%
- mNetworkCycleData.add(createNetworkCycleData(1521666000000L, 1521669600000L, 1)); //100
- mNetworkCycleData.add(createNetworkCycleData(1521669600000L, 1521673200000L, 0)); //100%
+ mNetworkCycleData.add(new NetworkUsageData(1521583200000L, 1521586800000L, 100)); //33%
+ mNetworkCycleData.add(new NetworkUsageData(1521586800000L, 1521590400000L, 1)); //33%
+ mNetworkCycleData.add(new NetworkUsageData(1521590400000L, 1521655200000L, 0)); //33%
+ mNetworkCycleData.add(new NetworkUsageData(1521655200000L, 1521658800000L, 0)); //33%
+ mNetworkCycleData.add(new NetworkUsageData(1521658800000L, 1521662400000L, 200)); //99%
+ mNetworkCycleData.add(new NetworkUsageData(1521662400000L, 1521666000000L, 1)); //99%
+ mNetworkCycleData.add(new NetworkUsageData(1521666000000L, 1521669600000L, 1)); //100
+ mNetworkCycleData.add(new NetworkUsageData(1521669600000L, 1521673200000L, 0)); //100%
}
- private NetworkCycleData createNetworkCycleData(long start, long end, long usage) {
- return new NetworkCycleData.Builder()
- .setStartTime(start).setEndTime(end).setTotalUsage(usage).build();
- }
}
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
deleted file mode 100644
index 1268032..0000000
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2018 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.datausage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-import androidx.loader.app.LoaderManager;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceManager;
-
-import com.android.settings.datausage.lib.BillingCycleRepository;
-import com.android.settings.network.MobileDataEnabledListener;
-import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.widget.LoadingViewController;
-import com.android.settingslib.NetworkPolicyEditor;
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
-import com.android.settingslib.net.NetworkCycleChartData;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.android.controller.ActivityController;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = DataUsageListTest.ShadowDataUsageBaseFragment.class)
-public class DataUsageListTest {
- @Rule
- public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
- @Mock
- private MobileDataEnabledListener mMobileDataEnabledListener;
- @Mock
- private TemplatePreference.NetworkServices mNetworkServices;
- @Mock
- private LoaderManager mLoaderManager;
- @Mock
- private UserManager mUserManager;
- @Mock
- private BillingCycleRepository mBillingCycleRepository;
- @Mock
- private DataUsageListHeaderController mDataUsageListHeaderController;
-
- private Activity mActivity;
-
- @Spy
- private TestDataUsageList mDataUsageList;
-
- @Before
- public void setUp() {
- FakeFeatureFactory.setupForTest();
- final ActivityController<Activity> mActivityController =
- Robolectric.buildActivity(Activity.class);
- mActivity = spy(mActivityController.get());
- mNetworkServices.mPolicyEditor = mock(NetworkPolicyEditor.class);
- mDataUsageList.mDataStateListener = mMobileDataEnabledListener;
-
- doReturn(mActivity).when(mDataUsageList).getContext();
- doReturn(mUserManager).when(mActivity).getSystemService(UserManager.class);
- doReturn(false).when(mUserManager).isGuestUser();
- ReflectionHelpers.setField(mDataUsageList, "mDataStateListener",
- mMobileDataEnabledListener);
- ReflectionHelpers.setField(mDataUsageList, "services", mNetworkServices);
- doReturn(mLoaderManager).when(mDataUsageList).getLoaderManager();
- mDataUsageList.mLoadingViewController = mock(LoadingViewController.class);
- doNothing().when(mDataUsageList).updateSubscriptionInfoEntity();
- when(mBillingCycleRepository.isBandwidthControlEnabled()).thenReturn(true);
- mDataUsageList.mDataUsageListHeaderController = mDataUsageListHeaderController;
- }
-
- @Test
- public void onCreate_isNotGuestUser_shouldNotFinish() {
- mDataUsageList.mTemplate = mock(NetworkTemplate.class);
- doReturn(false).when(mUserManager).isGuestUser();
- doNothing().when(mDataUsageList).processArgument();
-
- mDataUsageList.onCreate(null);
-
- verify(mDataUsageList, never()).finish();
- }
-
- @Test
- public void onCreate_isGuestUser_shouldFinish() {
- doReturn(true).when(mUserManager).isGuestUser();
-
- mDataUsageList.onCreate(null);
-
- verify(mDataUsageList).finish();
- }
-
- @Test
- public void resume_shouldListenDataStateChange() {
- mDataUsageList.onCreate(null);
- ReflectionHelpers.setField(
- mDataUsageList, "mVisibilityLoggerMixin", mock(VisibilityLoggerMixin.class));
- ReflectionHelpers.setField(
- mDataUsageList, "mPreferenceManager", mock(PreferenceManager.class));
-
- mDataUsageList.onResume();
-
- verify(mMobileDataEnabledListener).start(anyInt());
-
- mDataUsageList.onPause();
- }
-
- @Test
- public void pause_shouldUnlistenDataStateChange() {
- mDataUsageList.onCreate(null);
- ReflectionHelpers.setField(
- mDataUsageList, "mVisibilityLoggerMixin", mock(VisibilityLoggerMixin.class));
- ReflectionHelpers.setField(
- mDataUsageList, "mPreferenceManager", mock(PreferenceManager.class));
-
- mDataUsageList.onResume();
- mDataUsageList.onPause();
-
- verify(mMobileDataEnabledListener).stop();
- }
-
- @Test
- public void processArgument_shouldGetTemplateFromArgument() {
- final Bundle args = new Bundle();
- args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mock(NetworkTemplate.class));
- args.putInt(DataUsageList.EXTRA_SUB_ID, 3);
- mDataUsageList.setArguments(args);
-
- mDataUsageList.processArgument();
-
- assertThat(mDataUsageList.mTemplate).isNotNull();
- assertThat(mDataUsageList.mSubId).isEqualTo(3);
- }
-
- @Test
- public void processArgument_fromIntent_shouldGetTemplateFromIntent() {
- final Intent intent = new Intent();
- intent.putExtra(Settings.EXTRA_NETWORK_TEMPLATE, mock(NetworkTemplate.class));
- intent.putExtra(Settings.EXTRA_SUB_ID, 3);
- doReturn(intent).when(mDataUsageList).getIntent();
-
- mDataUsageList.processArgument();
-
- assertThat(mDataUsageList.mTemplate).isNotNull();
- assertThat(mDataUsageList.mSubId).isEqualTo(3);
- }
-
- @Test
- public void onLoadFinished_networkCycleDataCallback_shouldShowCycleSpinner() {
- mDataUsageList.mTemplate = mock(NetworkTemplate.class);
- mDataUsageList.onCreate(null);
- mDataUsageList.updatePolicy();
- List<NetworkCycleChartData> mockData = Collections.emptyList();
-
- mDataUsageList.mNetworkCycleDataCallbacks.onLoadFinished(null, mockData);
-
- verify(mDataUsageListHeaderController).updateCycleData(mockData);
- verify(mDataUsageListHeaderController).setConfigButtonVisible(true);
- }
-
- @Test
- public void onPause_shouldDestroyLoaders() {
- mDataUsageList.onPause();
-
- verify(mLoaderManager).destroyLoader(DataUsageList.LOADER_CHART_DATA);
- }
-
- @Implements(DataUsageBaseFragment.class)
- public static class ShadowDataUsageBaseFragment {
- @Implementation
- public void onCreate(Bundle icicle) {
- // do nothing
- }
- }
-
- public class TestDataUsageList extends DataUsageList {
- @Override
- protected <T extends AbstractPreferenceController> T use(Class<T> clazz) {
- return mock(clazz);
- }
-
- @Override
- public <T extends Preference> T findPreference(CharSequence key) {
- if (key.toString().equals("chart_data")) {
- return (T) mock(ChartDataUsagePreference.class);
- }
- return (T) mock(Preference.class);
- }
-
- @Override
- public Intent getIntent() {
- return new Intent();
- }
-
- @NonNull
- @Override
- BillingCycleRepository createBillingCycleRepository() {
- return mBillingCycleRepository;
- }
-
- @Override
- boolean isBillingCycleModifiable() {
- return true;
- }
- }
-}
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt
new file mode 100644
index 0000000..90bb048
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt
@@ -0,0 +1,216 @@
+/*
+ * 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.datausage
+
+import android.content.Context
+import android.content.Intent
+import android.net.NetworkTemplate
+import android.os.Bundle
+import android.os.UserManager
+import android.provider.Settings
+import androidx.preference.Preference
+import androidx.preference.PreferenceManager
+import androidx.test.core.app.ApplicationProvider
+import com.android.settings.datausage.DataUsageListTest.ShadowDataUsageBaseFragment
+import com.android.settings.datausage.TemplatePreference.NetworkServices
+import com.android.settings.datausage.lib.BillingCycleRepository
+import com.android.settings.network.MobileDataEnabledListener
+import com.android.settings.testutils.FakeFeatureFactory
+import com.android.settingslib.NetworkPolicyEditor
+import com.android.settingslib.core.AbstractPreferenceController
+import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.Implementation
+import org.robolectric.annotation.Implements
+import org.robolectric.util.ReflectionHelpers
+
+@RunWith(RobolectricTestRunner::class)
+@Config(shadows = [ShadowDataUsageBaseFragment::class])
+class DataUsageListTest {
+ @get:Rule
+ val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @Mock
+ private lateinit var mobileDataEnabledListener: MobileDataEnabledListener
+
+ @Mock
+ private lateinit var networkServices: NetworkServices
+
+ @Mock
+ private lateinit var userManager: UserManager
+
+ @Mock
+ private lateinit var billingCycleRepository: BillingCycleRepository
+
+ @Mock
+ private lateinit var dataUsageListHeaderController: DataUsageListHeaderController
+
+ @Spy
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Spy
+ private val dataUsageList = TestDataUsageList()
+
+ @Before
+ fun setUp() {
+ FakeFeatureFactory.setupForTest()
+ networkServices.mPolicyEditor = mock(NetworkPolicyEditor::class.java)
+ dataUsageList.dataStateListener = mobileDataEnabledListener
+ doReturn(context).`when`(dataUsageList).context
+ doReturn(userManager).`when`(context).getSystemService(UserManager::class.java)
+ doReturn(false).`when`(userManager).isGuestUser
+ ReflectionHelpers.setField(dataUsageList, "services", networkServices)
+ doNothing().`when`(dataUsageList).updateSubscriptionInfoEntity()
+ `when`(billingCycleRepository.isBandwidthControlEnabled()).thenReturn(true)
+ dataUsageList.dataUsageListHeaderController = dataUsageListHeaderController
+ }
+
+ @Test
+ fun onCreate_isNotGuestUser_shouldNotFinish() {
+ dataUsageList.template = mock<NetworkTemplate>(NetworkTemplate::class.java)
+ doReturn(false).`when`(userManager).isGuestUser
+ doNothing().`when`(dataUsageList).processArgument()
+ dataUsageList.onCreate(null)
+ verify(dataUsageList, never()).finish()
+ }
+
+ @Test
+ fun onCreate_isGuestUser_shouldFinish() {
+ doReturn(true).`when`(userManager).isGuestUser
+ dataUsageList.onCreate(null)
+ verify(dataUsageList).finish()
+ }
+
+ @Test
+ fun resume_shouldListenDataStateChange() {
+ dataUsageList.template = mock(NetworkTemplate::class.java)
+ dataUsageList.onCreate(null)
+ dataUsageList.dataStateListener = mobileDataEnabledListener
+ ReflectionHelpers.setField(
+ dataUsageList,
+ "mVisibilityLoggerMixin",
+ mock(VisibilityLoggerMixin::class.java),
+ )
+ ReflectionHelpers.setField(
+ dataUsageList,
+ "mPreferenceManager",
+ mock(PreferenceManager::class.java),
+ )
+ dataUsageList.onResume()
+ verify(mobileDataEnabledListener).start(ArgumentMatchers.anyInt())
+ dataUsageList.onPause()
+ }
+
+ @Test
+ fun pause_shouldUnlistenDataStateChange() {
+ dataUsageList.template = mock(NetworkTemplate::class.java)
+ dataUsageList.onCreate(null)
+ dataUsageList.dataStateListener = mobileDataEnabledListener
+ ReflectionHelpers.setField(
+ dataUsageList, "mVisibilityLoggerMixin", mock(
+ VisibilityLoggerMixin::class.java
+ )
+ )
+ ReflectionHelpers.setField(
+ dataUsageList, "mPreferenceManager", mock(
+ PreferenceManager::class.java
+ )
+ )
+ dataUsageList.onResume()
+ dataUsageList.onPause()
+ verify(mobileDataEnabledListener).stop()
+ }
+
+ @Test
+ fun processArgument_shouldGetTemplateFromArgument() {
+ val args = Bundle()
+ args.putParcelable(
+ DataUsageList.EXTRA_NETWORK_TEMPLATE, mock(
+ NetworkTemplate::class.java
+ )
+ )
+ args.putInt(DataUsageList.EXTRA_SUB_ID, 3)
+ dataUsageList.arguments = args
+ dataUsageList.processArgument()
+ assertThat(dataUsageList.template).isNotNull()
+ assertThat(dataUsageList.subId).isEqualTo(3)
+ }
+
+ @Test
+ fun processArgument_fromIntent_shouldGetTemplateFromIntent() {
+ val intent = Intent()
+ intent.putExtra(
+ Settings.EXTRA_NETWORK_TEMPLATE, mock(
+ NetworkTemplate::class.java
+ )
+ )
+ intent.putExtra(Settings.EXTRA_SUB_ID, 3)
+ doReturn(intent).`when`(dataUsageList).intent
+ dataUsageList.processArgument()
+ assertThat(dataUsageList.template).isNotNull()
+ assertThat(dataUsageList.subId).isEqualTo(3)
+ }
+
+ @Test
+ fun updatePolicy_setConfigButtonVisible() {
+ dataUsageList.template = mock(NetworkTemplate::class.java)
+ dataUsageList.onCreate(null)
+
+ dataUsageList.updatePolicy()
+
+ verify(dataUsageListHeaderController).setConfigButtonVisible(true)
+ }
+
+ @Implements(DataUsageBaseFragment::class)
+ class ShadowDataUsageBaseFragment {
+ @Implementation
+ fun onCreate(@Suppress("UNUSED_PARAMETER") icicle: Bundle?) {
+ // do nothing
+ }
+ }
+
+ open inner class TestDataUsageList : DataUsageList() {
+ override fun <T : AbstractPreferenceController?> use(clazz: Class<T>): T = mock(clazz)
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : Preference?> findPreference(key: CharSequence): T =
+ mock(Preference::class.java) as T
+
+ public override fun getIntent() = Intent()
+
+ override fun createBillingCycleRepository() = billingCycleRepository
+
+ override fun isBillingCycleModifiable() = true
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/development/quarantine/QuarantinedAppsScreenControllerTest.java b/tests/robotests/src/com/android/settings/development/quarantine/QuarantinedAppsScreenControllerTest.java
index 32ad0ad..a93e529 100644
--- a/tests/robotests/src/com/android/settings/development/quarantine/QuarantinedAppsScreenControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/quarantine/QuarantinedAppsScreenControllerTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -28,6 +29,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.SuspendDialogInfo;
import android.os.UserHandle;
import androidx.test.core.app.ApplicationProvider;
@@ -70,12 +72,12 @@
mController.onPreferenceChange(preference, true);
verify(packageManager).setPackagesSuspended(aryEq(new String[] {TEST_PACKAGE}), eq(true),
- any(), any(), any(),
+ isNull(), isNull(), any(SuspendDialogInfo.class),
eq(PackageManager.FLAG_SUSPEND_QUARANTINED));
mController.onPreferenceChange(preference, false);
verify(packageManager).setPackagesSuspended(aryEq(new String[] {TEST_PACKAGE}), eq(false),
- any(), any(), any(),
+ isNull(), isNull(), isNull(),
eq(PackageManager.FLAG_SUSPEND_QUARANTINED));
}
diff --git a/tests/robotests/src/com/android/settings/notification/app/FullScreenIntentPermissionPreferenceControllerTest.kt b/tests/robotests/src/com/android/settings/notification/app/FullScreenIntentPermissionPreferenceControllerTest.kt
index 64bb849..89e33ee 100644
--- a/tests/robotests/src/com/android/settings/notification/app/FullScreenIntentPermissionPreferenceControllerTest.kt
+++ b/tests/robotests/src/com/android/settings/notification/app/FullScreenIntentPermissionPreferenceControllerTest.kt
@@ -30,6 +30,7 @@
import android.content.pm.PackageManager
import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
import android.content.pm.PackageManager.GET_PERMISSIONS
+import android.content.pm.PackageManager.NameNotFoundException
import android.os.UserHandle
import android.permission.PermissionManager.PERMISSION_GRANTED
import android.permission.PermissionManager.PERMISSION_HARD_DENIED
@@ -134,6 +135,14 @@
}
@Test
+ fun testIsAvailable_notWhenPackageNotFound() {
+ setPackageInfoNotFound()
+ initController()
+
+ assertFalse(controller.isAvailable)
+ }
+
+ @Test
fun testIsEnabled_notWhenDisabledByAdmin() {
setPermissionRequestedInManifest()
initController(admin = makeTestAdmin())
@@ -242,6 +251,12 @@
})
}
+ private fun setPackageInfoNotFound() {
+ whenever(packageManager.getPackageInfo(TEST_PACKAGE, GET_PERMISSIONS)).thenThrow(
+ NameNotFoundException(TEST_PACKAGE)
+ )
+ }
+
private fun setPermissionResult(@PermissionResult result: Int) {
ShadowPermissionChecker.setResult(TEST_PACKAGE, USE_FULL_SCREEN_INTENT, result)
}
diff --git a/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java b/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
index e1b2b4e..cbdcf3c 100644
--- a/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
+++ b/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
@@ -30,7 +30,6 @@
import static com.android.settings.sim.SimDialogActivity.DATA_PICK;
import static com.android.settings.sim.SimDialogActivity.INVALID_PICK;
-import static com.android.settings.sim.SimDialogActivity.PICK_DISMISS;
import static com.android.settings.sim.SimSelectNotification.ENABLE_MMS_NOTIFICATION_CHANNEL;
import static com.android.settings.sim.SimSelectNotification.ENABLE_MMS_NOTIFICATION_ID;
import static com.android.settings.sim.SimSelectNotification.SIM_WARNING_NOTIFICATION_CHANNEL;
@@ -61,6 +60,8 @@
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
+import androidx.test.core.app.ApplicationProvider;
+
import com.android.settings.R;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
@@ -71,6 +72,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@@ -80,8 +82,8 @@
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowAlertDialogCompat.class)
public class SimSelectNotificationTest {
- @Mock
- private Context mContext;
+ @Spy
+ private Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private Executor mExecutor;
@Mock
@@ -92,8 +94,8 @@
private SubscriptionManager mSubscriptionManager;
@Mock
private PackageManager mPackageManager;
- @Mock
- private Resources mResources;
+ @Spy
+ private Resources mResources = mContext.getResources();
@Mock
private SubscriptionInfo mSubInfo;
@Mock
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
index c9cc02e..5a39368 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
@@ -27,7 +27,9 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -42,6 +44,7 @@
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.wifi.AccessPoint;
+import com.android.wifitrackerlib.NetworkDetailsTracker;
import com.android.wifitrackerlib.WifiEntry;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -59,14 +62,13 @@
static final String CALLING_PACKAGE = "calling_package";
static final int REQUEST_CODE = REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER;
+ private static final String SSID = "SSID";
@Mock
UserManager mUserManager;
@Mock
PackageManager mPackageManager;
@Mock
- WifiManager mWifiManager;
- @Mock
WifiDialog mWifiDialog;
@Mock
WifiConfiguration mWifiConfiguration;
@@ -91,16 +93,22 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mWifiDialog.getController()).thenReturn(mController);
+ mWifiConfiguration.SSID = SSID;
when(mController.getConfig()).thenReturn(mWifiConfiguration);
when(mController.getAccessPoint()).thenReturn(mAccessPoint);
when(mWifiDialog2.getController()).thenReturn(mWifiConfiguration2);
when(mWifiConfiguration2.getWifiEntry()).thenReturn(mWifiEntry);
when(mWifiEntry.canConnect()).thenReturn(true);
FakeFeatureFactory.setupForTest();
+ WifiTrackerLibProvider mockWifiTrackerLibProvider =
+ FakeFeatureFactory.getFeatureFactory().getWifiTrackerLibProvider();
+ when(mockWifiTrackerLibProvider.createNetworkDetailsTracker(
+ any(), any(), any(), any(), any(), anyLong(), anyLong(), any())
+ ).thenReturn(mock(NetworkDetailsTracker.class));
mActivity = spy(Robolectric.setupActivity(WifiDialogActivity.class));
when(mActivity.getSystemService(UserManager.class)).thenReturn(mUserManager);
- when(mActivity.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
+
when(mActivity.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
}
@@ -108,7 +116,8 @@
public void onSubmit_shouldConnectToNetwork() {
mActivity.onSubmit(mWifiDialog);
- verify(mWifiManager).connect(any(), any());
+ WifiManager wifiManager = mActivity.getSystemService(WifiManager.class);
+ assertThat(wifiManager.getConnectionInfo().getSSID()).isEqualTo("\"SSID\"");
}
@Test
@@ -155,7 +164,6 @@
intent.putExtra(WifiDialogActivity.KEY_CHOSEN_WIFIENTRY_KEY, "FAKE_KEY");
intent.putExtra(WifiDialogActivity.KEY_CONNECT_FOR_CALLER, true);
mActivity = spy(Robolectric.buildActivity(WifiDialogActivity.class, intent).setup().get());
- when(mActivity.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
mActivity.onSubmit(mWifiDialog2);
@@ -167,11 +175,11 @@
final Intent intent = new Intent();
intent.putExtra(WifiDialogActivity.KEY_CONNECT_FOR_CALLER, false);
mActivity = spy(Robolectric.buildActivity(WifiDialogActivity.class, intent).setup().get());
- when(mActivity.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
mActivity.onSubmit(mWifiDialog);
- verify(mWifiManager, never()).connect(any(), any());
+ WifiManager wifiManager = mActivity.getSystemService(WifiManager.class);
+ assertThat(wifiManager.getConnectionInfo().getSSID()).isEqualTo(WifiManager.UNKNOWN_SSID);
}
@Test
@@ -180,7 +188,6 @@
intent.putExtra(WifiDialogActivity.KEY_CHOSEN_WIFIENTRY_KEY, "FAKE_KEY");
intent.putExtra(WifiDialogActivity.KEY_CONNECT_FOR_CALLER, false);
mActivity = spy(Robolectric.buildActivity(WifiDialogActivity.class, intent).setup().get());
- when(mActivity.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
mActivity.onSubmit(mWifiDialog2);
@@ -193,8 +200,7 @@
intent.putExtra(WifiDialogActivity.KEY_CONNECT_FOR_CALLER, false);
intent.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true);
intent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true);
- mActivity = spy(Robolectric.buildActivity(WifiDialogActivity.class, intent).setup().get());
- when(mActivity.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
+ mActivity = spy(Robolectric.buildActivity(WifiDialogActivity.class, intent).create().get());
doNothing().when(mActivity).createDialogWithSuwTheme();
mActivity.onStart();
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
index bcc0933..2d64e16 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
@@ -47,7 +47,6 @@
import androidx.slice.widget.SliceContent;
import androidx.slice.widget.SliceLiveData;
-import com.android.ims.ImsManager;
import com.android.settings.R;
import com.android.settings.network.ims.MockWifiCallingQueryImsState;
import com.android.settings.slices.CustomSliceRegistry;
@@ -79,9 +78,6 @@
private CarrierConfigManager mMockCarrierConfigManager;
@Mock
- private ImsManager mMockImsManager;
-
- @Mock
private ImsMmTelManager mMockImsMmTelManager;
private MockWifiCallingQueryImsState mQueryImsState;
@@ -122,20 +118,7 @@
}
@Test
- public void test_CreateWifiCallingSlice_invalidSubId() {
- mQueryImsState.setIsEnabledByUser(true);
- mQueryImsState.setIsProvisionedOnDevice(false);
- mWfcSliceHelper.setDefaultVoiceSubId(-1);
- mQueryImsState.setIsReadyToWifiCalling(true);
-
- final Slice slice = mWfcSliceHelper.createWifiCallingSlice(
- CustomSliceRegistry.WIFI_CALLING_URI);
-
- assertThat(slice).isNull();
- }
-
- @Test
- public void test_CreateWifiCallingSlice_wfcNotSupported() {
+ public void createWifiCallingSlice_notReadyToWifiCalling_wfcNotSupported() {
mQueryImsState.setIsProvisionedOnDevice(false);
mQueryImsState.setIsReadyToWifiCalling(false);
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
index 38b7463..11d421c 100644
--- a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
@@ -437,16 +437,6 @@
}
@Test
- public void latestWifiInfo_shouldBeFetchedInDisplayPreferenceForConnectedNetwork() {
- setUpForConnectedNetwork();
- setUpSpyController();
-
- displayAndResume();
-
- verify(mMockWifiManager, times(1)).getConnectionInfo();
- }
-
- @Test
public void latestWifiInfo_shouldNotBeFetchedInDisplayPreferenceForDisconnectedNetwork() {
setUpForDisconnectedNetwork();
@@ -465,16 +455,6 @@
}
@Test
- public void latestNetworkInfo_shouldBeFetchedInDisplayPreferenceForConnectedNetwork() {
- setUpForConnectedNetwork();
- setUpSpyController();
-
- displayAndResume();
-
- verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
- }
-
- @Test
public void latestNetworkInfo_shouldNotBeFetchedInDisplayPreferenceForDisconnectedNetwork() {
setUpForDisconnectedNetwork();
@@ -1283,8 +1263,9 @@
displayAndResume();
- verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
- verify(mMockWifiManager, times(1)).getConnectionInfo();
+ verify(mMockWifiManager, times(1)).getCurrentNetwork();
+ verify(mMockConnectivityManager, times(1)).getLinkProperties(any(Network.class));
+ verify(mMockConnectivityManager, times(1)).getNetworkCapabilities(any(Network.class));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java
index a8b1d9c..99286cf 100644
--- a/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2pSettingsTest.java
@@ -51,6 +51,7 @@
import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -65,6 +66,7 @@
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowInteractionJankMonitor.class)
+@Ignore
public class WifiP2pSettingsTest {
private Context mContext;
diff --git a/tests/spa_unit/src/com/android/settings/datausage/ChartDataUsagePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/ChartDataUsagePreferenceControllerTest.kt
new file mode 100644
index 0000000..1748f07
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/ChartDataUsagePreferenceControllerTest.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.datausage
+
+import android.content.Context
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.preference.PreferenceScreen
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.datausage.lib.INetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkCycleChartData
+import com.android.settings.datausage.lib.NetworkUsageData
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class ChartDataUsagePreferenceControllerTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val repository = object : INetworkCycleDataRepository {
+ override suspend fun loadCycles() = emptyList<NetworkUsageData>()
+
+ override fun getPolicy() = null
+
+ override suspend fun querySummary(startTime: Long, endTime: Long) = when {
+ startTime == START_TIME && endTime == END_TIME -> CycleChartDate
+ else -> null
+ }
+ }
+
+ private val preference = mock<ChartDataUsagePreference>()
+ private val preferenceScreen = mock<PreferenceScreen> {
+ onGeneric { findPreference(KEY) } doReturn preference
+ }
+
+ private val controller = ChartDataUsagePreferenceController(context, KEY)
+
+ @Before
+ fun setUp() {
+ controller.init(repository)
+ controller.displayPreference(preferenceScreen)
+ controller.onViewCreated(TestLifecycleOwner())
+ }
+
+ @Test
+ fun update() = runBlocking {
+ controller.update(START_TIME, END_TIME)
+ delay(100L)
+
+ verify(preference).setTime(START_TIME, END_TIME)
+ verify(preference).setNetworkCycleData(CycleChartDate)
+ }
+
+ private companion object {
+ const val KEY = "test_key"
+ const val START_TIME = 1L
+ const val END_TIME = 2L
+
+ val UsageData = NetworkUsageData(startTime = START_TIME, endTime = END_TIME, usage = 10)
+ val CycleChartDate =
+ NetworkCycleChartData(total = UsageData, dailyUsage = listOf(UsageData))
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt
index af5dc89..2646323 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt
@@ -22,8 +22,8 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.SettingsActivity
+import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settingslib.AppItem
-import com.android.settingslib.net.NetworkCycleChartData
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -48,11 +48,8 @@
@Before
fun setUp() {
controller.init(mock<NetworkTemplate>())
- val data = NetworkCycleChartData.Builder().apply {
- setStartTime(START_TIME)
- setEndTime(END_TIME)
- }.build()
- controller.setCycleData(listOf(data))
+ val data = NetworkUsageData(START_TIME, END_TIME, 0)
+ controller.updateCycles(listOf(data))
}
@Test
diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataUsageListHeaderControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListHeaderControllerTest.kt
index a1eebe7..35b70d6 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/DataUsageListHeaderControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListHeaderControllerTest.kt
@@ -21,10 +21,16 @@
import android.view.LayoutInflater
import android.view.View
import android.widget.Spinner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
+import com.android.settings.datausage.lib.INetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkUsageData
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
@@ -40,6 +46,14 @@
doNothing().whenever(mock).startActivity(any())
}
+ private val repository = object : INetworkCycleDataRepository {
+ override suspend fun loadCycles() = emptyList<NetworkUsageData>()
+
+ override fun getPolicy() = null
+
+ override suspend fun querySummary(startTime: Long, endTime: Long) = null
+ }
+
private val header =
LayoutInflater.from(context).inflate(R.layout.apps_filter_spinner, null, false)
@@ -47,11 +61,16 @@
private val spinner: Spinner = header.requireViewById(R.id.filter_spinner)
+ private val testLifecycleOwner = TestLifecycleOwner(initialState = Lifecycle.State.CREATED)
+
private val controller = DataUsageListHeaderController(
header = header,
template = mock<NetworkTemplate>(),
sourceMetricsCategory = 0,
- onItemSelected = { _, _ -> },
+ viewLifecycleOwner = testLifecycleOwner,
+ onCyclesLoad = {},
+ onItemSelected = {},
+ repository = repository,
)
@Test
@@ -60,8 +79,9 @@
}
@Test
- fun updateCycleData_shouldShowCycleSpinner() {
- controller.updateCycleData(emptyList())
+ fun updateCycleData_shouldShowCycleSpinner() = runBlocking {
+ testLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+ delay(100)
assertThat(spinner.visibility).isEqualTo(View.VISIBLE)
}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt
new file mode 100644
index 0000000..fb5e820
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt
@@ -0,0 +1,168 @@
+/*
+ * 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.datausage.lib
+
+import android.app.usage.NetworkStats.Bucket
+import android.app.usage.NetworkStatsManager
+import android.content.Context
+import android.net.NetworkPolicy
+import android.net.NetworkTemplate
+import android.text.format.DateUtils
+import android.util.Range
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import java.time.ZoneId
+import java.time.ZonedDateTime
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class NetworkCycleDataRepositoryTest {
+ private val mockNetworkStatsManager = mock<NetworkStatsManager> {
+ on { querySummaryForDevice(any(), eq(CYCLE1_START_TIME), eq(CYCLE1_END_TIME)) } doReturn
+ CYCLE1_BUCKET
+
+ on {
+ querySummaryForDevice(
+ any(),
+ eq(CYCLE2_END_TIME - DateUtils.WEEK_IN_MILLIS * 4),
+ eq(CYCLE2_END_TIME),
+ )
+ } doReturn CYCLE2_BUCKET
+
+ on { querySummaryForDevice(any(), eq(CYCLE3_START_TIME), eq(CYCLE4_END_TIME)) } doReturn
+ CYCLE3_AND_4_BUCKET
+
+ on { querySummaryForDevice(any(), eq(CYCLE3_START_TIME), eq(CYCLE3_END_TIME)) } doReturn
+ CYCLE3_BUCKET
+
+ on { querySummaryForDevice(any(), eq(CYCLE4_START_TIME), eq(CYCLE4_END_TIME)) } doReturn
+ CYCLE4_BUCKET
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(NetworkStatsManager::class.java) } doReturn mockNetworkStatsManager
+ }
+
+ private val template = mock<NetworkTemplate>()
+
+ private val repository = spy(NetworkCycleDataRepository(context, template))
+
+ @Test
+ fun loadCycles_byPolicy() = runTest {
+ val policy = mock<NetworkPolicy> {
+ on { cycleIterator() } doReturn listOf(
+ Range(zonedDateTime(CYCLE1_START_TIME), zonedDateTime(CYCLE1_END_TIME))
+ ).iterator()
+ }
+ doReturn(policy).whenever(repository).getPolicy()
+
+ val cycles = repository.loadCycles()
+
+ assertThat(cycles).containsExactly(NetworkUsageData(startTime = 1, endTime = 2, usage = 11))
+ }
+
+ @Test
+ fun loadCycles_asFourWeeks() = runTest {
+ doReturn(null).whenever(repository).getPolicy()
+ doReturn(Range(CYCLE2_START_TIME, CYCLE2_END_TIME)).whenever(repository).getTimeRange()
+
+ val cycles = repository.loadCycles()
+
+ assertThat(cycles).containsExactly(
+ NetworkUsageData(
+ startTime = CYCLE2_END_TIME - DateUtils.WEEK_IN_MILLIS * 4,
+ endTime = CYCLE2_END_TIME,
+ usage = 22,
+ ),
+ )
+ }
+
+ @Test
+ fun querySummary() = runTest {
+ val summary = repository.querySummary(CYCLE3_START_TIME, CYCLE4_END_TIME)
+
+ assertThat(summary).isEqualTo(
+ NetworkCycleChartData(
+ total = NetworkUsageData(
+ startTime = CYCLE3_START_TIME,
+ endTime = CYCLE4_END_TIME,
+ usage = 77,
+ ),
+ dailyUsage = listOf(
+ NetworkUsageData(
+ startTime = CYCLE3_START_TIME,
+ endTime = CYCLE3_END_TIME,
+ usage = 33,
+ ),
+ NetworkUsageData(
+ startTime = CYCLE4_START_TIME,
+ endTime = CYCLE4_END_TIME,
+ usage = 44,
+ ),
+ ),
+ )
+ )
+ }
+
+ private fun zonedDateTime(epochMilli: Long): ZonedDateTime? =
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault())
+
+ private companion object {
+ const val CYCLE1_START_TIME = 1L
+ const val CYCLE1_END_TIME = 2L
+ val CYCLE1_BUCKET = mock<Bucket> {
+ on { rxBytes } doReturn 1
+ on { txBytes } doReturn 10
+ }
+
+ const val CYCLE2_START_TIME = 1695555555000L
+ const val CYCLE2_END_TIME = 1695566666000L
+ val CYCLE2_BUCKET = mock<Bucket> {
+ on { rxBytes } doReturn 2
+ on { txBytes } doReturn 20
+ }
+
+ const val CYCLE3_START_TIME = 1695555555000L
+ const val CYCLE3_END_TIME = CYCLE3_START_TIME + DateUtils.DAY_IN_MILLIS
+ val CYCLE3_BUCKET = mock<Bucket> {
+ on { rxBytes } doReturn 3
+ on { txBytes } doReturn 30
+ }
+
+ const val CYCLE4_START_TIME = CYCLE3_END_TIME
+ const val CYCLE4_END_TIME = CYCLE4_START_TIME + DateUtils.DAY_IN_MILLIS
+ val CYCLE4_BUCKET = mock<Bucket> {
+ on { rxBytes } doReturn 4
+ on { txBytes } doReturn 40
+ }
+
+ val CYCLE3_AND_4_BUCKET = mock<Bucket> {
+ on { rxBytes } doReturn 7
+ on { txBytes } doReturn 70
+ }
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt
index 9a17032..dd5b929 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt
@@ -17,10 +17,10 @@
package com.android.settings.spa.app.appcompat
import android.content.Context
-import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
+import android.content.pm.LauncherActivityInfo
+import android.content.pm.LauncherApps
import android.content.pm.PackageManager
-import android.content.pm.ResolveInfo
import android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER
import android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE
import androidx.compose.runtime.CompositionLocalProvider
@@ -46,9 +46,9 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.any
-import org.mockito.Mockito.anyInt
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
@@ -76,6 +76,12 @@
@Mock
private lateinit var packageManager: PackageManager
+ @Mock
+ private lateinit var launcherApps: LauncherApps
+
+ @Mock
+ private lateinit var launcherActivities: List<LauncherActivityInfo>
+
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
@@ -86,6 +92,8 @@
.startMocking()
whenever(context.resources).thenReturn(resources)
whenever(context.packageManager).thenReturn(packageManager)
+ whenever(context.getSystemService(Context.LAUNCHER_APPS_SERVICE)).thenReturn(launcherApps)
+ whenever(launcherApps.getActivityList(anyString(), any())).thenReturn(launcherActivities)
// True is ignored but need this here or getBoolean will complain null object
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, true)
}
@@ -107,6 +115,8 @@
@Test
fun whenCannotDisplayAspectRatioUi_notDisplayed() {
+ whenever(launcherActivities.isEmpty()).thenReturn(true)
+
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
@@ -115,8 +125,7 @@
@Test
fun whenCanDisplayAspectRatioUiAndConfigFalse_notDisplayed() {
setConfig(false)
- whenever(packageManager.queryIntentActivities(any(), anyInt()))
- .thenReturn(listOf(RESOLVE_INFO))
+ whenever(launcherActivities.isEmpty()).thenReturn(false)
setContent()
@@ -127,6 +136,8 @@
fun whenCannotDisplayAspectRatioUiAndConfigTrue_notDisplayed() {
setConfig(true)
+ whenever(launcherActivities.isEmpty()).thenReturn(true)
+
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
@@ -135,9 +146,7 @@
@Test
fun whenCanDisplayAspectRatioUiAndConfigTrue_Displayed() {
setConfig(true)
- whenever(packageManager.queryIntentActivities(any(), anyInt()))
- .thenReturn(listOf(RESOLVE_INFO))
-
+ whenever(launcherActivities.isEmpty()).thenReturn(false)
setContent()
composeTestRule.onNode(
@@ -151,8 +160,7 @@
@Test
fun onClick_startActivity() {
setConfig(true)
- whenever(packageManager.queryIntentActivities(any(), anyInt()))
- .thenReturn(listOf(RESOLVE_INFO))
+ whenever(launcherActivities.isEmpty()).thenReturn(false)
setContent()
composeTestRule.onRoot().performClick()
@@ -196,10 +204,5 @@
packageName = PACKAGE_NAME
uid = UID
}
- private val RESOLVE_INFO = ResolveInfo().apply {
- activityInfo = ActivityInfo().apply {
- packageName = PACKAGE_NAME
- }
- }
}
}
\ No newline at end of file
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 4f044c7..eb38980 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -25,6 +25,7 @@
"androidx.preference_preference",
"mockito-target-minus-junit4",
"platform-test-annotations",
+ "platform-test-rules",
"truth-prebuilt",
"kotlinx_coroutines_test",
"flag-junit",
diff --git a/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
index 6cc386b..61c4507 100644
--- a/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
+++ b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
@@ -17,7 +17,6 @@
package com.android.settings.applications.appcompat;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
-import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
@@ -32,16 +31,23 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+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.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Resources;
+import android.platform.test.rule.DeviceTypeRule;
+import android.platform.test.rule.FoldableOnly;
+import android.platform.test.rule.LargeScreenOnly;
+import android.platform.test.rule.TabletOnly;
import android.provider.DeviceConfig;
import androidx.test.core.app.ApplicationProvider;
@@ -52,13 +58,18 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
+import java.util.List;
+
/**
* To run this test: atest SettingsUnitTests:UserAspectRatioManagerTest
*/
@RunWith(AndroidJUnit4.class)
+@LargeScreenOnly
public class UserAspectRatioManagerTest {
private Context mContext;
@@ -67,14 +78,27 @@
private String mOriginalSettingsFlag;
private String mOriginalFullscreenFlag;
private String mPackageName = "com.test.mypackage";
+ private LauncherApps mLauncherApps;
+ private List<LauncherActivityInfo> mLauncherActivities;
+
+ @Rule
+ public TestRule mDeviceTypeRule = new DeviceTypeRule();
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
mResources = spy(mContext.getResources());
- mUtils = new UserAspectRatioManager(mContext);
+ mLauncherApps = mock(LauncherApps.class);
+ mLauncherActivities = mock(List.class);
+ mUtils = new UserAspectRatioManager(mContext) {
+ @Override
+ LauncherApps getLauncherApps() {
+ return mLauncherApps;
+ }
+ };
when(mContext.getResources()).thenReturn(mResources);
+ doReturn(mLauncherActivities).when(mLauncherApps).getActivityList(anyString(), any());
mOriginalSettingsFlag = DeviceConfig.getProperty(
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS);
@@ -98,13 +122,14 @@
public void testCanDisplayAspectRatioUi() {
final ApplicationInfo canDisplay = new ApplicationInfo();
canDisplay.packageName = "com.app.candisplay";
- addResolveInfoLauncherEntry(canDisplay.packageName);
+ doReturn(false).when(mLauncherActivities).isEmpty();
assertTrue(mUtils.canDisplayAspectRatioUi(canDisplay));
final ApplicationInfo noLauncherEntry = new ApplicationInfo();
noLauncherEntry.packageName = "com.app.nolauncherentry";
+ doReturn(true).when(mLauncherActivities).isEmpty();
assertFalse(mUtils.canDisplayAspectRatioUi(noLauncherEntry));
}
@@ -112,10 +137,10 @@
public void testCanDisplayAspectRatioUi_hasLauncher_propertyFalse_returnFalse()
throws PackageManager.NameNotFoundException {
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, false);
+ doReturn(true).when(mLauncherActivities).isEmpty();
final ApplicationInfo canDisplay = new ApplicationInfo();
canDisplay.packageName = mPackageName;
- addResolveInfoLauncherEntry(canDisplay.packageName);
assertFalse(mUtils.canDisplayAspectRatioUi(canDisplay));
}
@@ -124,6 +149,7 @@
public void testCanDisplayAspectRatioUi_noLauncher_propertyTrue_returnFalse()
throws PackageManager.NameNotFoundException {
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, true);
+ doReturn(true).when(mLauncherActivities).isEmpty();
final ApplicationInfo noLauncherEntry = new ApplicationInfo();
noLauncherEntry.packageName = mPackageName;
@@ -203,7 +229,8 @@
}
@Test
- public void testGetUserMinAspectRatioEntry() {
+ @FoldableOnly
+ public void testGetUserMinAspectRatioEntry_Foldable() {
// R.string.user_aspect_ratio_app_default
final String appDefault = ResourcesUtils.getResourcesString(mContext,
"user_aspect_ratio_app_default");
@@ -216,19 +243,35 @@
assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(mContext,
"user_aspect_ratio_half_screen"));
- // R.string.user_aspect_ratio_3_2
- assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_3_2, mPackageName))
- .isEqualTo(ResourcesUtils.getResourcesString(mContext, "user_aspect_ratio_3_2"));
// R,string.user_aspect_ratio_4_3
assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_4_3, mPackageName))
.isEqualTo(ResourcesUtils.getResourcesString(mContext, "user_aspect_ratio_4_3"));
- // R.string.user_aspect_ratio_16_9
+ assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN,
+ mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(mContext,
+ "user_aspect_ratio_fullscreen"));
+ }
+
+ @Test
+ @TabletOnly
+ public void testGetUserMinAspectRatioEntry_Tablet() {
+ // R.string.user_aspect_ratio_app_default
+ final String appDefault = ResourcesUtils.getResourcesString(mContext,
+ "user_aspect_ratio_app_default");
+ assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
+ .isEqualTo(appDefault);
+ // should always return default if value does not correspond to anything
+ assertThat(mUtils.getUserMinAspectRatioEntry(-1, mPackageName))
+ .isEqualTo(appDefault);
+ // R.string.user_aspect_ratio_half_screen
+ assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
+ mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(mContext,
+ "user_aspect_ratio_half_screen"));
assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_16_9, mPackageName))
.isEqualTo(ResourcesUtils.getResourcesString(mContext, "user_aspect_ratio_16_9"));
// R.string.user_aspect_ratio_fullscreen
assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN,
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(mContext,
- "user_aspect_ratio_fullscreen"));
+ "user_aspect_ratio_fullscreen"));
}
@Test
@@ -267,12 +310,4 @@
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, enabled, makeDefault);
}
-
- private void addResolveInfoLauncherEntry(String packageName) {
- final ResolveInfo resolveInfo = mock(ResolveInfo.class);
- final ActivityInfo activityInfo = mock(ActivityInfo.class);
- activityInfo.packageName = packageName;
- resolveInfo.activityInfo = activityInfo;
- mUtils.addInfoHasLauncherEntry(resolveInfo);
- }
}
diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java
new file mode 100644
index 0000000..c04e5f9
--- /dev/null
+++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.localepicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.settings.widget.TickButtonPreference;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class TermsOfAddressFeminineControllerTest {
+
+ private static final String KEY_CATEGORY_TERMS_OF_ADDRESS = "key_category_terms_of_address";
+ private static final String KEY_FEMININE = "key_terms_of_address_feminine";
+ private static final String KEY_MASCULINE = "key_terms_of_address_masculine";
+ private static final String KEY_NEUTRAL = "key_terms_of_address_neutral";
+ private static final String KEY_NOT_SPECIFIED = "key_terms_of_address_not_specified";
+
+ private Context mContext;
+ private PreferenceManager mPreferenceManager;
+ private PreferenceCategory mPreferenceCategory;
+ private PreferenceScreen mPreferenceScreen;
+ private TermsOfAddressFeminineController mController;
+ private TickButtonPreference mFemininePreference;
+ private TickButtonPreference mMasculinePreference;
+ private TickButtonPreference mNotSpecifiedPreference;
+ private TickButtonPreference mNeutralPreference;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mPreferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
+ mPreferenceCategory = new PreferenceCategory(mContext);
+ mPreferenceCategory.setKey(KEY_CATEGORY_TERMS_OF_ADDRESS);
+ mNotSpecifiedPreference = new TickButtonPreference(mContext);
+ mNotSpecifiedPreference.setKey(KEY_NOT_SPECIFIED);
+ mFemininePreference = new TickButtonPreference(mContext);
+ mFemininePreference.setKey(KEY_FEMININE);
+ mMasculinePreference = new TickButtonPreference(mContext);
+ mMasculinePreference.setKey(KEY_MASCULINE);
+ mNeutralPreference = new TickButtonPreference(mContext);
+ mNeutralPreference.setKey(KEY_NEUTRAL);
+ mPreferenceScreen.addPreference(mPreferenceCategory);
+ mPreferenceScreen.addPreference(mNotSpecifiedPreference);
+ mPreferenceScreen.addPreference(mFemininePreference);
+ mPreferenceScreen.addPreference(mMasculinePreference);
+ mPreferenceScreen.addPreference(mNeutralPreference);
+ mController = new TermsOfAddressFeminineController(mContext, KEY_FEMININE);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void displayPreference_setGrammaticalGenderIsFeminine_FeminineIsSelected() {
+ TickButtonPreference selectedPreference =
+ (TickButtonPreference) mPreferenceScreen.getPreference(2);
+ TickButtonPreference pref = (TickButtonPreference) mPreferenceScreen.getPreference(1);
+
+ selectedPreference.performClick();
+
+ assertThat(selectedPreference.getKey()).isEqualTo(KEY_FEMININE);
+ assertThat(selectedPreference.isSelected()).isTrue();
+ assertThat(pref.isSelected()).isFalse();
+ }
+}
diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java
new file mode 100644
index 0000000..c2298be
--- /dev/null
+++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.localepicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.settings.widget.TickButtonPreference;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class TermsOfAddressMasculineControllerTest {
+
+ private static final String KEY_CATEGORY_TERMS_OF_ADDRESS = "key_category_terms_of_address";
+ private static final String KEY_FEMININE = "key_terms_of_address_feminine";
+ private static final String KEY_MASCULINE = "key_terms_of_address_masculine";
+ private static final String KEY_NEUTRAL = "key_terms_of_address_neutral";
+ private static final String KEY_NOT_SPECIFIED = "key_terms_of_address_not_specified";
+
+ private Context mContext;
+ private PreferenceManager mPreferenceManager;
+ private PreferenceCategory mPreferenceCategory;
+ private PreferenceScreen mPreferenceScreen;
+ private TermsOfAddressMasculineController mController;
+ private TickButtonPreference mFemininePreference;
+ private TickButtonPreference mMasculinePreference;
+ private TickButtonPreference mNotSpecifiedPreference;
+ private TickButtonPreference mNeutralPreference;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mPreferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
+ mPreferenceCategory = new PreferenceCategory(mContext);
+ mPreferenceCategory.setKey(KEY_CATEGORY_TERMS_OF_ADDRESS);
+ mNotSpecifiedPreference = new TickButtonPreference(mContext);
+ mNotSpecifiedPreference.setKey(KEY_NOT_SPECIFIED);
+ mFemininePreference = new TickButtonPreference(mContext);
+ mFemininePreference.setKey(KEY_FEMININE);
+ mMasculinePreference = new TickButtonPreference(mContext);
+ mMasculinePreference.setKey(KEY_MASCULINE);
+ mNeutralPreference = new TickButtonPreference(mContext);
+ mNeutralPreference.setKey(KEY_NEUTRAL);
+ mPreferenceScreen.addPreference(mPreferenceCategory);
+ mPreferenceScreen.addPreference(mNotSpecifiedPreference);
+ mPreferenceScreen.addPreference(mFemininePreference);
+ mPreferenceScreen.addPreference(mMasculinePreference);
+ mPreferenceScreen.addPreference(mNeutralPreference);
+ mController = new TermsOfAddressMasculineController(mContext, KEY_MASCULINE);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void displayPreference_setGrammaticalGenderIsMasculine_MasculineIsSelected() {
+ TickButtonPreference selectedPreference =
+ (TickButtonPreference) mPreferenceScreen.getPreference(3);
+ TickButtonPreference pref = (TickButtonPreference) mPreferenceScreen.getPreference(1);
+
+ selectedPreference.performClick();
+
+ assertThat(selectedPreference.getKey()).isEqualTo(KEY_MASCULINE);
+ assertThat(selectedPreference.isSelected()).isTrue();
+ assertThat(pref.isSelected()).isFalse();
+ }
+}
diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java
new file mode 100644
index 0000000..fb207fc
--- /dev/null
+++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.localepicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.settings.widget.TickButtonPreference;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class TermsOfAddressNeutralControllerTest {
+
+ private static final String KEY_CATEGORY_TERMS_OF_ADDRESS = "key_category_terms_of_address";
+ private static final String KEY_FEMININE = "key_terms_of_address_feminine";
+ private static final String KEY_MASCULINE = "key_terms_of_address_masculine";
+ private static final String KEY_NEUTRAL = "key_terms_of_address_neutral";
+ private static final String KEY_NOT_SPECIFIED = "key_terms_of_address_not_specified";
+
+ private Context mContext;
+ private PreferenceManager mPreferenceManager;
+ private PreferenceCategory mPreferenceCategory;
+ private PreferenceScreen mPreferenceScreen;
+ private TermsOfAddressNeutralController mController;
+ private TickButtonPreference mFemininePreference;
+ private TickButtonPreference mMasculinePreference;
+ private TickButtonPreference mNotSpecifiedPreference;
+ private TickButtonPreference mNeutralPreference;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mPreferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
+ mPreferenceCategory = new PreferenceCategory(mContext);
+ mPreferenceCategory.setKey(KEY_CATEGORY_TERMS_OF_ADDRESS);
+ mNotSpecifiedPreference = new TickButtonPreference(mContext);
+ mNotSpecifiedPreference.setKey(KEY_NOT_SPECIFIED);
+ mFemininePreference = new TickButtonPreference(mContext);
+ mFemininePreference.setKey(KEY_FEMININE);
+ mMasculinePreference = new TickButtonPreference(mContext);
+ mMasculinePreference.setKey(KEY_MASCULINE);
+ mNeutralPreference = new TickButtonPreference(mContext);
+ mNeutralPreference.setKey(KEY_NEUTRAL);
+ mPreferenceScreen.addPreference(mPreferenceCategory);
+ mPreferenceScreen.addPreference(mNotSpecifiedPreference);
+ mPreferenceScreen.addPreference(mFemininePreference);
+ mPreferenceScreen.addPreference(mMasculinePreference);
+ mPreferenceScreen.addPreference(mNeutralPreference);
+ mController = new TermsOfAddressNeutralController(mContext, KEY_NEUTRAL);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void displayPreference_setGrammaticalGenderIsNotSpecified_NotSpecifiedIsSelected() {
+ TickButtonPreference selectedPreference =
+ (TickButtonPreference) mPreferenceScreen.getPreference(4);
+ TickButtonPreference pref = (TickButtonPreference) mPreferenceScreen.getPreference(1);
+
+ selectedPreference.performClick();
+
+ assertThat(selectedPreference.getKey()).isEqualTo(KEY_NEUTRAL);
+ assertThat(selectedPreference.isSelected()).isTrue();
+ assertThat(pref.isSelected()).isFalse();
+ }
+}
diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedControllerTest.java
new file mode 100644
index 0000000..8492c3b
--- /dev/null
+++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedControllerTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.localepicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.settings.widget.TickButtonPreference;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class TermsOfAddressNotSpecifiedControllerTest {
+
+ private static final String KEY_CATEGORY_TERMS_OF_ADDRESS = "key_category_terms_of_address";
+ private static final String KEY_FEMININE = "key_terms_of_address_feminine";
+ private static final String KEY_MASCULINE = "key_terms_of_address_masculine";
+ private static final String KEY_NEUTRAL = "key_terms_of_address_neutral";
+ private static final String KEY_NOT_SPECIFIED = "key_terms_of_address_not_specified";
+
+ private Context mContext;
+ private PreferenceManager mPreferenceManager;
+ private PreferenceCategory mPreferenceCategory;
+ private PreferenceScreen mPreferenceScreen;
+ private TermsOfAddressNotSpecifiedController mController;
+ private TickButtonPreference mFemininePreference;
+ private TickButtonPreference mMasculinePreference;
+ private TickButtonPreference mNotSpecifiedPreference;
+ private TickButtonPreference mNeutralPreference;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mPreferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
+ mPreferenceCategory = new PreferenceCategory(mContext);
+ mPreferenceCategory.setKey(KEY_CATEGORY_TERMS_OF_ADDRESS);
+ mNotSpecifiedPreference = new TickButtonPreference(mContext);
+ mNotSpecifiedPreference.setKey(KEY_NOT_SPECIFIED);
+ mFemininePreference = new TickButtonPreference(mContext);
+ mFemininePreference.setKey(KEY_FEMININE);
+ mMasculinePreference = new TickButtonPreference(mContext);
+ mMasculinePreference.setKey(KEY_MASCULINE);
+ mNeutralPreference = new TickButtonPreference(mContext);
+ mNeutralPreference.setKey(KEY_NEUTRAL);
+ mPreferenceScreen.addPreference(mPreferenceCategory);
+ mPreferenceScreen.addPreference(mNotSpecifiedPreference);
+ mPreferenceScreen.addPreference(mFemininePreference);
+ mPreferenceScreen.addPreference(mMasculinePreference);
+ mPreferenceScreen.addPreference(mNeutralPreference);
+ mController = new TermsOfAddressNotSpecifiedController(mContext, KEY_NOT_SPECIFIED);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void displayPreference_setGrammaticalGenderIsNotSpecified_NotSpecifiedIsSelected() {
+ TickButtonPreference selectedPreference =
+ (TickButtonPreference) mPreferenceScreen.getPreference(1);
+ TickButtonPreference pref = (TickButtonPreference) mPreferenceScreen.getPreference(2);
+
+ selectedPreference.performClick();
+
+ assertThat(selectedPreference.getKey()).isEqualTo(KEY_NOT_SPECIFIED);
+ assertThat(selectedPreference.isSelected()).isTrue();
+ assertThat(pref.isSelected()).isFalse();
+ }
+}