Snap for 4641295 from 6949175423 to pi-release

Change-Id: I7f62e2c3f112137791fdbd28bb3c9b97ca2e9f3b
This commit is contained in:
android-build-team Robot
2018-03-08 08:22:32 +00:00
70 changed files with 2361 additions and 518 deletions

View File

@@ -3786,8 +3786,8 @@
<string name="disable_text">Disable</string>
<!-- [CHAR LIMIT=25] Manage applications, individual application info screen, button label under Storage heading. Button to re-enable an existing application. -->
<string name="enable_text">Enable</string>
<!-- Manage applications, individual application info screen, button label under Storage heading. Button to clear all data associated with tis app (for exampel, remove all cached emails for an Email app) -->
<string name="clear_user_data_text">Clear data</string>
<!-- Manage applications, individual application info screen, button label under Storage heading. Button to clear all data associated with tis app (for example, remove all cached emails for an Email app) -->
<string name="clear_user_data_text">Clear storage</string>
<!-- Manage applications, restore updated system application to factory version -->
<string name="app_factory_reset">Uninstall updates</string>
<!-- Manage applications, individual application info screen, screen, message text under Launch by default heading. This is present if the app is set as a default for some actions. -->
@@ -3883,12 +3883,8 @@
<string name="app_not_found_dlg_title"></string>
<!-- Manage applications, individual application dialog box message. Shown when the user somehow got into a state where it wants to manage some app that isn't found. -->
<string name="app_not_found_dlg_text"> The app wasn\u2019t found in the list of installed apps.</string>
<!-- Manage applications, individual application dialog box message. Shown when there was an error trying to clear the data. -->
<string name="clear_data_failed">Couldn\u2019t clear app data.</string>
<!-- Manage applications, title for dialog if clear data fails-->
<string name="clear_failed_dlg_title">Clear data</string>
<!-- Manage applications, text for dialog if clear data fails-->
<string name="clear_failed_dlg_text">Couldn\u2019t clear data for app.</string>
<string name="clear_failed_dlg_text">Couldn\u2019t clear storage for app.</string>
<!-- Manage applications, individual application info screen, text that appears under the "Permissions" heading. This describes the permissions that the application has. -->
<string name="security_settings_desc" product="tablet">This app can access the following on your tablet:</string>
<!-- Manage applications, individual application info screen, text that appears under the "Permissions" heading. This describes the permissions that the application has. -->
@@ -7549,10 +7545,10 @@
<string name="zen_mode_alarms">Alarms</string>
<!-- [CHAR LIMIT=50] Zen mode settings: Media option -->
<string name="zen_mode_media_system_other">Media</string>
<string name="zen_mode_media">Media</string>
<!-- [CHAR LIMIT=120] Zen mode settings: Media secondary text explaining sounds include system feedback such as system tapping sounds, haptic feedback, etc. -->
<string name="zen_mode_media_system_other_secondary_text">Includes system feedback like touch and charging sounds</string>
<!-- [CHAR LIMIT=50] Zen mode settings: System option which includes sounds such as touch and charging sounds -->
<string name="zen_mode_system">Touch and charging sounds</string>
<!-- [CHAR LIMIT=50] Zen mode settings: Reminders option -->
<string name="zen_mode_reminders">Reminders</string>
@@ -8633,8 +8629,11 @@
<!-- Format for a summary describing the amount of data before the user is warned or limited [CHAR LIMIT=NONE] -->
<string name="cell_warning_and_limit"><xliff:g name="amount" example="1 GB">%1$s</xliff:g> Data warning / <xliff:g name="amount" example="2 GB">%2$s</xliff:g> Data limit</string>
<!-- Title of button and screen for billing cycle preferences [CHAR LIMIT=30 -->
<string name="billing_cycle">Billing cycle</string>
<!-- Title of button and screen for billing cycle preferences [CHAR LIMIT=30] -->
<string name="billing_cycle">Data warning &amp; limit</string>
<!-- Title of button for application usage cycle preferences [CHAR LIMIT=30] -->
<string name="app_usage_cycle">App data usage cycle</string>
<!-- Summary describing when the billing cycle for their phone carrier starts [CHAR LIMIT=NONE] -->
<string name="billing_cycle_fragment_summary">Monthly on day <xliff:g name="day_of_month" example="17">%1$s</xliff:g></string>

View File

@@ -14,17 +14,21 @@
limitations under the License.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="accessibility_settings_vibration_screen"
android:title="@string/accessibility_vibration_settings_title">
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:key="accessibility_settings_vibration_screen"
android:title="@string/accessibility_vibration_settings_title">
<Preference
android:fragment="com.android.settings.accessibility.NotificationVibrationPreferenceFragment"
android:key="notification_vibration_preference_screen"
android:title="@string/accessibility_notification_vibration_title" />
android:title="@string/accessibility_notification_vibration_title"
app:controller="com.android.settings.accessibility.NotificationVibrationIntensityPreferenceController" />
<Preference
android:fragment="com.android.settings.accessibility.TouchVibrationPreferenceFragment"
android:key="touch_vibration_preference_screen"
android:title="@string/accessibility_touch_vibration_title" />
android:title="@string/accessibility_touch_vibration_title"
app:controller="com.android.settings.accessibility.HapticFeedbackIntensityPreferenceController" />
</PreferenceScreen>

View File

@@ -23,6 +23,25 @@
<PreferenceCategory
android:key="dashboard_tile_placeholder"
android:order="200"/>
android:order="30"/>
<SwitchPreference
android:key="auto_sync_account_data"
android:title="@string/auto_sync_account_title"
android:summary="@string/auto_sync_account_summary"
android:order="102"
settings:allowDividerAbove="true"/>
<SwitchPreference
android:key="auto_sync_work_account_data"
android:title="@string/account_settings_menu_auto_sync_work"
android:summary="@string/auto_sync_account_summary"
android:order="103"/>
<SwitchPreference
android:key="auto_sync_personal_account_data"
android:title="@string/account_settings_menu_auto_sync_personal"
android:summary="@string/auto_sync_account_summary"
android:order="104"/>
</PreferenceScreen>

View File

@@ -20,7 +20,7 @@
<Preference
android:key="billing_cycle"
android:title="@string/billing_cycle" />
android:title="@string/app_usage_cycle" />
<SwitchPreference
android:key="set_data_warning"

View File

@@ -28,13 +28,15 @@
android:key="saved_device_list"
android:title="@string/connected_device_saved_title"/>
<Preference
<com.android.settingslib.RestrictedPreference
android:fragment="com.android.settings.bluetooth.BluetoothPairingDetail"
android:key="add_bt_devices"
android:title="@string/connected_device_add_device_title"
android:icon="@drawable/ic_menu_add"
android:summary="@string/connected_device_add_device_summary"
settings:allowDividerAbove="true"/>
settings:allowDividerAbove="true"
settings:userRestriction="no_config_bluetooth"
settings:useAdminDisabledSummary="true"/>
<Preference
android:fragment="com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment"

View File

@@ -16,8 +16,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="data_usage_cellular_screen"
android:title="@string/data_usage_summary_title">
android:key="data_usage_cellular_screen">
<com.android.settings.datausage.TemplatePreferenceCategory
android:key="mobile_category"

View File

@@ -16,8 +16,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="data_usage_wifi_screen"
android:title="@string/data_usage_summary_title">
android:key="data_usage_wifi_screen">
<com.android.settings.datausage.TemplatePreferenceCategory
android:key="wifi_category"

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="time_zone_settings_screen"
android:title="@string/date_time_set_timezone">
<PreferenceCategory
android:key="time_zone_region_preference_category">
<com.android.settingslib.RestrictedPreference
android:key="region"
android:title="@string/date_time_select_region"
android:summary="@string/summary_placeholder" />
<com.android.settingslib.RestrictedPreference
android:key="region_zone"
android:title="@string/date_time_select_zone"
android:summary="@string/summary_placeholder" />
<com.android.settingslib.widget.FooterPreference/>
</PreferenceCategory>
<PreferenceCategory
android:key="time_zone_fixed_offset_preference_category">
<com.android.settingslib.RestrictedPreference
android:key="fixed_offset"
android:title="@string/date_time_select_fixed_offset_time_zones"
android:summary="@string/summary_placeholder"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -16,7 +16,6 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="user_settings_screen"
android:title="@string/user_settings_title">
@@ -32,25 +31,6 @@
android:icon="@drawable/ic_menu_add"
android:order="20"/>
<SwitchPreference
android:key="auto_sync_account_data"
android:title="@string/auto_sync_account_title"
android:summary="@string/auto_sync_account_summary"
android:order="102"
settings:allowDividerAbove="true"/>
<SwitchPreference
android:key="auto_sync_work_account_data"
android:title="@string/account_settings_menu_auto_sync_work"
android:summary="@string/auto_sync_account_summary"
android:order="103"/>
<SwitchPreference
android:key="auto_sync_personal_account_data"
android:title="@string/account_settings_menu_auto_sync_personal"
android:summary="@string/auto_sync_account_summary"
android:order="104"/>
<com.android.settingslib.RestrictedSwitchPreference
android:key="user_settings_add_users_when_locked"
android:title="@string/user_add_on_lockscreen_menu"

View File

@@ -20,7 +20,7 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="zen_mode_behavior_settings_page"
android:title="@string/zen_mode_behavior_settings_title"
settings:initialExpandedChildrenCount="7">
settings:initialExpandedChildrenCount="8">
<PreferenceCategory
android:title="@string/zen_mode_behavior_allow_title"
@@ -34,8 +34,12 @@
<!-- Media -->
<SwitchPreference
android:key="zen_mode_media"
android:title="@string/zen_mode_media_system_other"
android:summary="@string/zen_mode_media_system_other_secondary_text"/>
android:title="@string/zen_mode_media"/>
<!-- System -->
<SwitchPreference
android:key="zen_mode_system"
android:title="@string/zen_mode_system"/>
<!-- Reminders -->
<SwitchPreference

View File

@@ -23,8 +23,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.ArrayList;
import java.util.List;
@@ -51,28 +49,6 @@ public class VibrationSettings extends DashboardFragment {
return TAG;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildControllers(context, getLifecycle());
}
public static List<AbstractPreferenceController> buildControllers(Context context,
Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final NotificationVibrationIntensityPreferenceController notifVibPrefController =
new NotificationVibrationIntensityPreferenceController(context);
final HapticFeedbackIntensityPreferenceController hapticPreferenceController =
new HapticFeedbackIntensityPreferenceController(context);
controllers.add(hapticPreferenceController);
controllers.add(notifVibPrefController);
if (lifecycle != null) {
lifecycle.addObserver(hapticPreferenceController);
lifecycle.addObserver(notifVibPrefController);
}
return controllers;
}
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
@@ -84,11 +60,5 @@ public class VibrationSettings extends DashboardFragment {
indexables.add(indexable);
return indexables;
}
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
return buildControllers(context, null /* lifecycle */);
}
};
}

View File

@@ -29,6 +29,9 @@ import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.users.AutoSyncDataPreferenceController;
import com.android.settings.users.AutoSyncPersonalDataPreferenceController;
import com.android.settings.users.AutoSyncWorkDataPreferenceController;
import com.android.settingslib.accounts.AuthenticatorHelper;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -69,6 +72,9 @@ public class AccountDashboardFragment extends DashboardFragment {
new AccountPreferenceController(context, this, authorities);
getLifecycle().addObserver(accountPrefController);
controllers.add(accountPrefController);
controllers.add(new AutoSyncDataPreferenceController(context, this /*parent */));
controllers.add(new AutoSyncPersonalDataPreferenceController(context, this /*parent */));
controllers.add(new AutoSyncWorkDataPreferenceController(context, this /* parent */));
return controllers;
}

View File

@@ -28,7 +28,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.content.UriPermission;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageManager;
@@ -487,7 +486,7 @@ public class AppStorageSettings extends AppInfoWithHeader
.create();
case DLG_CANNOT_CLEAR_DATA:
return new AlertDialog.Builder(getActivity())
.setTitle(getActivity().getText(R.string.clear_failed_dlg_title))
.setTitle(getActivity().getText(R.string.clear_user_data_text))
.setMessage(getActivity().getText(R.string.clear_failed_dlg_text))
.setNeutralButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {

View File

@@ -58,9 +58,17 @@ public class BackupSettingsActivity extends Activity implements Indexable {
"No manufacturer settings found, launching the backup settings directly");
}
Intent intent = backupHelper.getIntentForBackupSettings();
// enable the activity before launching it
getPackageManager().setComponentEnabledSetting(intent.getComponent(),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
try {
// enable the activity before launching it
getPackageManager().setComponentEnabledSetting(
intent.getComponent(),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
} catch (SecurityException e) {
Log.w(TAG, "Trying to enable activity " + intent.getComponent() + " but couldn't: "
+ e.getMessage());
// the activity may already be enabled
}
// use startActivityForResult to let the activity check the caller signature
startActivityForResult(intent, 1);

View File

@@ -34,8 +34,6 @@ import java.util.List;
* Abstract class to consolidate utility between preference controllers and act as an interface
* for Slices. The abstract classes that inherit from this class will act as the direct interfaces
* for each type when plugging into Slices.
*
* TODO (b/73074893) Add Lifecycle Setting method.
*/
public abstract class BasePreferenceController extends AbstractPreferenceController {

View File

@@ -19,11 +19,11 @@ package com.android.settings.datetime;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.util.FeatureFlagUtils;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.datetime.timezone.ZonePicker;
import com.android.settings.datetime.timezone.TimeZoneSettings;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.datetime.ZoneGetter;
@@ -51,7 +51,7 @@ public class TimeZonePreferenceController extends AbstractPreferenceController
return;
}
if (mZonePickerV2) {
preference.setFragment(ZonePicker.class.getName());
preference.setFragment(TimeZoneSettings.class.getName());
}
preference.setSummary(getTimeZoneOffsetAndName());
if( !((RestrictedPreference) preference).isDisabledByAdmin()) {

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.content.Context;
import android.support.v7.preference.Preference;
import com.android.settings.core.BasePreferenceController;
import com.google.common.base.Objects;
public abstract class BaseTimeZonePreferenceController extends BasePreferenceController {
private OnPreferenceClickListener mOnClickListener;
protected BaseTimeZonePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (mOnClickListener == null || !Objects.equal(getPreferenceKey(), preference.getKey())) {
return false;
}
mOnClickListener.onClick();
return true;
}
public void setOnClickListener(OnPreferenceClickListener listener) {
mOnClickListener = listener;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.content.Context;
import android.support.v7.preference.Preference;
public class FixedOffsetPreferenceController extends BaseTimeZonePreferenceController {
private static final String PREFERENCE_KEY = "fixed_offset";
private TimeZoneInfo mTimeZoneInfo;
public FixedOffsetPreferenceController(Context context) {
super(context, PREFERENCE_KEY);
}
@Override
public CharSequence getSummary() {
// This is a Spannable object, which contains TTS span. It shouldn't be converted to String.
return mTimeZoneInfo == null ? "" : mTimeZoneInfo.getGmtOffset();
}
public void setTimeZoneInfo(TimeZoneInfo timeZoneInfo) {
mTimeZoneInfo = timeZoneInfo;
}
public TimeZoneInfo getTimeZoneInfo() {
return mTimeZoneInfo;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
/**
* Callback when a preference is clicked in {@class TimeZoneSettings}
*/
public interface OnPreferenceClickListener {
void onClick();
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.content.Context;
import android.icu.text.LocaleDisplayNames;
import android.support.v7.preference.Preference;
import java.util.Locale;
public class RegionPreferenceController extends BaseTimeZonePreferenceController {
private static final String PREFERENCE_KEY = "region";
private final LocaleDisplayNames mLocaleDisplayNames;
private String mRegionId = "";
public RegionPreferenceController(Context context) {
super(context, PREFERENCE_KEY);
Locale locale = context.getResources().getConfiguration().getLocales().get(0);
mLocaleDisplayNames = LocaleDisplayNames.getInstance(locale);
}
@Override
public CharSequence getSummary() {
return mLocaleDisplayNames.regionDisplayName(mRegionId);
}
public void setRegionId(String regionId) {
mRegionId = regionId;
}
public String getRegionId() {
return mRegionId;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.content.Context;
import android.support.v7.preference.Preference;
import com.android.settings.R;
public class RegionZonePreferenceController extends BaseTimeZonePreferenceController {
private static final String PREFERENCE_KEY = "region_zone";
private TimeZoneInfo mTimeZoneInfo;
private boolean mIsClickable;
public RegionZonePreferenceController(Context context) {
super(context, PREFERENCE_KEY);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
preference.setEnabled(isClickable());
}
@Override
public CharSequence getSummary() {
return mTimeZoneInfo == null ? ""
: SpannableUtil.getResourcesText(mContext.getResources(),
R.string.zone_info_exemplar_location_and_offset,
mTimeZoneInfo.getExemplarLocation(), mTimeZoneInfo.getGmtOffset());
}
public void setTimeZoneInfo(TimeZoneInfo timeZoneInfo) {
mTimeZoneInfo = timeZoneInfo;
}
public TimeZoneInfo getTimeZoneInfo() {
return mTimeZoneInfo;
}
public void setClickable(boolean clickable) {
mIsClickable = clickable;
}
public boolean isClickable() {
return mIsClickable;
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.content.Context;
import android.icu.impl.OlsonTimeZone;
import android.icu.text.DateFormat;
import android.icu.text.DisplayContext;
import android.icu.text.SimpleDateFormat;
import android.icu.util.Calendar;
import android.icu.util.TimeZone;
import android.icu.util.TimeZoneTransition;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import com.android.settings.R;
import com.android.settingslib.widget.FooterPreference;
import java.util.Date;
public class TimeZoneInfoPreferenceController extends BaseTimeZonePreferenceController {
private static final String PREFERENCE_KEY = FooterPreference.KEY_FOOTER;
private TimeZoneInfo mTimeZoneInfo;
private final DateFormat mDateFormat;
private final Date mDate;
public TimeZoneInfoPreferenceController(Context context) {
this(context, new Date());
}
@VisibleForTesting
TimeZoneInfoPreferenceController(Context context, Date date) {
super(context, PREFERENCE_KEY);
mDateFormat = DateFormat.getDateInstance(SimpleDateFormat.LONG);
mDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
mDate = date;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void updateState(Preference preference) {
CharSequence formattedTimeZone = mTimeZoneInfo == null ? "" : formatInfo(mTimeZoneInfo);
preference.setTitle(formattedTimeZone);
preference.setVisible(mTimeZoneInfo != null);
}
public void setTimeZoneInfo(TimeZoneInfo timeZoneInfo) {
mTimeZoneInfo = timeZoneInfo;
}
public TimeZoneInfo getTimeZoneInfo() {
return mTimeZoneInfo;
}
private CharSequence formatOffsetAndName(TimeZoneInfo item) {
String name = item.getGenericName();
if (name == null) {
if (item.getTimeZone().inDaylightTime(mDate)) {
name = item.getDaylightName();
} else {
name = item.getStandardName();
}
}
if (name == null) {
return item.getGmtOffset().toString();
} else {
return SpannableUtil.getResourcesText(mContext.getResources(),
R.string.zone_info_offset_and_name, item.getGmtOffset(),
name);
}
}
private CharSequence formatInfo(TimeZoneInfo item) {
final CharSequence offsetAndName = formatOffsetAndName(item);
final TimeZone timeZone = item.getTimeZone();
if (!timeZone.observesDaylightTime()) {
return mContext.getString(R.string.zone_info_footer_no_dst, offsetAndName);
}
final TimeZoneTransition nextDstTransition = findNextDstTransition(timeZone);
if (nextDstTransition == null) {
return null;
}
final boolean toDst = nextDstTransition.getTo().getDSTSavings() != 0;
String timeType = toDst ? item.getDaylightName() : item.getStandardName();
if (timeType == null) {
// Fall back to generic "summer time" and "standard time" if the time zone has no
// specific names.
timeType = toDst ?
mContext.getString(R.string.zone_time_type_dst) :
mContext.getString(R.string.zone_time_type_standard);
}
final Calendar transitionTime = Calendar.getInstance(timeZone);
transitionTime.setTimeInMillis(nextDstTransition.getTime());
final String date = mDateFormat.format(transitionTime);
return SpannableUtil.getResourcesText(mContext.getResources(),
R.string.zone_info_footer, offsetAndName, timeType, date);
}
private TimeZoneTransition findNextDstTransition(TimeZone timeZone) {
if (!(timeZone instanceof OlsonTimeZone)) {
return null;
}
final OlsonTimeZone olsonTimeZone = (OlsonTimeZone) timeZone;
TimeZoneTransition transition = olsonTimeZone.getNextTransition(
mDate.getTime(), /* inclusive */ false);
do {
if (transition.getTo().getDSTSavings() != transition.getFrom().getDSTSavings()) {
break;
}
transition = olsonTimeZone.getNextTransition(
transition.getTime(), /*inclusive */ false);
} while (transition != null);
return transition;
}
}

View File

@@ -0,0 +1,372 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.app.Activity;
import android.app.AlarmManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.icu.util.TimeZone;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.PreferenceCategory;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.datetime.timezone.model.FilteredCountryTimeZones;
import com.android.settings.datetime.timezone.model.TimeZoneData;
import com.android.settings.datetime.timezone.model.TimeZoneDataLoader;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
/**
* The class displays a time zone picker either by regions or fixed offset time zones.
*/
public class TimeZoneSettings extends DashboardFragment {
private static final String TAG = "TimeZoneSettings";
private static final int MENU_BY_REGION = Menu.FIRST;
private static final int MENU_BY_OFFSET = Menu.FIRST + 1;
private static final int REQUEST_CODE_REGION_PICKER = 1;
private static final int REQUEST_CODE_ZONE_PICKER = 2;
private static final int REQUEST_CODE_FIXED_OFFSET_ZONE_PICKER = 3;
private static final String PREF_KEY_REGION = "time_zone_region";
private static final String PREF_KEY_REGION_CATEGORY = "time_zone_region_preference_category";
private static final String PREF_KEY_FIXED_OFFSET_CATEGORY =
"time_zone_fixed_offset_preference_category";
private Locale mLocale;
private boolean mSelectByRegion;
private TimeZoneData mTimeZoneData;
private String mSelectedTimeZoneId;
private TimeZoneInfo.Formatter mTimeZoneInfoFormatter;
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.ZONE_PICKER;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.time_zone_prefs;
}
@Override
protected String getLogTag() {
return TAG;
}
/**
* Called during onAttach
*/
@VisibleForTesting
@Override
public List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mLocale = context.getResources().getConfiguration().getLocales().get(0);
mTimeZoneInfoFormatter = new TimeZoneInfo.Formatter(mLocale, new Date());
final List<AbstractPreferenceController> controllers = new ArrayList<>();
RegionPreferenceController regionPreferenceController =
new RegionPreferenceController(context);
regionPreferenceController.setOnClickListener(this::onRegionPreferenceClicked);
RegionZonePreferenceController regionZonePreferenceController =
new RegionZonePreferenceController(context);
regionZonePreferenceController.setOnClickListener(this::onRegionZonePreferenceClicked);
TimeZoneInfoPreferenceController timeZoneInfoPreferenceController =
new TimeZoneInfoPreferenceController(context);
FixedOffsetPreferenceController fixedOffsetPreferenceController =
new FixedOffsetPreferenceController(context);
fixedOffsetPreferenceController.setOnClickListener(this::onFixedOffsetPreferenceClicked);
controllers.add(regionPreferenceController);
controllers.add(regionZonePreferenceController);
controllers.add(timeZoneInfoPreferenceController);
controllers.add(fixedOffsetPreferenceController);
return controllers;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Hide all interactive preferences
setPreferenceCategoryVisible((PreferenceCategory) findPreference(
PREF_KEY_REGION_CATEGORY), false);
setPreferenceCategoryVisible((PreferenceCategory) findPreference(
PREF_KEY_FIXED_OFFSET_CATEGORY), false);
// Start loading TimeZoneData
getLoaderManager().initLoader(0, null, new TimeZoneDataLoader.LoaderCreator(
getContext(), this::onTimeZoneDataReady));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK || data == null) {
return;
}
switch (requestCode) {
case REQUEST_CODE_REGION_PICKER:
case REQUEST_CODE_ZONE_PICKER: {
String regionId = data.getStringExtra(RegionSearchPicker.EXTRA_RESULT_REGION_ID);
String tzId = data.getStringExtra(RegionZonePicker.EXTRA_RESULT_TIME_ZONE_ID);
// Ignore the result if user didn't change the region or time zone.
if (!Objects.equals(regionId, use(RegionPreferenceController.class).getRegionId())
|| !Objects.equals(tzId, mSelectedTimeZoneId)) {
onRegionZoneChanged(regionId, tzId);
}
break;
}
case REQUEST_CODE_FIXED_OFFSET_ZONE_PICKER: {
String tzId = data.getStringExtra(FixedOffsetPicker.EXTRA_RESULT_TIME_ZONE_ID);
// Ignore the result if user didn't change the time zone.
if (tzId != null && !tzId.equals(mSelectedTimeZoneId)) {
onFixedOffsetZoneChanged(tzId);
}
break;
}
}
}
@VisibleForTesting
void setTimeZoneData(TimeZoneData timeZoneData) {
mTimeZoneData = timeZoneData;
}
private void onTimeZoneDataReady(TimeZoneData timeZoneData) {
if (mTimeZoneData == null && timeZoneData != null) {
mTimeZoneData = timeZoneData;
setupForCurrentTimeZone();
getActivity().invalidateOptionsMenu();
}
}
private void onRegionPreferenceClicked() {
startPickerFragment(RegionSearchPicker.class, new Bundle(), REQUEST_CODE_REGION_PICKER);
}
private void onRegionZonePreferenceClicked() {
final Bundle args = new Bundle();
args.putString(RegionZonePicker.EXTRA_REGION_ID,
use(RegionPreferenceController.class).getRegionId());
startPickerFragment(RegionZonePicker.class, args, REQUEST_CODE_ZONE_PICKER);
}
private void onFixedOffsetPreferenceClicked() {
startPickerFragment(FixedOffsetPicker.class, new Bundle(),
REQUEST_CODE_FIXED_OFFSET_ZONE_PICKER);
}
private void startPickerFragment(Class<? extends BaseTimeZonePicker> fragmentClass, Bundle args,
int resultRequestCode) {
new SubSettingLauncher(getContext())
.setDestination(fragmentClass.getCanonicalName())
.setArguments(args)
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, resultRequestCode)
.launch();
}
private void setDisplayedRegion(String regionId) {
use(RegionPreferenceController.class).setRegionId(regionId);
updatePreferenceStates();
}
private void setDisplayedTimeZoneInfo(String regionId, String tzId) {
final TimeZoneInfo tzInfo = tzId == null ? null : mTimeZoneInfoFormatter.format(tzId);
final FilteredCountryTimeZones countryTimeZones =
mTimeZoneData.lookupCountryTimeZones(regionId);
use(RegionZonePreferenceController.class).setTimeZoneInfo(tzInfo);
// Only clickable when the region has more than 1 time zones or no time zone is selected.
use(RegionZonePreferenceController.class).setClickable(tzInfo == null ||
(countryTimeZones != null && countryTimeZones.getTimeZoneIds().size() > 1));
use(TimeZoneInfoPreferenceController.class).setTimeZoneInfo(tzInfo);
updatePreferenceStates();
}
private void setDisplayedFixedOffsetTimeZoneInfo(String tzId) {
if (isFixedOffset(tzId)) {
use(FixedOffsetPreferenceController.class).setTimeZoneInfo(
mTimeZoneInfoFormatter.format(tzId));
} else {
use(FixedOffsetPreferenceController.class).setTimeZoneInfo(null);
}
updatePreferenceStates();
}
private void onRegionZoneChanged(String regionId, String tzId) {
FilteredCountryTimeZones countryTimeZones =
mTimeZoneData.lookupCountryTimeZones(regionId);
if (countryTimeZones == null || !countryTimeZones.getTimeZoneIds().contains(tzId)) {
Log.e(TAG, "Unknown time zone id is selected: " + tzId);
return;
}
mSelectedTimeZoneId = tzId;
setDisplayedRegion(regionId);
setDisplayedTimeZoneInfo(regionId, mSelectedTimeZoneId);
saveTimeZone(regionId, mSelectedTimeZoneId);
}
private void onFixedOffsetZoneChanged(String tzId) {
mSelectedTimeZoneId = tzId;
setDisplayedFixedOffsetTimeZoneInfo(tzId);
saveTimeZone(null, mSelectedTimeZoneId);
}
private void saveTimeZone(String regionId, String tzId) {
SharedPreferences.Editor editor = getPreferenceManager().getSharedPreferences().edit();
if (regionId == null) {
editor.remove(PREF_KEY_REGION);
} else {
editor.putString(PREF_KEY_REGION, regionId);
}
editor.apply();
getActivity().getSystemService(AlarmManager.class).setTimeZone(tzId);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(0, MENU_BY_REGION, 0, R.string.zone_menu_by_region);
menu.add(0, MENU_BY_OFFSET, 0, R.string.zone_menu_by_offset);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
// Do not show menu when data is not ready,
menu.findItem(MENU_BY_REGION).setVisible(mTimeZoneData != null && !mSelectByRegion);
menu.findItem(MENU_BY_OFFSET).setVisible(mTimeZoneData != null && mSelectByRegion);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_BY_REGION:
setSelectByRegion(true);
return true;
case MENU_BY_OFFSET:
setSelectByRegion(false);
return true;
default:
return false;
}
}
private void setupForCurrentTimeZone() {
mSelectedTimeZoneId = TimeZone.getDefault().getID();
setSelectByRegion(!isFixedOffset(mSelectedTimeZoneId));
}
private static boolean isFixedOffset(String tzId) {
return tzId.startsWith("Etc/GMT") || tzId.equals("Etc/UTC");
}
/**
* Switch the current view to select region or select fixed offset time zone.
* When showing the selected region, it guess the selected region from time zone id.
* See {@link #findRegionIdForTzId} for more info.
*/
private void setSelectByRegion(boolean selectByRegion) {
mSelectByRegion = selectByRegion;
setPreferenceCategoryVisible((PreferenceCategory) findPreference(
PREF_KEY_REGION_CATEGORY), selectByRegion);
setPreferenceCategoryVisible((PreferenceCategory) findPreference(
PREF_KEY_FIXED_OFFSET_CATEGORY), !selectByRegion);
final String localeRegionId = getLocaleRegionId();
final Set<String> allCountryIsoCodes = mTimeZoneData.getRegionIds();
String displayRegion = allCountryIsoCodes.contains(localeRegionId) ? localeRegionId : null;
setDisplayedRegion(displayRegion);
setDisplayedTimeZoneInfo(displayRegion, null);
if (!mSelectByRegion) {
setDisplayedFixedOffsetTimeZoneInfo(mSelectedTimeZoneId);
return;
}
String regionId = findRegionIdForTzId(mSelectedTimeZoneId);
if (regionId != null) {
setDisplayedRegion(regionId);
setDisplayedTimeZoneInfo(regionId, mSelectedTimeZoneId);
}
}
/**
* Find the a region associated with the specified time zone, based on the time zone data.
* If there are multiple regions associated with the given time zone, the priority will be given
* to the region the user last picked and the country in user's locale.
* @return null if no region associated with the time zone
*/
private String findRegionIdForTzId(String tzId) {
return findRegionIdForTzId(tzId,
getPreferenceManager().getSharedPreferences().getString(PREF_KEY_REGION, null),
getLocaleRegionId());
}
@VisibleForTesting
String findRegionIdForTzId(String tzId, String sharePrefRegionId, String localeRegionId) {
final Set<String> matchedRegions = mTimeZoneData.lookupCountryCodesForZoneId(tzId);
if (matchedRegions.size() == 0) {
return null;
}
if (sharePrefRegionId != null && matchedRegions.contains(sharePrefRegionId)) {
return sharePrefRegionId;
}
if (localeRegionId != null && matchedRegions.contains(localeRegionId)) {
return localeRegionId;
}
return matchedRegions.toArray(new String[matchedRegions.size()])[0];
}
private void setPreferenceCategoryVisible(PreferenceCategory category,
boolean isVisible) {
// Hiding category doesn't hide all the children preference. Set visibility of its children.
// Do not care grandchildren as time_zone_pref.xml has only 2 levels.
category.setVisible(isVisible);
for (int i = 0; i < category.getPreferenceCount(); i++) {
category.getPreference(i).setVisible(isVisible);
}
}
private String getLocaleRegionId() {
return mLocale.getCountry().toUpperCase(Locale.US);
}
}

View File

@@ -35,18 +35,15 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.R;
import com.android.settingslib.utils.PowerUtil;
import com.android.settingslib.utils.StringUtil;
import java.util.concurrent.TimeUnit;
public class BatteryInfo {
private static final long SEVEN_MINUTES_MICROS = TimeUnit.MINUTES.toMicros(7);
private static final long FIFTEEN_MINUTES_MICROS = TimeUnit.MINUTES.toMicros(15);
private static final long ONE_DAY_MICROS = TimeUnit.DAYS.toMicros(1);
public CharSequence chargeLabel;
public CharSequence remainingLabel;
public int batteryLevel;
public boolean discharging = true;
public long remainingTimeUs = 0;
public long averageTimeToDischarge = Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN;
public String batteryPercentString;
public String statusLabel;
private boolean mCharging;
@@ -180,16 +177,18 @@ public class BatteryInfo {
BatteryUtils
.logRuntime(LOG_TAG, "time for enhanced BatteryInfo", startTime);
return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
elapsedRealtimeUs, shortString,
PowerUtil.convertMsToUs(estimate.estimateMillis),
estimate.isBasedOnUsage);
estimate, elapsedRealtimeUs, shortString);
}
}
long prediction = discharging
? stats.computeBatteryTimeRemaining(elapsedRealtimeUs) : 0;
Estimate estimate = new Estimate(
PowerUtil.convertUsToMs(prediction),
false, /* isBasedOnUsage */
Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
BatteryUtils.logRuntime(LOG_TAG, "time for regular BatteryInfo", startTime);
return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
elapsedRealtimeUs, shortString, prediction, false);
estimate, elapsedRealtimeUs, shortString);
}
@Override
@@ -204,25 +203,29 @@ public class BatteryInfo {
@WorkerThread
public static BatteryInfo getBatteryInfoOld(Context context, Intent batteryBroadcast,
BatteryStats stats, long elapsedRealtimeUs, boolean shortString) {
return getBatteryInfo(context, batteryBroadcast, stats, elapsedRealtimeUs, shortString,
stats.computeBatteryTimeRemaining(elapsedRealtimeUs), false);
Estimate estimate = new Estimate(
PowerUtil.convertUsToMs(stats.computeBatteryTimeRemaining(elapsedRealtimeUs)),
false,
Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
return getBatteryInfo(context, batteryBroadcast, stats, estimate, elapsedRealtimeUs,
shortString);
}
@WorkerThread
public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
BatteryStats stats, long elapsedRealtimeUs, boolean shortString, long drainTimeUs,
boolean basedOnUsage) {
BatteryStats stats, Estimate estimate, long elapsedRealtimeUs, boolean shortString) {
final long startTime = System.currentTimeMillis();
BatteryInfo info = new BatteryInfo();
info.mStats = stats;
info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
info.batteryPercentString = Utils.formatPercentage(info.batteryLevel);
info.mCharging = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
info.averageTimeToDischarge = estimate.averageDischargeTime;
final Resources resources = context.getResources();
info.statusLabel = Utils.getBatteryStatus(resources, batteryBroadcast);
if (!info.mCharging) {
updateBatteryInfoDischarging(context, shortString, drainTimeUs, basedOnUsage, info);
updateBatteryInfoDischarging(context, shortString, estimate, info);
} else {
updateBatteryInfoCharging(context, batteryBroadcast, stats, elapsedRealtimeUs, info);
}
@@ -256,20 +259,21 @@ public class BatteryInfo {
}
private static void updateBatteryInfoDischarging(Context context, boolean shortString,
long drainTimeUs, boolean basedOnUsage, BatteryInfo info) {
Estimate estimate, BatteryInfo info) {
final long drainTimeUs = PowerUtil.convertMsToUs(estimate.estimateMillis);
if (drainTimeUs > 0) {
info.remainingTimeUs = drainTimeUs;
info.remainingLabel = PowerUtil.getBatteryRemainingStringFormatted(
context,
PowerUtil.convertUsToMs(drainTimeUs),
null /* percentageString */,
basedOnUsage && !shortString
estimate.isBasedOnUsage && !shortString
);
info.chargeLabel = PowerUtil.getBatteryRemainingStringFormatted(
context,
PowerUtil.convertUsToMs(drainTimeUs),
info.batteryPercentString,
basedOnUsage && !shortString
estimate.isBasedOnUsage && !shortString
);
} else {
info.remainingLabel = null;

View File

@@ -438,14 +438,15 @@ public class BatteryUtils {
if (estimate != null) {
batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast, stats,
elapsedRealtimeUs, false /* shortString */,
PowerUtil.convertMsToUs(estimate.estimateMillis),
estimate.isBasedOnUsage);
estimate, elapsedRealtimeUs, false /* shortString */);
} else {
estimate = new Estimate(
PowerUtil.convertUsToMs(stats.computeBatteryTimeRemaining(elapsedRealtimeUs)),
false,
Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN
);
batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast, stats,
elapsedRealtimeUs, false /* shortString */,
discharging ? stats.computeBatteryTimeRemaining(elapsedRealtimeUs) : 0,
false /* basedOnUsage */);
estimate, elapsedRealtimeUs, false /* shortString */);
}
BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime);

View File

@@ -58,12 +58,10 @@ public class DebugEstimatesLoader extends AsyncLoader<List<BatteryInfo>> {
Estimate estimate = powerUsageFeatureProvider.getEnhancedBatteryPrediction(context);
if (estimate == null) {
estimate = new Estimate(0, false);
estimate = new Estimate(0, false, Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
}
BatteryInfo newInfo = BatteryInfo.getBatteryInfo(getContext(), batteryBroadcast, stats,
elapsedRealtimeUs, false,
PowerUtil.convertMsToUs(estimate.estimateMillis),
estimate.isBasedOnUsage);
estimate, elapsedRealtimeUs, false);
List<BatteryInfo> infos = new ArrayList<>();
infos.add(oldinfo);

View File

@@ -2,11 +2,17 @@ package com.android.settings.fuelgauge;
public class Estimate {
public final long estimateMillis;
public final boolean isBasedOnUsage;
// Value to indicate averageTimeToDischarge could not be obtained
public static final int AVERAGE_TIME_TO_DISCHARGE_UNKNOWN = -1;
public Estimate(long estimateMillis, boolean isBasedOnUsage) {
this.estimateMillis = estimateMillis;
this.isBasedOnUsage = isBasedOnUsage;
}
public final long estimateMillis;
public final boolean isBasedOnUsage;
public final long averageDischargeTime;
public Estimate(long estimateMillis, boolean isBasedOnUsage,
long averageDischargeTime) {
this.estimateMillis = estimateMillis;
this.isBasedOnUsage = isBasedOnUsage;
this.averageDischargeTime = averageDischargeTime;
}
}

View File

@@ -74,6 +74,7 @@ public class RestrictAppPreferenceController extends BasePreferenceController {
final AppOpsManager.PackageOps packageOps = packageOpsList.get(i);
mAppInfos.add(new AppInfo.Builder()
.setPackageName(packageOps.getPackageName())
.setUid(packageOps.getUid())
.build());
}

View File

@@ -130,19 +130,15 @@ public class RestrictedAppDetails extends DashboardFragment {
appInfo.packageName, 0 /* flags */);
checkBoxPreference.setChecked(true);
checkBoxPreference.setTitle(mPackageManager.getApplicationLabel(applicationInfo));
checkBoxPreference.setKey(appInfo.packageName);
checkBoxPreference.setIcon(
Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager,
appInfo.packageName,
UserHandle.getUserId(
mBatteryUtils.getPackageUid(appInfo.packageName))));
UserHandle.getUserId(appInfo.uid)));
checkBoxPreference.setOnPreferenceChangeListener((pref, value) -> {
// change the toggle
final int mode = (Boolean) value ? AppOpsManager.MODE_IGNORED
: AppOpsManager.MODE_ALLOWED;
final String packageName = pref.getKey();
final int uid = mBatteryUtils.getPackageUid(packageName);
mBatteryUtils.setForceAppStandby(uid, packageName, mode);
mBatteryUtils.setForceAppStandby(appInfo.uid, appInfo.packageName, mode);
return true;
});
mRestrictedAppListGroup.addPreference(checkBoxPreference);

View File

@@ -41,6 +41,7 @@ import android.util.Log;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
import com.android.settingslib.utils.ThreadUtils;
import java.util.List;
@@ -81,11 +82,12 @@ public class AnomalyDetectionJobService extends JobService {
final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(this,
true /* collectBatteryBroadcast */);
final UserManager userManager = getSystemService(UserManager.class);
final PowerWhitelistBackend powerWhitelistBackend = PowerWhitelistBackend.getInstance();
for (JobWorkItem item = params.dequeueWork(); item != null;
item = params.dequeueWork()) {
saveAnomalyToDatabase(batteryStatsHelper, userManager, batteryDatabaseManager,
batteryUtils, policy, contentResolver,
batteryUtils, policy, powerWhitelistBackend, contentResolver,
item.getIntent().getExtras());
}
jobFinished(params, false /* wantsReschedule */);
@@ -102,7 +104,8 @@ public class AnomalyDetectionJobService extends JobService {
@VisibleForTesting
void saveAnomalyToDatabase(BatteryStatsHelper batteryStatsHelper, UserManager userManager,
BatteryDatabaseManager databaseManager, BatteryUtils batteryUtils,
BatteryTipPolicy policy, ContentResolver contentResolver, Bundle bundle) {
BatteryTipPolicy policy, PowerWhitelistBackend powerWhitelistBackend,
ContentResolver contentResolver, Bundle bundle) {
// The Example of intentDimsValue is: 35:{1:{1:{1:10013|}|}|}
final StatsDimensionsValue intentDimsValue =
bundle.getParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE);
@@ -119,24 +122,25 @@ public class AnomalyDetectionJobService extends JobService {
final boolean smartBatteryOn = Settings.Global.getInt(contentResolver,
Settings.Global.APP_STANDBY_ENABLED, ON) == ON;
final String packageName = batteryUtils.getPackageName(uid);
if (anomalyType == StatsManagerConfig.AnomalyType.EXCESSIVE_BG) {
// TODO(b/72385333): check battery percentage draining in batterystats
if (batteryUtils.isLegacyApp(packageName) && batteryUtils.isAppHeavilyUsed(
batteryStatsHelper, userManager, uid,
policy.excessiveBgDrainPercentage)) {
Log.e(TAG, "Excessive detected uid=" + uid);
batteryUtils.setForceAppStandby(uid, packageName,
AppOpsManager.MODE_IGNORED);
databaseManager.insertAnomaly(packageName, anomalyType,
smartBatteryOn
? AnomalyDatabaseHelper.State.AUTO_HANDLED
: AnomalyDatabaseHelper.State.NEW,
timeMs);
if (!powerWhitelistBackend.isSysWhitelisted(packageName)) {
if (anomalyType == StatsManagerConfig.AnomalyType.EXCESSIVE_BG) {
// TODO(b/72385333): check battery percentage draining in batterystats
if (batteryUtils.isLegacyApp(packageName) && batteryUtils.isAppHeavilyUsed(
batteryStatsHelper, userManager, uid,
policy.excessiveBgDrainPercentage)) {
Log.e(TAG, "Excessive detected uid=" + uid);
batteryUtils.setForceAppStandby(uid, packageName,
AppOpsManager.MODE_IGNORED);
databaseManager.insertAnomaly(uid, packageName, anomalyType,
smartBatteryOn
? AnomalyDatabaseHelper.State.AUTO_HANDLED
: AnomalyDatabaseHelper.State.NEW,
timeMs);
}
} else {
databaseManager.insertAnomaly(uid, packageName, anomalyType,
AnomalyDatabaseHelper.State.NEW, timeMs);
}
} else {
databaseManager.insertAnomaly(packageName, anomalyType,
AnomalyDatabaseHelper.State.NEW, timeMs);
}
} catch (NullPointerException | IndexOutOfBoundsException e) {
Log.e(TAG, "Parse stats dimensions value error.", e);

View File

@@ -19,6 +19,7 @@ package com.android.settings.fuelgauge.batterytip;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly;
@@ -33,11 +34,13 @@ public class AppInfo implements Comparable<AppInfo>, Parcelable {
*/
public final int anomalyType;
public final long screenOnTimeMs;
public final int uid;
private AppInfo(AppInfo.Builder builder) {
packageName = builder.mPackageName;
anomalyType = builder.mAnomalyType;
screenOnTimeMs = builder.mScreenOnTimeMs;
uid = builder.mUid;
}
@VisibleForTesting
@@ -45,6 +48,7 @@ public class AppInfo implements Comparable<AppInfo>, Parcelable {
packageName = in.readString();
anomalyType = in.readInt();
screenOnTimeMs = in.readLong();
uid = in.readInt();
}
@Override
@@ -62,6 +66,29 @@ public class AppInfo implements Comparable<AppInfo>, Parcelable {
dest.writeString(packageName);
dest.writeInt(anomalyType);
dest.writeLong(screenOnTimeMs);
dest.writeInt(uid);
}
@Override
public String toString() {
return "packageName=" + packageName + ",anomalyType=" + anomalyType + ",screenTime="
+ screenOnTimeMs;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AppInfo)) {
return false;
}
AppInfo other = (AppInfo) obj;
return anomalyType == other.anomalyType
&& uid == other.uid
&& screenOnTimeMs == other.screenOnTimeMs
&& TextUtils.equals(packageName, other.packageName);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@@ -78,6 +105,7 @@ public class AppInfo implements Comparable<AppInfo>, Parcelable {
private int mAnomalyType;
private String mPackageName;
private long mScreenOnTimeMs;
private int mUid;
public Builder setAnomalyType(int type) {
mAnomalyType = type;
@@ -94,6 +122,11 @@ public class AppInfo implements Comparable<AppInfo>, Parcelable {
return this;
}
public Builder setUid(int uid) {
mUid = uid;
return this;
}
public AppInfo build() {
return new AppInfo(this);
}

View File

@@ -18,12 +18,13 @@ package com.android.settings.fuelgauge.batterytip;
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
.ANOMALY_STATE;
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
.PACKAGE_NAME;
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
.ANOMALY_TYPE;
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
.PACKAGE_NAME;
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
.TIME_STAMP_MS;
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns.UID;
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.Tables.TABLE_ANOMALY;
import android.content.ContentValues;
@@ -60,15 +61,17 @@ public class BatteryDatabaseManager {
/**
* Insert an anomaly log to database.
* @param packageName the package name of the app
* @param type the type of the anomaly
* @param anomalyState the state of the anomaly
* @param timestampMs the time when it is happened
*
* @param packageName the package name of the app
* @param type the type of the anomaly
* @param anomalyState the state of the anomaly
* @param timestampMs the time when it is happened
*/
public synchronized void insertAnomaly(String packageName, int type, int anomalyState,
public synchronized void insertAnomaly(int uid, String packageName, int type, int anomalyState,
long timestampMs) {
try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
ContentValues values = new ContentValues();
values.put(UID, uid);
values.put(PACKAGE_NAME, packageName);
values.put(ANOMALY_TYPE, type);
values.put(ANOMALY_STATE, anomalyState);
@@ -83,7 +86,7 @@ public class BatteryDatabaseManager {
public synchronized List<AppInfo> queryAllAnomalies(long timestampMsAfter, int state) {
final List<AppInfo> appInfos = new ArrayList<>();
try (SQLiteDatabase db = mDatabaseHelper.getReadableDatabase()) {
final String[] projection = {PACKAGE_NAME, ANOMALY_TYPE};
final String[] projection = {PACKAGE_NAME, ANOMALY_TYPE, UID};
final String orderBy = AnomalyDatabaseHelper.AnomalyColumns.TIME_STAMP_MS + " DESC";
try (Cursor cursor = db.query(TABLE_ANOMALY, projection,
@@ -94,6 +97,7 @@ public class BatteryDatabaseManager {
AppInfo appInfo = new AppInfo.Builder()
.setPackageName(cursor.getString(cursor.getColumnIndex(PACKAGE_NAME)))
.setAnomalyType(cursor.getInt(cursor.getColumnIndex(ANOMALY_TYPE)))
.setUid(cursor.getInt(cursor.getColumnIndex(UID)))
.build();
appInfos.add(appInfo);
}

View File

@@ -76,7 +76,7 @@ public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.View
final AppInfo app = mHighUsageAppList.get(position);
holder.appIcon.setImageDrawable(
Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, app.packageName,
UserHandle.myUserId()));
UserHandle.getUserId(app.uid)));
holder.appName.setText(Utils.getApplicationLabel(mContext, app.packageName));
if (app.screenOnTimeMs != 0) {
holder.appTime.setText(StringUtil.formatElapsedTime(mContext, app.screenOnTimeMs, false));

View File

@@ -73,6 +73,7 @@ public class HighUsageDetector implements BatteryTipDetector {
BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
BatteryStats.STATS_SINCE_CHARGED);
mHighUsageAppList.add(new AppInfo.Builder()
.setUid(batterySipper.getUid())
.setPackageName(
mBatteryUtils.getPackageName(batterySipper.getUid()))
.setScreenOnTimeMs(foregroundTimeMs)

View File

@@ -158,4 +158,9 @@ public abstract class BatteryTip implements Comparable<BatteryTip>, Parcelable {
public int compareTo(BatteryTip o) {
return TIP_ORDER.get(mType) - TIP_ORDER.get(o.mType);
}
@Override
public String toString() {
return "type=" + mType + " state=" + mState;
}
}

View File

@@ -86,6 +86,19 @@ public class HighUsageTip extends BatteryTip {
return mHighUsageAppList;
}
@Override
public String toString() {
final StringBuilder stringBuilder = new StringBuilder(super.toString());
stringBuilder.append(" {");
for (int i = 0, size = mHighUsageAppList.size(); i < size; i++) {
final AppInfo appInfo = mHighUsageAppList.get(i);
stringBuilder.append(" " + appInfo.toString() + " ");
}
stringBuilder.append('}');
return stringBuilder.toString();
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public BatteryTip createFromParcel(Parcel in) {
return new HighUsageTip(in);

View File

@@ -97,6 +97,19 @@ public class RestrictAppTip extends BatteryTip {
return mRestrictAppList;
}
@Override
public String toString() {
final StringBuilder stringBuilder = new StringBuilder(super.toString());
stringBuilder.append(" {");
for (int i = 0, size = mRestrictAppList.size(); i < size; i++) {
final AppInfo appInfo = mRestrictAppList.get(i);
stringBuilder.append(" " + appInfo.toString() + " ");
}
stringBuilder.append('}');
return stringBuilder.toString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);

View File

@@ -22,7 +22,8 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.Immutable;
import com.android.internal.util.Preconditions;
import java.util.Objects;
/**
* Specifies a setting that is being injected into Settings &gt; Location &gt; Location services.
@@ -65,32 +66,19 @@ class InjectedSetting {
*/
public final String settingsActivity;
private InjectedSetting(String packageName, String className,
String title, int iconId, UserHandle userHandle, String settingsActivity) {
this.packageName = Preconditions.checkNotNull(packageName, "packageName");
this.className = Preconditions.checkNotNull(className, "className");
this.title = Preconditions.checkNotNull(title, "title");
this.iconId = iconId;
this.mUserHandle = userHandle;
this.settingsActivity = Preconditions.checkNotNull(settingsActivity);
}
/**
* Returns a new instance, or null.
* The user restriction associated with this setting.
*/
public static InjectedSetting newInstance(String packageName, String className,
String title, int iconId, UserHandle userHandle, String settingsActivity) {
if (packageName == null || className == null ||
TextUtils.isEmpty(title) || TextUtils.isEmpty(settingsActivity)) {
if (Log.isLoggable(SettingsInjector.TAG, Log.WARN)) {
Log.w(SettingsInjector.TAG, "Illegal setting specification: package="
+ packageName + ", class=" + className
+ ", title=" + title + ", settingsActivity=" + settingsActivity);
}
return null;
}
return new InjectedSetting(packageName, className, title, iconId, userHandle,
settingsActivity);
public final String userRestriction;
private InjectedSetting(Builder builder) {
this.packageName = builder.mPackageName;
this.className = builder.mClassName;
this.title = builder.mTitle;
this.iconId = builder.mIconId;
this.mUserHandle = builder.mUserHandle;
this.settingsActivity = builder.mSettingsActivity;
this.userRestriction = builder.mUserRestriction;
}
@Override
@@ -102,6 +90,7 @@ class InjectedSetting {
", iconId=" + iconId +
", userId=" + mUserHandle.getIdentifier() +
", settingsActivity='" + settingsActivity + '\'' +
", userRestriction='" + userRestriction +
'}';
}
@@ -121,10 +110,13 @@ class InjectedSetting {
InjectedSetting that = (InjectedSetting) o;
return packageName.equals(that.packageName) && className.equals(that.className)
&& title.equals(that.title) && iconId == that.iconId
&& mUserHandle.equals(that.mUserHandle)
&& settingsActivity.equals(that.settingsActivity);
return Objects.equals(packageName, that.packageName)
&& Objects.equals(className, that.className)
&& Objects.equals(title, that.title)
&& Objects.equals(iconId, that.iconId)
&& Objects.equals(mUserHandle, that.mUserHandle)
&& Objects.equals(settingsActivity, that.settingsActivity)
&& Objects.equals(userRestriction, that.userRestriction);
}
@Override
@@ -133,8 +125,67 @@ class InjectedSetting {
result = 31 * result + className.hashCode();
result = 31 * result + title.hashCode();
result = 31 * result + iconId;
result = 31 * result + mUserHandle.hashCode();
result = 31 * result + (mUserHandle == null ? 0 : mUserHandle.hashCode());
result = 31 * result + settingsActivity.hashCode();
result = 31 * result + (userRestriction == null ? 0 : userRestriction.hashCode());
return result;
}
public static class Builder {
private String mPackageName;
private String mClassName;
private String mTitle;
private int mIconId;
private UserHandle mUserHandle;
private String mSettingsActivity;
private String mUserRestriction;
public Builder setPackageName(String packageName) {
mPackageName = packageName;
return this;
}
public Builder setClassName(String className) {
mClassName = className;
return this;
}
public Builder setTitle(String title) {
mTitle = title;
return this;
}
public Builder setIconId(int iconId) {
mIconId = iconId;
return this;
}
public Builder setUserHandle(UserHandle userHandle) {
mUserHandle = userHandle;
return this;
}
public Builder setSettingsActivity(String settingsActivity) {
mSettingsActivity = settingsActivity;
return this;
}
public Builder setUserRestriction(String userRestriction) {
mUserRestriction = userRestriction;
return this;
}
public InjectedSetting build() {
if (mPackageName == null || mClassName == null || TextUtils.isEmpty(mTitle)
|| TextUtils.isEmpty(mSettingsActivity)) {
if (Log.isLoggable(SettingsInjector.TAG, Log.WARN)) {
Log.w(SettingsInjector.TAG, "Illegal setting specification: package="
+ mPackageName + ", class=" + mClassName
+ ", title=" + mTitle + ", settingsActivity=" + mSettingsActivity);
}
return null;
}
return new InjectedSetting(this);
}
}
}

View File

@@ -25,6 +25,7 @@ import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
import com.android.settings.widget.RestrictedAppPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
@@ -88,7 +89,13 @@ public class LocationServicePreferenceController extends LocationBasePreferenceC
@Override
public void updateState(Preference preference) {
mCategoryLocationServices.removeAll();
LocationSettings.addPreferencesSorted(getLocationServices(), mCategoryLocationServices);
final List<Preference> prefs = getLocationServices();
for (Preference pref : prefs) {
if (pref instanceof RestrictedAppPreference) {
((RestrictedAppPreference) pref).checkRestrictionAndSetDisabled();
}
}
LocationSettings.addPreferencesSorted(prefs, mCategoryLocationServices);
}
@Override

View File

@@ -20,6 +20,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -37,11 +38,14 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Xml;
import com.android.settings.widget.AppPreference;
import com.android.settings.widget.RestrictedAppPreference;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -215,12 +219,21 @@ class SettingsInjector {
sa.getResourceId(android.R.styleable.SettingInjectorService_icon, 0);
final String settingsActivity =
sa.getString(android.R.styleable.SettingInjectorService_settingsActivity);
final String userRestriction = sa.getString(
android.R.styleable.SettingInjectorService_userRestriction);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "parsed title: " + title + ", iconId: " + iconId
+ ", settingsActivity: " + settingsActivity);
}
return InjectedSetting.newInstance(packageName, className,
title, iconId, userHandle, settingsActivity);
return new InjectedSetting.Builder()
.setPackageName(packageName)
.setClassName(className)
.setTitle(title)
.setIconId(iconId)
.setUserHandle(userHandle)
.setSettingsActivity(settingsActivity)
.setUserRestriction(userRestriction)
.build();
} finally {
sa.recycle();
}
@@ -290,15 +303,26 @@ class SettingsInjector {
*/
private Preference addServiceSetting(Context prefContext, List<Preference> prefs,
InjectedSetting info) {
PackageManager pm = mContext.getPackageManager();
Drawable appIcon = pm.getDrawable(info.packageName, info.iconId, null);
Drawable icon = pm.getUserBadgedIcon(appIcon, info.mUserHandle);
Preference pref = new AppPreference(prefContext);
final PackageManager pm = mContext.getPackageManager();
Drawable appIcon = null;
try {
final PackageItemInfo itemInfo = new PackageItemInfo();
itemInfo.icon = info.iconId;
itemInfo.packageName = info.packageName;
final ApplicationInfo appInfo = pm.getApplicationInfo(info.packageName,
PackageManager.GET_META_DATA);
appIcon = IconDrawableFactory.newInstance(mContext)
.getBadgedIcon(itemInfo, appInfo, info.mUserHandle.getIdentifier());
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Can't get ApplicationInfo for " + info.packageName, e);
}
Preference pref = TextUtils.isEmpty(info.userRestriction)
? new AppPreference(prefContext)
: new RestrictedAppPreference(prefContext, info.userRestriction);
pref.setTitle(info.title);
pref.setSummary(null);
pref.setIcon(icon);
pref.setIcon(appIcon);
pref.setOnPreferenceClickListener(new ServiceSettingClickedListener(info));
prefs.add(pref);
return pref;
}

View File

@@ -40,7 +40,8 @@ public class ZenModeBehaviorSettings extends ZenModeSettingsBase implements Inde
Lifecycle lifecycle) {
List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new ZenModeAlarmsPreferenceController(context, lifecycle));
controllers.add(new ZenModeMediaSystemOtherPreferenceController(context, lifecycle));
controllers.add(new ZenModeMediaPreferenceController(context, lifecycle));
controllers.add(new ZenModeSystemPreferenceController(context, lifecycle));
controllers.add(new ZenModeEventsPreferenceController(context, lifecycle));
controllers.add(new ZenModeRemindersPreferenceController(context, lifecycle));
controllers.add(new ZenModeMessagesPreferenceController(context, lifecycle));
@@ -85,7 +86,7 @@ public class ZenModeBehaviorSettings extends ZenModeSettingsBase implements Inde
public List<String> getNonIndexableKeys(Context context) {
final List<String> keys = super.getNonIndexableKeys(context);
keys.add(ZenModeAlarmsPreferenceController.KEY);
keys.add(ZenModeMediaSystemOtherPreferenceController.KEY);
keys.add(ZenModeMediaPreferenceController.KEY);
keys.add(ZenModeEventsPreferenceController.KEY);
keys.add(ZenModeRemindersPreferenceController.KEY);
keys.add(ZenModeMessagesPreferenceController.KEY);

View File

@@ -25,13 +25,13 @@ import android.util.Log;
import com.android.settingslib.core.lifecycle.Lifecycle;
public class ZenModeMediaSystemOtherPreferenceController extends AbstractZenModePreferenceController
public class ZenModeMediaPreferenceController extends AbstractZenModePreferenceController
implements Preference.OnPreferenceChangeListener {
protected static final String KEY = "zen_mode_media";
private final ZenModeBackend mBackend;
public ZenModeMediaSystemOtherPreferenceController(Context context, Lifecycle lifecycle) {
public ZenModeMediaPreferenceController(Context context, Lifecycle lifecycle) {
super(context, KEY, lifecycle);
mBackend = ZenModeBackend.getInstance(context);
}
@@ -63,7 +63,7 @@ public class ZenModeMediaSystemOtherPreferenceController extends AbstractZenMode
default:
pref.setEnabled(true);
pref.setChecked(mBackend.isPriorityCategoryEnabled(
Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER));
Policy.PRIORITY_CATEGORY_MEDIA));
}
}
@@ -71,9 +71,9 @@ public class ZenModeMediaSystemOtherPreferenceController extends AbstractZenMode
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean allowMedia = (Boolean) newValue;
if (ZenModeSettingsBase.DEBUG) {
Log.d(TAG, "onPrefChange allowMediaSystemOther=" + allowMedia);
Log.d(TAG, "onPrefChange allowMedia=" + allowMedia);
}
mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, allowMedia);
mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_MEDIA, allowMedia);
return true;
}
}

View File

@@ -77,9 +77,11 @@ public class ZenModeSettings extends ZenModeSettingsBase {
mContext = context;
}
// these should match NotificationManager.Policy#ALL_PRIORITY_CATEGORIES
private static final int[] ALL_PRIORITY_CATEGORIES = {
Policy.PRIORITY_CATEGORY_ALARMS,
Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
Policy.PRIORITY_CATEGORY_MEDIA,
Policy.PRIORITY_CATEGORY_SYSTEM,
Policy.PRIORITY_CATEGORY_REMINDERS,
Policy.PRIORITY_CATEGORY_EVENTS,
Policy.PRIORITY_CATEGORY_MESSAGES,
@@ -104,10 +106,10 @@ public class ZenModeSettings extends ZenModeSettingsBase {
return mContext.getString(R.string.zen_mode_behavior_total_silence);
}
// only alarms and media/system can bypass dnd
// only alarms and media can bypass dnd
if (numCategories == 2 &&
isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_ALARMS) &&
isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER)) {
isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_MEDIA)) {
return mContext.getString(R.string.zen_mode_behavior_alarms_only);
}
@@ -164,9 +166,12 @@ public class ZenModeSettings extends ZenModeSettingsBase {
if (isCategoryEnabled(policy, category)) {
if (category == Policy.PRIORITY_CATEGORY_ALARMS) {
enabledCategories.add(mContext.getString(R.string.zen_mode_alarms));
} else if (category == Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) {
} else if (category == Policy.PRIORITY_CATEGORY_MEDIA) {
enabledCategories.add(mContext.getString(
R.string.zen_mode_media_system_other));
R.string.zen_mode_media));
} else if (category == Policy.PRIORITY_CATEGORY_SYSTEM) {
enabledCategories.add(mContext.getString(
R.string.zen_mode_system));
} else if (category == Policy.PRIORITY_CATEGORY_REMINDERS) {
enabledCategories.add(mContext.getString(R.string.zen_mode_reminders));
} else if (category == Policy.PRIORITY_CATEGORY_EVENTS) {

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import android.app.NotificationManager.Policy;
import android.content.Context;
import android.provider.Settings;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.util.Log;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.core.lifecycle.Lifecycle;
public class ZenModeSystemPreferenceController extends
AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener {
protected static final String KEY = "zen_mode_system";
public ZenModeSystemPreferenceController(Context context, Lifecycle lifecycle) {
super(context, KEY, lifecycle);
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
SwitchPreference pref = (SwitchPreference) preference;
switch (getZenMode()) {
case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
pref.setEnabled(false);
pref.setChecked(false);
break;
case Settings.Global.ZEN_MODE_ALARMS:
pref.setEnabled(false);
pref.setChecked(false);
break;
default:
pref.setEnabled(true);
pref.setChecked(mBackend.isPriorityCategoryEnabled(
Policy.PRIORITY_CATEGORY_SYSTEM));
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean allowSystem = (Boolean) newValue;
if (ZenModeSettingsBase.DEBUG) {
Log.d(TAG, "onPrefChange allowSystem=" + allowSystem);
}
mMetricsFeatureProvider.action(mContext, MetricsProto.MetricsEvent.ACTION_ZEN_ALLOW_SYSTEM,
allowSystem);
mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_SYSTEM, allowSystem);
return true;
}
}

View File

@@ -22,10 +22,10 @@ import android.support.annotation.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.users.AddUserWhenLockedPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.LockScreenNotificationPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.users.AddUserWhenLockedPreferenceController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -130,6 +130,7 @@ public class LockscreenDashboardFragment extends DashboardFragment
public List<String> getNonIndexableKeys(Context context) {
final List<String> niks = super.getNonIndexableKeys(context);
niks.add(KEY_ADD_USER_FROM_LOCK_SCREEN);
niks.add(KEY_LOCK_SCREEN_NOTIFICATON_WORK_PROFILE);
return niks;
}
};

View File

@@ -41,6 +41,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.ContactsContract;
import android.provider.SearchIndexableResource;
import android.provider.Settings.Global;
import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
@@ -68,7 +69,6 @@ import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedPreference;
@@ -148,9 +148,6 @@ public class UserSettings extends SettingsPreferenceFragment
private EditUserInfoController mEditUserInfoController = new EditUserInfoController();
private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController;
private AutoSyncDataPreferenceController mAutoSyncDataPreferenceController;
private AutoSyncPersonalDataPreferenceController mAutoSyncPersonalDataPreferenceController;
private AutoSyncWorkDataPreferenceController mAutoSyncWorkDataPreferenceController;
// A place to cache the generated default avatar
private Drawable mDefaultIconDrawable;
@@ -159,15 +156,15 @@ public class UserSettings extends SettingsPreferenceFragment
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_UPDATE_LIST:
updateUserList();
break;
case MESSAGE_SETUP_USER:
onUserCreated(msg.arg1);
break;
case MESSAGE_CONFIG_USER:
onManageUserClicked(msg.arg1, true);
break;
case MESSAGE_UPDATE_LIST:
updateUserList();
break;
case MESSAGE_SETUP_USER:
onUserCreated(msg.arg1);
break;
case MESSAGE_CONFIG_USER:
onManageUserClicked(msg.arg1, true);
break;
}
}
};
@@ -203,18 +200,9 @@ public class UserSettings extends SettingsPreferenceFragment
final Context context = getActivity();
mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController(
context, KEY_ADD_USER_WHEN_LOCKED, getLifecycle());
mAutoSyncDataPreferenceController = new AutoSyncDataPreferenceController(context, this);
mAutoSyncPersonalDataPreferenceController =
new AutoSyncPersonalDataPreferenceController(context, this);
mAutoSyncWorkDataPreferenceController =
new AutoSyncWorkDataPreferenceController(context, this);
final PreferenceScreen screen = getPreferenceScreen();
mAddUserWhenLockedPreferenceController.displayPreference(screen);
mAutoSyncDataPreferenceController.displayPreference(screen);
mAutoSyncPersonalDataPreferenceController.displayPreference(screen);
mAutoSyncWorkDataPreferenceController.displayPreference(screen);
screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey())
.setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController);
@@ -275,22 +263,10 @@ public class UserSettings extends SettingsPreferenceFragment
}
final PreferenceScreen screen = getPreferenceScreen();
if (mAutoSyncDataPreferenceController.isAvailable()) {
mAutoSyncDataPreferenceController.updateState(screen.findPreference(
mAutoSyncDataPreferenceController.getPreferenceKey()));
}
if (mAddUserWhenLockedPreferenceController.isAvailable()) {
mAddUserWhenLockedPreferenceController.updateState(screen.findPreference(
mAddUserWhenLockedPreferenceController.getPreferenceKey()));
}
if (mAutoSyncPersonalDataPreferenceController.isAvailable()) {
mAutoSyncPersonalDataPreferenceController.updateState(screen.findPreference(
mAutoSyncPersonalDataPreferenceController.getPreferenceKey()));
}
if (mAutoSyncWorkDataPreferenceController.isAvailable()) {
mAutoSyncWorkDataPreferenceController.updateState(screen.findPreference(
mAutoSyncWorkDataPreferenceController.getPreferenceKey()));
}
if (mShouldUpdateUserList) {
mUserCaps.updateAddUserCapabilities(getActivity());
@@ -330,20 +306,6 @@ public class UserSettings extends SettingsPreferenceFragment
super.startActivityForResult(intent, requestCode);
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (mAutoSyncDataPreferenceController.handlePreferenceTreeClick(preference)) {
return true;
}
if (mAutoSyncPersonalDataPreferenceController.handlePreferenceTreeClick(preference)) {
return true;
}
if (mAutoSyncWorkDataPreferenceController.handlePreferenceTreeClick(preference)) {
return true;
}
return super.onPreferenceTreeClick(preference);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
int pos = 0;
@@ -439,16 +401,16 @@ public class UserSettings extends SettingsPreferenceFragment
synchronized (mUserLock) {
if (mRemovingUserId == -1 && !mAddingUser) {
switch (userType) {
case USER_TYPE_USER:
showDialog(DIALOG_ADD_USER);
break;
case USER_TYPE_RESTRICTED_PROFILE:
if (hasLockscreenSecurity()) {
addUserNow(USER_TYPE_RESTRICTED_PROFILE);
} else {
showDialog(DIALOG_NEED_LOCKSCREEN);
}
break;
case USER_TYPE_USER:
showDialog(DIALOG_ADD_USER);
break;
case USER_TYPE_RESTRICTED_PROFILE:
if (hasLockscreenSecurity()) {
addUserNow(USER_TYPE_RESTRICTED_PROFILE);
} else {
showDialog(DIALOG_NEED_LOCKSCREEN);
}
break;
}
}
}
@@ -557,9 +519,9 @@ public class UserSettings extends SettingsPreferenceFragment
}
case DIALOG_USER_CANNOT_MANAGE:
return new AlertDialog.Builder(context)
.setMessage(R.string.user_cannot_manage_message)
.setPositiveButton(android.R.string.ok, null)
.create();
.setMessage(R.string.user_cannot_manage_message)
.setPositiveButton(android.R.string.ok, null)
.create();
case DIALOG_ADD_USER: {
final SharedPreferences preferences = getActivity().getPreferences(
Context.MODE_PRIVATE);
@@ -571,55 +533,56 @@ public class UserSettings extends SettingsPreferenceFragment
final int userType = dialogId == DIALOG_ADD_USER
? USER_TYPE_USER : USER_TYPE_RESTRICTED_PROFILE;
Dialog dlg = new AlertDialog.Builder(context)
.setTitle(R.string.user_add_user_title)
.setMessage(messageResId)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
addUserNow(userType);
if (!longMessageDisplayed) {
preferences.edit().putBoolean(
KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, true).apply();
}
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
.setTitle(R.string.user_add_user_title)
.setMessage(messageResId)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
addUserNow(userType);
if (!longMessageDisplayed) {
preferences.edit().putBoolean(
KEY_ADD_USER_LONG_MESSAGE_DISPLAYED,
true).apply();
}
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
return dlg;
}
case DIALOG_SETUP_USER: {
Dialog dlg = new AlertDialog.Builder(context)
.setTitle(R.string.user_setup_dialog_title)
.setMessage(R.string.user_setup_dialog_message)
.setPositiveButton(R.string.user_setup_button_setup_now,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
switchUserNow(mAddedUserId);
}
})
.setNegativeButton(R.string.user_setup_button_setup_later, null)
.create();
.setTitle(R.string.user_setup_dialog_title)
.setMessage(R.string.user_setup_dialog_message)
.setPositiveButton(R.string.user_setup_button_setup_now,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
switchUserNow(mAddedUserId);
}
})
.setNegativeButton(R.string.user_setup_button_setup_later, null)
.create();
return dlg;
}
case DIALOG_SETUP_PROFILE: {
Dialog dlg = new AlertDialog.Builder(context)
.setMessage(R.string.user_setup_profile_dialog_message)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
switchUserNow(mAddedUserId);
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
.setMessage(R.string.user_setup_profile_dialog_message)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
switchUserNow(mAddedUserId);
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
return dlg;
}
case DIALOG_CHOOSE_USER_TYPE: {
List<HashMap<String, String>> data = new ArrayList<HashMap<String,String>>();
HashMap<String,String> addUserItem = new HashMap<String,String>();
List<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();
HashMap<String, String> addUserItem = new HashMap<String, String>();
addUserItem.put(KEY_TITLE, getString(R.string.user_add_user_item_title));
addUserItem.put(KEY_SUMMARY, getString(R.string.user_add_user_item_summary));
HashMap<String,String> addProfileItem = new HashMap<String,String>();
HashMap<String, String> addProfileItem = new HashMap<String, String>();
addProfileItem.put(KEY_TITLE, getString(R.string.user_add_profile_item_title));
addProfileItem.put(KEY_SUMMARY, getString(R.string.user_add_profile_item_summary));
data.add(addUserItem);
@@ -892,7 +855,7 @@ public class UserSettings extends SettingsPreferenceFragment
// Add a virtual Guest user for guest defaults
UserPreference pref = new UserPreference(getPrefContext(), null,
UserPreference.USERID_GUEST_DEFAULTS,
mUserCaps.mIsAdmin && voiceCapable? this : null /* settings icon handler */,
mUserCaps.mIsAdmin && voiceCapable ? this : null /* settings icon handler */,
null /* delete icon handler */);
pref.setTitle(R.string.user_guest);
pref.setIcon(getEncircledDefaultIcon());
@@ -1062,20 +1025,20 @@ public class UserSettings extends SettingsPreferenceFragment
if (v.getTag() instanceof UserPreference) {
int userId = ((UserPreference) v.getTag()).getUserId();
switch (v.getId()) {
case UserPreference.DELETE_ID:
final EnforcedAdmin removeDisallowedAdmin =
RestrictedLockUtils.checkIfRestrictionEnforced(getContext(),
UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId());
if (removeDisallowedAdmin != null) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
removeDisallowedAdmin);
} else {
onRemoveUserClicked(userId);
}
break;
case UserPreference.SETTINGS_ID:
onManageUserClicked(userId, false);
break;
case UserPreference.DELETE_ID:
final EnforcedAdmin removeDisallowedAdmin =
RestrictedLockUtils.checkIfRestrictionEnforced(getContext(),
UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId());
if (removeDisallowedAdmin != null) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
removeDisallowedAdmin);
} else {
onRemoveUserClicked(userId);
}
break;
case UserPreference.SETTINGS_ID:
onManageUserClicked(userId, false);
break;
}
}
}
@@ -1107,8 +1070,9 @@ public class UserSettings extends SettingsPreferenceFragment
* Returns a default user icon (as a {@link Bitmap}) for the given user.
*
* Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
*
* @param resources resources object to fetch the user icon.
* @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
* @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
*/
private static Bitmap getDefaultUserIconAsBitmap(Resources resources, int userId) {
Bitmap bitmap = null;
@@ -1125,6 +1089,7 @@ public class UserSettings extends SettingsPreferenceFragment
/**
* Assign the default photo to user with {@paramref userId}
*
* @param context used to get the {@link UserManager}
* @param userId used to get the icon bitmap
* @return true if assign photo successfully, false if failed
@@ -1161,7 +1126,8 @@ public class UserSettings extends SettingsPreferenceFragment
um.setUserIcon(userId, icon);
try {
avatarDataStream.close();
} catch (IOException ioe) { }
} catch (IOException ioe) {
}
}
private static class SummaryProvider implements SummaryLoader.SummaryProvider {
@@ -1178,49 +1144,49 @@ public class UserSettings extends SettingsPreferenceFragment
public void setListening(boolean listening) {
if (listening) {
UserInfo info = mContext.getSystemService(UserManager.class).getUserInfo(
UserHandle.myUserId());
UserHandle.myUserId());
mSummaryLoader.setSummary(this, mContext.getString(R.string.users_summary,
info.name));
info.name));
}
}
}
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY =
new SummaryLoader.SummaryProviderFactory() {
@Override
public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
SummaryLoader summaryLoader) {
return new SummaryProvider(activity, summaryLoader);
}
};
(activity, summaryLoader) -> new SummaryProvider(activity, summaryLoader);
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
boolean enabled) {
final List<SearchIndexableRaw> result = new ArrayList<>();
final UserCapabilities userCaps = UserCapabilities.create(context);
if (!userCaps.mEnabled) {
return result;
}
final Resources res = context.getResources();
SearchIndexableRaw data = new SearchIndexableRaw(context);
data.title = res.getString(R.string.user_settings_title);
data.key = "users_settings";
data.screenTitle = res.getString(R.string.user_settings_title);
result.add(data);
if (userCaps.mCanAddUser || userCaps.mDisallowAddUserSetByAdmin) {
data = new SearchIndexableRaw(context);
data.title = res.getString(userCaps.mCanAddRestrictedProfile ?
R.string.user_add_user_or_profile_menu
: R.string.user_add_user_menu);
data.screenTitle = res.getString(R.string.user_settings_title);
data.key = "user_settings_add_users";
result.add(data);
}
return result;
@Override
protected boolean isPageSearchEnabled(Context context) {
final UserCapabilities userCaps = UserCapabilities.create(context);
return userCaps.mEnabled;
}
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
boolean enabled) {
final List<SearchIndexableResource> index = new ArrayList<>();
// Append the rest of the settings
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.user_settings;
index.add(sir);
return index;
}
@Override
public List<String> getNonIndexableKeysFromXml(Context context, int xmlResId) {
final List<String> niks = super.getNonIndexableKeysFromXml(context, xmlResId);
new AddUserWhenLockedPreferenceController(
context, KEY_ADD_USER_WHEN_LOCKED, null /* lifecycle */)
.updateNonIndexableKeys(niks);
new AutoSyncDataPreferenceController(context, null /* parent */)
.updateNonIndexableKeys(niks);
new AutoSyncPersonalDataPreferenceController(context, null /* parent */)
.updateNonIndexableKeys(niks);
new AutoSyncWorkDataPreferenceController(context, null /* parent */)
.updateNonIndexableKeys(niks);
return niks;
}
};

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.widget;
import android.content.Context;
import android.os.UserHandle;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceViewHolder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import com.android.settings.R;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreferenceHelper;
/**
* {@link AppPreference} that implements user restriction utilities using
* {@link com.android.settingslib.RestrictedPreferenceHelper}.
* Used to show policy transparency on {@link AppPreference}.
*/
public class RestrictedAppPreference extends AppPreference {
private RestrictedPreferenceHelper mHelper;
private String userRestriction;
public RestrictedAppPreference(Context context) {
super(context);
initialize(null, null);
}
public RestrictedAppPreference(Context context, String userRestriction) {
super(context);
initialize(null, userRestriction);
}
public RestrictedAppPreference(Context context, AttributeSet attrs, String userRestriction) {
super(context, attrs);
initialize(attrs, userRestriction);
}
private void initialize(AttributeSet attrs, String userRestriction) {
setWidgetLayoutResource(R.layout.restricted_icon);
mHelper = new RestrictedPreferenceHelper(getContext(), this, attrs);
this.userRestriction = userRestriction;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
if (restrictedIcon != null) {
restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
}
}
@Override
public void performClick() {
if (!mHelper.performClick()) {
super.performClick();
}
}
@Override
public void setEnabled(boolean enabled) {
if (isDisabledByAdmin() && enabled) {
return;
}
super.setEnabled(enabled);
}
public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
if (mHelper.setDisabledByAdmin(admin)) {
notifyChanged();
}
}
public boolean isDisabledByAdmin() {
return mHelper.isDisabledByAdmin();
}
public void useAdminDisabledSummary(boolean useSummary) {
mHelper.useAdminDisabledSummary(useSummary);
}
@Override
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
mHelper.onAttachedToHierarchy();
super.onAttachedToHierarchy(preferenceManager);
}
public void checkRestrictionAndSetDisabled() {
if (TextUtils.isEmpty(userRestriction)) {
return;
}
mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
}
public void checkRestrictionAndSetDisabled(String userRestriction) {
mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
}
public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
}
}

View File

@@ -24,3 +24,4 @@ com.android.settings.wifi.SavedAccessPointsWifiSettings
com.android.settings.notification.ZenModeEventRuleSettings
com.android.settings.notification.ZenModeScheduleRuleSettings
com.android.settings.fuelgauge.RestrictedAppDetails
com.android.settings.datetime.timezone.TimeZoneSettings

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.app.Activity;
import android.content.Context;
import android.support.v7.preference.Preference;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
public class BaseTimeZonePreferenceControllerTest {
private Activity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.setupActivity(Activity.class);
}
@Test
public void handlePreferenceTreeClick_correctKey_triggerOnClickListener() {
String prefKey = "key1";
TestClickListener clickListener = new TestClickListener();
TestPreference preference = new TestPreference(mActivity, prefKey);
TestPreferenceController controller = new TestPreferenceController(mActivity, prefKey);
controller.setOnClickListener(clickListener);
controller.handlePreferenceTreeClick(preference);
assertThat(clickListener.isClicked()).isTrue();
}
@Test
public void handlePreferenceTreeClick_wrongKey_triggerOnClickListener() {
String prefKey = "key1";
TestClickListener clickListener = new TestClickListener();
TestPreference preference = new TestPreference(mActivity, "wrong_key");
TestPreferenceController controller = new TestPreferenceController(mActivity, prefKey);
controller.setOnClickListener(clickListener);
controller.handlePreferenceTreeClick(preference);
assertThat(clickListener.isClicked()).isFalse();
}
private static class TestPreferenceController extends BaseTimeZonePreferenceController {
private final Preference mTestPreference;
public TestPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mTestPreference = new Preference(context);
mTestPreference.setKey(preferenceKey);
}
}
private static class TestPreference extends Preference {
public TestPreference(Context context, String preferenceKey) {
super(context);
setKey(preferenceKey);
}
}
private static class TestClickListener implements OnPreferenceClickListener {
private boolean isClicked = false;
@Override
public void onClick() {
isClicked = true;
}
public boolean isClicked() {
return isClicked;
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.app.Activity;
import android.icu.util.TimeZone;
import android.support.v7.preference.Preference;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
public class FixedOffsetPreferenceControllerTest {
private Activity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.setupActivity(Activity.class);
}
@Test
public void updateState_matchTimeZoneSummary() {
TimeZoneInfo fixedOffsetZone = new TimeZoneInfo.Builder(
TimeZone.getFrozenTimeZone("Etc/GMT-8"))
.setExemplarLocation("Los Angeles")
.setGmtOffset("GMT-08:00")
.setItemId(0)
.build();
Preference preference = new Preference(mActivity);
FixedOffsetPreferenceController controller = new FixedOffsetPreferenceController(mActivity);
controller.setTimeZoneInfo(fixedOffsetZone);
controller.updateState(preference);
assertThat(preference.getSummary()).isEqualTo("GMT-08:00");
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.app.Activity;
import android.support.v7.preference.Preference;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
public class RegionPreferenceControllerTest {
private Activity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.setupActivity(Activity.class);
}
@Test
public void updateState_matchCountryName() {
Preference preference = new Preference(mActivity);
RegionPreferenceController controller = new RegionPreferenceController(mActivity);
controller.setRegionId("US");
controller.updateState(preference);
assertThat(controller.getSummary()).isEqualTo("United States");
assertThat(preference.getSummary()).isEqualTo("United States");
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.app.Activity;
import android.icu.util.TimeZone;
import android.support.v7.preference.Preference;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
public class RegionZonePreferenceControllerTest {
private Activity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.setupActivity(Activity.class);
}
@Test
public void updateState_matchTimeZoneName() {
TimeZoneInfo tzInfo = new TimeZoneInfo.Builder(
TimeZone.getFrozenTimeZone("America/Los_Angeles"))
.setGenericName("Pacific Time")
.setStandardName("Pacific Standard Time")
.setDaylightName("Pacific Daylight Time")
.setExemplarLocation("Los Angeles")
.setGmtOffset("GMT-08:00")
.setItemId(0)
.build();
Preference preference = new Preference(mActivity);
RegionZonePreferenceController controller = new RegionZonePreferenceController(mActivity);
controller.setTimeZoneInfo(tzInfo);
controller.setClickable(false);
controller.updateState(preference);
String expectedSummary = "Los Angeles (GMT-08:00)";
assertThat(controller.getSummary().toString()).isEqualTo(expectedSummary);
assertThat(preference.getSummary().toString()).isEqualTo(expectedSummary);
assertThat(preference.isEnabled()).isFalse();
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import android.support.v7.preference.Preference;
import com.android.settings.datetime.timezone.TimeZoneInfo.Formatter;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import java.util.Date;
import java.util.Locale;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.spy;
@RunWith(SettingsRobolectricTestRunner.class)
public class TimeZoneInfoPreferenceControllerTest {
@Test
public void updateState_matchExpectedFormattedText() {
Date now = new Date(0L); // 00:00 1/1/1970
Formatter formatter = new Formatter(Locale.US, now);
TimeZoneInfo timeZoneInfo = formatter.format("America/Los_Angeles");
TimeZoneInfoPreferenceController controller =
new TimeZoneInfoPreferenceController(RuntimeEnvironment.application, now);
controller.setTimeZoneInfo(timeZoneInfo);
Preference preference = spy(new Preference(RuntimeEnvironment.application));
controller.updateState(preference);
assertEquals("Uses Pacific Time (GMT-08:00). "
+ "Pacific Daylight Time starts on April 26, 1970.",
preference.getTitle().toString());
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import com.android.settings.datetime.timezone.model.TimeZoneData;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {
SettingsShadowResources.class,
SettingsShadowResources.SettingsShadowTheme.class,
})
public class TimeZoneSettingsTest {
@Test
public void findRegionIdForTzId_matchExpectedCountry() {
String tzId = "Unknown/Secret_City";
TimeZoneData timeZoneData = mock(TimeZoneData.class);
when(timeZoneData.lookupCountryCodesForZoneId(tzId))
.thenReturn(new HashSet<>(Arrays.asList("US", "GB")));
TimeZoneSettings settings = new TimeZoneSettings();
settings.setTimeZoneData(timeZoneData);
assertThat(settings.findRegionIdForTzId(tzId, null, "")).matches("US|GB");
assertThat(settings.findRegionIdForTzId(tzId, "GB", "")).isEqualTo("GB");
assertThat(settings.findRegionIdForTzId(tzId, null, "GB")).isEqualTo("GB");
}
@Test
public void createPreferenceControllers_matchExpectedControllers() {
TimeZoneSettings settings = new TimeZoneSettings();
List<AbstractPreferenceController> controllers =
settings.createPreferenceControllers(RuntimeEnvironment.application);
assertThat(controllers).hasSize(4);
assertThat(controllers.get(0)).isInstanceOf(RegionPreferenceController.class);
assertThat(controllers.get(1)).isInstanceOf(RegionZonePreferenceController.class);
assertThat(controllers.get(2)).isInstanceOf(TimeZoneInfoPreferenceController.class);
assertThat(controllers.get(3)).isInstanceOf(FixedOffsetPreferenceController.class);
}
}

View File

@@ -42,8 +42,10 @@ import java.util.List;
public class BatteryDatabaseManagerTest {
private static String PACKAGE_NAME_NEW = "com.android.app1";
private static int UID_NEW = 345;
private static int TYPE_NEW = 1;
private static String PACKAGE_NAME_OLD = "com.android.app2";
private static int UID_OLD = 543;
private static int TYPE_OLD = 2;
private static long NOW = System.currentTimeMillis();
private static long ONE_DAY_BEFORE = NOW - DateUtils.DAY_IN_MILLIS;
@@ -67,23 +69,23 @@ public class BatteryDatabaseManagerTest {
@Test
public void testAllFunctions() {
mBatteryDatabaseManager.insertAnomaly(PACKAGE_NAME_NEW, TYPE_NEW,
mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW,
AnomalyDatabaseHelper.State.NEW, NOW);
mBatteryDatabaseManager.insertAnomaly(PACKAGE_NAME_OLD, TYPE_OLD,
mBatteryDatabaseManager.insertAnomaly(UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD,
AnomalyDatabaseHelper.State.NEW, TWO_DAYS_BEFORE);
// In database, it contains two record
List<AppInfo> totalAppInfos = mBatteryDatabaseManager.queryAllAnomalies(0 /* timeMsAfter */,
AnomalyDatabaseHelper.State.NEW);
assertThat(totalAppInfos).hasSize(2);
assertAppInfo(totalAppInfos.get(0), PACKAGE_NAME_NEW, TYPE_NEW);
assertAppInfo(totalAppInfos.get(1), PACKAGE_NAME_OLD, TYPE_OLD);
assertAppInfo(totalAppInfos.get(0), UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW);
assertAppInfo(totalAppInfos.get(1), UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD);
// Only one record shows up if we query by timestamp
List<AppInfo> appInfos = mBatteryDatabaseManager.queryAllAnomalies(ONE_DAY_BEFORE,
AnomalyDatabaseHelper.State.NEW);
assertThat(appInfos).hasSize(1);
assertAppInfo(appInfos.get(0), PACKAGE_NAME_NEW, TYPE_NEW);
assertAppInfo(appInfos.get(0), UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW);
mBatteryDatabaseManager.deleteAllAnomaliesBeforeTimeStamp(ONE_DAY_BEFORE);
@@ -91,14 +93,14 @@ public class BatteryDatabaseManagerTest {
List<AppInfo> appInfos1 = mBatteryDatabaseManager.queryAllAnomalies(0 /* timeMsAfter */,
AnomalyDatabaseHelper.State.NEW);
assertThat(appInfos1).hasSize(1);
assertAppInfo(appInfos1.get(0), PACKAGE_NAME_NEW, TYPE_NEW);
assertAppInfo(appInfos1.get(0), UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW);
}
@Test
public void testUpdateAnomalies_updateSuccessfully() {
mBatteryDatabaseManager.insertAnomaly(PACKAGE_NAME_NEW, TYPE_NEW,
mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW,
AnomalyDatabaseHelper.State.NEW, NOW);
mBatteryDatabaseManager.insertAnomaly(PACKAGE_NAME_OLD, TYPE_OLD,
mBatteryDatabaseManager.insertAnomaly(UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD,
AnomalyDatabaseHelper.State.NEW, NOW);
final AppInfo appInfo = new AppInfo.Builder().setPackageName(PACKAGE_NAME_OLD).build();
final List<AppInfo> updateAppInfos = new ArrayList<>();
@@ -112,17 +114,18 @@ public class BatteryDatabaseManagerTest {
List<AppInfo> newAppInfos = mBatteryDatabaseManager.queryAllAnomalies(ONE_DAY_BEFORE,
AnomalyDatabaseHelper.State.NEW);
assertThat(newAppInfos).hasSize(1);
assertAppInfo(newAppInfos.get(0), PACKAGE_NAME_NEW, TYPE_NEW);
assertAppInfo(newAppInfos.get(0), UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW);
// The state of PACKAGE_NAME_OLD is changed to handled
List<AppInfo> handledAppInfos = mBatteryDatabaseManager.queryAllAnomalies(ONE_DAY_BEFORE,
AnomalyDatabaseHelper.State.HANDLED);
assertThat(handledAppInfos).hasSize(1);
assertAppInfo(handledAppInfos.get(0), PACKAGE_NAME_OLD, TYPE_OLD);
assertAppInfo(handledAppInfos.get(0), UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD);
}
private void assertAppInfo(final AppInfo appInfo, String packageName, int type) {
private void assertAppInfo(final AppInfo appInfo, int uid, String packageName, int type) {
assertThat(appInfo.packageName).isEqualTo(packageName);
assertThat(appInfo.anomalyType).isEqualTo(type);
assertThat(appInfo.uid).isEqualTo(uid);
}
}

View File

@@ -63,7 +63,6 @@ public class BatteryInfoTest {
private static final String STATUS_CHARGING_NO_TIME = "50% - charging";
private static final String STATUS_CHARGING_TIME = "50% - 0m until fully charged";
private static final String STATUS_NOT_CHARGING = "Not charging";
private static final int PLUGGED_IN = 1;
private static final long REMAINING_TIME_NULL = -1;
private static final long REMAINING_TIME = 2;
private static final String ENHANCED_STRING_SUFFIX = "based on your usage";
@@ -72,6 +71,11 @@ public class BatteryInfoTest {
"1m left until fully charged";
private static final String TEST_BATTERY_LEVEL_10 = "10%";
private static final String FIFTEEN_MIN_FORMATTED = "15m";
public static final Estimate DUMMY_ESTIMATE = new Estimate(
1000, /* estimateMillis */
false, /* isBasedOnUsage */
1000 /* averageDischargeTime */);
private Intent mDisChargingBatteryBroadcast;
private Intent mChargingBatteryBroadcast;
private Context mContext;
@@ -132,14 +136,15 @@ public class BatteryInfoTest {
@Test
public void testGetBatteryInfo_basedOnUsageTrueMoreThanFifteenMinutes_usesCorrectString() {
Estimate estimate = new Estimate(Duration.ofHours(4).toMillis(),
true /* isBasedOnUsage */,
1000 /* averageDischargeTime */);
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false /* shortString */,
PowerUtil.convertMsToUs(Duration.ofHours(4).toMillis()),
true /* basedOnUsage */);
mBatteryStats, estimate, SystemClock.elapsedRealtime() * 1000,
false /* shortString */);
BatteryInfo info2 = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, true /* shortString */,
PowerUtil.convertMsToUs(Duration.ofHours(4).toMillis()),
true /* basedOnUsage */);
mBatteryStats, estimate, SystemClock.elapsedRealtime() * 1000,
true /* shortString */);
// We only add special mention for the long string
assertThat(info.remainingLabel.toString()).contains(ENHANCED_STRING_SUFFIX);
@@ -149,14 +154,15 @@ public class BatteryInfoTest {
@Test
public void testGetBatteryInfo_basedOnUsageTrueLessThanSevenMinutes_usesCorrectString() {
Estimate estimate = new Estimate(Duration.ofMinutes(7).toMillis(),
true /* isBasedOnUsage */,
1000 /* averageDischargeTime */);
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false /* shortString */,
PowerUtil.convertMsToUs(Duration.ofMinutes(7).toMillis()),
true /* basedOnUsage */);
mBatteryStats, estimate, SystemClock.elapsedRealtime() * 1000,
false /* shortString */);
BatteryInfo info2 = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, true /* shortString */,
PowerUtil.convertMsToUs(Duration.ofMinutes(7).toMillis()),
true /* basedOnUsage */);
mBatteryStats, estimate, SystemClock.elapsedRealtime() * 1000,
true /* shortString */);
// These should be identical in either case
assertThat(info.remainingLabel.toString()).isEqualTo(
@@ -167,10 +173,12 @@ public class BatteryInfoTest {
@Test
public void testGetBatteryInfo_basedOnUsageTrueBetweenSevenAndFifteenMinutes_usesCorrectString() {
Estimate estimate = new Estimate(Duration.ofMinutes(10).toMillis(),
true /* isBasedOnUsage */,
1000 /* averageDischargeTime */);
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false /* shortString */,
PowerUtil.convertMsToUs(Duration.ofMinutes(10).toMillis()),
true /* basedOnUsage */);
mBatteryStats, estimate, SystemClock.elapsedRealtime() * 1000,
false /* shortString */);
// Check that strings are showing less than 15 minutes remaining regardless of exact time.
assertThat(info.chargeLabel.toString()).isEqualTo(
@@ -184,11 +192,11 @@ public class BatteryInfoTest {
@Test
public void testGetBatteryInfo_basedOnUsageFalse_usesDefaultString() {
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false /* shortString */,
1000, false /* basedOnUsage */);
mBatteryStats, DUMMY_ESTIMATE, SystemClock.elapsedRealtime() * 1000,
false /* shortString */);
BatteryInfo info2 = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, true /* shortString */,
1000, false /* basedOnUsage */);
mBatteryStats, DUMMY_ESTIMATE, SystemClock.elapsedRealtime() * 1000,
true /* shortString */);
assertThat(info.remainingLabel.toString()).doesNotContain(ENHANCED_STRING_SUFFIX);
assertThat(info2.remainingLabel.toString()).doesNotContain(ENHANCED_STRING_SUFFIX);
@@ -199,8 +207,10 @@ public class BatteryInfoTest {
doReturn(TEST_CHARGE_TIME_REMAINING)
.when(mBatteryStats)
.computeChargeTimeRemaining(anyLong());
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false, 1000, false);
mBatteryStats, DUMMY_ESTIMATE, SystemClock.elapsedRealtime() * 1000,
false /* shortString */);
assertThat(info.remainingTimeUs).isEqualTo(TEST_CHARGE_TIME_REMAINING);
assertThat(info.remainingLabel.toString())
.isEqualTo(TEST_CHARGE_TIME_REMAINING_STRINGIFIED);
@@ -211,8 +221,8 @@ public class BatteryInfoTest {
mChargingBatteryBroadcast.putExtra(BatteryManager.EXTRA_LEVEL, 100);
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false /* shortString */,
1000, false /* basedOnUsage */);
mBatteryStats, DUMMY_ESTIMATE, SystemClock.elapsedRealtime() * 1000,
false /* shortString */);
assertThat(info.chargeLabel).isEqualTo("100%");
}
@@ -296,10 +306,13 @@ public class BatteryInfoTest {
} else {
doReturn(0L).when(mBatteryStats).computeChargeTimeRemaining(anyLong());
}
Estimate batteryEstimate = new Estimate(
estimate ? 1000 : 0,
false /* isBasedOnUsage */,
1000 /* averageDischargeTime */);
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext,
charging ? mChargingBatteryBroadcast : mDisChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false,
estimate ? 1000 : 0 /* drainTimeUs */, false);
mBatteryStats, batteryEstimate, SystemClock.elapsedRealtime() * 1000, false);
doReturn(enhanced).when(mFeatureFactory.powerUsageFeatureProvider)
.isEnhancedBatteryPredictionEnabled(mContext);
return info;

View File

@@ -54,6 +54,7 @@ import java.util.List;
public class RestrictedAppDetailsTest {
private static final String PACKAGE_NAME = "com.android.app";
private static final int UID = 234;
private static final String APP_NAME = "app";
@Mock
@@ -70,60 +71,62 @@ public class RestrictedAppDetailsTest {
private InstrumentedPreferenceFragment mFragment;
private RestrictedAppDetails mRestrictedAppDetails;
private Context mContext;
private AppInfo mAppInfo;
private Intent mIntent;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mRestrictedAppDetails = spy(new RestrictedAppDetails());
mContext = spy(RuntimeEnvironment.application);
mRestrictedAppDetails = spy(new RestrictedAppDetails());
mAppInfo = new AppInfo.Builder()
.setPackageName(PACKAGE_NAME)
.setUid(UID)
.build();
when(mRestrictedAppDetails.getPreferenceManager()).thenReturn(mPreferenceManager);
when(mPreferenceManager.getContext()).thenReturn(mContext);
mRestrictedAppDetails.mPackageManager = mPackageManager;
mRestrictedAppDetails.mIconDrawableFactory = mIconDrawableFactory;
mRestrictedAppDetails.mAppInfos = new ArrayList<>();
mRestrictedAppDetails.mAppInfos.add(new AppInfo.Builder()
.setPackageName(PACKAGE_NAME)
.build());
mRestrictedAppDetails.mRestrictedAppListGroup = spy(new PreferenceCategory(mContext));
mRestrictedAppDetails.mBatteryUtils = new BatteryUtils(mContext);
when(mRestrictedAppDetails.mRestrictedAppListGroup.getPreferenceManager())
.thenReturn(mPreferenceManager);
doReturn(mPreferenceManager).when(mRestrictedAppDetails).getPreferenceManager();
doReturn(mContext).when(mPreferenceManager).getContext();
mRestrictedAppDetails.mPackageManager = mPackageManager;
mRestrictedAppDetails.mIconDrawableFactory = mIconDrawableFactory;
mRestrictedAppDetails.mAppInfos = new ArrayList<>();
mRestrictedAppDetails.mAppInfos.add(mAppInfo);
mRestrictedAppDetails.mRestrictedAppListGroup = spy(new PreferenceCategory(mContext));
mRestrictedAppDetails.mBatteryUtils = new BatteryUtils(mContext);
doReturn(mPreferenceManager).when(
mRestrictedAppDetails.mRestrictedAppListGroup).getPreferenceManager();
}
@Test
public void testRefreshUi_displayPreference() throws Exception {
doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME, 0);
doReturn(APP_NAME).when(mPackageManager).getApplicationLabel(mApplicationInfo);
doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME, 0);
doReturn(APP_NAME).when(mPackageManager).getApplicationLabel(mApplicationInfo);
mRestrictedAppDetails.refreshUi();
mRestrictedAppDetails.refreshUi();
assertThat(mRestrictedAppDetails.mRestrictedAppListGroup.getPreferenceCount()).isEqualTo(1);
final Preference preference = mRestrictedAppDetails.mRestrictedAppListGroup.getPreference(0);
assertThat(preference.getKey()).isEqualTo(PACKAGE_NAME);
assertThat(preference.getTitle()).isEqualTo(APP_NAME);
assertThat(mRestrictedAppDetails.mRestrictedAppListGroup.getPreferenceCount()).isEqualTo(1);
final Preference preference = mRestrictedAppDetails.mRestrictedAppListGroup.getPreference(
0);
assertThat(preference.getTitle()).isEqualTo(APP_NAME);
}
@Test
public void testStartRestrictedAppDetails_startWithCorrectData() {
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
doAnswer(invocation -> {
// Get the intent in which it has the app info bundle
mIntent = captor.getValue();
return true;
}).when(mSettingsActivity).startActivity(captor.capture());
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
doAnswer(invocation -> {
// Get the intent in which it has the app info bundle
mIntent = captor.getValue();
return true;
}).when(mSettingsActivity).startActivity(captor.capture());
RestrictedAppDetails.
startRestrictedAppDetails(mSettingsActivity, mFragment, mRestrictedAppDetails.mAppInfos);
RestrictedAppDetails.startRestrictedAppDetails(mSettingsActivity, mFragment,
mRestrictedAppDetails.mAppInfos);
final Bundle bundle = mIntent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
// Verify the bundle has the correct info
final List<AppInfo> appInfos =
bundle.getParcelableArrayList(RestrictedAppDetails.EXTRA_APP_INFO_LIST);
assertThat(appInfos).isNotNull();
assertThat(appInfos).hasSize(1);
assertThat(appInfos.get(0).packageName).isEqualTo(PACKAGE_NAME);
final Bundle bundle = mIntent.getBundleExtra(
SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
// Verify the bundle has the correct info
final List<AppInfo> appInfos = bundle.getParcelableArrayList(
RestrictedAppDetails.EXTRA_APP_INFO_LIST);
assertThat(appInfos).containsExactly(mAppInfo);
}
}

View File

@@ -17,17 +17,36 @@
package com.android.settings.fuelgauge.batterytip;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.robolectric.RuntimeEnvironment.application;
import android.app.StatsManager;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.StatsDimensionsValue;
import android.os.UserManager;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowJobScheduler;
@@ -36,6 +55,37 @@ import java.util.concurrent.TimeUnit;
@RunWith(SettingsRobolectricTestRunner.class)
public class AnomalyDetectionJobServiceTest {
private static final int UID = 123;
private static final String SYSTEM_PACKAGE = "com.android.system";
@Mock
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
private UserManager mUserManager;
@Mock
private BatteryDatabaseManager mBatteryDatabaseManager;
@Mock
private BatteryUtils mBatteryUtils;
@Mock
private PowerWhitelistBackend mPowerWhitelistBackend;
@Mock
private StatsDimensionsValue mStatsDimensionsValue;
private BatteryTipPolicy mPolicy;
private Bundle mBundle;
private AnomalyDetectionJobService mAnomalyDetectionJobService;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPolicy = new BatteryTipPolicy(mContext);
mBundle = new Bundle();
mBundle.putParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, mStatsDimensionsValue);
mAnomalyDetectionJobService = new AnomalyDetectionJobService();
}
@Test
public void testScheduleCleanUp() {
@@ -50,4 +100,30 @@ public class AnomalyDetectionJobServiceTest {
assertThat(pendingJob.getMaxExecutionDelayMillis())
.isEqualTo(TimeUnit.MINUTES.toMillis(30));
}
@Test
public void testSaveAnomalyToDatabase_systemWhitelisted_doNotSave() {
doReturn(SYSTEM_PACKAGE).when(mBatteryUtils).getPackageName(anyInt());
doReturn(true).when(mPowerWhitelistBackend).isSysWhitelisted(SYSTEM_PACKAGE);
mAnomalyDetectionJobService.saveAnomalyToDatabase(mBatteryStatsHelper, mUserManager,
mBatteryDatabaseManager, mBatteryUtils, mPolicy, mPowerWhitelistBackend,
mContext.getContentResolver(), mBundle);
verify(mBatteryDatabaseManager, never()).insertAnomaly(anyInt(), anyString(), anyInt(),
anyInt(), anyLong());
}
@Test
public void testSaveAnomalyToDatabase_normalApp_save() {
doReturn(SYSTEM_PACKAGE).when(mBatteryUtils).getPackageName(anyInt());
doReturn(false).when(mPowerWhitelistBackend).isSysWhitelisted(SYSTEM_PACKAGE);
mAnomalyDetectionJobService.saveAnomalyToDatabase(mBatteryStatsHelper, mUserManager,
mBatteryDatabaseManager, mBatteryUtils, mPolicy, mPowerWhitelistBackend,
mContext.getContentResolver(), mBundle);
verify(mBatteryDatabaseManager).insertAnomaly(anyInt(), anyString(), anyInt(), anyInt(),
anyLong());
}
}

View File

@@ -38,6 +38,7 @@ public class AppInfoTest {
private static final String PACKAGE_NAME = "com.android.app";
private static final int ANOMALY_TYPE = Anomaly.AnomalyType.WAKE_LOCK;
private static final long SCREEN_TIME_MS = DateUtils.HOUR_IN_MILLIS;
private static final int UID = 3452;
private AppInfo mAppInfo;
@@ -47,6 +48,7 @@ public class AppInfoTest {
.setPackageName(PACKAGE_NAME)
.setAnomalyType(ANOMALY_TYPE)
.setScreenOnTimeMs(SCREEN_TIME_MS)
.setUid(UID)
.build();
}
@@ -61,6 +63,7 @@ public class AppInfoTest {
assertThat(appInfo.packageName).isEqualTo(PACKAGE_NAME);
assertThat(appInfo.anomalyType).isEqualTo(ANOMALY_TYPE);
assertThat(appInfo.screenOnTimeMs).isEqualTo(SCREEN_TIME_MS);
assertThat(appInfo.uid).isEqualTo(UID);
}
@Test
@@ -84,5 +87,6 @@ public class AppInfoTest {
assertThat(mAppInfo.packageName).isEqualTo(PACKAGE_NAME);
assertThat(mAppInfo.anomalyType).isEqualTo(ANOMALY_TYPE);
assertThat(mAppInfo.screenOnTimeMs).isEqualTo(SCREEN_TIME_MS);
assertThat(mAppInfo.uid).isEqualTo(UID);
}
}

View File

@@ -29,8 +29,10 @@ import android.text.format.DateUtils;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.HighUsageDataParser;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
@@ -46,7 +48,8 @@ import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class HighUsageDetectorTest {
private static final int UID = 123;
private static final long SCREEN_ON_TIME_MS = DateUtils.HOUR_IN_MILLIS;
private Context mContext;
@Mock
private BatteryStatsHelper mBatteryStatsHelper;
@@ -57,6 +60,7 @@ public class HighUsageDetectorTest {
@Mock
private HighUsageDataParser mDataParser;
private AppInfo mAppInfo;
private BatteryTipPolicy mPolicy;
private HighUsageDetector mHighUsageDetector;
private List<BatterySipper> mUsageList;
@@ -71,6 +75,11 @@ public class HighUsageDetectorTest {
mHighUsageDetector.mBatteryUtils = mBatteryUtils;
mHighUsageDetector.mDataParser = mDataParser;
doNothing().when(mHighUsageDetector).parseBatteryData();
doReturn(UID).when(mBatterySipper).getUid();
mAppInfo = new AppInfo.Builder()
.setUid(UID)
.setScreenOnTimeMs(SCREEN_ON_TIME_MS)
.build();
mUsageList = new ArrayList<>();
mUsageList.add(mBatterySipper);
@@ -87,10 +96,12 @@ public class HighUsageDetectorTest {
public void testDetect_containsHighUsageApp_tipVisible() {
doReturn(true).when(mDataParser).isDeviceHeavilyUsed();
when(mBatteryStatsHelper.getUsageList()).thenReturn(mUsageList);
doReturn(DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).getProcessTimeMs(
doReturn(SCREEN_ON_TIME_MS).when(mBatteryUtils).getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, mBatterySipper.uidObj,
BatteryStats.STATS_SINCE_CHARGED);
assertThat(mHighUsageDetector.detect().isVisible()).isTrue();
final HighUsageTip highUsageTip = (HighUsageTip) mHighUsageDetector.detect();
assertThat(highUsageTip.isVisible()).isTrue();
assertThat(highUsageTip.getHighUsageAppList()).containsExactly(mAppInfo);
}
}

View File

@@ -52,7 +52,7 @@ public class BatteryTipTest {
}
@Test
public void testBuildPreference() {
public void buildPreference() {
final Preference preference = mBatteryTip.buildPreference(mContext);
assertThat(preference.getTitle()).isEqualTo(TITLE);
@@ -61,7 +61,7 @@ public class BatteryTipTest {
}
@Test
public void testParcelable() {
public void parcelable() {
final BatteryTip batteryTip = new TestBatteryTip();
Parcel parcel = Parcel.obtain();
@@ -76,7 +76,7 @@ public class BatteryTipTest {
}
@Test
public void testTipOrder_orderUnique() {
public void tipOrder_orderUnique() {
final List<Integer> orders = new ArrayList<>();
for (int i = 0, size = BatteryTip.TIP_ORDER.size(); i < size; i++) {
orders.add(BatteryTip.TIP_ORDER.valueAt(i));
@@ -85,6 +85,11 @@ public class BatteryTipTest {
assertThat(orders).containsNoDuplicates();
}
@Test
public void toString_containBatteryTipData() {
assertThat(mBatteryTip.toString()).isEqualTo("type=6 state=0");
}
/**
* Used to test the non abstract methods in {@link TestBatteryTip}
*/

View File

@@ -73,4 +73,10 @@ public class HighUsageTipTest {
assertThat(app.packageName).isEqualTo(PACKAGE_NAME);
assertThat(app.screenOnTimeMs).isEqualTo(SCREEN_TIME);
}
@Test
public void toString_containsAppData() {
assertThat(mBatteryTip.toString()).isEqualTo(
"type=2 state=0 { packageName=com.android.app,anomalyType=0,screenTime=1800000 }");
}
}

View File

@@ -71,7 +71,7 @@ public class RestrictAppTipTest {
}
@Test
public void testParcelable() {
public void parcelable() {
Parcel parcel = Parcel.obtain();
mNewBatteryTip.writeToParcel(parcel, mNewBatteryTip.describeContents());
parcel.setDataPosition(0);
@@ -85,40 +85,46 @@ public class RestrictAppTipTest {
}
@Test
public void testGetTitle_stateNew_showRestrictTitle() {
public void getTitle_stateNew_showRestrictTitle() {
assertThat(mNewBatteryTip.getTitle(mContext)).isEqualTo("Restrict 1 app");
}
@Test
public void testGetTitle_stateHandled_showHandledTitle() {
public void getTitle_stateHandled_showHandledTitle() {
assertThat(mHandledBatteryTip.getTitle(mContext)).isEqualTo("1 recently restricted");
}
@Test
public void testGetSummary_stateNew_showRestrictSummary() {
public void getSummary_stateNew_showRestrictSummary() {
assertThat(mNewBatteryTip.getSummary(mContext))
.isEqualTo("app has high battery usage");
}
@Test
public void testGetSummary_stateHandled_showHandledSummary() {
public void getSummary_stateHandled_showHandledSummary() {
assertThat(mHandledBatteryTip.getSummary(mContext))
.isEqualTo("App changes are in progress");
}
@Test
public void testUpdate_anomalyBecomeInvisible_stateHandled() {
public void update_anomalyBecomeInvisible_stateHandled() {
mNewBatteryTip.updateState(mInvisibleBatteryTip);
assertThat(mNewBatteryTip.getState()).isEqualTo(BatteryTip.StateType.HANDLED);
}
@Test
public void testUpdate_newAnomalyComes_stateNew() {
public void update_newAnomalyComes_stateNew() {
mInvisibleBatteryTip.updateState(mNewBatteryTip);
assertThat(mInvisibleBatteryTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
mHandledBatteryTip.updateState(mNewBatteryTip);
assertThat(mHandledBatteryTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
}
@Test
public void toString_containsAppData() {
assertThat(mNewBatteryTip.toString()).isEqualTo(
"type=1 state=0 { packageName=com.android.app,anomalyType=0,screenTime=0 }");
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.location;
import static com.google.common.truth.Truth.assertThat;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(SettingsRobolectricTestRunner.class)
public final class InjectedSettingTest {
private static final String TEST_STRING = "test";
@Test
public void buildWithoutPackageName_ShouldReturnNull() {
assertThat(((new InjectedSetting.Builder())
.setClassName(TEST_STRING)
.setTitle(TEST_STRING)
.setSettingsActivity(TEST_STRING).build())).isNull();
}
private InjectedSetting getTestSetting() {
return new InjectedSetting.Builder()
.setPackageName(TEST_STRING)
.setClassName(TEST_STRING)
.setTitle(TEST_STRING)
.setSettingsActivity(TEST_STRING).build();
}
@Test
public void testEquals() {
InjectedSetting setting1 = getTestSetting();
InjectedSetting setting2 = getTestSetting();
assertThat(setting1).isEqualTo(setting2);
}
@Test
public void testHashCode() {
InjectedSetting setting = getTestSetting();
assertThat(setting.hashCode()).isEqualTo(1225314048);
}
}

View File

@@ -24,16 +24,25 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.arch.lifecycle.LifecycleOwner;
import android.content.ComponentName;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settings.widget.RestrictedAppPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,11 +50,13 @@ import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(
shadows = {
ShadowUserManager.class
})
public class LocationServicePreferenceControllerTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -56,6 +67,8 @@ public class LocationServicePreferenceControllerTest {
private PreferenceScreen mScreen;
@Mock
private SettingsInjector mSettingsInjector;
@Mock
private DevicePolicyManager mDevicePolicyManager;
private Context mContext;
private LocationServicePreferenceController mController;
@@ -73,6 +86,9 @@ public class LocationServicePreferenceControllerTest {
final String key = mController.getPreferenceKey();
when(mScreen.findPreference(key)).thenReturn(mCategory);
when(mCategory.getKey()).thenReturn(key);
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
.thenReturn(mDevicePolicyManager);
}
@Test
@@ -132,4 +148,33 @@ public class LocationServicePreferenceControllerTest {
verify(mSettingsInjector).reloadStatusMessages();
}
@Test
public void withUserRestriction_shouldDisableLocationAccuracy() {
final List<Preference> preferences = new ArrayList<>();
final RestrictedAppPreference pref = new RestrictedAppPreference(mContext,
UserManager.DISALLOW_CONFIG_LOCATION);
pref.setTitle("Location Accuracy");
preferences.add(pref);
doReturn(preferences).when(mSettingsInjector)
.getInjectedSettings(any(Context.class), anyInt());
int userId = UserHandle.myUserId();
List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
enforcingUsers.add(new UserManager.EnforcingUser(userId,
UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
ComponentName componentName = new ComponentName("test", "test");
// Ensure that RestrictedLockUtils.checkIfRestrictionEnforced doesn't return null.
ShadowUserManager.getShadow().setUserRestrictionSources(
UserManager.DISALLOW_CONFIG_LOCATION,
UserHandle.of(userId),
enforcingUsers);
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(componentName);
mController.displayPreference(mScreen);
mController.updateState(mCategory);
assertThat(pref.isEnabled()).isFalse();
assertThat(pref.isDisabledByAdmin()).isTrue();
}
}

View File

@@ -46,10 +46,7 @@ import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
public class ZenModeMediaPreferenceControllerTest {
private static final boolean MEDIA_SETTINGS = true;
private ZenModeMediaSystemOtherPreferenceController mController;
private ZenModeMediaPreferenceController mController;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@@ -75,7 +72,7 @@ public class ZenModeMediaPreferenceControllerTest {
mContentResolver = RuntimeEnvironment.application.getContentResolver();
when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
mController = new ZenModeMediaSystemOtherPreferenceController(mContext,
mController = new ZenModeMediaPreferenceController(mContext,
mock(Lifecycle.class));
ReflectionHelpers.setField(mController, "mBackend", mBackend);
@@ -111,13 +108,13 @@ public class ZenModeMediaPreferenceControllerTest {
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
when(mBackend.isPriorityCategoryEnabled(
NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER)).
thenReturn(MEDIA_SETTINGS);
NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA)).
thenReturn(true);
mController.updateState(mockPref);
verify(mockPref).setEnabled(true);
verify(mockPref).setChecked(MEDIA_SETTINGS);
verify(mockPref).setChecked(true);
}
@Test
@@ -126,7 +123,7 @@ public class ZenModeMediaPreferenceControllerTest {
mController.onPreferenceChange(mockPref, allow);
verify(mBackend).saveSoundPolicy(
NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, allow);
NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA, allow);
}
@Test
@@ -135,6 +132,6 @@ public class ZenModeMediaPreferenceControllerTest {
mController.onPreferenceChange(mockPref, allow);
verify(mBackend).saveSoundPolicy(
NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, allow);
NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA, allow);
}
}

View File

@@ -52,7 +52,7 @@ public class ZenModeSettingsTest {
NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS
| NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS
| NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
| NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
| NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA,
0, 0);
final String result = mBuilder.getBehaviorSettingSummary(policy,
Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
@@ -75,7 +75,7 @@ public class ZenModeSettingsTest {
public void testGetBehaviorSettingSummary_alarmsAndMedia() {
NotificationManager.Policy policy = new NotificationManager.Policy(
NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
| NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
| NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA,
0, 0);
final String result = mBuilder.getBehaviorSettingSummary(policy,
Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import static android.provider.Settings.Global.ZEN_MODE;
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.provider.Settings;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
public class ZenModeSystemPreferenceControllerTest {
private ZenModeSystemPreferenceController mController;
@Mock
private ZenModeBackend mBackend;
@Mock
private NotificationManager mNotificationManager;
@Mock
private SwitchPreference mockPref;
@Mock
private NotificationManager.Policy mPolicy;
@Mock
private PreferenceScreen mPreferenceScreen;
private Context mContext;
private ContentResolver mContentResolver;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
ShadowApplication shadowApplication = ShadowApplication.getInstance();
shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
mContext = shadowApplication.getApplicationContext();
mContentResolver = RuntimeEnvironment.application.getContentResolver();
when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
mController = new ZenModeSystemPreferenceController(mContext, mock(Lifecycle.class));
ReflectionHelpers.setField(mController, "mBackend", mBackend);
when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
mockPref);
mController.displayPreference(mPreferenceScreen);
}
@Test
public void updateState_TotalSilence() {
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
final SwitchPreference mockPref = mock(SwitchPreference.class);
mController.updateState(mockPref);
verify(mockPref).setEnabled(false);
verify(mockPref).setChecked(false);
}
@Test
public void updateState_AlarmsOnly() {
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
final SwitchPreference mockPref = mock(SwitchPreference.class);
mController.updateState(mockPref);
verify(mockPref).setEnabled(false);
verify(mockPref).setChecked(false);
}
@Test
public void updateState_Priority() {
Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
when(mBackend.isPriorityCategoryEnabled(
NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM)).thenReturn(true);
mController.updateState(mockPref);
verify(mockPref).setEnabled(true);
verify(mockPref).setChecked(true);
}
@Test
public void onPreferenceChanged_EnableSystem() {
mController.onPreferenceChange(mockPref, true);
verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM,
true);
}
@Test
public void onPreferenceChanged_DisableSystem() {
mController.onPreferenceChange(mockPref, false);
verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM,
false);
}
}

View File

@@ -16,16 +16,17 @@
package com.android.settings.slices;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_CONTROLLER;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Xml;
import com.android.settings.core.PreferenceXmlParserUtils;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.core.codeinspection.ClassScanner;
import com.android.settings.core.codeinspection.CodeInspector;
@@ -34,7 +35,6 @@ import com.android.settings.search.DatabaseIndexingUtils;
import com.android.settings.search.Indexable;
import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.search.SearchFeatureProviderImpl;
import com.android.settings.core.PreferenceXmlParserUtils;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -42,8 +42,9 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -53,7 +54,7 @@ import java.util.List;
public class SliceControllerInXmlTest {
private static final List<Class> mSliceControllerClasses = Collections.singletonList(
TogglePreferenceController.class
TogglePreferenceController.class
);
private final List<String> mXmlDeclaredControllers = new ArrayList<>();
@@ -71,7 +72,7 @@ public class SliceControllerInXmlTest {
private FakeFeatureFactory mFakeFeatureFactory;
@Before
public void setUp() {
public void setUp() throws IOException, XmlPullParserException {
mContext = spy(RuntimeEnvironment.application);
mSearchProvider = new SearchFeatureProviderImpl();
@@ -83,44 +84,28 @@ public class SliceControllerInXmlTest {
initDeclaredControllers();
}
private void initDeclaredControllers() {
private void initDeclaredControllers() throws IOException, XmlPullParserException {
final List<Integer> xmlResources = getIndexableXml();
XmlResourceParser parser;
for (int xmlResId : xmlResources) {
try {
parser = mContext.getResources().getXml(xmlResId);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
// Parse next until start tag is found
final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(mContext,
xmlResId, PreferenceXmlParserUtils.MetadataFlag.FLAG_NEED_PREF_CONTROLLER);
for (Bundle bundle : metadata) {
final String controllerClassName = bundle.getString(METADATA_CONTROLLER);
if (TextUtils.isEmpty(controllerClassName)) {
continue;
}
final int outerDepth = parser.getDepth();
final AttributeSet attrs = Xml.asAttributeSet(parser);
String controllerClassName;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
controllerClassName = PreferenceXmlParserUtils.getController(mContext, attrs);
if (!TextUtils.isEmpty(controllerClassName)) {
mXmlDeclaredControllers.add(controllerClassName);
}
}
} catch (Exception e) {
// Assume an issue with robolectric resources
mXmlDeclaredControllers.add(controllerClassName);
}
}
// We definitely have some controllers in xml, so assert not-empty here as a proxy to
// make sure the parser didn't fail
assertThat(mXmlDeclaredControllers).isNotEmpty();
}
@Test
public void testAllControllersDeclaredInXml() throws Exception {
final List<Class<?>> classes =
new ClassScanner().getClassesForPackage(mContext.getPackageName());
new ClassScanner().getClassesForPackage(mContext.getPackageName());
final List<String> missingControllersInXml = new ArrayList<>();
for (Class<?> clazz : classes) {
@@ -139,7 +124,7 @@ public class SliceControllerInXmlTest {
missingControllersInXml.removeAll(mGrandfatheredClasses);
final String missingControllerError =
buildErrorMessage(ERROR_MISSING_CONTROLLER, missingControllersInXml);
buildErrorMessage(ERROR_MISSING_CONTROLLER, missingControllersInXml);
assertWithMessage(missingControllerError).that(missingControllersInXml).isEmpty();
}

View File

@@ -32,8 +32,6 @@ import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.system.helpers.SettingsHelper;
import android.system.helpers.SettingsHelper.SettingsType;
import android.widget.ListView;
import android.widget.Spinner;
import org.junit.After;
import org.junit.Before;
@@ -102,16 +100,16 @@ public class ZonePickerSettingsTest {
// Test 2 time zones with no DST
@Test
public void testSelectReykjavik() throws Exception {
testSelectTimeZone("Iceland", "Reykjavik", "GMT+00:00", "Atlantic/Reykjavik");
testSelectTimeZone("Iceland", "Reykjavik", "GMT+00:00", "Atlantic/Reykjavik", true);
}
@Test
public void testSelectPhoenix() throws Exception {
testSelectTimeZone("United States", "Phoenix", "GMT-07:00", "America/Phoenix");
testSelectTimeZone("United States", "Phoenix", "GMT-07:00", "America/Phoenix", false);
}
private void testSelectTimeZone(String region, String timezone, String expectedTimeZoneOffset,
String expectedTimeZoneId) throws Exception {
String expectedTimeZoneId, boolean assumeOneTimeZoneInRegion) throws Exception {
mHelper.setIntSetting(SettingsType.GLOBAL, Settings.Global.AUTO_TIME_ZONE, 0);
SettingsHelper.launchSettingsPage(
@@ -121,16 +119,21 @@ public class ZonePickerSettingsTest {
assertTrue(selectTimeZone.isEnabled());
selectTimeZone.click();
// Select region in the dropdown list
selectScrollableItem(selectDropDownInSpinner(By.clazz(Spinner.class)),
new UiSelector().textContains(region))
wait(By.text("Region")).click();
// Speed-up the test by searching with the first 2 characters of the region name
wait(By.res("android", "search_src_text")).setText(region.substring(0, 2));
// Select region in the list
selectItemInList(new UiSelector().textContains(region))
.click();
// Select time zone
selectScrollableItem(selectTimeZoneList(),
new UiSelector().textContains(timezone))
.click();
// Only select time zone explicitly if there are more than one time zones in a region
if (!assumeOneTimeZoneInRegion) {
wait(By.text("Time Zone"));
selectItemInList(new UiSelector().textContains(timezone))
.click();
}
mDevice.pressBack();
// The select button should include the GMT offset in the summary
BySelector summarySelector = By.res("android:id/summary");
UiObject2 selectedTimeZone = selectTimeZone.findObject(summarySelector);
@@ -162,21 +165,10 @@ public class ZonePickerSettingsTest {
assertEquals(expectedTimeZoneId, TimeZone.getDefault().getID());
}
/**
* Perform click on {@link Spinner} and return the pop-up dropdown list.
* @return UiScrollable representing the pop-up dropdown after clicking on the spinner
*/
private UiScrollable selectDropDownInSpinner(BySelector spinnerSelector)
throws UiObjectNotFoundException {
UiObject2 spinner = wait(spinnerSelector);
spinner.click();
UiSelector dropDownSelector = new UiSelector().className(ListView.class);
return new UiScrollable(dropDownSelector);
}
private UiScrollable selectTimeZoneList() {
return new UiScrollable(new UiSelector().resourceId(SETTINGS_PACKAGE + ":id/tz_list"));
private UiObject selectItemInList(UiSelector childSelector) throws UiObjectNotFoundException {
UiScrollable recyclerView = new UiScrollable(
new UiSelector().resourceId(SETTINGS_PACKAGE + ":id/recycler_view"));
return selectScrollableItem(recyclerView, childSelector);
}
/**