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.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"
|
||||
|
||||
@@ -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] -->
|
||||
<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>
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
.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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@Ignore
|
||||
@Config(shadows = ShadowStrictMode.class)
|
||||
public void onBindSlice_backgroundThread_shouldOverrideStrictMode() {
|
||||
ShadowThreadUtils.setIsMainThread(false);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.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);
|
||||
|
||||
Reference in New Issue
Block a user