Snap for 7314842 from e14cc634dc to sc-release
Change-Id: I9459ee8072eead32b91f477fbbe9d9a461ee0a07
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
android:id="@+id/chart_summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="45dp"
|
||||
android:layout_marginBottom="30dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
settings:textColor="?android:attr/textColorSecondary"
|
||||
android:text="@string/battery_usage_chart_graph_hint" />
|
||||
@@ -34,9 +34,16 @@
|
||||
<com.android.settings.fuelgauge.BatteryChartView
|
||||
android:id="@+id/battery_chart"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="141dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
settings:textColor="?android:attr/textColorSecondary" />
|
||||
<TextView
|
||||
android:id="@+id/companion_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorSecondary"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
24
res/layout/companion_apps_remove_button_widget.xml
Normal file
24
res/layout/companion_apps_remove_button_widget.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:contentDescription="@string/remove_association_button"
|
||||
android:id="@+id/remove_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_clear"
|
||||
android:background="@android:color/transparent"/>
|
||||
26
res/layout/preference_companion_app.xml
Normal file
26
res/layout/preference_companion_app.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
||||
</LinearLayout>
|
||||
@@ -205,6 +205,9 @@
|
||||
<dimen name="bluetooth_pairing_edittext_padding">21dp</dimen>
|
||||
<dimen name="bluetooth_checkbox_padding">16dp</dimen>
|
||||
|
||||
<!-- CompanionAppWidgetPreferencce Padding -->
|
||||
<dimen name="bluetooth_companion_app_widget">20dp</dimen>
|
||||
|
||||
<!-- WiFi Preferences -->
|
||||
<dimen name="wifi_divider_height">1px</dimen>
|
||||
<dimen name="wifi_ap_band_checkbox_padding">16dp</dimen>
|
||||
@@ -441,6 +444,7 @@
|
||||
<dimen name="subtitle_bottom_padding">24dp</dimen>
|
||||
|
||||
<!-- Battery usage chart view component -->
|
||||
<dimen name="chartview_text_padding">3dp</dimen>
|
||||
<dimen name="chartview_divider_width">1dp</dimen>
|
||||
<dimen name="chartview_divider_height">4dp</dimen>
|
||||
<dimen name="chartview_trapezoid_radius">3dp</dimen>
|
||||
|
||||
@@ -1815,6 +1815,10 @@
|
||||
<string name="bluetooth_device_mac_address">Device\'s Bluetooth address: <xliff:g id="address">%1$s</xliff:g></string>
|
||||
<!-- Bluetooth device details. The title of a confirmation dialog for unpairing a paired device. [CHAR LIMIT=60] -->
|
||||
<string name="bluetooth_unpair_dialog_title">Forget device?</string>
|
||||
<!-- Content Description for companion device app associations removal button [CHAR LIMIT=28]-->
|
||||
<string name="remove_association_button">Remove association</string>
|
||||
<!-- Bluetooth device details companion apps. The title of a confirmation dialog for remove an app. [CHAR LIMIT=60] -->
|
||||
<string name="bluetooth_companion_app_remove_association_dialog_title">Disconnect App?</string>
|
||||
|
||||
<!-- Bluetooth device details. The body of a confirmation dialog for unpairing a paired device. -->
|
||||
<string name="bluetooth_unpair_dialog_body" product="default">Your phone will no longer be paired with <xliff:g id="device_name">%1$s</xliff:g></string>
|
||||
@@ -1822,12 +1826,16 @@
|
||||
<string name="bluetooth_unpair_dialog_body" product="tablet">Your tablet will no longer be paired with <xliff:g id="device_name">%1$s</xliff:g></string>
|
||||
<!-- Bluetooth device details. The body of a confirmation dialog for unpairing a paired device. -->
|
||||
<string name="bluetooth_unpair_dialog_body" product="device">Your device will no longer be paired with <xliff:g id="device_name">%1$s</xliff:g></string>
|
||||
<!-- Bluetooth device details companion apps. The body of confirmation dialog for remove association. [CHAR LIMIT=60] -->
|
||||
<string name="bluetooth_companion_app_body"><xliff:g id="app_name" example="App Name">%1$s</xliff:g> app will no longer connect to your <xliff:g id="device_name" example="Device Name">%2$s</xliff:g></string>
|
||||
|
||||
<!-- Bluetooth device details. The body of a confirmation dialog for unpairing a paired device. [CHAR LIMIT=NONE] -->
|
||||
<string name="bluetooth_untethered_unpair_dialog_body"><xliff:g id="device_name" example="Jack's headphone">%1$s</xliff:g> will no longer be paired with any device linked to this account</string>
|
||||
|
||||
<!-- Bluetooth device details. In the confirmation dialog for unpairing a paired device, this is the label on the button that will complete the unpairing action. -->
|
||||
<string name="bluetooth_unpair_dialog_forget_confirm_button">Forget device</string>
|
||||
<!-- Bluetooth device details companion apps. In the confirmation dialog for removing an associated app, this is the label on the button that will complete the disassociate action. [CHAR LIMIT=80] -->
|
||||
<string name = "bluetooth_companion_app_remove_association_confirm_button">Disconnect app</string>
|
||||
|
||||
<!-- Bluetooth settings. The title of the screen to pick which profiles to connect to on the device. For example, headphones may have both A2DP and headset, this allows the users to choose which one they want to connect to. -->
|
||||
<string name="bluetooth_connect_specific_profiles_title">Connect to\u2026</string>
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
<com.android.settings.widget.SeekBarPreference
|
||||
android:key="accessibility_button_opacity"
|
||||
android:title="@string/accessibility_button_opacity_title"
|
||||
android:selectable="true"
|
||||
android:persistent="false"
|
||||
settings:controller="com.android.settings.accessibility.FloatingMenuOpacityPreferenceController"/>
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
settings:allowDividerBelow="true"
|
||||
settings:allowDividerAbove="true"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="device_companion_apps"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="bluetooth_profiles"/>
|
||||
|
||||
|
||||
@@ -38,26 +38,31 @@
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:userRestriction="no_config_date_time"/>
|
||||
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="auto_zone"
|
||||
android:title="@string/zone_auto_title"
|
||||
settings:allowDividerAbove="true"
|
||||
settings:userRestriction="no_config_date_time"/>
|
||||
<PreferenceCategory
|
||||
android:key="timezone_preference_category"
|
||||
android:title="@string/date_time_set_timezone_title">
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:key="timezone"
|
||||
android:title="@string/date_time_set_timezone_title"
|
||||
android:summary="@string/summary_placeholder"
|
||||
android:fragment="com.android.settings.datetime.timezone.TimeZoneSettings"
|
||||
settings:userRestriction="no_config_date_time"
|
||||
settings:keywords="@string/keywords_time_zone"/>
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="auto_zone"
|
||||
android:title="@string/zone_auto_title"
|
||||
settings:allowDividerAbove="true"
|
||||
settings:userRestriction="no_config_date_time"/>
|
||||
|
||||
<!-- This preference gets removed if location-based time zone detection is not supported -->
|
||||
<SwitchPreference
|
||||
android:key="location_time_zone_detection"
|
||||
android:title="@string/location_time_zone_detection_toggle_title"
|
||||
settings:controller="com.android.settings.datetime.LocationTimeZoneDetectionPreferenceController"
|
||||
/>
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:key="timezone"
|
||||
android:title="@string/date_time_set_timezone_title"
|
||||
android:summary="@string/summary_placeholder"
|
||||
android:fragment="com.android.settings.datetime.timezone.TimeZoneSettings"
|
||||
settings:userRestriction="no_config_date_time"
|
||||
settings:keywords="@string/keywords_time_zone"/>
|
||||
|
||||
<!-- This preference gets removed if location-based time zone detection is not supported -->
|
||||
<SwitchPreference
|
||||
android:key="location_time_zone_detection"
|
||||
android:title="@string/location_time_zone_detection_toggle_title"
|
||||
settings:controller="com.android.settings.datetime.LocationTimeZoneDetectionPreferenceController"
|
||||
/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="time_format_preference_category"
|
||||
|
||||
66
res/xml/power_usage_detail_legacy.xml
Normal file
66
res/xml/power_usage_detail_legacy.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<com.android.settingslib.widget.LayoutPreference
|
||||
android:key="header_view"
|
||||
android:layout="@layout/settings_entity_header"
|
||||
android:selectable="false"
|
||||
android:order="-10000"
|
||||
settings:allowDividerBelow="true"/>
|
||||
|
||||
<com.android.settingslib.widget.ActionButtonsPreference
|
||||
android:key="action_buttons"
|
||||
android:order="-9999"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/battery_detail_manage_title"
|
||||
settings:allowDividerAbove="true">
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:key="background_activity"
|
||||
android:title="@string/background_activity_title"
|
||||
android:selectable="true"
|
||||
settings:userRestriction="no_control_apps"/>
|
||||
|
||||
<Preference
|
||||
android:key="battery_optimization"
|
||||
android:title="@string/high_power_apps"
|
||||
android:summary="@string/high_power_off"
|
||||
android:selectable="true"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/battery_detail_info_title">
|
||||
|
||||
<Preference
|
||||
android:key="app_usage_foreground"
|
||||
android:title="@string/battery_detail_foreground"
|
||||
android:selectable="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="app_usage_background"
|
||||
android:title="@string/battery_detail_background"
|
||||
android:selectable="false"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -78,7 +78,7 @@
|
||||
<!-- On lock screen notifications -->
|
||||
<com.android.settings.RestrictedListPreference
|
||||
android:key="privacy_lock_screen_notifications"
|
||||
android:title="@string/lock_screen_notifications_title"
|
||||
android:title="@string/lock_screen_notifs_title"
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:searchable="false"/>
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.VectorDrawable;
|
||||
@@ -94,6 +95,7 @@ import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TabWidget;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
@@ -111,6 +113,7 @@ import com.android.settings.dashboard.profileselector.ProfileFragmentBridge;
|
||||
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settingslib.widget.ActionBarShadowController;
|
||||
import com.android.settingslib.widget.AdaptiveIcon;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -967,15 +970,36 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preference icon with a drawable that is scaled down to to avoid crashing Settings if
|
||||
* it's too big.
|
||||
* Gets the adaptive icon with a drawable that wrapped with an adaptive background using {@code
|
||||
* backgroundColor} if it is not a {@link AdaptiveIconDrawable}
|
||||
*
|
||||
* If the given {@code icon} is too big, it will be auto scaled down to to avoid crashing
|
||||
* Settings.
|
||||
*/
|
||||
public static void setSafeIcon(Preference pref, Drawable icon) {
|
||||
public static Drawable getAdaptiveIcon(Context context, Drawable icon,
|
||||
@ColorInt int backgroundColor) {
|
||||
Drawable adaptiveIcon = getSafeIcon(icon);
|
||||
|
||||
if (!(adaptiveIcon instanceof AdaptiveIconDrawable)) {
|
||||
adaptiveIcon = new AdaptiveIcon(context, adaptiveIcon);
|
||||
((AdaptiveIcon) adaptiveIcon).setBackgroundColor(backgroundColor);
|
||||
}
|
||||
|
||||
return adaptiveIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the icon with a drawable that is scaled down to to avoid crashing Settings if it's too
|
||||
* big and not a {@link VectorDrawable}.
|
||||
*/
|
||||
public static Drawable getSafeIcon(Drawable icon) {
|
||||
Drawable safeIcon = icon;
|
||||
|
||||
if ((icon != null) && !(icon instanceof VectorDrawable)) {
|
||||
safeIcon = getSafeDrawable(icon, 500, 500);
|
||||
}
|
||||
pref.setIcon(safeIcon);
|
||||
|
||||
return safeIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -985,7 +1009,7 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
* @param maxWidth maximum width, in pixels.
|
||||
* @param maxHeight maximum height, in pixels.
|
||||
*/
|
||||
public static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) {
|
||||
private static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) {
|
||||
final int actualWidth = original.getMinimumWidth();
|
||||
final int actualHeight = original.getMinimumHeight();
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -663,7 +664,7 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
preference.setKey(key);
|
||||
preference.setTitle(title);
|
||||
preference.setSummary(summary);
|
||||
Utils.setSafeIcon(preference, icon);
|
||||
preference.setIcon(Utils.getAdaptiveIcon(mContext, icon, Color.WHITE));
|
||||
preference.setFragment(fragment);
|
||||
preference.setIconSize(ICON_SIZE_MEDIUM);
|
||||
preference.setPersistent(false); // Disable SharedPreferences.
|
||||
|
||||
@@ -124,7 +124,7 @@ public class PasswordsPreferenceController extends BasePreferenceController
|
||||
serviceInfo,
|
||||
serviceInfo.applicationInfo,
|
||||
user);
|
||||
Utils.setSafeIcon(pref, icon);
|
||||
pref.setIcon(Utils.getSafeIcon(icon));
|
||||
pref.setIntent(
|
||||
new Intent(Intent.ACTION_MAIN)
|
||||
.setClassName(serviceInfo.packageName, service.getPasswordsActivity()));
|
||||
|
||||
@@ -66,7 +66,7 @@ public abstract class DefaultAppPreferenceController extends AbstractPreferenceC
|
||||
}
|
||||
if (!TextUtils.isEmpty(defaultAppLabel)) {
|
||||
preference.setSummary(defaultAppLabel);
|
||||
Utils.setSafeIcon(preference, getDefaultAppIcon());
|
||||
preference.setIcon(Utils.getSafeIcon(getDefaultAppIcon()));
|
||||
} else {
|
||||
Log.d(TAG, "No default app");
|
||||
preference.setSummary(R.string.app_list_preference_none);
|
||||
|
||||
@@ -57,15 +57,15 @@ public class FaceSettingsAttentionPreferenceController extends FaceSettingsPrefe
|
||||
|
||||
private final GetFeatureCallback mGetFeatureCallback = new GetFeatureCallback() {
|
||||
@Override
|
||||
public void onCompleted(boolean success, int feature, boolean value) {
|
||||
if (feature == FaceManager.FEATURE_REQUIRE_ATTENTION && success) {
|
||||
if (!mFaceManager.hasEnrolledTemplates(getUserId())) {
|
||||
mPreference.setEnabled(false);
|
||||
} else {
|
||||
mPreference.setEnabled(true);
|
||||
mPreference.setChecked(value);
|
||||
public void onCompleted(boolean success, int[] features, boolean[] featureState) {
|
||||
boolean requireAttentionEnabled = false;
|
||||
for (int i = 0; i < features.length; i++) {
|
||||
if (features[i] == FaceManager.FEATURE_REQUIRE_ATTENTION) {
|
||||
requireAttentionEnabled = featureState[i];
|
||||
}
|
||||
}
|
||||
mPreference.setEnabled(success);
|
||||
mPreference.setChecked(requireAttentionEnabled);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
|
||||
import static com.android.internal.util.CollectionUtils.filter;
|
||||
|
||||
import android.companion.Association;
|
||||
import android.companion.CompanionDeviceManager;
|
||||
import android.companion.ICompanionDeviceManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* This class adds Companion Device app rows to launch the app or remove the associations
|
||||
*/
|
||||
public class BluetoothDetailsCompanionAppsController extends BluetoothDetailsController {
|
||||
public static final String KEY_DEVICE_COMPANION_APPS = "device_companion_apps";
|
||||
private static final String LOG_TAG = "BTCompanionController";
|
||||
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
|
||||
@VisibleForTesting
|
||||
PreferenceCategory mProfilesContainer;
|
||||
|
||||
@VisibleForTesting
|
||||
CompanionDeviceManager mCompanionDeviceManager;
|
||||
|
||||
@VisibleForTesting
|
||||
PackageManager mPackageManager;
|
||||
|
||||
public BluetoothDetailsCompanionAppsController(Context context,
|
||||
PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) {
|
||||
super(context, fragment, device, lifecycle);
|
||||
mCachedDevice = device;
|
||||
mCompanionDeviceManager = context.getSystemService(CompanionDeviceManager.class);
|
||||
mPackageManager = context.getPackageManager();
|
||||
lifecycle.addObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init(PreferenceScreen screen) {
|
||||
mProfilesContainer = screen.findPreference(getPreferenceKey());
|
||||
mProfilesContainer.setLayoutResource(R.layout.preference_companion_app);
|
||||
}
|
||||
|
||||
private List<Association> getAssociations(String address) {
|
||||
return filter(
|
||||
mCompanionDeviceManager.getAllAssociations(),
|
||||
a -> Objects.equal(address, a.getDeviceMacAddress()));
|
||||
}
|
||||
|
||||
private static void removePreference(PreferenceCategory container, String packageName) {
|
||||
Preference preference = container.findPreference(packageName);
|
||||
if (preference != null) {
|
||||
container.removePreference(preference);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeAssociationDialog(String packageName, String address,
|
||||
PreferenceCategory container, CharSequence appName, Context context) {
|
||||
DialogInterface.OnClickListener dialogClickListener = (dialog, which) -> {
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
removeAssociation(packageName, address, container);
|
||||
}
|
||||
};
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
|
||||
builder.setPositiveButton(
|
||||
R.string.bluetooth_companion_app_remove_association_confirm_button,
|
||||
dialogClickListener)
|
||||
.setNegativeButton(android.R.string.cancel, dialogClickListener)
|
||||
.setTitle(R.string.bluetooth_companion_app_remove_association_dialog_title)
|
||||
.setMessage(mContext.getString(
|
||||
R.string.bluetooth_companion_app_body, appName, mCachedDevice.getName()))
|
||||
.show();
|
||||
}
|
||||
|
||||
private static void removeAssociation(String packageName, String address,
|
||||
PreferenceCategory container) {
|
||||
try {
|
||||
java.util.Objects.requireNonNull(ICompanionDeviceManager.Stub.asInterface(
|
||||
ServiceManager.getService(
|
||||
Context.COMPANION_DEVICE_SERVICE))).disassociate(
|
||||
address, packageName);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
removePreference(container, packageName);
|
||||
}
|
||||
|
||||
private CharSequence getAppName(String packageName) {
|
||||
CharSequence appName = null;
|
||||
try {
|
||||
appName = mPackageManager.getApplicationLabel(
|
||||
mPackageManager.getApplicationInfo(packageName, 0));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(LOG_TAG, "Package Not Found", e);
|
||||
}
|
||||
|
||||
return appName;
|
||||
}
|
||||
|
||||
private List<String> getPreferencesNeedToShow(String address, PreferenceCategory container) {
|
||||
List<String> preferencesToRemove = new ArrayList<>();
|
||||
Set<String> packages = getAssociations(address)
|
||||
.stream().map(Association::getPackageName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (int i = 0; i < container.getPreferenceCount(); i++) {
|
||||
String preferenceKey = container.getPreference(i).getKey();
|
||||
if (packages.isEmpty() || !packages.contains(preferenceKey)) {
|
||||
preferencesToRemove.add(preferenceKey);
|
||||
}
|
||||
}
|
||||
|
||||
for (String preferenceName : preferencesToRemove) {
|
||||
removePreference(container, preferenceName);
|
||||
}
|
||||
|
||||
return packages.stream()
|
||||
.filter(p -> container.findPreference(p) == null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the state of the preferences for all the associations, possibly adding or
|
||||
* removing preferences as needed.
|
||||
*/
|
||||
@Override
|
||||
protected void refresh() {
|
||||
updatePreferences(mContext, mCachedDevice.getAddress(), mProfilesContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add preferences for each association for the bluetooth device
|
||||
*/
|
||||
public void updatePreferences(Context context,
|
||||
String address, PreferenceCategory container) {
|
||||
Set<String> addedPackages = new HashSet<>();
|
||||
|
||||
for (String packageName : getPreferencesNeedToShow(address, container)) {
|
||||
CharSequence appName = getAppName(packageName);
|
||||
|
||||
if (TextUtils.isEmpty(appName) || !addedPackages.add(packageName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Drawable removeIcon = context.getResources().getDrawable(R.drawable.ic_clear);
|
||||
CompanionAppWidgetPreference preference = new CompanionAppWidgetPreference(
|
||||
removeIcon,
|
||||
v -> removeAssociationDialog(packageName, address, container, appName, context),
|
||||
context
|
||||
);
|
||||
|
||||
Drawable appIcon;
|
||||
|
||||
try {
|
||||
appIcon = mPackageManager.getApplicationIcon(packageName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(LOG_TAG, "Icon Not Found", e);
|
||||
continue;
|
||||
}
|
||||
Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
preference.setIcon(appIcon);
|
||||
preference.setTitle(appName.toString());
|
||||
preference.setOnPreferenceClickListener(v -> {
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
});
|
||||
|
||||
preference.setKey(packageName);
|
||||
preference.setVisible(true);
|
||||
container.addPreference(preference);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY_DEVICE_COMPANION_APPS;
|
||||
}
|
||||
}
|
||||
@@ -185,6 +185,8 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
lifecycle, mManager));
|
||||
controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice,
|
||||
lifecycle));
|
||||
controllers.add(new BluetoothDetailsCompanionAppsController(context, this,
|
||||
mCachedDevice, lifecycle));
|
||||
controllers.add(new BluetoothDetailsProfilesController(context, this, mManager,
|
||||
mCachedDevice, lifecycle));
|
||||
controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* A custom preference for companion device apps. Added a button for association removal
|
||||
*/
|
||||
public class CompanionAppWidgetPreference extends Preference {
|
||||
private Drawable mWidgetIcon;
|
||||
private View.OnClickListener mWidgetListener;
|
||||
private int mImageButtonPadding;
|
||||
|
||||
public CompanionAppWidgetPreference(Drawable widgetIcon, View.OnClickListener widgetListener,
|
||||
Context context) {
|
||||
super(context);
|
||||
mWidgetIcon = widgetIcon;
|
||||
mWidgetListener = widgetListener;
|
||||
mImageButtonPadding = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.bluetooth_companion_app_widget);
|
||||
setWidgetLayoutResource(R.layout.companion_apps_remove_button_widget);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
ImageButton imageButton = (ImageButton) holder.findViewById(R.id.remove_button);
|
||||
imageButton.setPadding(
|
||||
mImageButtonPadding, mImageButtonPadding, mImageButtonPadding, mImageButtonPadding);
|
||||
imageButton.setColorFilter(getContext().getColor(android.R.color.darker_gray));
|
||||
imageButton.setImageDrawable(mWidgetIcon);
|
||||
imageButton.setOnClickListener(mWidgetListener);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -88,6 +88,6 @@ public class CurrentDreamPreferenceController extends BasePreferenceController {
|
||||
}
|
||||
final GearPreference gearPref = (GearPreference) preference;
|
||||
gearPref.setIconSize(RestrictedPreference.ICON_SIZE_SMALL);
|
||||
Utils.setSafeIcon(gearPref, mBackend.getActiveIcon());
|
||||
gearPref.setIcon(Utils.getSafeIcon(mBackend.getActiveIcon()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ import com.android.settings.applications.appinfo.ButtonActionDialogFragment;
|
||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
|
||||
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
|
||||
import com.android.settings.widget.EntityHeaderController;
|
||||
import com.android.settingslib.applications.AppUtils;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
@@ -61,7 +63,7 @@ import java.util.List;
|
||||
*/
|
||||
public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
ButtonActionDialogFragment.AppButtonsDialogListener,
|
||||
RadioButtonPreference.OnClickListener {
|
||||
BatteryTipPreferenceController.BatteryTipListener, RadioButtonPreference.OnClickListener {
|
||||
|
||||
public static final String TAG = "AdvancedPowerDetail";
|
||||
public static final String EXTRA_UID = "extra_uid";
|
||||
@@ -73,6 +75,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
public static final String EXTRA_POWER_USAGE_PERCENT = "extra_power_usage_percent";
|
||||
public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount";
|
||||
|
||||
private static final String KEY_PREF_FOREGROUND = "app_usage_foreground";
|
||||
private static final String KEY_PREF_BACKGROUND = "app_usage_background";
|
||||
private static final String KEY_PREF_HEADER = "header_view";
|
||||
private static final String KEY_PREF_UNRESTRICTED = "unrestricted_pref";
|
||||
private static final String KEY_PREF_OPTIMIZED = "optimized_pref";
|
||||
@@ -92,7 +96,10 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
BatteryUtils mBatteryUtils;
|
||||
@VisibleForTesting
|
||||
BatteryOptimizeUtils mBatteryOptimizeUtils;
|
||||
|
||||
@VisibleForTesting
|
||||
Preference mForegroundPreference;
|
||||
@VisibleForTesting
|
||||
Preference mBackgroundPreference;
|
||||
@VisibleForTesting
|
||||
Preference mFooterPreference;
|
||||
@VisibleForTesting
|
||||
@@ -101,18 +108,18 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
RadioButtonPreference mOptimizePreference;
|
||||
@VisibleForTesting
|
||||
RadioButtonPreference mUnrestrictedPreference;
|
||||
private AppButtonsPreferenceController mAppButtonsPreferenceController;
|
||||
private UnrestrictedPreferenceController mUnrestrictedPreferenceController;
|
||||
private OptimizedPreferenceController mOptimizedPreferenceController;
|
||||
private RestrictedPreferenceController mRestrictedPreferenceController;
|
||||
@VisibleForTesting
|
||||
boolean enableTriState = true;
|
||||
|
||||
private String mPackageName;
|
||||
private AppButtonsPreferenceController mAppButtonsPreferenceController;
|
||||
private BackgroundActivityPreferenceController mBackgroundActivityPreferenceController;
|
||||
|
||||
// A wrapper class to carry LaunchBatteryDetailPage required arguments.
|
||||
private static final class LaunchBatteryDetailPageArgs {
|
||||
private String mUsagePercent;
|
||||
private String mPackageName;
|
||||
private String mAppLabel;
|
||||
private String mSlotInformation;
|
||||
private int mUid;
|
||||
private int mIconId;
|
||||
private int mConsumedPower;
|
||||
@@ -124,18 +131,22 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
/** Launches battery details page for an individual battery consumer. */
|
||||
public static void startBatteryDetailPage(
|
||||
Activity caller, InstrumentedPreferenceFragment fragment,
|
||||
BatteryDiffEntry diffEntry, String usagePercent) {
|
||||
BatteryDiffEntry diffEntry, String usagePercent,
|
||||
boolean isValidToShowSummary, String slotInformation) {
|
||||
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
|
||||
final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs();
|
||||
// configure the launch argument.
|
||||
launchArgs.mUsagePercent = usagePercent;
|
||||
launchArgs.mPackageName = diffEntry.getPackageName();
|
||||
launchArgs.mAppLabel = diffEntry.getAppLabel();
|
||||
launchArgs.mSlotInformation = slotInformation;
|
||||
launchArgs.mUid = (int) histEntry.mUid;
|
||||
launchArgs.mIconId = diffEntry.getAppIconId();
|
||||
launchArgs.mConsumedPower = (int) diffEntry.mConsumePower;
|
||||
launchArgs.mForegroundTimeMs = diffEntry.mForegroundUsageTimeInMs;
|
||||
launchArgs.mBackgroundTimeMs = diffEntry.mBackgroundUsageTimeInMs;
|
||||
launchArgs.mForegroundTimeMs =
|
||||
isValidToShowSummary ? diffEntry.mForegroundUsageTimeInMs : 0;
|
||||
launchArgs.mBackgroundTimeMs =
|
||||
isValidToShowSummary ? diffEntry.mBackgroundUsageTimeInMs : 0;
|
||||
launchArgs.mIsUserEntry = histEntry.isUserEntry();
|
||||
startBatteryDetailPage(caller, fragment, launchArgs);
|
||||
}
|
||||
@@ -227,22 +238,17 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
mPackageName = getArguments().getString(EXTRA_PACKAGE_NAME);
|
||||
mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE);
|
||||
mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
|
||||
final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME);
|
||||
if (enableTriState) {
|
||||
onCreateForTriState(packageName);
|
||||
} else {
|
||||
mForegroundPreference = findPreference(KEY_PREF_FOREGROUND);
|
||||
mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
|
||||
}
|
||||
mHeaderPreference = findPreference(KEY_PREF_HEADER);
|
||||
|
||||
mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED);
|
||||
mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED);
|
||||
mRestrictedPreference = findPreference(KEY_PREF_RESTRICTED);
|
||||
mUnrestrictedPreference.setOnClickListener(this);
|
||||
mOptimizePreference.setOnClickListener(this);
|
||||
mRestrictedPreference.setOnClickListener(this);
|
||||
|
||||
mBatteryOptimizeUtils = new BatteryOptimizeUtils(
|
||||
getContext(), getArguments().getInt(EXTRA_UID), mPackageName);
|
||||
|
||||
if (mPackageName != null) {
|
||||
mAppEntry = mState.getEntry(mPackageName, UserHandle.myUserId());
|
||||
if (packageName != null) {
|
||||
mAppEntry = mState.getEntry(packageName, UserHandle.myUserId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +257,11 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
super.onResume();
|
||||
|
||||
initHeader();
|
||||
initPreference();
|
||||
if (enableTriState) {
|
||||
initPreferenceForTriState(getContext());
|
||||
} else {
|
||||
initPreference(getContext());
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -281,17 +291,39 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
controller.setIsInstantApp(AppUtils.isInstant(mAppEntry.info));
|
||||
}
|
||||
|
||||
final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
|
||||
final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
|
||||
//TODO(b/178197718) Update layout to support multiple lines
|
||||
controller.setSummary(getAppActiveTime(foregroundTimeMs, backgroundTimeMs));
|
||||
if (enableTriState) {
|
||||
final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
|
||||
final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
|
||||
//TODO(b/178197718) Update layout to support multiple lines
|
||||
controller.setSummary(getAppActiveTime(foregroundTimeMs, backgroundTimeMs));
|
||||
}
|
||||
|
||||
controller.done(context, true /* rebindActions */);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initPreference() {
|
||||
final Context context = getContext();
|
||||
void initPreference(Context context) {
|
||||
final Bundle bundle = getArguments();
|
||||
final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
|
||||
final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
|
||||
mForegroundPreference.setSummary(
|
||||
TextUtils.expandTemplate(getText(R.string.battery_used_for),
|
||||
StringUtil.formatElapsedTime(
|
||||
context,
|
||||
foregroundTimeMs,
|
||||
/* withSeconds */ false,
|
||||
/* collapseTimeUnit */ false)));
|
||||
mBackgroundPreference.setSummary(
|
||||
TextUtils.expandTemplate(getText(R.string.battery_active_for),
|
||||
StringUtil.formatElapsedTime(
|
||||
context,
|
||||
backgroundTimeMs,
|
||||
/* withSeconds */ false,
|
||||
/* collapseTimeUnit */ false)));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initPreferenceForTriState(Context context) {
|
||||
final String stateString;
|
||||
final String footerString;
|
||||
//TODO(b/178197718) Update strings
|
||||
@@ -308,7 +340,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
} else {
|
||||
//Present default string to normal app.
|
||||
footerString = context.getString(R.string.manager_battery_usage_footer);
|
||||
|
||||
}
|
||||
mFooterPreference.setTitle(Html.fromHtml(footerString, Html.FROM_HTML_MODE_COMPACT));
|
||||
}
|
||||
@@ -325,7 +356,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.power_usage_detail;
|
||||
return enableTriState ? R.xml.power_usage_detail : R.xml.power_usage_detail_legacy;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -336,18 +367,20 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
final String packageName = bundle.getString(EXTRA_PACKAGE_NAME);
|
||||
|
||||
mAppButtonsPreferenceController = new AppButtonsPreferenceController(
|
||||
(SettingsActivity) getActivity(), this, getSettingsLifecycle(), packageName, mState,
|
||||
REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
|
||||
(SettingsActivity) getActivity(), this, getSettingsLifecycle(), packageName,
|
||||
mState, REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
|
||||
controllers.add(mAppButtonsPreferenceController);
|
||||
mUnrestrictedPreferenceController =
|
||||
new UnrestrictedPreferenceController(context, uid, packageName);
|
||||
mOptimizedPreferenceController =
|
||||
new OptimizedPreferenceController(context, uid, packageName);
|
||||
mRestrictedPreferenceController =
|
||||
new RestrictedPreferenceController(context, uid, packageName);
|
||||
controllers.add(mUnrestrictedPreferenceController);
|
||||
controllers.add(mOptimizedPreferenceController);
|
||||
controllers.add(mRestrictedPreferenceController);
|
||||
if (enableTriState) {
|
||||
controllers.add(new UnrestrictedPreferenceController(context, uid, packageName));
|
||||
controllers.add(new OptimizedPreferenceController(context, uid, packageName));
|
||||
controllers.add(new RestrictedPreferenceController(context, uid, packageName));
|
||||
} else {
|
||||
mBackgroundActivityPreferenceController = new BackgroundActivityPreferenceController(
|
||||
context, this, uid, packageName);
|
||||
controllers.add(mBackgroundActivityPreferenceController);
|
||||
controllers.add(new BatteryOptimizationPreferenceController(
|
||||
(SettingsActivity) getActivity(), this, packageName));
|
||||
}
|
||||
|
||||
return controllers;
|
||||
}
|
||||
@@ -367,6 +400,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatteryTipHandled(BatteryTip batteryTip) {
|
||||
mBackgroundActivityPreferenceController.updateSummary(
|
||||
findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRadioButtonClicked(RadioButtonPreference selected) {
|
||||
updatePreferenceState(mUnrestrictedPreference, selected.getKey());
|
||||
@@ -378,6 +417,19 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
preference.setChecked(selectedKey.equals(preference.getKey()));
|
||||
}
|
||||
|
||||
private void onCreateForTriState(String packageName) {
|
||||
mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED);
|
||||
mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED);
|
||||
mRestrictedPreference = findPreference(KEY_PREF_RESTRICTED);
|
||||
mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE);
|
||||
mUnrestrictedPreference.setOnClickListener(this);
|
||||
mOptimizePreference.setOnClickListener(this);
|
||||
mRestrictedPreference.setOnClickListener(this);
|
||||
|
||||
mBatteryOptimizeUtils = new BatteryOptimizeUtils(
|
||||
getContext(), getArguments().getInt(EXTRA_UID), packageName);
|
||||
}
|
||||
|
||||
//TODO(b/178197718) Update method to support time period
|
||||
private CharSequence getAppActiveTime(long foregroundTimeMs, long backgroundTimeMs) {
|
||||
final long totalTimeMs = foregroundTimeMs + backgroundTimeMs;
|
||||
|
||||
@@ -151,21 +151,22 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
final PowerGaugePreference powerPref = (PowerGaugePreference) preference;
|
||||
final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
|
||||
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
|
||||
final String packageName = histEntry.mPackageName;
|
||||
// Checks whether the package is installed or not.
|
||||
boolean isValidPackage = true;
|
||||
if (histEntry.isAppEntry()) {
|
||||
if (mBatteryUtils == null) {
|
||||
mBatteryUtils = BatteryUtils.getInstance(mPrefContext);
|
||||
}
|
||||
isValidPackage = mBatteryUtils.getPackageUid(histEntry.mPackageName)
|
||||
isValidPackage = mBatteryUtils.getPackageUid(packageName)
|
||||
!= BatteryUtils.UID_NULL;
|
||||
}
|
||||
Log.d(TAG, String.format("handleClick() label=%s key=%s isValid:%b %s",
|
||||
diffEntry.getAppLabel(), histEntry.getKey(), isValidPackage,
|
||||
histEntry.mPackageName));
|
||||
diffEntry.getAppLabel(), histEntry.getKey(), isValidPackage, packageName));
|
||||
if (isValidPackage) {
|
||||
AdvancedPowerUsageDetail.startBatteryDetailPage(
|
||||
mActivity, mFragment, diffEntry, powerPref.getPercent());
|
||||
mActivity, mFragment, diffEntry, powerPref.getPercent(),
|
||||
isValidToShowSummary(packageName), getSlotInformation());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -267,8 +268,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
|| (mTrapezoidIndex == trapezoidIndex && !isForce)) {
|
||||
return false;
|
||||
}
|
||||
Log.d(TAG, String.format("refreshUi: index=%d batteryIndexedMap.size=%d",
|
||||
mTrapezoidIndex, mBatteryIndexedMap.size()));
|
||||
Log.d(TAG, String.format("refreshUi: index=%d size=%d isForce:%b",
|
||||
trapezoidIndex, mBatteryIndexedMap.size(), isForce));
|
||||
|
||||
mTrapezoidIndex = trapezoidIndex;
|
||||
mHandler.post(() -> {
|
||||
@@ -471,11 +472,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
|
||||
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
|
||||
// Checks whether the package is allowed to show summary or not.
|
||||
for (CharSequence notAllowPackageName : mNotAllowShowSummaryPackages) {
|
||||
if (TextUtils.equals(entry.getPackageName(), notAllowPackageName)) {
|
||||
preference.setSummary(null);
|
||||
return;
|
||||
}
|
||||
if (!isValidToShowSummary(entry.getPackageName())) {
|
||||
preference.setSummary(null);
|
||||
return;
|
||||
}
|
||||
String usageTimeSummary = null;
|
||||
// Not shows summary for some system components without usage time.
|
||||
@@ -515,6 +514,17 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
return mPrefContext.getString(resourceId, timeSequence);
|
||||
}
|
||||
|
||||
private boolean isValidToShowSummary(String packageName) {
|
||||
if (mNotAllowShowSummaryPackages != null) {
|
||||
for (CharSequence notAllowPackageName : mNotAllowShowSummaryPackages) {
|
||||
if (TextUtils.equals(packageName, notAllowPackageName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String utcToLocalTime(long[] timestamps) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (int index = 0; index < timestamps.length; index++) {
|
||||
|
||||
@@ -22,11 +22,13 @@ import android.graphics.Color;
|
||||
import android.graphics.CornerPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
@@ -38,6 +40,8 @@ import java.util.Locale;
|
||||
/** A widget component to draw chart graph. */
|
||||
public class BatteryChartView extends AppCompatImageView implements View.OnClickListener {
|
||||
private static final String TAG = "BatteryChartView";
|
||||
// For drawing the percentage information.
|
||||
private static final String[] PERCENTAGES = new String[] {"100%", "50%", "0%"};
|
||||
private static final int DEFAULT_TRAPEZOID_COUNT = 12;
|
||||
/** Selects all trapezoid shapes. */
|
||||
public static final int SELECTED_INDEX_ALL = -1;
|
||||
@@ -58,8 +62,14 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
private int mTrapezoidColor;
|
||||
private int mTrapezoidSolidColor;
|
||||
private final int mDividerColor = Color.parseColor("#CDCCC5");
|
||||
// For drawing the percentage information.
|
||||
private int mTextPadding;
|
||||
private final Rect mIndent = new Rect();
|
||||
private final Rect[] mPercentageBound =
|
||||
new Rect[] {new Rect(), new Rect(), new Rect()};
|
||||
|
||||
private int[] mLevels;
|
||||
private Paint mTextPaint;
|
||||
private Paint mDividerPaint;
|
||||
private Paint mTrapezoidPaint;
|
||||
private TrapezoidSlot[] mTrapezoidSlot;
|
||||
@@ -128,6 +138,37 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
mOnSelectListener = listener;
|
||||
}
|
||||
|
||||
/** Sets the companion {@link TextView} for percentage information. */
|
||||
public void setCompanionTextView(TextView textView) {
|
||||
requestLayout();
|
||||
if (textView != null) {
|
||||
// Pre-draws the view first to load style atttributions into paint.
|
||||
textView.draw(new Canvas());
|
||||
mTextPaint = textView.getPaint();
|
||||
} else {
|
||||
mTextPaint = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
// Measures text bounds and updates indent configuration.
|
||||
if (mTextPaint != null) {
|
||||
for (int index = 0; index < PERCENTAGES.length; index++) {
|
||||
mTextPaint.getTextBounds(
|
||||
PERCENTAGES[index], 0, PERCENTAGES[index].length(),
|
||||
mPercentageBound[index]);
|
||||
}
|
||||
// Updates the indent configurations.
|
||||
mIndent.top = mPercentageBound[0].height();
|
||||
mIndent.right = mPercentageBound[0].width() + mTextPadding * 2;
|
||||
Log.d(TAG, "setIndent:" + mPercentageBound[0]);
|
||||
} else {
|
||||
mIndent.set(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
super.draw(canvas);
|
||||
@@ -194,32 +235,56 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
mTrapezoidPaint.setPathEffect(
|
||||
new CornerPathEffect(
|
||||
resources.getDimensionPixelSize(R.dimen.chartview_trapezoid_radius)));
|
||||
// Initializes for drawing text information.
|
||||
mTextPadding = resources.getDimensionPixelSize(R.dimen.chartview_text_padding);
|
||||
}
|
||||
|
||||
private void drawHorizontalDividers(Canvas canvas) {
|
||||
final int width = getWidth() - mIndent.right;
|
||||
final int height = getHeight() - mIndent.top - mIndent.bottom;
|
||||
// Draws the top divider line for 100% curve.
|
||||
float offsetY = mDividerWidth * 0.5f;
|
||||
canvas.drawLine(0, offsetY, getWidth(), offsetY, mDividerPaint);
|
||||
float offsetY = mIndent.top + mDividerWidth * .5f;
|
||||
canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
|
||||
if (mTextPaint != null) {
|
||||
canvas.drawText(
|
||||
PERCENTAGES[0],
|
||||
getWidth() - mPercentageBound[0].width(),
|
||||
offsetY + mPercentageBound[0].height() *.5f , mTextPaint);
|
||||
}
|
||||
// Draws the center divider line for 50% curve.
|
||||
final float availableSpace =
|
||||
getHeight() - mDividerWidth * 2 - mTrapezoidVOffset - mDividerHeight;
|
||||
offsetY = mDividerWidth + availableSpace * 0.5f;
|
||||
canvas.drawLine(0, offsetY, getWidth(), offsetY, mDividerPaint);
|
||||
// Draws the center divider line for 0% curve.
|
||||
offsetY = getHeight() - mDividerHeight - mDividerWidth * 0.5f;
|
||||
canvas.drawLine(0, offsetY, getWidth(), offsetY, mDividerPaint);
|
||||
height - mDividerWidth * 2 - mTrapezoidVOffset - mDividerHeight;
|
||||
offsetY = mIndent.top + mDividerWidth + availableSpace * .5f;
|
||||
canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
|
||||
if (mTextPaint != null) {
|
||||
canvas.drawText(
|
||||
PERCENTAGES[1],
|
||||
getWidth() - mPercentageBound[1].width(),
|
||||
offsetY + mPercentageBound[1].height() *.5f , mTextPaint);
|
||||
}
|
||||
// Draws the bottom divider line for 0% curve.
|
||||
offsetY = mIndent.top + (height - mDividerHeight - mDividerWidth * .5f);
|
||||
canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
|
||||
if (mTextPaint != null) {
|
||||
canvas.drawText(
|
||||
PERCENTAGES[2],
|
||||
getWidth() - mPercentageBound[2].width(),
|
||||
offsetY + mPercentageBound[2].height() *.5f , mTextPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawVerticalDividers(Canvas canvas) {
|
||||
final int width = getWidth() - mIndent.right;
|
||||
final int dividerCount = mTrapezoidCount + 1;
|
||||
final float dividerSpace = dividerCount * mDividerWidth;
|
||||
final float unitWidth = (getWidth() - dividerSpace) / (float) mTrapezoidCount;
|
||||
final float startY = getHeight() - mDividerHeight;
|
||||
final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * 0.5f;
|
||||
final float unitWidth = (width - dividerSpace) / (float) mTrapezoidCount;
|
||||
final float bottomY = getHeight() - mIndent.bottom;
|
||||
final float startY = bottomY - mDividerHeight;
|
||||
final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f;
|
||||
// Draws each vertical dividers.
|
||||
float startX = mDividerWidth * 0.5f;
|
||||
float startX = mDividerWidth * .5f;
|
||||
for (int index = 0; index < dividerCount; index++) {
|
||||
canvas.drawLine(startX, startY, startX, getHeight(), mDividerPaint);
|
||||
canvas.drawLine(startX, startY, startX, bottomY, mDividerPaint);
|
||||
final float nextX = startX + mDividerWidth + unitWidth;
|
||||
// Updates the trapezoid slots for drawing.
|
||||
if (index < mTrapezoidSlot.length) {
|
||||
@@ -236,8 +301,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
return;
|
||||
}
|
||||
final float trapezoidBottom =
|
||||
getHeight() - mDividerHeight - mDividerWidth - mTrapezoidVOffset;
|
||||
final float availableSpace = trapezoidBottom - mDividerWidth;
|
||||
getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth
|
||||
- mTrapezoidVOffset;
|
||||
final float availableSpace = trapezoidBottom - mDividerWidth * .5f - mIndent.top;
|
||||
final float unitHeight = availableSpace / 100f;
|
||||
// Draws all trapezoid shapes into the canvas.
|
||||
final Path trapezoidPath = new Path();
|
||||
@@ -249,8 +315,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
// Configures the trapezoid paint color.
|
||||
mTrapezoidPaint.setColor(
|
||||
mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
|
||||
? mTrapezoidSolidColor
|
||||
: mTrapezoidColor);
|
||||
? mTrapezoidSolidColor
|
||||
: mTrapezoidColor);
|
||||
final float leftTop = round(trapezoidBottom - mLevels[index] * unitHeight);
|
||||
final float rightTop = round(trapezoidBottom - mLevels[index + 1] * unitHeight);
|
||||
trapezoidPath.reset();
|
||||
|
||||
@@ -100,6 +100,8 @@ public class BatteryHistoryPreference extends Preference {
|
||||
}
|
||||
if (mIsChartGraphEnabled) {
|
||||
mBatteryChartView = (BatteryChartView) view.findViewById(R.id.battery_chart);
|
||||
mBatteryChartView.setCompanionTextView(
|
||||
(TextView) view.findViewById(R.id.companion_text));
|
||||
if (mChartPreferenceController != null) {
|
||||
mChartPreferenceController.setBatteryChartView(mBatteryChartView);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public class ExpandDividerPreference extends Preference {
|
||||
private OnExpandListener mOnExpandListener;
|
||||
|
||||
private boolean mIsExpanded = false;
|
||||
private String mTitleContent = null;
|
||||
|
||||
/** A callback listener for expand state is changed by users. */
|
||||
public interface OnExpandListener {
|
||||
@@ -72,6 +73,7 @@ public class ExpandDividerPreference extends Preference {
|
||||
}
|
||||
|
||||
void setTitle(final String titleContent) {
|
||||
mTitleContent = titleContent;
|
||||
if (mTextView != null) {
|
||||
mTextView.postDelayed(
|
||||
() -> mTextView.setText(titleContent), 50);
|
||||
@@ -95,5 +97,6 @@ public class ExpandDividerPreference extends Preference {
|
||||
if (mImageView != null) {
|
||||
mImageView.setImageResource(iconId);
|
||||
}
|
||||
setTitle(mTitleContent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class LocaleRecyclerView extends RecyclerView {
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent e) {
|
||||
if (e.getAction() == MotionEvent.ACTION_UP) {
|
||||
if (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL) {
|
||||
LocaleDragAndDropAdapter adapter = (LocaleDragAndDropAdapter) this.getAdapter();
|
||||
if (adapter != null) {
|
||||
adapter.doTheUpdate();
|
||||
|
||||
@@ -205,7 +205,7 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr
|
||||
public RadioButtonPreference bindPreference(RadioButtonPreference pref,
|
||||
String key, CandidateInfo info, String defaultKey) {
|
||||
pref.setTitle(info.loadLabel());
|
||||
Utils.setSafeIcon(pref, info.loadIcon());
|
||||
pref.setIcon(Utils.getSafeIcon(info.loadIcon()));
|
||||
pref.setKey(key);
|
||||
if (TextUtils.equals(defaultKey, key)) {
|
||||
pref.setChecked(true);
|
||||
|
||||
@@ -1339,12 +1339,13 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
mIpAddressView = (TextView) mView.findViewById(R.id.ipaddress);
|
||||
mIpAddressView.addTextChangedListener(this);
|
||||
mGatewayView = (TextView) mView.findViewById(R.id.gateway);
|
||||
mGatewayView.addTextChangedListener(this);
|
||||
mGatewayView.addTextChangedListener(getIpConfigFieldsTextWatcher(mGatewayView));
|
||||
mNetworkPrefixLengthView = (TextView) mView.findViewById(
|
||||
R.id.network_prefix_length);
|
||||
mNetworkPrefixLengthView.addTextChangedListener(this);
|
||||
mNetworkPrefixLengthView.addTextChangedListener(
|
||||
getIpConfigFieldsTextWatcher(mNetworkPrefixLengthView));
|
||||
mDns1View = (TextView) mView.findViewById(R.id.dns1);
|
||||
mDns1View.addTextChangedListener(this);
|
||||
mDns1View.addTextChangedListener(getIpConfigFieldsTextWatcher(mDns1View));
|
||||
mDns2View = (TextView) mView.findViewById(R.id.dns2);
|
||||
mDns2View.addTextChangedListener(this);
|
||||
}
|
||||
@@ -1562,6 +1563,47 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
// work done in afterTextChanged
|
||||
}
|
||||
|
||||
/* TODO: Add more test cases for this TextWatcher b/186368002
|
||||
* This TextWatcher is for IP config fields (Gateway/Network Prefix Length/DNS1) to prevent
|
||||
* to rewrite the value in these columns that the user wanted to change after they saved.
|
||||
* When afterTextChanged we will check the text is empty or not then set the Hint for user.
|
||||
*/
|
||||
private TextWatcher getIpConfigFieldsTextWatcher(final TextView view) {
|
||||
return new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// work done in afterTextChanged
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// work done in afterTextChanged
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (s.length() == 0) {
|
||||
if (view.getId() == R.id.gateway) {
|
||||
mGatewayView.setHint(R.string.wifi_gateway_hint);
|
||||
} else if (view.getId() == R.id.network_prefix_length) {
|
||||
mNetworkPrefixLengthView.setHint(R.string.wifi_network_prefix_length_hint);
|
||||
} else if (view.getId() == R.id.dns1) {
|
||||
mDns1View.setHint(R.string.wifi_dns1_hint);
|
||||
}
|
||||
Button submit = mConfigUi.getSubmitButton();
|
||||
if (submit == null) return;
|
||||
|
||||
submit.setEnabled(false);
|
||||
} else {
|
||||
ThreadUtils.postOnMainThread(() -> {
|
||||
showWarningMessagesIfAppropriate();
|
||||
enableSubmitIfAppropriate();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
|
||||
if (textView == mPasswordView) {
|
||||
|
||||
@@ -144,31 +144,8 @@ public class WifiDppConfiguratorActivity extends WifiDppBaseActivity implements
|
||||
}
|
||||
break;
|
||||
case Settings.ACTION_PROCESS_WIFI_EASY_CONNECT_URI:
|
||||
final Uri uri = intent.getData();
|
||||
final String uriString = (uri == null) ? null : uri.toString();
|
||||
mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(uriString);
|
||||
mWifiDppRemoteBandSupport = intent.getIntArrayExtra(
|
||||
Settings.EXTRA_EASY_CONNECT_BAND_LIST); // returns null if none
|
||||
final boolean isDppSupported = WifiDppUtils.isWifiDppEnabled(this);
|
||||
if (!isDppSupported) {
|
||||
Log.e(TAG,
|
||||
"ACTION_PROCESS_WIFI_EASY_CONNECT_URI for a device that doesn't "
|
||||
+ "support Wifi DPP - use WifiManager#isEasyConnectSupported");
|
||||
}
|
||||
if (mWifiDppQrCode == null) {
|
||||
Log.e(TAG, "ACTION_PROCESS_WIFI_EASY_CONNECT_URI with null URI!");
|
||||
}
|
||||
if (mWifiDppQrCode == null || !isDppSupported) {
|
||||
cancelActivity = true;
|
||||
} else {
|
||||
final WifiNetworkConfig connectedConfig = getConnectedWifiNetworkConfigOrNull();
|
||||
if (connectedConfig == null || !connectedConfig.isSupportWifiDpp(this)) {
|
||||
showChooseSavedWifiNetworkFragment(/* addToBackStack */ false);
|
||||
} else {
|
||||
mWifiNetworkConfig = connectedConfig;
|
||||
showAddDeviceFragment(/* addToBackStack */ false);
|
||||
}
|
||||
}
|
||||
WifiDppUtils.showLockScreen(this,
|
||||
() -> handleActionProcessWifiEasyConnectUriIntent(intent));
|
||||
break;
|
||||
default:
|
||||
cancelActivity = true;
|
||||
@@ -180,6 +157,34 @@ public class WifiDppConfiguratorActivity extends WifiDppBaseActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
private void handleActionProcessWifiEasyConnectUriIntent(Intent intent) {
|
||||
final Uri uri = intent.getData();
|
||||
final String uriString = (uri == null) ? null : uri.toString();
|
||||
mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(uriString);
|
||||
mWifiDppRemoteBandSupport = intent.getIntArrayExtra(
|
||||
Settings.EXTRA_EASY_CONNECT_BAND_LIST); // returns null if none
|
||||
final boolean isDppSupported = WifiDppUtils.isWifiDppEnabled(this);
|
||||
if (!isDppSupported) {
|
||||
Log.e(TAG,
|
||||
"ACTION_PROCESS_WIFI_EASY_CONNECT_URI for a device that doesn't "
|
||||
+ "support Wifi DPP - use WifiManager#isEasyConnectSupported");
|
||||
}
|
||||
if (mWifiDppQrCode == null) {
|
||||
Log.e(TAG, "ACTION_PROCESS_WIFI_EASY_CONNECT_URI with null URI!");
|
||||
}
|
||||
if (mWifiDppQrCode == null || !isDppSupported) {
|
||||
finish();
|
||||
} else {
|
||||
final WifiNetworkConfig connectedConfig = getConnectedWifiNetworkConfigOrNull();
|
||||
if (connectedConfig == null || !connectedConfig.isSupportWifiDpp(this)) {
|
||||
showChooseSavedWifiNetworkFragment(/* addToBackStack */ false);
|
||||
} else {
|
||||
mWifiNetworkConfig = connectedConfig;
|
||||
showAddDeviceFragment(/* addToBackStack */ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showQrCodeScannerFragment() {
|
||||
WifiDppQrCodeScannerFragment fragment =
|
||||
(WifiDppQrCodeScannerFragment) mFragmentManager.findFragmentByTag(
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.companion.Association;
|
||||
import android.companion.CompanionDeviceManager;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsCompanionAppsControllerTest extends
|
||||
BluetoothDetailsControllerTestBase {
|
||||
private static final String PACKAGE_NAME_ONE = "com.google.android.deskclock";
|
||||
private static final String PACKAGE_NAME_TWO = "com.google.android.calculator";
|
||||
private static final String PACKAGE_NAME_THREE = "com.google.android.GoogleCamera";
|
||||
private static final CharSequence APP_NAME_ONE = "deskclock";
|
||||
private static final CharSequence APP_NAME_TWO = "calculator";
|
||||
private static final CharSequence APP_NAME_THREE = "GoogleCamera";
|
||||
|
||||
@Mock
|
||||
private CompanionDeviceManager mCompanionDeviceManager;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private BluetoothDetailsCompanionAppsController mController;
|
||||
private PreferenceCategory mProfiles;
|
||||
private List<String> mPackages;
|
||||
private List<CharSequence> mAppNames;
|
||||
private List<Association> mAssociations;
|
||||
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
mPackages = Arrays.asList(PACKAGE_NAME_ONE, PACKAGE_NAME_TWO, PACKAGE_NAME_THREE);
|
||||
mAppNames = Arrays.asList(APP_NAME_ONE, APP_NAME_TWO, APP_NAME_THREE);
|
||||
mProfiles = spy(new PreferenceCategory(mContext));
|
||||
mAssociations = new ArrayList<>();
|
||||
when(mCompanionDeviceManager.getAllAssociations()).thenReturn(mAssociations);
|
||||
when(mProfiles.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||
setupDevice(mDeviceConfig);
|
||||
mController =
|
||||
new BluetoothDetailsCompanionAppsController(mContext, mFragment, mCachedDevice,
|
||||
mLifecycle);
|
||||
mController.mCompanionDeviceManager = mCompanionDeviceManager;
|
||||
mController.mPackageManager = mPackageManager;
|
||||
mController.mProfilesContainer = mProfiles;
|
||||
mProfiles.setKey(mController.getPreferenceKey());
|
||||
mScreen.addPreference(mProfiles);
|
||||
}
|
||||
|
||||
private void setupFakeLabelAndInfo(String packageName, CharSequence appName) {
|
||||
ApplicationInfo appInfo = mock(ApplicationInfo.class);
|
||||
try {
|
||||
when(mPackageManager.getApplicationInfo(packageName, 0)).thenReturn(appInfo);
|
||||
when(mPackageManager.getApplicationLabel(appInfo)).thenReturn(appName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addFakeAssociation(String packageName, CharSequence appName) {
|
||||
setupFakeLabelAndInfo(packageName, appName);
|
||||
Association association = new Association(
|
||||
0, mCachedDevice.getAddress(), packageName, "", true, System.currentTimeMillis());
|
||||
mAssociations.add(association);
|
||||
showScreen(mController);
|
||||
}
|
||||
|
||||
private Preference getPreference(int index) {
|
||||
PreferenceCategory preferenceCategory = mProfiles.findPreference(
|
||||
mController.getPreferenceKey());
|
||||
return Objects.requireNonNull(preferenceCategory).getPreference(index);
|
||||
}
|
||||
|
||||
private void removeAssociation(String packageName) {
|
||||
mAssociations = mAssociations.stream()
|
||||
.filter(a -> !a.getPackageName().equals(packageName))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
when(mCompanionDeviceManager.getAllAssociations()).thenReturn(mAssociations);
|
||||
|
||||
showScreen(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addOneAssociation_preferenceShouldBeAdded() {
|
||||
addFakeAssociation(PACKAGE_NAME_ONE, APP_NAME_ONE);
|
||||
|
||||
Preference preferenceOne = getPreference(0);
|
||||
|
||||
assertThat(preferenceOne.getClass()).isEqualTo(CompanionAppWidgetPreference.class);
|
||||
assertThat(preferenceOne.getKey()).isEqualTo(PACKAGE_NAME_ONE);
|
||||
assertThat(preferenceOne.getTitle()).isEqualTo(APP_NAME_ONE.toString());
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
|
||||
|
||||
removeAssociation(PACKAGE_NAME_ONE);
|
||||
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeOneAssociation_preferenceShouldBeRemoved() {
|
||||
addFakeAssociation(PACKAGE_NAME_ONE, APP_NAME_ONE);
|
||||
|
||||
removeAssociation(PACKAGE_NAME_ONE);
|
||||
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMultipleAssociations_preferencesShouldBeAdded() {
|
||||
for (int i = 0; i < mPackages.size(); i++) {
|
||||
addFakeAssociation(mPackages.get(i), mAppNames.get(i));
|
||||
|
||||
Preference preference = getPreference(i);
|
||||
|
||||
assertThat(preference.getClass()).isEqualTo(CompanionAppWidgetPreference.class);
|
||||
assertThat(preference.getKey()).isEqualTo(mPackages.get(i));
|
||||
assertThat(preference.getTitle()).isEqualTo(mAppNames.get(i).toString());
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeMultipleAssociations_preferencesShouldBeRemoved() {
|
||||
for (int i = 0; i < mPackages.size(); i++) {
|
||||
addFakeAssociation(mPackages.get(i), mAppNames.get(i).toString());
|
||||
}
|
||||
|
||||
for (int i = 0; i < mPackages.size(); i++) {
|
||||
removeAssociation(mPackages.get(i));
|
||||
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(mPackages.size() - i - 1);
|
||||
|
||||
if (i == mPackages.size() - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
assertThat(getPreference(0).getKey()).isEqualTo(mPackages.get(i + 1));
|
||||
assertThat(getPreference(0).getTitle()).isEqualTo(mAppNames.get(i + 1).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowAlertDialogCompat.class})
|
||||
public class CompanionAppWidgetPreferenceTest {
|
||||
private static final String TITLE_ONE = "Test Title 1";
|
||||
private static final String TITLE_TWO = "Test Title 1";
|
||||
private static final String KEY_ONE = "Test Key 1";
|
||||
private static final String KEY_TWO = "Test Key 1";
|
||||
|
||||
private Context mContext;
|
||||
private Drawable mWidgetIconOne;
|
||||
private Drawable mWidgetIconTwo;
|
||||
private Drawable mAppIconOne;
|
||||
private Drawable mAppIconTwo;
|
||||
|
||||
@Mock
|
||||
private View.OnClickListener mWidgetListenerOne;
|
||||
@Mock
|
||||
private View.OnClickListener mWidgetListenerTwo;
|
||||
|
||||
private List<CompanionAppWidgetPreference> mPreferenceContainer;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mPreferenceContainer = new ArrayList<>();
|
||||
Context context = spy(RuntimeEnvironment.application.getApplicationContext());
|
||||
mContext = new ContextThemeWrapper(context, R.style.Theme_Settings);
|
||||
mWidgetIconOne = mock(Drawable.class);
|
||||
mAppIconOne = mock(Drawable.class);
|
||||
mWidgetListenerOne = mock(View.OnClickListener.class);
|
||||
mWidgetIconTwo = mock(Drawable.class);
|
||||
mAppIconTwo = mock(Drawable.class);
|
||||
mWidgetListenerTwo = mock(View.OnClickListener.class);
|
||||
}
|
||||
|
||||
private void setUpPreferenceContainer(Drawable widgetIcon, Drawable appIcon,
|
||||
View.OnClickListener listener, String title, String key) {
|
||||
CompanionAppWidgetPreference preference = new CompanionAppWidgetPreference(
|
||||
widgetIcon, listener, mContext);
|
||||
preference.setIcon(appIcon);
|
||||
preference.setTitle(title);
|
||||
preference.setKey(key);
|
||||
mPreferenceContainer.add(preference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setUpPreferenceContainer_preferenceShouldBeAdded() {
|
||||
setUpPreferenceContainer(
|
||||
mWidgetIconOne, mAppIconOne, mWidgetListenerOne, TITLE_ONE, KEY_ONE);
|
||||
|
||||
assertThat(mPreferenceContainer.get(0).getIcon()).isEqualTo(mAppIconOne);
|
||||
assertThat(mPreferenceContainer.get(0).getKey()).isEqualTo(KEY_ONE);
|
||||
assertThat(mPreferenceContainer.get(0).getTitle()).isEqualTo(TITLE_ONE);
|
||||
|
||||
setUpPreferenceContainer(
|
||||
mWidgetIconTwo, mAppIconTwo, mWidgetListenerTwo, TITLE_TWO, KEY_TWO);
|
||||
|
||||
assertThat(mPreferenceContainer.get(1).getIcon()).isEqualTo(mAppIconTwo);
|
||||
assertThat(mPreferenceContainer.get(1).getKey()).isEqualTo(KEY_TWO);
|
||||
assertThat(mPreferenceContainer.get(1).getTitle()).isEqualTo(TITLE_TWO);
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import androidx.loader.app.LoaderManager;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowActivityManager;
|
||||
@@ -82,6 +83,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
private static final String USAGE_PERCENT = "16%";
|
||||
private static final int ICON_ID = 123;
|
||||
private static final int UID = 1;
|
||||
private static final int POWER_MAH = 150;
|
||||
private static final long BACKGROUND_TIME_MS = 100;
|
||||
private static final long FOREGROUND_ACTIVITY_TIME_MS = 123;
|
||||
private static final long FOREGROUND_SERVICE_TIME_MS = 444;
|
||||
@@ -117,6 +119,8 @@ public class AdvancedPowerUsageDetailTest {
|
||||
@Mock
|
||||
private BatteryOptimizeUtils mBatteryOptimizeUtils;
|
||||
private Context mContext;
|
||||
private Preference mForegroundPreference;
|
||||
private Preference mBackgroundPreference;
|
||||
private Preference mFooterPreference;
|
||||
private RadioButtonPreference mRestrictedPreference;
|
||||
private RadioButtonPreference mOptimizePreference;
|
||||
@@ -166,6 +170,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
|
||||
mFragment.mHeaderPreference = mHeaderPreference;
|
||||
mFragment.mState = mState;
|
||||
mFragment.enableTriState = true;
|
||||
mFragment.mBatteryUtils = new BatteryUtils(RuntimeEnvironment.application);
|
||||
mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils;
|
||||
mAppEntry.info = mock(ApplicationInfo.class);
|
||||
@@ -190,10 +195,14 @@ public class AdvancedPowerUsageDetailTest {
|
||||
nullable(UserHandle.class));
|
||||
doAnswer(callable).when(mActivity).startActivity(captor.capture());
|
||||
|
||||
mForegroundPreference = new Preference(mContext);
|
||||
mBackgroundPreference = new Preference(mContext);
|
||||
mFooterPreference = new Preference(mContext);
|
||||
mRestrictedPreference = new RadioButtonPreference(mContext);
|
||||
mOptimizePreference = new RadioButtonPreference(mContext);
|
||||
mUnrestrictedPreference = new RadioButtonPreference(mContext);
|
||||
mFragment.mForegroundPreference = mForegroundPreference;
|
||||
mFragment.mBackgroundPreference = mBackgroundPreference;
|
||||
mFragment.mFooterPreference = mFooterPreference;
|
||||
mFragment.mRestrictedPreference = mRestrictedPreference;
|
||||
mFragment.mOptimizePreference = mOptimizePreference;
|
||||
@@ -205,6 +214,17 @@ public class AdvancedPowerUsageDetailTest {
|
||||
ShadowEntityHeaderController.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPreferenceScreenResId_returnNewLayout() {
|
||||
assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(R.xml.power_usage_detail);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPreferenceScreenResId_disableTriState_returnLegacyLayout() {
|
||||
mFragment.enableTriState = false;
|
||||
assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(R.xml.power_usage_detail_legacy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_NoAppEntry_BuildByBundle() {
|
||||
mFragment.mAppEntry = null;
|
||||
@@ -455,32 +475,52 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitPreference_isValidPackageName_hasCorrectString() {
|
||||
public void testInitPreference_hasCorrectSummary() {
|
||||
Bundle bundle = new Bundle(4);
|
||||
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, BACKGROUND_TIME_MS);
|
||||
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, FOREGROUND_TIME_MS);
|
||||
bundle.putString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT, USAGE_PERCENT);
|
||||
bundle.putInt(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_AMOUNT, POWER_MAH);
|
||||
when(mFragment.getArguments()).thenReturn(bundle);
|
||||
|
||||
doReturn(mContext.getText(R.string.battery_used_for)).when(mFragment).getText(
|
||||
R.string.battery_used_for);
|
||||
doReturn(mContext.getText(R.string.battery_active_for)).when(mFragment).getText(
|
||||
R.string.battery_active_for);
|
||||
|
||||
mFragment.initPreference(mContext);
|
||||
|
||||
assertThat(mForegroundPreference.getSummary().toString()).isEqualTo("Used for 0 min");
|
||||
assertThat(mBackgroundPreference.getSummary().toString()).isEqualTo("Active for 0 min");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitPreferenceForTriState_isValidPackageName_hasCorrectString() {
|
||||
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
|
||||
|
||||
mFragment.initPreference();
|
||||
mFragment.initPreferenceForTriState(mContext);
|
||||
|
||||
assertThat(mFooterPreference.getTitle().toString())
|
||||
.isEqualTo("This app requires Optimized battery usage.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitPreference_isSystemOrDefaultApp_hasCorrectString() {
|
||||
public void testInitPreferenceForTriState_isSystemOrDefaultApp_hasCorrectString() {
|
||||
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
|
||||
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
|
||||
|
||||
mFragment.initPreference();
|
||||
mFragment.initPreferenceForTriState(mContext);
|
||||
|
||||
assertThat(mFooterPreference.getTitle()
|
||||
.toString()).isEqualTo("This app requires Unrestricted battery usage.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitPreference_hasCorrectString() {
|
||||
public void testInitPreferenceForTriState_hasCorrectString() {
|
||||
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
|
||||
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false);
|
||||
|
||||
mFragment.initPreference();
|
||||
mFragment.initPreferenceForTriState(mContext);
|
||||
|
||||
assertThat(mFooterPreference.getTitle().toString())
|
||||
.isEqualTo("Changing how an app uses your battery can affect its performance.");
|
||||
|
||||
Reference in New Issue
Block a user