Snap for 8124529 from f1128f0c21 to tm-release
Change-Id: I3a5503328844c342cc475b1c2f00f382f65437c1
This commit is contained in:
@@ -115,6 +115,7 @@
|
|||||||
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
|
<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.QUERY_ADMIN_POLICY" />
|
||||||
<uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
|
<uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
|
||||||
|
<uses-permission android:name="android.permission.START_VIEW_APP_FEATURES" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".SettingsApplication"
|
android:name=".SettingsApplication"
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -3542,8 +3542,6 @@
|
|||||||
|
|
||||||
<!-- Summary of a single storage volume used space. [CHAR LIMIT=24] -->
|
<!-- 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>
|
<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]-->
|
<!-- 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>
|
<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] -->
|
<!-- Summary for extra app info settings for a specific app [CHAR LIMIT=40] -->
|
||||||
<string name="extra_app_info_summary" translatable="false"></string>
|
<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] -->
|
<!-- 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>
|
<string name="show_clip_access_notification">Show clipboard access</string>
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,12 @@
|
|||||||
android:title="@string/app_settings_link"
|
android:title="@string/app_settings_link"
|
||||||
settings:controller="com.android.settings.applications.appinfo.AppSettingPreferenceController" />
|
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
|
<Preference
|
||||||
android:key="notification_settings"
|
android:key="notification_settings"
|
||||||
android:title="@string/notifications_label"
|
android:title="@string/notifications_label"
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import android.net.wifi.WifiManager;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerExecutor;
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.provider.SearchIndexableResource;
|
import android.provider.SearchIndexableResource;
|
||||||
@@ -324,14 +323,14 @@ public class TetherSettings extends RestrictedSettingsFragment
|
|||||||
|
|
||||||
mStartTetheringCallback = new OnStartTetheringCallback(this);
|
mStartTetheringCallback = new OnStartTetheringCallback(this);
|
||||||
mTetheringEventCallback = new TetheringEventCallback();
|
mTetheringEventCallback = new TetheringEventCallback();
|
||||||
mTm.registerTetheringEventCallback(new HandlerExecutor(mHandler), mTetheringEventCallback);
|
mTm.registerTetheringEventCallback(r -> mHandler.post(r), mTetheringEventCallback);
|
||||||
|
|
||||||
mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
|
mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
|
||||||
registerReceiver();
|
registerReceiver();
|
||||||
|
|
||||||
mEthernetListener = new EthernetListener();
|
mEthernetListener = new EthernetListener();
|
||||||
if (mEm != null)
|
if (mEm != null)
|
||||||
mEm.addListener(mEthernetListener);
|
mEm.addListener(mEthernetListener, r -> mHandler.post(r));
|
||||||
|
|
||||||
updateUsbState();
|
updateUsbState();
|
||||||
updateBluetoothAndEthernetState();
|
updateBluetoothAndEthernetState();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -162,6 +162,8 @@ public class AppInfoDashboardFragment extends DashboardFragment
|
|||||||
use(AppSettingPreferenceController.class)
|
use(AppSettingPreferenceController.class)
|
||||||
.setPackageName(packageName)
|
.setPackageName(packageName)
|
||||||
.setParentFragment(this);
|
.setParentFragment(this);
|
||||||
|
use(AppAllServicesPreferenceController.class).setParentFragment(this);
|
||||||
|
use(AppAllServicesPreferenceController.class).setPackageName(packageName);
|
||||||
use(AppStoragePreferenceController.class).setParentFragment(this);
|
use(AppStoragePreferenceController.class).setParentFragment(this);
|
||||||
use(AppVersionPreferenceController.class).setParentFragment(this);
|
use(AppVersionPreferenceController.class).setParentFragment(this);
|
||||||
use(InstantAppDomainsPreferenceController.class).setParentFragment(this);
|
use(InstantAppDomainsPreferenceController.class).setParentFragment(this);
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ import android.os.storage.StorageManager;
|
|||||||
import android.os.storage.VolumeInfo;
|
import android.os.storage.VolumeInfo;
|
||||||
import android.os.storage.VolumeRecord;
|
import android.os.storage.VolumeRecord;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -40,8 +38,10 @@ import androidx.preference.PreferenceScreen;
|
|||||||
import com.android.internal.util.Preconditions;
|
import com.android.internal.util.Preconditions;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
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.MountTask;
|
||||||
import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
|
import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
|
||||||
|
import com.android.settingslib.widget.UsageProgressBarPreference;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -59,7 +59,7 @@ public class PublicVolumeSettings extends SettingsPreferenceFragment {
|
|||||||
private VolumeInfo mVolume;
|
private VolumeInfo mVolume;
|
||||||
private DiskInfo mDisk;
|
private DiskInfo mDisk;
|
||||||
|
|
||||||
private StorageSummaryPreference mSummary;
|
private UsageProgressBarPreference mSummary;
|
||||||
|
|
||||||
private Preference mMount;
|
private Preference mMount;
|
||||||
private Preference mFormatPublic;
|
private Preference mFormatPublic;
|
||||||
@@ -114,7 +114,7 @@ public class PublicVolumeSettings extends SettingsPreferenceFragment {
|
|||||||
addPreferencesFromResource(R.xml.device_info_storage_volume);
|
addPreferencesFromResource(R.xml.device_info_storage_volume);
|
||||||
getPreferenceScreen().setOrderingAsAdded(true);
|
getPreferenceScreen().setOrderingAsAdded(true);
|
||||||
|
|
||||||
mSummary = new StorageSummaryPreference(getPrefContext());
|
mSummary = new UsageProgressBarPreference(getPrefContext());
|
||||||
|
|
||||||
mMount = buildAction(R.string.storage_menu_mount);
|
mMount = buildAction(R.string.storage_menu_mount);
|
||||||
mUnmount = new Button(getActivity());
|
mUnmount = new Button(getActivity());
|
||||||
@@ -162,12 +162,10 @@ public class PublicVolumeSettings extends SettingsPreferenceFragment {
|
|||||||
final long freeBytes = file.getFreeSpace();
|
final long freeBytes = file.getFreeSpace();
|
||||||
final long usedBytes = totalBytes - freeBytes;
|
final long usedBytes = totalBytes - freeBytes;
|
||||||
|
|
||||||
final Formatter.BytesResult result = Formatter.formatBytes(getResources(), usedBytes,
|
mSummary.setUsageSummary(StorageUtils.getStorageSummary(
|
||||||
0);
|
context, R.string.storage_usage_summary, usedBytes));
|
||||||
mSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large),
|
mSummary.setTotalSummary(StorageUtils.getStorageSummary(
|
||||||
result.value, result.units));
|
context, R.string.storage_total_summary, totalBytes));
|
||||||
mSummary.setSummary(getString(R.string.storage_volume_used,
|
|
||||||
Formatter.formatFileSize(context, totalBytes)));
|
|
||||||
mSummary.setPercent(usedBytes, totalBytes);
|
mSummary.setPercent(usedBytes, totalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,6 @@ package com.android.settings.deviceinfo.storage;
|
|||||||
|
|
||||||
import android.app.usage.StorageStatsManager;
|
import android.app.usage.StorageStatsManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.format.Formatter;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
@@ -113,16 +112,10 @@ public class StorageUsageProgressBarPreferenceController extends BasePreferenceC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mIsUpdateStateFromSelectedStorageEntry = false;
|
mIsUpdateStateFromSelectedStorageEntry = false;
|
||||||
mUsageProgressBarPreference.setUsageSummary(
|
mUsageProgressBarPreference.setUsageSummary(StorageUtils.getStorageSummary(
|
||||||
getStorageSummary(R.string.storage_usage_summary, mUsedBytes));
|
mContext, R.string.storage_usage_summary, mUsedBytes));
|
||||||
mUsageProgressBarPreference.setTotalSummary(
|
mUsageProgressBarPreference.setTotalSummary(StorageUtils.getStorageSummary(
|
||||||
getStorageSummary(R.string.storage_total_summary, mTotalBytes));
|
mContext, R.string.storage_total_summary, mTotalBytes));
|
||||||
mUsageProgressBarPreference.setPercent(mUsedBytes, 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ public class StorageUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shows information about system storage. */
|
/** Shows information about system storage. */
|
||||||
public static class SystemInfoFragment extends InstrumentedDialogFragment {
|
public static class SystemInfoFragment extends InstrumentedDialogFragment {
|
||||||
/** Shows the fragment. */
|
/** Shows the fragment. */
|
||||||
public static void show(Fragment parent) {
|
public static void show(Fragment parent) {
|
||||||
@@ -224,4 +224,11 @@ public class StorageUtils {
|
|||||||
.create();
|
.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
|
|||||||
import com.android.settingslib.widget.OnMainSwitchChangeListener;
|
import com.android.settingslib.widget.OnMainSwitchChangeListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The switch controller for the location.
|
* The switch controller for auto-rotate.
|
||||||
*/
|
*/
|
||||||
public class AutoRotateSwitchBarController implements OnMainSwitchChangeListener,
|
public class AutoRotateSwitchBarController implements OnMainSwitchChangeListener,
|
||||||
LifecycleObserver, OnStart, OnStop {
|
LifecycleObserver, OnStart, OnStop {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
|
|||||||
updateState(mPreference);
|
updateState(mPreference);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private Preference mPreference;
|
protected Preference mPreference;
|
||||||
private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
|
private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
|
||||||
|
|
||||||
public SmartAutoRotateController(Context context, String preferenceKey) {
|
public SmartAutoRotateController(Context context, String preferenceKey) {
|
||||||
@@ -84,10 +84,14 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
|
|||||||
if (!isRotationResolverServiceAvailable(mContext)) {
|
if (!isRotationResolverServiceAvailable(mContext)) {
|
||||||
return UNSUPPORTED_ON_DEVICE;
|
return UNSUPPORTED_ON_DEVICE;
|
||||||
}
|
}
|
||||||
return !RotationPolicy.isRotationLocked(mContext) && hasSufficientPermission(mContext)
|
return !isRotationLocked() && hasSufficientPermission(mContext)
|
||||||
&& !isCameraLocked() && !isPowerSaveMode() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
|
&& !isCameraLocked() && !isPowerSaveMode() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isRotationLocked() {
|
||||||
|
return RotationPolicy.isRotationLocked(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateState(Preference preference) {
|
public void updateState(Preference preference) {
|
||||||
super.updateState(preference);
|
super.updateState(preference);
|
||||||
@@ -136,7 +140,7 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isChecked() {
|
public boolean isChecked() {
|
||||||
return !RotationPolicy.isRotationLocked(mContext) && hasSufficientPermission(mContext)
|
return !isRotationLocked() && hasSufficientPermission(mContext)
|
||||||
&& !isCameraLocked() && !isPowerSaveMode() && Settings.Secure.getInt(
|
&& !isCameraLocked() && !isPowerSaveMode() && Settings.Secure.getInt(
|
||||||
mContext.getContentResolver(),
|
mContext.getContentResolver(),
|
||||||
CAMERA_AUTOROTATE, 0) == 1;
|
CAMERA_AUTOROTATE, 0) == 1;
|
||||||
@@ -163,7 +167,10 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
|
|||||||
return R.string.menu_key_display;
|
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 PackageManager packageManager = context.getPackageManager();
|
||||||
final String resolvePackage = packageManager.getRotationResolverPackageName();
|
final String resolvePackage = packageManager.getRotationResolverPackageName();
|
||||||
if (TextUtils.isEmpty(resolvePackage)) {
|
if (TextUtils.isEmpty(resolvePackage)) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import android.provider.SearchIndexableResource;
|
|||||||
import android.view.inputmethod.InputMethodInfo;
|
import android.view.inputmethod.InputMethodInfo;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
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.
|
* The fragment for on-screen keyboard settings which used to display user installed IMEs.
|
||||||
*
|
|
||||||
* TODO(b/207452897): Add test for AvailableVirtualKeyboardFragment
|
|
||||||
*/
|
*/
|
||||||
@SearchIndexable
|
@SearchIndexable
|
||||||
public final class AvailableVirtualKeyboardFragment extends DashboardFragment
|
public class AvailableVirtualKeyboardFragment extends DashboardFragment
|
||||||
implements InputMethodPreference.OnSavePreferenceListener {
|
implements InputMethodPreference.OnSavePreferenceListener {
|
||||||
private static final String TAG = "AvailableVirtualKeyboardFragment";
|
private static final String TAG = "AvailableVirtualKeyboardFragment";
|
||||||
|
|
||||||
private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
|
@VisibleForTesting
|
||||||
private InputMethodSettingValuesWrapper mInputMethodSettingValues;
|
final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
InputMethodSettingValuesWrapper mInputMethodSettingValues;
|
||||||
private Context mUserAwareContext;
|
private Context mUserAwareContext;
|
||||||
private int mUserId;
|
private int mUserId;
|
||||||
|
|
||||||
@@ -118,7 +120,8 @@ public final class AvailableVirtualKeyboardFragment extends DashboardFragment
|
|||||||
return SettingsEnums.ENABLE_VIRTUAL_KEYBOARDS;
|
return SettingsEnums.ENABLE_VIRTUAL_KEYBOARDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateInputMethodPreferenceViews() {
|
@VisibleForTesting
|
||||||
|
void updateInputMethodPreferenceViews() {
|
||||||
mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
|
mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
|
||||||
// Clear existing "InputMethodPreference"s
|
// Clear existing "InputMethodPreference"s
|
||||||
mInputMethodPreferenceList.clear();
|
mInputMethodPreferenceList.clear();
|
||||||
|
|||||||
@@ -48,13 +48,12 @@ public final class EthernetTetherPreferenceController extends TetherBasePreferen
|
|||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
mEthernetListener = new EthernetManager.Listener() {
|
mEthernetListener = (iface, isAvailable) -> updateState(mPreference);
|
||||||
@Override
|
final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
public void onAvailabilityChanged(String iface, boolean isAvailable) {
|
// Executor will execute to post the updateState event to a new handler which is created
|
||||||
new Handler(Looper.getMainLooper()).post(() -> updateState(mPreference));
|
// from the main looper when the {@link EthernetManager.Listener.onAvailabilityChanged}
|
||||||
}
|
// is triggerd.
|
||||||
};
|
mEthernetManager.addListener(mEthernetListener, r -> handler.post(r));
|
||||||
mEthernetManager.addListener(mEthernetListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ import com.android.settings.R;
|
|||||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
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;
|
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 ZenRuleScheduleHelper mScheduleHelper = new ZenRuleScheduleHelper();
|
||||||
private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEE");
|
|
||||||
|
|
||||||
private Preference mDays;
|
private Preference mDays;
|
||||||
private TimePickerPreference mStart;
|
private TimePickerPreference mStart;
|
||||||
@@ -149,30 +147,11 @@ public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateDays() {
|
private void updateDays() {
|
||||||
// Compute an ordered, delimited list of day names based on the persisted user config.
|
String desc = mScheduleHelper.getDaysDescription(mContext, mSchedule);
|
||||||
final int[] days = mSchedule.days;
|
if (desc != null) {
|
||||||
if (days != null && days.length > 0) {
|
mDays.setSummary(desc);
|
||||||
final StringBuilder sb = new StringBuilder();
|
mDays.notifyDependencyChange(false);
|
||||||
final Calendar c = Calendar.getInstance();
|
return;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mDays.setSummary(R.string.zen_mode_schedule_rule_days_none);
|
mDays.setSummary(R.string.zen_mode_schedule_rule_days_none);
|
||||||
mDays.notifyDependencyChange(true);
|
mDays.notifyDependencyChange(true);
|
||||||
|
|||||||
@@ -23,22 +23,20 @@ import android.content.Intent;
|
|||||||
import android.content.pm.ComponentInfo;
|
import android.content.pm.ComponentInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.service.notification.ZenModeConfig;
|
import android.service.notification.ZenModeConfig;
|
||||||
import android.view.View;
|
import android.service.notification.ZenModeConfig.ScheduleInfo;
|
||||||
import android.widget.CheckBox;
|
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceViewHolder;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.utils.ManagedServiceSettings;
|
import com.android.settings.utils.ManagedServiceSettings;
|
||||||
import com.android.settings.utils.ZenServiceListing;
|
import com.android.settings.utils.ZenServiceListing;
|
||||||
|
import com.android.settingslib.PrimarySwitchPreference;
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
import com.android.settingslib.widget.TwoTargetPreference;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ZenRulePreference extends TwoTargetPreference {
|
public class ZenRulePreference extends PrimarySwitchPreference {
|
||||||
private static final ManagedServiceSettings.Config CONFIG =
|
private static final ManagedServiceSettings.Config CONFIG =
|
||||||
ZenModeAutomationSettings.getConditionProviderConfig();
|
ZenModeAutomationSettings.getConditionProviderConfig();
|
||||||
final String mId;
|
final String mId;
|
||||||
@@ -53,14 +51,13 @@ public class ZenRulePreference extends TwoTargetPreference {
|
|||||||
CharSequence mName;
|
CharSequence mName;
|
||||||
|
|
||||||
private Intent mIntent;
|
private Intent mIntent;
|
||||||
private boolean mChecked;
|
|
||||||
private CheckBox mCheckBox;
|
private final ZenRuleScheduleHelper mScheduleHelper = new ZenRuleScheduleHelper();
|
||||||
|
|
||||||
public ZenRulePreference(Context context,
|
public ZenRulePreference(Context context,
|
||||||
final Map.Entry<String, AutomaticZenRule> ruleEntry,
|
final Map.Entry<String, AutomaticZenRule> ruleEntry,
|
||||||
Fragment parent, MetricsFeatureProvider metricsProvider) {
|
Fragment parent, MetricsFeatureProvider metricsProvider) {
|
||||||
super(context);
|
super(context);
|
||||||
setLayoutResource(R.layout.preference_checkable_two_target);
|
|
||||||
mBackend = ZenModeBackend.getInstance(context);
|
mBackend = ZenModeBackend.getInstance(context);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mRule = ruleEntry.getValue();
|
mRule = ruleEntry.getValue();
|
||||||
@@ -72,50 +69,11 @@ public class ZenRulePreference extends TwoTargetPreference {
|
|||||||
mServiceListing.reloadApprovedServices();
|
mServiceListing.reloadApprovedServices();
|
||||||
mPref = this;
|
mPref = this;
|
||||||
mMetricsFeatureProvider = metricsProvider;
|
mMetricsFeatureProvider = metricsProvider;
|
||||||
mChecked = mRule.isEnabled();
|
|
||||||
setAttributes(mRule);
|
setAttributes(mRule);
|
||||||
setWidgetLayoutResource(getSecondTargetResId());
|
setWidgetLayoutResource(getSecondTargetResId());
|
||||||
}
|
|
||||||
|
|
||||||
protected int getSecondTargetResId() {
|
// initialize the checked state of the preference
|
||||||
if (mIntent != null) {
|
super.setChecked(mRule.isEnabled());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePreference(AutomaticZenRule rule) {
|
public void updatePreference(AutomaticZenRule rule) {
|
||||||
@@ -126,34 +84,24 @@ public class ZenRulePreference extends TwoTargetPreference {
|
|||||||
|
|
||||||
if (mRule.isEnabled() != rule.isEnabled()) {
|
if (mRule.isEnabled() != rule.isEnabled()) {
|
||||||
setChecked(rule.isEnabled());
|
setChecked(rule.isEnabled());
|
||||||
setSummary(computeRuleSummary(rule));
|
|
||||||
}
|
}
|
||||||
|
setSummary(computeRuleSummary(rule));
|
||||||
mRule = rule;
|
mRule = rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick() {
|
public void onClick() {
|
||||||
mOnCheckBoxClickListener.onClick(null);
|
mContext.startActivity(mIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setChecked(boolean checked) {
|
@Override
|
||||||
mChecked = checked;
|
public void setChecked(boolean checked) {
|
||||||
if (mCheckBox != null) {
|
mRule.setEnabled(checked);
|
||||||
mCheckBox.setChecked(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) {
|
protected void setAttributes(AutomaticZenRule rule) {
|
||||||
final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
|
final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
|
||||||
rule.getConditionId(), true);
|
rule.getConditionId(), true);
|
||||||
@@ -178,6 +126,30 @@ public class ZenRulePreference extends TwoTargetPreference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String computeRuleSummary(AutomaticZenRule rule) {
|
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())
|
return (rule == null || !rule.isEnabled())
|
||||||
? mContext.getResources().getString(R.string.switch_off_text)
|
? mContext.getResources().getString(R.string.switch_off_text)
|
||||||
: mContext.getResources().getString(R.string.switch_on_text);
|
: mContext.getResources().getString(R.string.switch_on_text);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -246,7 +246,6 @@ public class SettingsSliceProviderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
|
||||||
@Config(shadows = ShadowStrictMode.class)
|
@Config(shadows = ShadowStrictMode.class)
|
||||||
public void onBindSlice_backgroundThread_shouldOverrideStrictMode() {
|
public void onBindSlice_backgroundThread_shouldOverrideStrictMode() {
|
||||||
ShadowThreadUtils.setIsMainThread(false);
|
ShadowThreadUtils.setIsMainThread(false);
|
||||||
|
|||||||
@@ -21,13 +21,14 @@ import android.net.Uri;
|
|||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
import com.android.settings.core.TogglePreferenceController;
|
import com.android.settings.core.TogglePreferenceController;
|
||||||
import com.android.settings.slices.SliceBackgroundWorker;
|
import com.android.settings.slices.SliceBackgroundWorker;
|
||||||
|
|
||||||
public class FakeToggleController extends TogglePreferenceController {
|
public class FakeToggleController extends TogglePreferenceController {
|
||||||
|
|
||||||
public static final String AVAILABILITY_KEY = "fake_toggle_availability_key";
|
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(
|
public static final IntentFilter INTENT_FILTER = new IntentFilter(
|
||||||
WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
|
WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,13 +21,14 @@ import android.net.Uri;
|
|||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
import com.android.settings.core.TogglePreferenceController;
|
import com.android.settings.core.TogglePreferenceController;
|
||||||
import com.android.settings.slices.SliceBackgroundWorker;
|
import com.android.settings.slices.SliceBackgroundWorker;
|
||||||
|
|
||||||
public class FakeToggleController extends TogglePreferenceController {
|
public class FakeToggleController extends TogglePreferenceController {
|
||||||
|
|
||||||
public static final String AVAILABILITY_KEY = "fake_toggle_availability_key";
|
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(
|
public static final IntentFilter INTENT_FILTER = new IntentFilter(
|
||||||
WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
|
WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
|
||||||
|
|||||||
Reference in New Issue
Block a user