Snap for 4796401 from 4376993a87 to pi-release

Change-Id: I209618e244432f13904cf1e094be86b4cd174418
This commit is contained in:
android-build-team Robot
2018-05-22 07:27:27 +00:00
36 changed files with 677 additions and 141 deletions

View File

@@ -715,7 +715,7 @@
<activity
android:name="Settings$ZenModeSettingsActivity"
android:label="@string/zen_mode_settings_title"
android:icon="@drawable/ic_settings_notifications"
android:icon="@drawable/ic_notifications"
android:exported="true"
android:taskAffinity="com.android.settings"
android:parentActivityName="Settings">
@@ -762,7 +762,7 @@
<activity
android:name=".notification.ZenOnboardingActivity"
android:label="@string/zen_onboarding_dnd_visual_disturbances_header"
android:icon="@drawable/ic_settings_notifications"
android:icon="@drawable/ic_notifications"
android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog.NoActionBar"
android:exported="true"
android:taskAffinity="com.android.settings"
@@ -775,7 +775,7 @@
<activity
android:name="Settings$ZenModeAutomationSettingsActivity"
android:label="@string/zen_mode_automation_settings_title"
android:icon="@drawable/ic_settings_notifications"
android:icon="@drawable/ic_notifications"
android:exported="true"
android:taskAffinity="com.android.settings"
android:parentActivityName="Settings$ZenModeSettingsActivity">

View File

@@ -21,5 +21,5 @@
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M18,3v2h-2V3H8v2H6V3H5C4.45,3 4,3.45 4,4v16c0,0.55 0.45,1 1,1h1v-2h2v2h8v-2h2v2h1c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1H18zM8,17H6v-2h2V17zM8,13H6v-2h2V13zM8,9H6V7h2V9zM18,17h-2v-2h2V17zM18,13h-2v-2h2V13zM18,9h-2V7h2V9z"/>
android:pathData="M20,4h-3l2,4h-3l-2,-4h-2l2,4h-3L9,4H7l2,4H6L4,4C2.9,4 2.01,4.9 2.01,6L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6C22,4.9 21.1,4 20,4zM20,18L4,18v-8h16V18z"/>
</vector>

View File

@@ -21,5 +21,5 @@
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M18,3h-5c-0.55,0 -1,0.45 -1,1v8.3a3.88,3.88 0,0 0,-2.9 -0.04c-1.79,0.67 -3.11,2.35 -3.1,4.26A4.483,4.483 0,0 0,10.5 21c2.5,0 4.5,-2.3 4.5,-4.5V6h3c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1z"/>
android:pathData="M12,3l0.01,10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55C7.79,13 6,14.79 6,17c0,2.21 1.79,4 4.01,4S14,19.21 14,17V7h4V3H12zM10.01,19c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C12.01,18.1 11.11,19 10.01,19z"/>
</vector>

View File

@@ -20,6 +20,9 @@
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"
android:fillColor="#FFFFFFFF"/>
android:fillColor="#FFFFFFFF"
android:pathData="M18,17v-6c0,-3.07 -1.63,-5.64 -4.5,-6.32V4c0,-0.83 -0.67,-1.5 -1.5,-1.5S10.5,3.17 10.5,4v0.68C7.64,5.36 6,7.92 6,11v6H4v2h10h0.38H20v-2H18zM16,17H8v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5V17z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22z"/>
</vector>

View File

@@ -21,6 +21,11 @@
android:viewportHeight="24.0">
<path
android:fillColor="#0F9D58"
android:pathData="M13,7h-2v2h2L13,7zM13,11h-2v6h2v-6zM17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,
1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14z"/>
android:pathData="M11,7h2v2h-2z"/>
<path
android:fillColor="#0F9D58"
android:pathData="M11,11h2v6h-2z"/>
<path
android:fillColor="#0F9D58"
android:pathData="M17,1.01L7,1C5.9,1 5,1.9 5,3v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3C19,1.9 18.1,1.01 17,1.01zM17,21H7l0,-1h10V21zM17,18H7V6h10V18zM7,4V3h10v1H7z"/>
</vector>

View File

@@ -22,6 +22,11 @@
android:tint="?android:attr/colorError">
<path
android:fillColor="#FF000000"
android:pathData="M13,7h-2v2h2L13,7zM13,11h-2v6h2v-6zM17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,
1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14z"/>
android:pathData="M11,7h2v2h-2z"/>
<path
android:fillColor="#FF000000"
android:pathData="M11,11h2v6h-2z"/>
<path
android:fillColor="#FF000000"
android:pathData="M17,1.01L7,1C5.9,1 5,1.9 5,3v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3C19,1.9 18.1,1.01 17,1.01zM17,21H7l0,-1h10V21zM17,18H7V6h10V18zM7,4V3h10v1H7z"/>
</vector>

View File

@@ -1,25 +0,0 @@
<!--
Copyright (C) 2016 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12.0,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0c0.0,1.0 0.89,2.0 2.0,2.0zm6.0,-6.0l0.0,-5.0c0.0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4.0c0.0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.0,0.67 -1.5,1.5l0.0,0.68C7.63,5.36 6.0,7.92 6.0,11.0l0.0,5.0l-2.0,2.0l0.0,1.0l16.0,0.0l0.0,-1.0l-2.0,-2.0z"/>
</vector>

View File

@@ -14,11 +14,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/suw_color_accent_light"
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4a2,2 0,0 0,2 2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2.15,2.15a0.5,0.5 0,0 0,0.35 0.85h15.6c0.45,0 0.67,-0.54 0.35,-0.85L18,16z"/>
android:pathData="M18,17v-6c0,-3.07 -1.63,-5.64 -4.5,-6.32V4c0,-0.83 -0.67,-1.5 -1.5,-1.5S10.5,3.17 10.5,4v0.68C7.64,5.36 6,7.92 6,11v6H4v2h10h0.38H20v-2H18zM16,17H8v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5V17z"/>
<path
android:fillColor="@color/suw_color_accent_light"
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22z"/>
</vector>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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.
-->
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item android:drawable="?android:attr/colorSecondary"/>
</ripple>

View File

@@ -24,7 +24,6 @@
android:orientation="horizontal"
android:paddingTop="24dp"
android:paddingBottom="24dp"
android:background="@drawable/selectable_card_grey"
style="@style/EntityHeader">
<LinearLayout

View File

@@ -23,7 +23,6 @@
android:paddingStart="@dimen/preference_no_icon_padding_start"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="vertical"
android:background="@drawable/selectable_card_grey"
android:selectable="false"
style="@style/EntityHeader">
@@ -31,7 +30,7 @@
android:id="@+id/usage_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:attr/colorAccent"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/data_usage_title" />
<com.android.settings.datausage.MeasurableLinearLayout

View File

@@ -16,8 +16,8 @@
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="320dp"
android:layout_height="wrap_content" >
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/zen_onboarding_choices"

View File

@@ -7264,15 +7264,15 @@
<!-- Do not disturb: restrict notifications title [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_title">Notifications</string>
<!-- Do not disturb: Mute notifications option [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_mute">Show notifications silently</string>
<string name="zen_mode_restrict_notifications_mute">No sound from notifications</string>
<!-- Do not disturb:Mute notifications summary [CHAR LIMIT=NONE] -->
<string name="zen_mode_restrict_notifications_mute_summary">Notifications will be muted</string>
<string name="zen_mode_restrict_notifications_mute_summary">You will see notifications on your screen</string>
<!-- Do not disturb:Mute notifications footer [CHAR LIMIT=NONE] -->
<string name="zen_mode_restrict_notifications_mute_footer">When notifications arrive, your phone won\u2019t make a sound or vibrate.</string>
<!-- Do not disturb: Hide notifications option [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_hide">Hide notifications</string>
<string name="zen_mode_restrict_notifications_hide">No visuals or sound from notifications</string>
<!-- Do not disturb: Hide notifications summary [CHAR LIMIT=NONE] -->
<string name="zen_mode_restrict_notifications_hide_summary">You won\u2019t see new or existing notifications</string>
<string name="zen_mode_restrict_notifications_hide_summary">You won\u2019t see or hear notifications</string>
<!-- Do not disturb: Hide notifications footer [CHAR LIMIT=NONE] -->
<string name="zen_mode_restrict_notifications_hide_footer">Your phone won\u2019t show new or existing notifications, and won\u2019t make a sound or vibrate. Notifications won\u2019t appear when you swipe down from the top of your screen.\n\nKeep in mind, critical notifications for phone activity and status will still appear.</string>
<!-- Do not disturb: Custom settings option [CHAR LIMIT=60] -->
@@ -7282,11 +7282,11 @@
<!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_disable_custom">Remove custom setting</string>
<!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_summary_muted">Will appear</string>
<string name="zen_mode_restrict_notifications_summary_muted">No sound from notifications</string>
<!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_summary_custom">Partially hidden</string>
<!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_summary_hidden">Hidden</string>
<!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=100] -->
<string name="zen_mode_restrict_notifications_summary_hidden">No visuals or sound from notifications</string>
<!-- Do not disturb: what to block title [CHAR LIMIT = 60] -->
<string name="zen_mode_what_to_block_title">Custom restrictions</string>
@@ -7429,7 +7429,7 @@
<string name="zen_onboarding_settings">Settings</string>
<!-- Do not disturb onboarding dialog, title for radio button [CHAR LIMIT=80]-->
<string name="zen_onboarding_new_setting_title">No visuals or sounds from notifications</string>
<string name="zen_onboarding_new_setting_title">No visuals or sound from notifications</string>
<!-- Do not disturb onboarding dialog, title for radio button [CHAR LIMIT=80]-->
<string name="zen_onboarding_current_setting_title">No sound from notifications</string>
<!-- Do not disturb onboarding dialog, secondary text for radio button [CHAR LIMIT=NONE]-->
@@ -9933,7 +9933,7 @@
<string name="zen_suggestion_title">Update Do Not Disturb</string>
<!-- Summary label for dnd suggestion, which is displayed in Settings homepage [CHAR LIMIT=100] -->
<string name="zen_suggestion_summary">Hide notifications to stay focused</string>
<string name="zen_suggestion_summary">Pause notifications to stay focused</string>
<!-- Title label for new device suggestion, which is displayed in Settings homepage [CHAR LIMIT=100] -->
<string name="new_device_suggestion_title">What\'s new and exciting?</string>

View File

@@ -412,7 +412,7 @@
</style>
<style name="EntityHeader">
<item name="android:background">?android:attr/colorSecondary</item>
<item name="android:background">?android:attr/colorPrimary</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:paddingTop">16dp</item>
<item name="android:paddingStart">16dp</item>

View File

@@ -45,7 +45,7 @@
<com.android.settings.applications.LayoutPreference
android:key="night_display_activated"
android:title="@string/night_display_status_title"
android:title="@string/night_display_title"
android:selectable="false"
android:layout="@layout/night_display_activation_button"
settings:keywords="@string/keywords_display_night_display"

View File

@@ -16,6 +16,7 @@
package com.android.settings.connecteddevice;
import android.content.Context;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
@@ -26,6 +27,9 @@ import com.android.settings.nfc.NfcPreferenceController;
*/
public class AdvancedConnectedDeviceController extends BasePreferenceController {
private static final String DRIVING_MODE_SETTINGS_ENABLED =
"gearhead:driving_mode_settings_enabled";
public AdvancedConnectedDeviceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@@ -47,10 +51,15 @@ public class AdvancedConnectedDeviceController extends BasePreferenceController
public static int getConnectedDevicesSummaryResourceId(Context context) {
final NfcPreferenceController nfcPreferenceController =
new NfcPreferenceController(context);
final boolean isDrivingModeAvailable = false;
return getConnectedDevicesSummaryResourceId(nfcPreferenceController,
isDrivingModeAvailable);
isDrivingModeAvailable(context));
}
@VisibleForTesting
static boolean isDrivingModeAvailable(Context context) {
return Settings.System.
getInt(context.getContentResolver(), DRIVING_MODE_SETTINGS_ENABLED, 0) == 1;
}
@VisibleForTesting

View File

@@ -26,7 +26,10 @@ import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.nfc.NfcPreferenceController;
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.Arrays;
import java.util.List;
@@ -59,12 +62,32 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
return R.xml.connected_devices;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getLifecycle());
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final DiscoverableFooterPreferenceController discoverableFooterPreferenceController =
new DiscoverableFooterPreferenceController(context);
controllers.add(discoverableFooterPreferenceController);
if (lifecycle != null) {
lifecycle.addObserver(discoverableFooterPreferenceController);
}
return controllers;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(AvailableMediaDeviceGroupController.class).init(this);
use(ConnectedDeviceGroupController.class).init(this);
use(PreviouslyConnectedDevicePreferenceController.class).init(this);
use(DiscoverableFooterPreferenceController.class).init(this);
}
@VisibleForTesting
@@ -72,25 +95,17 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
private final Context mContext;
private final SummaryLoader mSummaryLoader;
private final NfcPreferenceController mNfcPreferenceController;
public SummaryProvider(Context context, SummaryLoader summaryLoader) {
mContext = context;
mSummaryLoader = summaryLoader;
mNfcPreferenceController = new NfcPreferenceController(context);
}
@Override
public void setListening(boolean listening) {
if (listening) {
if (mNfcPreferenceController.isAvailable()) {
mSummaryLoader.setSummary(this,
mContext.getString(R.string.connected_devices_dashboard_summary));
} else {
mSummaryLoader.setSummary(this, mContext.getString(
R.string.connected_devices_dashboard_no_nfc_summary));
}
mSummaryLoader.setSummary(this, mContext.getText(AdvancedConnectedDeviceController.
getConnectedDevicesSummaryResourceId(mContext)));
}
}
}
@@ -117,6 +132,12 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
return Arrays.asList(sir);
}
@Override
public List<AbstractPreferenceController> createPreferenceControllers(Context
context) {
return buildPreferenceControllers(context, null /* lifecycle */);
}
@Override
public List<String> getNonIndexableKeys(Context context) {
List<String> keys = super.getNonIndexableKeys(context);

View File

@@ -0,0 +1,145 @@
/*
* 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.connecteddevice;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.support.v7.preference.PreferenceScreen;
import android.text.BidiFormatter;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.bluetooth.AlwaysDiscoverable;
import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.R;
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.FooterPreferenceMixin;
/**
* Controller that shows and updates the bluetooth device name
*/
public class DiscoverableFooterPreferenceController extends BasePreferenceController
implements LifecycleObserver, OnResume, OnPause {
private static final String KEY = "discoverable_footer_preference";
@VisibleForTesting
BroadcastReceiver mBluetoothChangedReceiver;
private FooterPreferenceMixin mFooterPreferenceMixin;
private FooterPreference mPreference;
private LocalBluetoothManager mLocalManager;
private LocalBluetoothAdapter mLocalAdapter;
private AlwaysDiscoverable mAlwaysDiscoverable;
public DiscoverableFooterPreferenceController(Context context) {
super(context, KEY);
mLocalManager = Utils.getLocalBtManager(context);
if (mLocalManager == null) {
return;
}
mLocalAdapter = mLocalManager.getBluetoothAdapter();
mAlwaysDiscoverable = new AlwaysDiscoverable(context, mLocalAdapter);
initReceiver();
}
private void initReceiver() {
mBluetoothChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
updateFooterPreferenceTitle(state);
}
}
};
}
public void init(DashboardFragment fragment) {
mFooterPreferenceMixin = new FooterPreferenceMixin(fragment, fragment.getLifecycle());
}
@VisibleForTesting
void init(FooterPreferenceMixin footerPreferenceMixin, FooterPreference preference,
AlwaysDiscoverable alwaysDiscoverable) {
mFooterPreferenceMixin = footerPreferenceMixin;
mPreference = preference;
mAlwaysDiscoverable = alwaysDiscoverable;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
addFooterPreference(screen);
}
@Override
public int getAvailabilityStatus() {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
}
private void addFooterPreference(PreferenceScreen screen) {
mPreference = mFooterPreferenceMixin.createFooterPreference();
mPreference.setKey(KEY);
screen.addPreference(mPreference);
}
@Override
public void onResume() {
mContext.registerReceiver(mBluetoothChangedReceiver,
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
mAlwaysDiscoverable.start();
updateFooterPreferenceTitle(mLocalAdapter.getState());
}
@Override
public void onPause() {
mContext.unregisterReceiver(mBluetoothChangedReceiver);
mAlwaysDiscoverable.stop();
}
private void updateFooterPreferenceTitle (int bluetoothState) {
if (bluetoothState == BluetoothAdapter.STATE_ON) {
mPreference.setTitle(getPreferenceTitle());
} else {
mPreference.setTitle(R.string.bluetooth_off_footer);
}
}
private CharSequence getPreferenceTitle() {
final String deviceName = mLocalAdapter.getName();
if (TextUtils.isEmpty(deviceName)) {
return null;
}
return TextUtils.expandTemplate(
mContext.getText(R.string.bluetooth_device_name_summary),
BidiFormatter.getInstance().unicodeWrap(deviceName));
}
}

View File

@@ -129,6 +129,7 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
@VisibleForTesting
void showDialog(boolean restricted) {
final AppInfo appInfo = new AppInfo.Builder()
.setUid(mUid)
.setPackageName(mTargetPackage)
.build();
BatteryTip tip = restricted

View File

@@ -16,8 +16,6 @@
package com.android.settings.fuelgauge;
import android.app.AppOpsManager;
import android.app.Fragment;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -31,12 +29,16 @@ import android.util.IconDrawableFactory;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment;
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
import com.android.settings.widget.AppCheckBoxPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.widget.FooterPreferenceMixin;
@@ -46,7 +48,8 @@ import java.util.List;
/**
* Fragment to show a list of anomaly apps, where user could handle these anomalies
*/
public class RestrictedAppDetails extends DashboardFragment {
public class RestrictedAppDetails extends DashboardFragment implements
BatteryTipPreferenceController.BatteryTipListener {
public static final String TAG = "RestrictedAppDetails";
@@ -145,12 +148,14 @@ public class RestrictedAppDetails extends DashboardFragment {
Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager,
appInfo.packageName,
UserHandle.getUserId(appInfo.uid)));
checkBoxPreference.setKey(getKeyFromAppInfo(appInfo));
checkBoxPreference.setOnPreferenceChangeListener((pref, value) -> {
// change the toggle
final int mode = (Boolean) value ? AppOpsManager.MODE_IGNORED
: AppOpsManager.MODE_ALLOWED;
mBatteryUtils.setForceAppStandby(appInfo.uid, appInfo.packageName, mode);
return true;
final BatteryTipDialogFragment fragment = createDialogFragment(appInfo,
(Boolean) value);
fragment.setTargetFragment(this, 0 /* requestCode */);
fragment.show(getFragmentManager(), TAG);
return false;
});
mRestrictedAppListGroup.addPreference(checkBoxPreference);
} catch (PackageManager.NameNotFoundException e) {
@@ -159,4 +164,35 @@ public class RestrictedAppDetails extends DashboardFragment {
}
}
@Override
public void onBatteryTipHandled(BatteryTip batteryTip) {
final AppInfo appInfo;
final boolean isRestricted = batteryTip instanceof RestrictAppTip;
if (isRestricted) {
appInfo = ((RestrictAppTip) batteryTip).getRestrictAppList().get(0);
} else {
appInfo = ((UnrestrictAppTip) batteryTip).getUnrestrictAppInfo();
}
CheckBoxPreference preference = (CheckBoxPreference) mRestrictedAppListGroup
.findPreference(getKeyFromAppInfo(appInfo));
if (preference != null) {
preference.setChecked(isRestricted);
}
}
@VisibleForTesting
BatteryTipDialogFragment createDialogFragment(AppInfo appInfo, boolean toRestrict) {
final BatteryTip batteryTip = toRestrict
? new RestrictAppTip(BatteryTip.StateType.NEW, appInfo)
: new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo);
return BatteryTipDialogFragment.newInstance(
batteryTip, getMetricsCategory());
}
@VisibleForTesting
String getKeyFromAppInfo(AppInfo appInfo) {
return appInfo.uid + "," + appInfo.packageName;
}
}

View File

@@ -59,7 +59,7 @@ public class RestrictAppAction extends BatteryTipAction {
final AppInfo appInfo = appInfos.get(i);
final String packageName = appInfo.packageName;
// Force app standby, then app can't run in the background
mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName,
mBatteryUtils.setForceAppStandby(appInfo.uid, packageName,
AppOpsManager.MODE_IGNORED);
if (CollectionUtils.isEmpty(appInfo.anomalyTypes)) {
// Only log context if there is no anomaly type

View File

@@ -23,6 +23,7 @@ import android.util.Pair;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
/**
@@ -44,12 +45,12 @@ public class UnrestrictAppAction extends BatteryTipAction {
*/
@Override
public void handlePositiveAction(int metricsKey) {
final String packageName = mUnRestrictAppTip.getPackageName();
final AppInfo appInfo = mUnRestrictAppTip.getUnrestrictAppInfo();
// Clear force app standby, then app can run in the background
mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName,
mBatteryUtils.setForceAppStandby(appInfo.uid, appInfo.packageName,
AppOpsManager.MODE_ALLOWED);
mMetricsFeatureProvider.action(mContext,
MetricsProto.MetricsEvent.ACTION_TIP_UNRESTRICT_APP, packageName, Pair.create(
MetricsProto.MetricsEvent.FIELD_CONTEXT, metricsKey));
MetricsProto.MetricsEvent.ACTION_TIP_UNRESTRICT_APP, appInfo.packageName,
Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, metricsKey));
}
}

View File

@@ -72,6 +72,10 @@ public class UnrestrictAppTip extends BatteryTip {
// Do nothing
}
public AppInfo getUnrestrictAppInfo() {
return mAppInfo;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);

View File

@@ -18,6 +18,7 @@ package com.android.settings.notification;
import static com.android.settings.widget.EntityHeaderController.PREF_KEY_APP_HEADER;
import android.app.Activity;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Context;
@@ -26,6 +27,7 @@ import android.support.v7.preference.Preference;
import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.Slog;
import android.view.View;
import com.android.settings.R;
@@ -33,15 +35,13 @@ import com.android.settings.applications.LayoutPreference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.events.OnStart;
import java.util.Objects;
public class HeaderPreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, LifecycleObserver {
private final PreferenceFragment mFragment;
private EntityHeaderController mHeaderController;
private boolean mStarted = false;
public HeaderPreferenceController(Context context, PreferenceFragment fragment) {
super(context, null);
@@ -61,6 +61,13 @@ public class HeaderPreferenceController extends NotificationPreferenceController
@Override
public void updateState(Preference preference) {
if (mAppRow != null && mFragment != null) {
Activity activity = null;
if (mStarted) {
// don't call done on an activity if it hasn't started yet
activity = mFragment.getActivity();
}
LayoutPreference pref = (LayoutPreference) preference;
mHeaderController = EntityHeaderController.newInstance(
mFragment.getActivity(), mFragment, pref.findViewById(R.id.entity_header));
@@ -72,7 +79,7 @@ public class HeaderPreferenceController extends NotificationPreferenceController
.setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
EntityHeaderController.ActionType.ACTION_NONE)
.setHasAppInfoLink(true)
.done(null, mContext);
.done(activity, mContext);
pref.findViewById(R.id.entity_header).setVisibility(View.VISIBLE);
}
}
@@ -108,6 +115,7 @@ public class HeaderPreferenceController extends NotificationPreferenceController
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
mStarted = true;
if (mHeaderController != null) {
mHeaderController.styleActionBar(mFragment.getActivity());
}

View File

@@ -41,6 +41,7 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
@@ -59,6 +60,8 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* Abstract class for audio switcher controller to notify subclass
@@ -70,21 +73,22 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
implements Preference.OnPreferenceChangeListener, BluetoothCallback,
LifecycleObserver, OnStart, OnStop {
private static final String TAG = "AudioSwitchPreferenceController";
private static final int INVALID_INDEX = -1;
protected final List<BluetoothDevice> mConnectedDevices;
protected final AudioManager mAudioManager;
protected final MediaRouter mMediaRouter;
protected final LocalBluetoothProfileManager mProfileManager;
protected int mSelectedIndex;
protected Preference mPreference;
protected LocalBluetoothProfileManager mProfileManager;
protected AudioSwitchCallback mAudioSwitchPreferenceCallback;
private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
private final LocalBluetoothManager mLocalBluetoothManager;
private final MediaRouterCallback mMediaRouterCallback;
private final WiredHeadsetBroadcastReceiver mReceiver;
private final Handler mHandler;
private LocalBluetoothManager mLocalBluetoothManager;
public interface AudioSwitchCallback {
void onPreferenceDataChanged(ListPreference preference);
@@ -94,14 +98,23 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
super(context, preferenceKey);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
mLocalBluetoothManager.setForegroundActivity(context);
mProfileManager = mLocalBluetoothManager.getProfileManager();
mHandler = new Handler(Looper.getMainLooper());
mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback();
mReceiver = new WiredHeadsetBroadcastReceiver();
mMediaRouterCallback = new MediaRouterCallback();
mConnectedDevices = new ArrayList<>();
final FutureTask<LocalBluetoothManager> localBtManagerFutureTask = new FutureTask<>(
// Avoid StrictMode ThreadPolicy violation
() -> Utils.getLocalBtManager(mContext));
try {
localBtManagerFutureTask.run();
mLocalBluetoothManager = localBtManagerFutureTask.get();
} catch (InterruptedException | ExecutionException e) {
Log.w(TAG, "Error getting LocalBluetoothManager.", e);
return;
}
mLocalBluetoothManager.setForegroundActivity(mContext);
mProfileManager = mLocalBluetoothManager.getProfileManager();
}
/**

View File

@@ -317,7 +317,7 @@ public class EntityHeaderController {
return this;
}
actionBar.setBackgroundDrawable(
new ColorDrawable(Utils.getColorAttr(activity, android.R.attr.colorSecondary)));
new ColorDrawable(Utils.getColorAttr(activity, android.R.attr.colorPrimary)));
actionBar.setElevation(0);
if (mRecyclerView != null && mLifecycle != null) {
ActionBarShadowController.attachToRecyclerView(mActivity, mLifecycle, mRecyclerView);

View File

@@ -17,7 +17,9 @@ package com.android.settings.connecteddevice;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import android.content.ContentResolver;
import android.content.Context;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.nfc.NfcPreferenceController;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -39,16 +41,20 @@ import static org.robolectric.Shadows.shadowOf;
public class AdvancedConnectedDeviceControllerTest {
private static final String KEY = "test_key";
private static final String DRIVING_MODE_SETTINGS_ENABLED =
"gearhead:driving_mode_settings_enabled";
private Context mContext;
private NfcPreferenceController mNfcController;
private ShadowNfcAdapter mShadowNfcAdapter;
private ContentResolver mContentResolver;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mContentResolver = mContext.getContentResolver();
mNfcController = new NfcPreferenceController(mContext);
mShadowNfcAdapter = shadowOf(ShadowNfcAdapter.getNfcAdapter(mContext));
}
@@ -62,43 +68,53 @@ public class AdvancedConnectedDeviceControllerTest {
AVAILABLE);
}
@Test
public void isDrivingModeAvailable_returnTrue() {
Settings.System.putInt(mContentResolver, DRIVING_MODE_SETTINGS_ENABLED, 1);
assertThat(AdvancedConnectedDeviceController.isDrivingModeAvailable(mContext)).isTrue();
}
@Test
public void isDrivingModeAvailable_returnFalse() {
Settings.System.putInt(mContentResolver, DRIVING_MODE_SETTINGS_ENABLED, 0);
assertThat(AdvancedConnectedDeviceController.isDrivingModeAvailable(mContext)).isFalse();
}
@Test
public void getConnectedDevicesSummaryResourceId_NFCAndDrivingModeAvailable() {
// NFC available, driving mode available
final boolean isDrivingModeAvailable = true;
mShadowNfcAdapter.setEnabled(true);
assertThat(AdvancedConnectedDeviceController
.getConnectedDevicesSummaryResourceId(mNfcController, isDrivingModeAvailable))
.getConnectedDevicesSummaryResourceId(mNfcController, true))
.isEqualTo(R.string.connected_devices_dashboard_summary);
}
@Test
public void getConnectedDevicesSummaryResourceId_NFCAvailableAndDrivingModeNotAvailable() {
// NFC is available, driving mode not available
final boolean isDrivingModeAvailable = false;
mShadowNfcAdapter.setEnabled(true);
assertThat(AdvancedConnectedDeviceController
.getConnectedDevicesSummaryResourceId(mNfcController, isDrivingModeAvailable))
.getConnectedDevicesSummaryResourceId(mNfcController, false))
.isEqualTo(R.string.connected_devices_dashboard_no_driving_mode_summary);
}
@Test
public void getConnectedDevicesSummaryResourceId_NFCNotAvailableDrivingModeAvailable() {
// NFC not available, driving mode available
final boolean isDrivingModeAvailable = true;
ReflectionHelpers.setField(mNfcController, "mNfcAdapter", null);
assertThat(AdvancedConnectedDeviceController
.getConnectedDevicesSummaryResourceId(mNfcController, isDrivingModeAvailable))
.getConnectedDevicesSummaryResourceId(mNfcController, true))
.isEqualTo(R.string.connected_devices_dashboard_no_nfc_summary);
}
@Test
public void getConnectedDevicesSummaryResourceId_NFCAndDrivingModeNotAvailable() {
// NFC not available, driving mode not available
final boolean isDrivingModeAvailable = false;
ReflectionHelpers.setField(mNfcController, "mNfcAdapter", null);
assertThat(AdvancedConnectedDeviceController
.getConnectedDevicesSummaryResourceId(mNfcController, isDrivingModeAvailable))
.getConnectedDevicesSummaryResourceId(mNfcController, false))
.isEqualTo(R.string.connected_devices_dashboard_no_driving_mode_no_nfc_summary);
}
}

View File

@@ -30,8 +30,8 @@ import android.content.pm.PackageManager;
import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.nfc.NfcPreferenceController;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothPan;
import com.android.settings.testutils.shadow.ShadowConnectivityManager;
import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -48,7 +48,7 @@ import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothPan.class, ShadowUserManager.class,
ShadowConnectivityManager.class})
ShadowConnectivityManager.class, ShadowBluetoothAdapter.class})
public class ConnectedDeviceDashboardFragmentTest {
@Mock
private PackageManager mPackageManager;

View File

@@ -0,0 +1,180 @@
/*
* Copyright 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.connecteddevice;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.v7.preference.PreferenceScreen;
import android.text.BidiFormatter;
import android.text.TextUtils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.bluetooth.AlwaysDiscoverable;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothPan;
import com.android.settings.testutils.shadow.ShadowLocalBluetoothAdapter;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.FooterPreferenceMixin;
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.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothPan.class, ShadowBluetoothAdapter.class,
ShadowLocalBluetoothAdapter.class})
public class DiscoverableFooterPreferenceControllerTest {
private static final String DEVICE_NAME = "device name";
private static final String KEY = "discoverable_footer_preference";
@Mock
private PackageManager mPackageManager;
@Mock
private PreferenceScreen mScreen;
@Mock
private FooterPreferenceMixin mFooterPreferenceMixin;
@Mock
private AlwaysDiscoverable mAlwaysDiscoverable;
private Context mContext;
private FooterPreference mPreference;
private DiscoverableFooterPreferenceController mDiscoverableFooterPreferenceController;
private BroadcastReceiver mBluetoothChangedReceiver;
private ShadowApplication mShadowApplication;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowApplication = Shadows.shadowOf(RuntimeEnvironment.application);
mContext = spy(RuntimeEnvironment.application);
doReturn(mPackageManager).when(mContext).getPackageManager();
mDiscoverableFooterPreferenceController =
new DiscoverableFooterPreferenceController(mContext);
mPreference = spy(new FooterPreference(mContext));
mDiscoverableFooterPreferenceController.init(mFooterPreferenceMixin, mPreference,
mAlwaysDiscoverable);
mBluetoothChangedReceiver = mDiscoverableFooterPreferenceController
.mBluetoothChangedReceiver;
}
@Test
public void getAvailabilityStatus_noBluetoothFeature_returnUnSupported() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)).thenReturn(false);
assertThat(mDiscoverableFooterPreferenceController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_BluetoothFeature_returnAvailable() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)).thenReturn(true);
assertThat(mDiscoverableFooterPreferenceController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
@Test
public void displayPreference() {
when(mFooterPreferenceMixin.createFooterPreference()).thenReturn(mPreference);
mDiscoverableFooterPreferenceController.displayPreference(mScreen);
verify(mPreference).setKey(KEY);
verify(mScreen).addPreference(mPreference);
}
@Test
public void onResume() {
mDiscoverableFooterPreferenceController.onResume();
assertThat(getRegisteredBroadcastReceivers()).contains(mBluetoothChangedReceiver);
verify(mAlwaysDiscoverable).start();
}
@Test
public void onPause() {
mDiscoverableFooterPreferenceController.onResume();
mDiscoverableFooterPreferenceController.onPause();
assertThat(getRegisteredBroadcastReceivers()).doesNotContain(mBluetoothChangedReceiver);
verify(mAlwaysDiscoverable).stop();
}
@Test
public void onBluetoothStateChanged_bluetoothOn_updateTitle() {
ShadowLocalBluetoothAdapter.setName(DEVICE_NAME);
sendBluetoothStateChangedIntent(BluetoothAdapter.STATE_ON);
assertThat(mPreference.getTitle()).isEqualTo(generateTitle(DEVICE_NAME));
}
@Test
public void onBluetoothStateChanged_bluetoothOff_updateTitle(){
ShadowLocalBluetoothAdapter.setName(DEVICE_NAME);
sendBluetoothStateChangedIntent(BluetoothAdapter.STATE_OFF);
assertThat(mPreference.getTitle()).isEqualTo(generateTitle(null));
}
private CharSequence generateTitle(String deviceName) {
if (deviceName == null) {
return mContext.getString(R.string.bluetooth_off_footer);
} else {
return TextUtils.expandTemplate(
mContext.getText(R.string.bluetooth_device_name_summary),
BidiFormatter.getInstance().unicodeWrap(deviceName));
}
}
private void sendBluetoothStateChangedIntent(int state) {
Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
mBluetoothChangedReceiver.onReceive(mContext, intent);
}
/**
* Return a list of all the registered broadcast receivers
*/
private List<BroadcastReceiver> getRegisteredBroadcastReceivers() {
List<BroadcastReceiver> registeredBroadcastReceivers = new ArrayList();
List<ShadowApplication.Wrapper> registeredReceivers =
mShadowApplication.getRegisteredReceivers();
for (ShadowApplication.Wrapper wrapper : registeredReceivers) {
registeredBroadcastReceivers.add(wrapper.getBroadcastReceiver());
}
return registeredBroadcastReceivers;
}
}

View File

@@ -21,7 +21,9 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.robolectric.Shadows.shadowOf;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -38,6 +40,11 @@ import android.widget.CheckBox;
import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -46,13 +53,15 @@ import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowAlertDialog;
import org.robolectric.shadows.ShadowDialog;
import org.robolectric.util.FragmentTestUtil;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@RunWith(SettingsRobolectricTestRunner.class)
public class RestrictedAppDetailsTest {
private static final String PACKAGE_NAME = "com.android.app";
@@ -60,20 +69,21 @@ public class RestrictedAppDetailsTest {
private static final int UID = UserHandle.getUid(USER_ID, 234);
private static final String APP_NAME = "app";
@Mock
private PackageManager mPackageManager;
@Mock
private ApplicationInfo mApplicationInfo;
@Mock
private IconDrawableFactory mIconDrawableFactory;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceManager mPreferenceManager;
@Mock
private InstrumentedPreferenceFragment mFragment;
private PreferenceManager mPreferenceManager;
private RestrictedAppDetails mRestrictedAppDetails;
private Context mContext;
private AppInfo mAppInfo;
private Intent mIntent;
private CheckBoxPreference mCheckBoxPreference;
@Before
public void setUp() {
@@ -86,8 +96,9 @@ public class RestrictedAppDetailsTest {
.setUid(UID)
.build();
mPreferenceManager = new PreferenceManager(mContext);
doReturn(mPreferenceManager).when(mRestrictedAppDetails).getPreferenceManager();
doReturn(mContext).when(mPreferenceManager).getContext();
doReturn(mContext).when(mFragment).getContext();
mRestrictedAppDetails.mPackageManager = mPackageManager;
mRestrictedAppDetails.mIconDrawableFactory = mIconDrawableFactory;
@@ -97,10 +108,13 @@ public class RestrictedAppDetailsTest {
mRestrictedAppDetails.mBatteryUtils = spy(new BatteryUtils(mContext));
doReturn(mPreferenceManager).when(
mRestrictedAppDetails.mRestrictedAppListGroup).getPreferenceManager();
mCheckBoxPreference = new CheckBoxPreference(mContext);
mCheckBoxPreference.setKey(mRestrictedAppDetails.getKeyFromAppInfo(mAppInfo));
}
@Test
public void testRefreshUi_displayPreference() throws Exception {
public void refreshUi_displayPreference() throws Exception {
doReturn(mApplicationInfo).when(mPackageManager)
.getApplicationInfoAsUser(PACKAGE_NAME, 0, USER_ID);
doReturn(APP_NAME).when(mPackageManager).getApplicationLabel(mApplicationInfo);
@@ -117,7 +131,7 @@ public class RestrictedAppDetailsTest {
}
@Test
public void testStartRestrictedAppDetails_startWithCorrectData() {
public void startRestrictedAppDetails_startWithCorrectData() {
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
doAnswer(invocation -> {
// Get the intent in which it has the app info bundle
@@ -135,4 +149,50 @@ public class RestrictedAppDetailsTest {
RestrictedAppDetails.EXTRA_APP_INFO_LIST);
assertThat(appInfos).containsExactly(mAppInfo);
}
@Test
public void createDialogFragment_toRestrict_createRestrictDialog() {
final BatteryTipDialogFragment dialogFragment = mRestrictedAppDetails.createDialogFragment(
mAppInfo, true);
FragmentTestUtil.startFragment(dialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getTitle()).isEqualTo("Restrict app?");
}
@Test
public void createDialogFragment_toUnrestrict_createUnrestrictDialog() {
final BatteryTipDialogFragment dialogFragment = mRestrictedAppDetails.createDialogFragment(
mAppInfo, false);
FragmentTestUtil.startFragment(dialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getTitle()).isEqualTo("Remove restriction?");
}
@Test
public void onBatteryTipHandled_restrict_setChecked() {
final RestrictAppTip restrictAppTip = new RestrictAppTip(BatteryTip.StateType.NEW,
mAppInfo);
mRestrictedAppDetails.mRestrictedAppListGroup.addPreference(mCheckBoxPreference);
mRestrictedAppDetails.onBatteryTipHandled(restrictAppTip);
assertThat(mCheckBoxPreference.isChecked()).isTrue();
}
@Test
public void onBatteryTipHandled_unrestrict_setUnchecked() {
final UnrestrictAppTip unrestrictAppTip = new UnrestrictAppTip(BatteryTip.StateType.NEW,
mAppInfo);
mRestrictedAppDetails.mRestrictedAppListGroup.addPreference(mCheckBoxPreference);
mRestrictedAppDetails.onBatteryTipHandled(unrestrictAppTip);
assertThat(mCheckBoxPreference.isChecked()).isFalse();
}
}

View File

@@ -46,6 +46,8 @@ import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class RestrictAppActionTest {
private static final int UID_1 = 12345;
private static final int UID_2 = 23456;
private static final String PACKAGE_NAME_1 = "com.android.app1";
private static final String PACKAGE_NAME_2 = "com.android.app2";
private static final int ANOMALY_WAKEUP = 0;
@@ -63,9 +65,11 @@ public class RestrictAppActionTest {
final List<AppInfo> mAppInfos = new ArrayList<>();
mAppInfos.add(new AppInfo.Builder()
.setUid(UID_1)
.setPackageName(PACKAGE_NAME_1)
.build());
mAppInfos.add(new AppInfo.Builder()
.setUid(UID_2)
.setPackageName(PACKAGE_NAME_2)
.addAnomalyType(ANOMALY_BT)
.addAnomalyType(ANOMALY_WAKEUP)
@@ -87,9 +91,9 @@ public class RestrictAppActionTest {
mRestrictAppAction.handlePositiveAction(METRICS_KEY);
verify(mBatteryUtils)
.setForceAppStandby(anyInt(), eq(PACKAGE_NAME_1), eq(AppOpsManager.MODE_IGNORED));
.setForceAppStandby(UID_1, PACKAGE_NAME_1, AppOpsManager.MODE_IGNORED);
verify(mBatteryUtils)
.setForceAppStandby(anyInt(), eq(PACKAGE_NAME_2), eq(AppOpsManager.MODE_IGNORED));
.setForceAppStandby(UID_2, PACKAGE_NAME_2, AppOpsManager.MODE_IGNORED);
verify(mFeatureFactory.metricsFeatureProvider).action(RuntimeEnvironment.application,
MetricsProto.MetricsEvent.ACTION_TIP_RESTRICT_APP, PACKAGE_NAME_1, Pair.create(
MetricsProto.MetricsEvent.FIELD_CONTEXT, METRICS_KEY));

View File

@@ -42,6 +42,7 @@ import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsRobolectricTestRunner.class)
public class UnrestrictAppActionTest {
private static final int UID_1 = 12345;
private static final String PACKAGE_NAME_1 = "com.android.app1";
private static final int METRICS_KEY = 1;
@@ -54,7 +55,10 @@ public class UnrestrictAppActionTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
final AppInfo appInfo = new AppInfo.Builder().setPackageName(PACKAGE_NAME_1).build();
final AppInfo appInfo = new AppInfo.Builder()
.setUid(UID_1)
.setPackageName(PACKAGE_NAME_1)
.build();
mFeatureFactory = FakeFeatureFactory.setupForTest();
mUnrestrictAppAction = new UnrestrictAppAction(RuntimeEnvironment.application,
@@ -72,7 +76,7 @@ public class UnrestrictAppActionTest {
mUnrestrictAppAction.handlePositiveAction(METRICS_KEY);
verify(mBatteryUtils)
.setForceAppStandby(anyInt(), eq(PACKAGE_NAME_1), eq(AppOpsManager.MODE_ALLOWED));
.setForceAppStandby(UID_1, PACKAGE_NAME_1, AppOpsManager.MODE_ALLOWED);
verify(mFeatureFactory.metricsFeatureProvider).action(RuntimeEnvironment.application,
MetricsProto.MetricsEvent.ACTION_TIP_UNRESTRICT_APP, PACKAGE_NAME_1, Pair.create(
MetricsProto.MetricsEvent.FIELD_CONTEXT, METRICS_KEY));

View File

@@ -1,7 +1,6 @@
package com.android.settings.testutils.shadow;
import static android.util.TypedValue.TYPE_REFERENCE;
import static org.robolectric.RuntimeEnvironment.application;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.shadow.api.Shadow.directlyOn;
@@ -122,8 +121,6 @@ public class SettingsShadowResources extends ShadowResources {
id = R.drawable.ic_settings_wireless;
} else if (id == R.drawable.app_filter_spinner_background) {
id = R.drawable.ic_expand_more_inverse;
} else if (id == R.drawable.selectable_card_grey) {
id = R.drawable.ic_expand_more_inverse;
}
return super.loadDrawable(value, id, theme);
}

View File

@@ -50,8 +50,6 @@ public class SettingsShadowResourcesImpl extends ShadowResourcesImpl {
id = R.drawable.ic_settings_wireless;
} else if (id == R.drawable.app_filter_spinner_background) {
id = R.drawable.ic_expand_more_inverse;
} else if (id == R.drawable.selectable_card_grey) {
id = R.drawable.ic_expand_more_inverse;
} else if (id == R.drawable.color_bar_progress
|| id == R.drawable.ring_progress) {
// color_bar_progress and ring_progress use hidden resources, so just use the regular

View File

@@ -0,0 +1,36 @@
/*
* 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.testutils.shadow;
import android.bluetooth.BluetoothAdapter;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import java.util.ArrayList;
import java.util.List;
@Implements(value = BluetoothAdapter.class, inheritImplementationMethods = true)
public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBluetoothAdapter {
/**
* Do nothing, implement it to avoid null pointer error inside BluetoothAdapter
*/
@Implementation
public List<Integer> getSupportedProfiles() {
return new ArrayList<Integer>();
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.testutils.shadow;
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(LocalBluetoothAdapter.class)
public class ShadowLocalBluetoothAdapter {
private static String sName;
@Implementation
public String getName() {
return sName;
}
public static void setName(String name) {
sName = name;
}
}