Snap for 8124529 from f1128f0c21 to tm-release

Change-Id: I3a5503328844c342cc475b1c2f00f382f65437c1
This commit is contained in:
Android Build Coastguard Worker
2022-01-28 02:14:12 +00:00
23 changed files with 862 additions and 264 deletions

View File

@@ -115,6 +115,7 @@
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
<uses-permission android:name="android.permission.QUERY_ADMIN_POLICY" />
<uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
<uses-permission android:name="android.permission.START_VIEW_APP_FEATURES" />
<application
android:name=".SettingsApplication"

View File

@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:gravity="center_vertical"
android:paddingStart="@dimen/preference_no_icon_padding_start"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingTop="16dip"
android:paddingBottom="16dip"
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:textAlignment="viewStart"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead"
android:textColor="?android:attr/colorAccent"
android:textSize="36sp"/>
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
android:maxLines="10" />
<ProgressBar
android:id="@android:id/progress"
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp"
android:visibility="gone"
android:max="100"
style="?android:attr/progressBarStyleHorizontal" />
</LinearLayout>

View File

@@ -3542,8 +3542,6 @@
<!-- Summary of a single storage volume used space. [CHAR LIMIT=24] -->
<string name="storage_size_large"><xliff:g id="number" example="128">^1</xliff:g><small><small> <xliff:g id="unit" example="KB">^2</xliff:g></small></small></string>
<!-- Summary of a single storage volume total space. [CHAR LIMIT=48]-->
<string name="storage_volume_used">Used of <xliff:g id="total" example="32GB">%1$s</xliff:g></string>
<!-- Toast informing that storage mount operation was successful. [CHAR LIMIT=64]-->
<string name="storage_mount_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is mounted</string>
@@ -13543,6 +13541,9 @@
<!-- Summary for extra app info settings for a specific app [CHAR LIMIT=40] -->
<string name="extra_app_info_summary" translatable="false"></string>
<!-- Label for All Services preference in App info settings [CHAR LIMIT=40] -->
<string name="app_info_all_services_label">All Services</string>
<!-- Title for toggle controlling whether notifications are shown when an app pastes from clipboard. [CHAR LIMIT=50] -->
<string name="show_clip_access_notification">Show clipboard access</string>

View File

@@ -41,6 +41,12 @@
android:title="@string/app_settings_link"
settings:controller="com.android.settings.applications.appinfo.AppSettingPreferenceController" />
<Preference
android:key="all_services_settings"
android:title="@string/app_info_all_services_label"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.AppAllServicesPreferenceController" />
<Preference
android:key="notification_settings"
android:title="@string/notifications_label"

View File

@@ -39,7 +39,6 @@ import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
@@ -324,14 +323,14 @@ public class TetherSettings extends RestrictedSettingsFragment
mStartTetheringCallback = new OnStartTetheringCallback(this);
mTetheringEventCallback = new TetheringEventCallback();
mTm.registerTetheringEventCallback(new HandlerExecutor(mHandler), mTetheringEventCallback);
mTm.registerTetheringEventCallback(r -> mHandler.post(r), mTetheringEventCallback);
mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
registerReceiver();
mEthernetListener = new EthernetListener();
if (mEm != null)
mEm.addListener(mEthernetListener);
mEm.addListener(mEthernetListener, r -> mHandler.post(r));
updateUsbState();
updateBluetoothAndEthernetState();

View File

@@ -0,0 +1,168 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appinfo;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import java.util.Objects;
/**
* Preference Controller for the "All Services" preference in the "App Info" page.
*/
public class AppAllServicesPreferenceController extends AppInfoPreferenceControllerBase {
private static final String TAG = "AllServicesPrefControl";
private static final String SUMMARY_METADATA_KEY = "app_features_preference_summary";
private final PackageManager mPackageManager;
private String mPackageName;
private boolean mCanPackageHandleAllServicesIntent;
private boolean mIsLocationProvider;
public AppAllServicesPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey);
mPackageManager = context.getPackageManager();
// Set to false till we can confirm that the package can handle the intent.
mCanPackageHandleAllServicesIntent = false;
// Set to false till we can confirm that the package is a location provider.
mIsLocationProvider = false;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
CharSequence summary = getStorageSummary();
if (summary != null) {
mPreference.setSummary(summary);
}
}
@VisibleForTesting
@Nullable
CharSequence getStorageSummary() {
ResolveInfo resolveInfo = getResolveInfo(PackageManager.GET_META_DATA);
if (resolveInfo == null) {
Log.d(TAG, "mResolveInfo is null.");
return null;
}
final Bundle metaData = resolveInfo.activityInfo.metaData;
if (metaData != null) {
try {
final Resources pkgRes = mPackageManager.getResourcesForActivity(
new ComponentName(mPackageName, resolveInfo.activityInfo.name));
return pkgRes.getString(metaData.getInt(SUMMARY_METADATA_KEY));
} catch (Resources.NotFoundException exception) {
Log.d(TAG, "Resource not found for summary string.");
} catch (PackageManager.NameNotFoundException exception) {
Log.d(TAG, "Name of resource not found for summary string.");
}
}
return null;
}
@Override
public int getAvailabilityStatus() {
if (mCanPackageHandleAllServicesIntent && mIsLocationProvider) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
}
private boolean isLocationProvider() {
return Objects.requireNonNull(
mContext.getSystemService(LocationManager.class)).isProviderPackage(mPackageName);
}
private boolean canPackageHandleIntent() {
return getResolveInfo(0) != null;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (getPreferenceKey().equals(preference.getKey())) {
startAllServicesActivity();
return true;
}
return false;
}
/**
* Set the package name of the package for which the "All Services" activity needs to be shown.
*
* @param packageName Name of the package for which the services need to be shown.
*/
public void setPackageName(String packageName) {
mPackageName = packageName;
//Once we have package name. Update conditions for availability.
updateAvailabilityConditions();
}
private void updateAvailabilityConditions() {
mCanPackageHandleAllServicesIntent = canPackageHandleIntent();
mIsLocationProvider = isLocationProvider();
}
private void startAllServicesActivity() {
final Intent featuresIntent = new Intent(Intent.ACTION_VIEW_APP_FEATURES);
// This won't be null since the preference is only shown for packages that can handle the
// intent.
ResolveInfo resolveInfo = getResolveInfo(0);
featuresIntent.setComponent(
new ComponentName(mPackageName, resolveInfo.activityInfo.name));
Activity activity = mParent.getActivity();
try {
if (activity != null) {
activity.startActivity(featuresIntent);
}
} catch (ActivityNotFoundException e) {
Log.e(TAG, "The app cannot handle android.intent.action.VIEW_APP_FEATURES");
}
}
@Nullable
private ResolveInfo getResolveInfo(int flags) {
if (mPackageName == null) {
return null;
}
final Intent featuresIntent = new Intent(Intent.ACTION_VIEW_APP_FEATURES);
featuresIntent.setPackage(mPackageName);
return mPackageManager.resolveActivity(featuresIntent, flags);
}
}

View File

@@ -162,6 +162,8 @@ public class AppInfoDashboardFragment extends DashboardFragment
use(AppSettingPreferenceController.class)
.setPackageName(packageName)
.setParentFragment(this);
use(AppAllServicesPreferenceController.class).setParentFragment(this);
use(AppAllServicesPreferenceController.class).setPackageName(packageName);
use(AppStoragePreferenceController.class).setParentFragment(this);
use(AppVersionPreferenceController.class).setParentFragment(this);
use(InstantAppDomainsPreferenceController.class).setParentFragment(this);

View File

@@ -28,8 +28,6 @@ import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.provider.DocumentsContract;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -40,8 +38,10 @@ import androidx.preference.PreferenceScreen;
import com.android.internal.util.Preconditions;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.deviceinfo.storage.StorageUtils;
import com.android.settings.deviceinfo.storage.StorageUtils.MountTask;
import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
import com.android.settingslib.widget.UsageProgressBarPreference;
import java.io.File;
import java.util.Objects;
@@ -59,7 +59,7 @@ public class PublicVolumeSettings extends SettingsPreferenceFragment {
private VolumeInfo mVolume;
private DiskInfo mDisk;
private StorageSummaryPreference mSummary;
private UsageProgressBarPreference mSummary;
private Preference mMount;
private Preference mFormatPublic;
@@ -114,7 +114,7 @@ public class PublicVolumeSettings extends SettingsPreferenceFragment {
addPreferencesFromResource(R.xml.device_info_storage_volume);
getPreferenceScreen().setOrderingAsAdded(true);
mSummary = new StorageSummaryPreference(getPrefContext());
mSummary = new UsageProgressBarPreference(getPrefContext());
mMount = buildAction(R.string.storage_menu_mount);
mUnmount = new Button(getActivity());
@@ -162,12 +162,10 @@ public class PublicVolumeSettings extends SettingsPreferenceFragment {
final long freeBytes = file.getFreeSpace();
final long usedBytes = totalBytes - freeBytes;
final Formatter.BytesResult result = Formatter.formatBytes(getResources(), usedBytes,
0);
mSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large),
result.value, result.units));
mSummary.setSummary(getString(R.string.storage_volume_used,
Formatter.formatFileSize(context, totalBytes)));
mSummary.setUsageSummary(StorageUtils.getStorageSummary(
context, R.string.storage_usage_summary, usedBytes));
mSummary.setTotalSummary(StorageUtils.getStorageSummary(
context, R.string.storage_total_summary, totalBytes));
mSummary.setPercent(usedBytes, totalBytes);
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2015 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.deviceinfo;
import android.content.Context;
import android.graphics.Color;
import android.util.MathUtils;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
public class StorageSummaryPreference extends Preference {
private int mPercent = -1;
public StorageSummaryPreference(Context context) {
super(context);
setLayoutResource(R.layout.storage_summary);
setEnabled(false);
}
public void setPercent(long usedBytes, long totalBytes) {
mPercent = MathUtils.constrain((int) ((usedBytes * 100) / totalBytes),
(usedBytes > 0) ? 1 : 0, 100);
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress);
if (mPercent != -1) {
progress.setVisibility(View.VISIBLE);
progress.setProgress(mPercent);
progress.setScaleY(7f);
} else {
progress.setVisibility(View.GONE);
}
final TextView summary = (TextView) view.findViewById(android.R.id.summary);
summary.setTextColor(Color.parseColor("#8a000000"));
super.onBindViewHolder(view);
}
}

View File

@@ -18,7 +18,6 @@ package com.android.settings.deviceinfo.storage;
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.text.format.Formatter;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -113,16 +112,10 @@ public class StorageUsageProgressBarPreferenceController extends BasePreferenceC
return;
}
mIsUpdateStateFromSelectedStorageEntry = false;
mUsageProgressBarPreference.setUsageSummary(
getStorageSummary(R.string.storage_usage_summary, mUsedBytes));
mUsageProgressBarPreference.setTotalSummary(
getStorageSummary(R.string.storage_total_summary, mTotalBytes));
mUsageProgressBarPreference.setUsageSummary(StorageUtils.getStorageSummary(
mContext, R.string.storage_usage_summary, mUsedBytes));
mUsageProgressBarPreference.setTotalSummary(StorageUtils.getStorageSummary(
mContext, R.string.storage_total_summary, mTotalBytes));
mUsageProgressBarPreference.setPercent(mUsedBytes, mTotalBytes);
}
private String getStorageSummary(int resId, long bytes) {
final Formatter.BytesResult result = Formatter.formatBytes(mContext.getResources(),
bytes, Formatter.FLAG_SHORTER);
return mContext.getString(resId, result.value, result.units);
}
}

View File

@@ -199,7 +199,7 @@ public class StorageUtils {
}
}
/* Shows information about system storage. */
/** Shows information about system storage. */
public static class SystemInfoFragment extends InstrumentedDialogFragment {
/** Shows the fragment. */
public static void show(Fragment parent) {
@@ -224,4 +224,11 @@ public class StorageUtils {
.create();
}
}
/** Gets a summary which has a byte size information. */
public static String getStorageSummary(Context context, int resId, long bytes) {
final Formatter.BytesResult result = Formatter.formatBytes(context.getResources(),
bytes, Formatter.FLAG_SHORTER);
return context.getString(resId, result.value, result.units);
}
}

View File

@@ -31,7 +31,7 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
/**
* The switch controller for the location.
* The switch controller for auto-rotate.
*/
public class AutoRotateSwitchBarController implements OnMainSwitchChangeListener,
LifecycleObserver, OnStart, OnStop {

View File

@@ -63,7 +63,7 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
updateState(mPreference);
}
};
private Preference mPreference;
protected Preference mPreference;
private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
public SmartAutoRotateController(Context context, String preferenceKey) {
@@ -84,10 +84,14 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
if (!isRotationResolverServiceAvailable(mContext)) {
return UNSUPPORTED_ON_DEVICE;
}
return !RotationPolicy.isRotationLocked(mContext) && hasSufficientPermission(mContext)
return !isRotationLocked() && hasSufficientPermission(mContext)
&& !isCameraLocked() && !isPowerSaveMode() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
}
protected boolean isRotationLocked() {
return RotationPolicy.isRotationLocked(mContext);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
@@ -136,7 +140,7 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
@Override
public boolean isChecked() {
return !RotationPolicy.isRotationLocked(mContext) && hasSufficientPermission(mContext)
return !isRotationLocked() && hasSufficientPermission(mContext)
&& !isCameraLocked() && !isPowerSaveMode() && Settings.Secure.getInt(
mContext.getContentResolver(),
CAMERA_AUTOROTATE, 0) == 1;
@@ -163,7 +167,10 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
return R.string.menu_key_display;
}
static boolean isRotationResolverServiceAvailable(Context context) {
/**
* Returns true if there is a {@link RotationResolverService} available
*/
public static boolean isRotationResolverServiceAvailable(Context context) {
final PackageManager packageManager = context.getPackageManager();
final String resolvePackage = packageManager.getRotationResolverPackageName();
if (TextUtils.isEmpty(resolvePackage)) {

View File

@@ -27,6 +27,7 @@ import android.provider.SearchIndexableResource;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
@@ -43,16 +44,17 @@ import java.util.List;
/**
* The fragment for on-screen keyboard settings which used to display user installed IMEs.
*
* TODO(b/207452897): Add test for AvailableVirtualKeyboardFragment
*/
@SearchIndexable
public final class AvailableVirtualKeyboardFragment extends DashboardFragment
public class AvailableVirtualKeyboardFragment extends DashboardFragment
implements InputMethodPreference.OnSavePreferenceListener {
private static final String TAG = "AvailableVirtualKeyboardFragment";
private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
private InputMethodSettingValuesWrapper mInputMethodSettingValues;
@VisibleForTesting
final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
@VisibleForTesting
InputMethodSettingValuesWrapper mInputMethodSettingValues;
private Context mUserAwareContext;
private int mUserId;
@@ -118,7 +120,8 @@ public final class AvailableVirtualKeyboardFragment extends DashboardFragment
return SettingsEnums.ENABLE_VIRTUAL_KEYBOARDS;
}
private void updateInputMethodPreferenceViews() {
@VisibleForTesting
void updateInputMethodPreferenceViews() {
mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
// Clear existing "InputMethodPreference"s
mInputMethodPreferenceList.clear();

View File

@@ -48,13 +48,12 @@ public final class EthernetTetherPreferenceController extends TetherBasePreferen
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
mEthernetListener = new EthernetManager.Listener() {
@Override
public void onAvailabilityChanged(String iface, boolean isAvailable) {
new Handler(Looper.getMainLooper()).post(() -> updateState(mPreference));
}
};
mEthernetManager.addListener(mEthernetListener);
mEthernetListener = (iface, isAvailable) -> updateState(mPreference);
final Handler handler = new Handler(Looper.getMainLooper());
// Executor will execute to post the updateState event to a new handler which is created
// from the main looper when the {@link EthernetManager.Listener.onAvailabilityChanged}
// is triggerd.
mEthernetManager.addListener(mEthernetListener, r -> handler.post(r));
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)

View File

@@ -42,7 +42,6 @@ import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.core.AbstractPreferenceController;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -56,8 +55,7 @@ public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase {
public static final String ACTION = Settings.ACTION_ZEN_MODE_SCHEDULE_RULE_SETTINGS;
// per-instance to ensure we're always using the current locale
private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEE");
private final ZenRuleScheduleHelper mScheduleHelper = new ZenRuleScheduleHelper();
private Preference mDays;
private TimePickerPreference mStart;
@@ -149,30 +147,11 @@ public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase {
}
private void updateDays() {
// Compute an ordered, delimited list of day names based on the persisted user config.
final int[] days = mSchedule.days;
if (days != null && days.length > 0) {
final StringBuilder sb = new StringBuilder();
final Calendar c = Calendar.getInstance();
int[] daysOfWeek = ZenModeScheduleDaysSelection.getDaysOfWeekForLocale(c);
for (int i = 0; i < daysOfWeek.length; i++) {
final int day = daysOfWeek[i];
for (int j = 0; j < days.length; j++) {
if (day == days[j]) {
c.set(Calendar.DAY_OF_WEEK, day);
if (sb.length() > 0) {
sb.append(mContext.getString(R.string.summary_divider_text));
}
sb.append(mDayFormat.format(c.getTime()));
break;
}
}
}
if (sb.length() > 0) {
mDays.setSummary(sb);
mDays.notifyDependencyChange(false);
return;
}
String desc = mScheduleHelper.getDaysDescription(mContext, mSchedule);
if (desc != null) {
mDays.setSummary(desc);
mDays.notifyDependencyChange(false);
return;
}
mDays.setSummary(R.string.zen_mode_schedule_rule_days_none);
mDays.notifyDependencyChange(true);

View File

@@ -23,22 +23,20 @@ import android.content.Intent;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.service.notification.ZenModeConfig;
import android.view.View;
import android.widget.CheckBox;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.utils.ManagedServiceSettings;
import com.android.settings.utils.ZenServiceListing;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.widget.TwoTargetPreference;
import java.util.Map;
public class ZenRulePreference extends TwoTargetPreference {
public class ZenRulePreference extends PrimarySwitchPreference {
private static final ManagedServiceSettings.Config CONFIG =
ZenModeAutomationSettings.getConditionProviderConfig();
final String mId;
@@ -53,14 +51,13 @@ public class ZenRulePreference extends TwoTargetPreference {
CharSequence mName;
private Intent mIntent;
private boolean mChecked;
private CheckBox mCheckBox;
private final ZenRuleScheduleHelper mScheduleHelper = new ZenRuleScheduleHelper();
public ZenRulePreference(Context context,
final Map.Entry<String, AutomaticZenRule> ruleEntry,
Fragment parent, MetricsFeatureProvider metricsProvider) {
super(context);
setLayoutResource(R.layout.preference_checkable_two_target);
mBackend = ZenModeBackend.getInstance(context);
mContext = context;
mRule = ruleEntry.getValue();
@@ -72,50 +69,11 @@ public class ZenRulePreference extends TwoTargetPreference {
mServiceListing.reloadApprovedServices();
mPref = this;
mMetricsFeatureProvider = metricsProvider;
mChecked = mRule.isEnabled();
setAttributes(mRule);
setWidgetLayoutResource(getSecondTargetResId());
}
protected int getSecondTargetResId() {
if (mIntent != null) {
return R.layout.zen_rule_widget;
}
return 0;
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
View settingsWidget = view.findViewById(android.R.id.widget_frame);
View divider = view.findViewById(R.id.two_target_divider);
if (mIntent != null) {
divider.setVisibility(View.VISIBLE);
settingsWidget.setVisibility(View.VISIBLE);
settingsWidget.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mContext.startActivity(mIntent);
}
});
} else {
divider.setVisibility(View.GONE);
settingsWidget.setVisibility(View.GONE);
settingsWidget.setOnClickListener(null);
}
View checkboxContainer = view.findViewById(R.id.checkbox_container);
if (checkboxContainer != null) {
checkboxContainer.setOnClickListener(mOnCheckBoxClickListener);
}
mCheckBox = (CheckBox) view.findViewById(com.android.internal.R.id.checkbox);
if (mCheckBox != null) {
mCheckBox.setChecked(mChecked);
}
}
public boolean isChecked() {
return mChecked;
// initialize the checked state of the preference
super.setChecked(mRule.isEnabled());
}
public void updatePreference(AutomaticZenRule rule) {
@@ -126,34 +84,24 @@ public class ZenRulePreference extends TwoTargetPreference {
if (mRule.isEnabled() != rule.isEnabled()) {
setChecked(rule.isEnabled());
setSummary(computeRuleSummary(rule));
}
setSummary(computeRuleSummary(rule));
mRule = rule;
}
@Override
public void onClick() {
mOnCheckBoxClickListener.onClick(null);
mContext.startActivity(mIntent);
}
private void setChecked(boolean checked) {
mChecked = checked;
if (mCheckBox != null) {
mCheckBox.setChecked(checked);
}
@Override
public void setChecked(boolean checked) {
mRule.setEnabled(checked);
mBackend.updateZenRule(mId, mRule);
setAttributes(mRule);
super.setChecked(checked);
}
private View.OnClickListener mOnCheckBoxClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
mRule.setEnabled(!mChecked);
mBackend.updateZenRule(mId, mRule);
setChecked(mRule.isEnabled());
setAttributes(mRule);
}
};
protected void setAttributes(AutomaticZenRule rule) {
final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
rule.getConditionId(), true);
@@ -178,6 +126,30 @@ public class ZenRulePreference extends TwoTargetPreference {
}
private String computeRuleSummary(AutomaticZenRule rule) {
if (rule != null) {
// handle schedule-based rules
ScheduleInfo schedule =
ZenModeConfig.tryParseScheduleConditionId(rule.getConditionId());
if (schedule != null) {
String desc = mScheduleHelper.getDaysAndTimeSummary(mContext, schedule);
return (desc != null) ? desc :
mContext.getResources().getString(
R.string.zen_mode_schedule_rule_days_none);
}
// handle event-based rules
ZenModeConfig.EventInfo event =
ZenModeConfig.tryParseEventConditionId(rule.getConditionId());
if (event != null) {
if (event.calName != null) {
return event.calName;
} else {
return mContext.getResources().getString(
R.string.zen_mode_event_rule_calendar_any);
}
}
}
return (rule == null || !rule.isEnabled())
? mContext.getResources().getString(R.string.switch_off_text)
: mContext.getResources().getString(R.string.switch_on_text);

View File

@@ -0,0 +1,191 @@
/*
* Copyright (C) 2022 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.notification.zen;
import android.content.Context;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.text.format.DateFormat;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
/**
* Helper class for shared functionality regarding descriptions of custom zen rule schedules.
*/
public class ZenRuleScheduleHelper {
// per-instance to ensure we're always using the current locale
private SimpleDateFormat mDayFormat;
// Default constructor, which will use the current locale.
public ZenRuleScheduleHelper() {
mDayFormat = new SimpleDateFormat("EEE");
}
// Constructor for tests to provide an explicit locale
@VisibleForTesting
public ZenRuleScheduleHelper(Locale locale) {
mDayFormat = new SimpleDateFormat("EEE", locale);
}
/**
* Returns an ordered, comma-separated list of the days that a schedule applies, or null if no
* days.
*/
public String getDaysDescription(Context context, ScheduleInfo schedule) {
// Compute an ordered, delimited list of day names based on the persisted user config.
final int[] days = schedule.days;
if (days != null && days.length > 0) {
final StringBuilder sb = new StringBuilder();
final Calendar c = Calendar.getInstance();
int[] daysOfWeek = ZenModeScheduleDaysSelection.getDaysOfWeekForLocale(c);
for (int i = 0; i < daysOfWeek.length; i++) {
final int day = daysOfWeek[i];
for (int j = 0; j < days.length; j++) {
if (day == days[j]) {
c.set(Calendar.DAY_OF_WEEK, day);
if (sb.length() > 0) {
sb.append(context.getString(R.string.summary_divider_text));
}
sb.append(mDayFormat.format(c.getTime()));
break;
}
}
}
if (sb.length() > 0) {
return sb.toString();
}
}
return null;
}
/**
* Returns an ordered summarized list of the days on which this schedule applies, with
* adjacent days grouped together ("Sun-Wed" instead of "Sun,Mon,Tue,Wed").
*/
public String getShortDaysSummary(Context context, ScheduleInfo schedule) {
// Compute a list of days with contiguous days grouped together, for example: "Sun-Thu" or
// "Sun-Mon,Wed,Fri"
final int[] days = schedule.days;
if (days != null && days.length > 0) {
final StringBuilder sb = new StringBuilder();
final Calendar cStart = Calendar.getInstance();
final Calendar cEnd = Calendar.getInstance();
int[] daysOfWeek = ZenModeScheduleDaysSelection.getDaysOfWeekForLocale(cStart);
// the i for loop goes through days in order as determined by locale. as we walk through
// the days of the week, keep track of "start" and "last seen" as indicators for
// what's contiguous, and initialize them to something not near actual indices
int startDay = Integer.MIN_VALUE;
int lastSeenDay = Integer.MIN_VALUE;
for (int i = 0; i < daysOfWeek.length; i++) {
final int day = daysOfWeek[i];
// by default, output if this day is *not* included in the schedule, and thus
// ends a previously existing block. if this day is included in the schedule
// after all (as will be determined in the inner for loop), then output will be set
// to false.
boolean output = (i == lastSeenDay + 1);
for (int j = 0; j < days.length; j++) {
if (day == days[j]) {
// match for this day in the schedule (indicated by counter i)
if (i == lastSeenDay + 1) {
// contiguous to the block we're walking through right now, record it
// (specifically, i, the day index) and move on to the next day
lastSeenDay = i;
output = false;
} else {
// it's a match, but not 1 past the last match, we are starting a new
// block
startDay = i;
lastSeenDay = i;
}
// if there is a match on the last day, also make sure to output at the end
// of this loop, and mark the day as the last day we'll have seen in the
// scheduled days.
if (i == daysOfWeek.length - 1) {
output = true;
}
break;
}
}
// output in either of 2 cases: this day is not a match, so has ended any previous
// block, or this day *is* a match but is the last day of the week, so we need to
// summarize
if (output) {
// either describe just the single day if startDay == lastSeenDay, or
// output "startDay - lastSeenDay" as a group
if (sb.length() > 0) {
sb.append(context.getString(R.string.summary_divider_text));
}
if (startDay == lastSeenDay) {
// last group was only one day
cStart.set(Calendar.DAY_OF_WEEK, daysOfWeek[startDay]);
sb.append(mDayFormat.format(cStart.getTime()));
} else {
// last group was a contiguous group of days, so group them together
cStart.set(Calendar.DAY_OF_WEEK, daysOfWeek[startDay]);
cEnd.set(Calendar.DAY_OF_WEEK, daysOfWeek[lastSeenDay]);
sb.append(context.getString(R.string.summary_range_symbol_combination,
mDayFormat.format(cStart.getTime()),
mDayFormat.format(cEnd.getTime())));
}
}
}
if (sb.length() > 0) {
return sb.toString();
}
}
return null;
}
/**
* Convenience method for representing the specified time in string format.
*/
private String timeString(Context context, int hour, int minute) {
final Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, hour);
c.set(Calendar.MINUTE, minute);
return DateFormat.getTimeFormat(context).format(c.getTime());
}
/**
* Combination description for a zen rule schedule including both day summary and time bounds.
*/
public String getDaysAndTimeSummary(Context context, ScheduleInfo schedule) {
final StringBuilder sb = new StringBuilder();
String daysSummary = getShortDaysSummary(context, schedule);
if (daysSummary == null) {
// no use outputting times without dates
return null;
}
sb.append(daysSummary);
sb.append(context.getString(R.string.summary_divider_text));
sb.append(context.getString(R.string.summary_range_symbol_combination,
timeString(context, schedule.startHour, schedule.startMinute),
timeString(context, schedule.endHour, schedule.endMinute)));
return sb.toString();
}
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.inputmethod;
import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.EXTRA_PROFILE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.testutils.shadow.ShadowInputMethodManagerWithMethodList;
import com.android.settings.testutils.shadow.ShadowSecureSettings;
import com.android.settingslib.inputmethod.InputMethodPreference;
import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowSecureSettings.class,
ShadowInputMethodManagerWithMethodList.class
})
public class AvailableVirtualKeyboardFragmentTest {
@Mock
private InputMethodManager mInputMethodManager;
@Mock
private InputMethodSettingValuesWrapper mValuesWrapper;
@Mock
private PreferenceScreen mPreferenceScreen;
@Mock
private PreferenceManager mPreferenceManager;
@Mock
private InputMethodPreference mInputMethodPreference;
private Context mContext;
private AvailableVirtualKeyboardFragment mFragment;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
initFragment();
initMock();
}
@Test
public void onCreatePreferences_shouldAddResource() {
mFragment.onAttach(mContext);
mFragment.onCreatePreferences(new Bundle(), "test");
verify(mFragment).addPreferencesFromResource(R.xml.available_virtual_keyboard);
}
@Test
public void onResume_refreshAllInputMethodAndSubtypes() {
mFragment.onAttach(mContext);
mFragment.onResume();
// One invocation is in onResume(), another is in updateInputMethodPreferenceViews().
verify(mValuesWrapper, times(2)).refreshAllInputMethodAndSubtypes();
}
@Test
public void onResume_updateInputMethodPreferenceViews() {
mFragment.onAttach(mContext);
mFragment.onResume();
verify(mFragment).updateInputMethodPreferenceViews();
}
@Test
public void onSaveInputMethodPreference_refreshAllInputMethodAndSubtypes() {
mFragment.onAttach(mContext);
mFragment.onSaveInputMethodPreference(mInputMethodPreference);
verify(mValuesWrapper).refreshAllInputMethodAndSubtypes();
}
@Test
public void updateInputMethodPreferenceViews_callsExpectedMethods() {
mFragment.onAttach(mContext);
mFragment.updateInputMethodPreferenceViews();
verify(mValuesWrapper).getInputMethodList();
verify(mInputMethodManager).getEnabledInputMethodListAsUser(anyInt());
}
@Test
public void updateInputMethodPreferenceViews_addExpectedInputMethodPreference() {
final int inputMethodNums = 5;
mFragment.onAttach(mContext);
when(mValuesWrapper.getInputMethodList()).thenReturn(createFakeInputMethodInfoList(
"test", inputMethodNums));
mFragment.updateInputMethodPreferenceViews();
assertThat(mFragment.mInputMethodPreferenceList).hasSize(inputMethodNums);
}
@Test
public void searchIndexProvider_shouldIndexResource() {
final List<SearchIndexableResource> indexRes =
AvailableVirtualKeyboardFragment.SEARCH_INDEX_DATA_PROVIDER
.getXmlResourcesToIndex(RuntimeEnvironment.application, true /* enabled */);
assertThat(indexRes).isNotNull();
assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
}
private void initFragment() {
final Bundle bundle = new Bundle();
bundle.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.PERSONAL);
mFragment = spy(new AvailableVirtualKeyboardFragment());
mFragment.setArguments(bundle);
mFragment.mInputMethodSettingValues = mValuesWrapper;
ReflectionHelpers.setField(mFragment, "mPreferenceManager", mPreferenceManager);
}
private void initMock() {
when(mFragment.getContext()).thenReturn(mContext);
when(mFragment.getPreferenceScreen()).thenReturn(mPreferenceScreen);
when(mPreferenceManager.getContext()).thenReturn(mContext);
when(mContext.getSystemService(InputMethodManager.class)).thenReturn(mInputMethodManager);
}
private List<InputMethodInfo> createFakeInputMethodInfoList(final String name, int num) {
List<InputMethodSubtype> subtypes = new ArrayList<>();
subtypes.add(new InputMethodSubtype.InputMethodSubtypeBuilder()
.build());
subtypes.add(new InputMethodSubtype.InputMethodSubtypeBuilder()
.build());
final ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = new ServiceInfo();
resolveInfo.serviceInfo.packageName = "com.android.ime";
resolveInfo.serviceInfo.name = name;
resolveInfo.serviceInfo.applicationInfo = new ApplicationInfo();
resolveInfo.serviceInfo.applicationInfo.enabled = true;
List<InputMethodInfo> inputMethodInfoList = new ArrayList<>();
for (int i = 0; i < num; i++) {
inputMethodInfoList.add(new InputMethodInfo(
resolveInfo,
false /* isAuxIme */,
"TestSettingsActivity",
subtypes,
0 /* isDefaultResId */,
true /* forceDefault */));
}
return inputMethodInfoList;
}
}

View File

@@ -246,7 +246,6 @@ public class SettingsSliceProviderTest {
}
@Test
@Ignore
@Config(shadows = ShadowStrictMode.class)
public void onBindSlice_backgroundThread_shouldOverrideStrictMode() {
ShadowThreadUtils.setIsMainThread(false);

View File

@@ -21,13 +21,14 @@ import android.net.Uri;
import android.net.wifi.WifiManager;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.slices.SliceBackgroundWorker;
public class FakeToggleController extends TogglePreferenceController {
public static final String AVAILABILITY_KEY = "fake_toggle_availability_key";
public static final int HIGHLIGHT_MENU_RES = 5678;
public static final int HIGHLIGHT_MENU_RES = R.string.menu_key_about_device;
public static final IntentFilter INTENT_FILTER = new IntentFilter(
WifiManager.WIFI_AP_STATE_CHANGED_ACTION);

View File

@@ -0,0 +1,191 @@
/*
* Copyright (C) 2022 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.notification.zen;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.LocaleList;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Calendar;
import java.util.Locale;
@RunWith(AndroidJUnit4.class)
public class ZenRuleScheduleHelperTest {
private ZenRuleScheduleHelper mScheduleHelper;
private ScheduleInfo mScheduleInfo;
private Context mContext;
@Mock
private Resources mResources;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
// explicitly initialize to Locale.US just for ease of explicitly testing the string values
// of the days of the week if the test locale doesn't happen to be in the US
mScheduleHelper = new ZenRuleScheduleHelper(Locale.US);
mScheduleInfo = new ScheduleInfo();
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getResources()).thenReturn(mResources);
// Resources will be called upon to join strings together, either to get a divider
// or a combination of two strings. Conveniently, these have different signatures.
// Divider method calls getString(string divider id)
when(mResources.getString(anyInt())).thenReturn(",");
// Combination method calls getString(combination id, first item, second item)
// and returns "first - second"
when(mResources.getString(anyInt(), anyString(), anyString())).thenAnswer(
invocation -> {
return invocation.getArgument(1).toString() // first item
+ "-"
+ invocation.getArgument(2).toString(); // second item
});
// for locale used in time format
Configuration config = new Configuration();
config.setLocales(new LocaleList(Locale.US));
when(mResources.getConfiguration()).thenReturn(config);
}
@Test
public void getDaysDescription() {
// Test various cases of where the days are set.
// No days
mScheduleInfo.days = new int[] {};
assertThat(mScheduleHelper.getDaysDescription(mContext, mScheduleInfo)).isNull();
// one day
mScheduleInfo.days = new int[] {Calendar.FRIDAY};
assertThat(mScheduleHelper.getDaysDescription(mContext, mScheduleInfo)).isEqualTo("Fri");
// Monday through Friday
mScheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY,
Calendar.THURSDAY, Calendar.FRIDAY};
assertThat(mScheduleHelper.getDaysDescription(mContext, mScheduleInfo))
.isEqualTo("Mon,Tue,Wed,Thu,Fri");
// Some scattered days of the week
mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.WEDNESDAY, Calendar.THURSDAY,
Calendar.SATURDAY};
assertThat(mScheduleHelper.getDaysDescription(mContext, mScheduleInfo))
.isEqualTo("Sun,Wed,Thu,Sat");
}
@Test
public void getShortDaysSummary_noOrSingleDays() {
// Test various cases for grouping and not-grouping of days.
// No days
mScheduleInfo.days = new int[]{};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo)).isNull();
// A single day at the beginning of the week
mScheduleInfo.days = new int[]{Calendar.SUNDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo)).isEqualTo("Sun");
// A single day in the middle of the week
mScheduleInfo.days = new int[]{Calendar.THURSDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo)).isEqualTo("Thu");
// A single day at the end of the week
mScheduleInfo.days = new int[]{Calendar.SATURDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo)).isEqualTo("Sat");
}
@Test
public void getShortDaysSummary_oneGroup() {
// The whole week
mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
.isEqualTo("Sun-Sat");
// Various cases of one big group
// Sunday through Thursday
mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
Calendar.WEDNESDAY, Calendar.THURSDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
.isEqualTo("Sun-Thu");
// Wednesday through Saturday
mScheduleInfo.days = new int[] {Calendar.WEDNESDAY, Calendar.THURSDAY,
Calendar.FRIDAY, Calendar.SATURDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
.isEqualTo("Wed-Sat");
// Monday through Friday
mScheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.TUESDAY,
Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
.isEqualTo("Mon-Fri");
}
@Test
public void getShortDaysSummary_mixed() {
// cases combining groups and single days scattered around
mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.TUESDAY,
Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.SATURDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
.isEqualTo("Sun,Tue-Thu,Sat");
mScheduleInfo.days = new int[] {Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
Calendar.WEDNESDAY, Calendar.FRIDAY, Calendar.SATURDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
.isEqualTo("Sun-Wed,Fri-Sat");
mScheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.WEDNESDAY,
Calendar.FRIDAY, Calendar.SATURDAY};
assertThat(mScheduleHelper.getShortDaysSummary(mContext, mScheduleInfo))
.isEqualTo("Mon,Wed,Fri-Sat");
}
@Test
public void getDaysAndTimeSummary() {
// Combination days & time settings
// No days, no output, even if the times are set.
mScheduleInfo.startHour = 10;
mScheduleInfo.endHour = 16;
mScheduleInfo.days = new int[]{};
assertThat(mScheduleHelper.getDaysAndTimeSummary(mContext, mScheduleInfo)).isNull();
// If there are days then they are combined with the time combination
mScheduleInfo.days = new int[]{Calendar.SUNDAY, Calendar.MONDAY, Calendar.WEDNESDAY};
assertThat(mScheduleHelper.getDaysAndTimeSummary(mContext, mScheduleInfo))
.isEqualTo("Sun-Mon,Wed,10:00 AM-4:00 PM");
}
}

View File

@@ -21,13 +21,14 @@ import android.net.Uri;
import android.net.wifi.WifiManager;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.slices.SliceBackgroundWorker;
public class FakeToggleController extends TogglePreferenceController {
public static final String AVAILABILITY_KEY = "fake_toggle_availability_key";
public static final int HIGHLIGHT_MENU_RES = 5678;
public static final int HIGHLIGHT_MENU_RES = R.string.menu_key_about_device;
public static final IntentFilter INTENT_FILTER = new IntentFilter(
WifiManager.WIFI_AP_STATE_CHANGED_ACTION);