Merge rvc-qpr-dev-plus-aosp-without-vendor@6881855
Bug: 172690556 Merged-In: Iafcefc2aa64cf3c50b1d139ec0204a315be29da7 Change-Id: I5d4e70fe723d890b5694c3490d6ec841b1ac596e
This commit is contained in:
@@ -21,20 +21,26 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.FactoryResetProtectionPolicy;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.service.oemlock.OemLockManager;
|
||||
import android.service.persistentdata.PersistentDataBlockManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.telephony.UiccSlotInfo;
|
||||
import android.telephony.euicc.EuiccManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -54,6 +60,8 @@ import com.google.android.setupcompat.template.FooterButton.ButtonType;
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Confirm and execute a reset of the device to a clean "just out of the box"
|
||||
* state. Multiple confirmations are required: first, a general "are you sure
|
||||
@@ -83,6 +91,89 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the eSIM slot is in an error state, display a dialog to warn users that their eSIM
|
||||
// profiles may not be fully deleted during FDR.
|
||||
if (shouldShowEsimEraseFailureDialog()) {
|
||||
Log.e(TAG, "eUICC card is in an error state. Display a dialog to warn the user.");
|
||||
showEsimErrorDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
performFactoryReset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user choose to erase eSIM profile but the eUICC card is in an error
|
||||
* state.
|
||||
*/
|
||||
private boolean shouldShowEsimEraseFailureDialog() {
|
||||
EuiccManager euiccManager = getActivity().getSystemService(EuiccManager.class);
|
||||
TelephonyManager telephonyManager =
|
||||
getActivity().getSystemService(TelephonyManager.class);
|
||||
|
||||
if (euiccManager == null || !euiccManager.isEnabled()) {
|
||||
Log.i(
|
||||
TAG,
|
||||
"eSIM manager is disabled. No need to check eSIM slot before FDR.");
|
||||
return false;
|
||||
}
|
||||
if (!mEraseEsims) {
|
||||
Log.i(
|
||||
TAG,
|
||||
"eSIM does not need to be reset. No need to check eSIM slot before FDR.");
|
||||
return false;
|
||||
}
|
||||
UiccSlotInfo[] slotInfos = telephonyManager.getUiccSlotsInfo();
|
||||
if (slotInfos == null) {
|
||||
Log.i(TAG, "Unable to get UICC slots.");
|
||||
return false;
|
||||
}
|
||||
// If getIsEuicc() returns false for an eSIM slot, it means the eSIM is in the error
|
||||
// state.
|
||||
return Arrays.stream(slotInfos).anyMatch(
|
||||
slot -> slot != null && !slot.isRemovable() && !slot.getIsEuicc());
|
||||
}
|
||||
|
||||
private void showEsimErrorDialog() {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.fdr_esim_failure_title)
|
||||
.setMessage(R.string.fdr_esim_failure_text)
|
||||
.setNeutralButton(R.string.dlg_cancel,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton(R.string.fdr_esim_failure_reboot_btn,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
PowerManager pm = (PowerManager) getActivity()
|
||||
.getSystemService(Context.POWER_SERVICE);
|
||||
pm.reboot(null);
|
||||
})
|
||||
.setPositiveButton(R.string.lockpassword_continue_label,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
showContinueFdrDialog();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showContinueFdrDialog() {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.fdr_continue_title)
|
||||
.setMessage(R.string.fdr_continue_text)
|
||||
.setNegativeButton(R.string.dlg_cancel,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setPositiveButton(R.string.fdr_continue_btn,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
performFactoryReset();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void performFactoryReset() {
|
||||
final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
|
||||
getActivity().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
|
||||
|
||||
|
||||
@@ -80,6 +80,10 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
@VisibleForTesting
|
||||
static final String KEY_TETHER_PREFS_FOOTER = "tether_prefs_footer";
|
||||
|
||||
@VisibleForTesting
|
||||
static final String BLUETOOTH_TETHERING_STATE_CHANGED =
|
||||
"android.bluetooth.pan.profile.action.TETHERING_STATE_CHANGED";
|
||||
|
||||
private static final String TAG = "TetheringSettings";
|
||||
|
||||
private SwitchPreference mUsbTether;
|
||||
@@ -154,9 +158,7 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
BluetoothProfile.PAN);
|
||||
}
|
||||
|
||||
mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS);
|
||||
mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING);
|
||||
mEthernetTether = (SwitchPreference) findPreference(KEY_ENABLE_ETHERNET_TETHERING);
|
||||
setupTetherPreference();
|
||||
setFooterPreferenceTitle();
|
||||
|
||||
mDataSaverBackend.addListener(this);
|
||||
@@ -208,6 +210,13 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setupTetherPreference() {
|
||||
mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS);
|
||||
mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING);
|
||||
mEthernetTether = (SwitchPreference) findPreference(KEY_ENABLE_ETHERNET_TETHERING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataSaverChanged(boolean isDataSaving) {
|
||||
mDataSaverEnabled = isDataSaving;
|
||||
@@ -281,6 +290,8 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
}
|
||||
}
|
||||
updateState();
|
||||
} else if (action.equals(BLUETOOTH_TETHERING_STATE_CHANGED)) {
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,32 +308,13 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
return;
|
||||
}
|
||||
|
||||
final Activity activity = getActivity();
|
||||
|
||||
mStartTetheringCallback = new OnStartTetheringCallback(this);
|
||||
mTetheringEventCallback = new TetheringEventCallback();
|
||||
mTm.registerTetheringEventCallback(new HandlerExecutor(mHandler), mTetheringEventCallback);
|
||||
|
||||
mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
|
||||
mTetherChangeReceiver = new TetherChangeReceiver();
|
||||
IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
|
||||
Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter);
|
||||
|
||||
filter = new IntentFilter();
|
||||
filter.addAction(UsbManager.ACTION_USB_STATE);
|
||||
activity.registerReceiver(mTetherChangeReceiver, filter);
|
||||
|
||||
filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_MEDIA_SHARED);
|
||||
filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
|
||||
filter.addDataScheme("file");
|
||||
activity.registerReceiver(mTetherChangeReceiver, filter);
|
||||
|
||||
filter = new IntentFilter();
|
||||
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
activity.registerReceiver(mTetherChangeReceiver, filter);
|
||||
|
||||
if (intent != null) mTetherChangeReceiver.onReceive(activity, intent);
|
||||
registerReceiver();
|
||||
|
||||
mEthernetListener = new EthernetListener();
|
||||
if (mEm != null)
|
||||
@@ -348,10 +340,38 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
mEthernetListener = null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void registerReceiver() {
|
||||
final Activity activity = getActivity();
|
||||
|
||||
mTetherChangeReceiver = new TetherChangeReceiver();
|
||||
IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
|
||||
final Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter);
|
||||
|
||||
filter = new IntentFilter();
|
||||
filter.addAction(UsbManager.ACTION_USB_STATE);
|
||||
activity.registerReceiver(mTetherChangeReceiver, filter);
|
||||
|
||||
filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_MEDIA_SHARED);
|
||||
filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
|
||||
filter.addDataScheme("file");
|
||||
activity.registerReceiver(mTetherChangeReceiver, filter);
|
||||
|
||||
filter = new IntentFilter();
|
||||
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
filter.addAction(BLUETOOTH_TETHERING_STATE_CHANGED);
|
||||
activity.registerReceiver(mTetherChangeReceiver, filter);
|
||||
|
||||
if (intent != null) mTetherChangeReceiver.onReceive(activity, intent);
|
||||
}
|
||||
|
||||
private void updateState() {
|
||||
String[] available = mCm.getTetherableIfaces();
|
||||
String[] tethered = mCm.getTetheredIfaces();
|
||||
String[] errored = mCm.getTetheringErroredIfaces();
|
||||
final ConnectivityManager cm =
|
||||
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
final String[] available = cm.getTetherableIfaces();
|
||||
final String[] tethered = cm.getTetheredIfaces();
|
||||
final String[] errored = cm.getTetheringErroredIfaces();
|
||||
updateState(available, tethered, errored);
|
||||
}
|
||||
|
||||
@@ -362,7 +382,8 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
updateEthernetState(available, tethered);
|
||||
}
|
||||
|
||||
private void updateUsbState(String[] available, String[] tethered,
|
||||
@VisibleForTesting
|
||||
void updateUsbState(String[] available, String[] tethered,
|
||||
String[] errored) {
|
||||
boolean usbAvailable = mUsbConnected && !mMassStorageActive;
|
||||
int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||
@@ -400,20 +421,33 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBluetoothState() {
|
||||
@VisibleForTesting
|
||||
int getBluetoothState() {
|
||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (adapter == null) {
|
||||
return BluetoothAdapter.ERROR;
|
||||
}
|
||||
return adapter.getState();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isBluetoothTetheringOn() {
|
||||
final BluetoothPan bluetoothPan = mBluetoothPan.get();
|
||||
return bluetoothPan != null && bluetoothPan.isTetheringOn();
|
||||
}
|
||||
|
||||
private void updateBluetoothState() {
|
||||
final int btState = getBluetoothState();
|
||||
if (btState == BluetoothAdapter.ERROR) {
|
||||
return;
|
||||
}
|
||||
int btState = adapter.getState();
|
||||
|
||||
if (btState == BluetoothAdapter.STATE_TURNING_OFF) {
|
||||
mBluetoothTether.setEnabled(false);
|
||||
} else if (btState == BluetoothAdapter.STATE_TURNING_ON) {
|
||||
mBluetoothTether.setEnabled(false);
|
||||
} else {
|
||||
BluetoothPan bluetoothPan = mBluetoothPan.get();
|
||||
if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null
|
||||
&& bluetoothPan.isTetheringOn()) {
|
||||
if (btState == BluetoothAdapter.STATE_ON && isBluetoothTetheringOn()) {
|
||||
mBluetoothTether.setChecked(true);
|
||||
mBluetoothTether.setEnabled(!mDataSaverEnabled);
|
||||
} else {
|
||||
@@ -423,7 +457,8 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
}
|
||||
}
|
||||
|
||||
private void updateEthernetState(String[] available, String[] tethered) {
|
||||
@VisibleForTesting
|
||||
void updateEthernetState(String[] available, String[] tethered) {
|
||||
|
||||
boolean isAvailable = false;
|
||||
boolean isTethered = false;
|
||||
|
||||
@@ -18,6 +18,10 @@ package com.android.settings;
|
||||
|
||||
import static android.content.Intent.EXTRA_USER;
|
||||
import static android.content.Intent.EXTRA_USER_ID;
|
||||
import static android.media.MediaRoute2Info.TYPE_GROUP;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
|
||||
import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
|
||||
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
|
||||
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
|
||||
|
||||
@@ -53,6 +57,8 @@ import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.VectorDrawable;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
@@ -1137,4 +1143,31 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
drawable.draw(canvas);
|
||||
return roundedBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if needed to disable media output, otherwise returns {@code false}.
|
||||
*/
|
||||
public static boolean isMediaOutputDisabled(
|
||||
MediaRouter2Manager router2Manager, String packageName) {
|
||||
boolean isMediaOutputDisabled = false;
|
||||
if (!TextUtils.isEmpty(packageName)) {
|
||||
final List<MediaRoute2Info> infos = router2Manager.getAvailableRoutes(packageName);
|
||||
if (infos.size() == 1) {
|
||||
final MediaRoute2Info info = infos.get(0);
|
||||
final int deviceType = info.getType();
|
||||
switch (deviceType) {
|
||||
case TYPE_UNKNOWN:
|
||||
case TYPE_REMOTE_TV:
|
||||
case TYPE_REMOTE_SPEAKER:
|
||||
case TYPE_GROUP:
|
||||
isMediaOutputDisabled = true;
|
||||
break;
|
||||
default:
|
||||
isMediaOutputDisabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isMediaOutputDisabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,12 +58,12 @@ public class AppFilterRegistry {
|
||||
// Filters will appear sorted based on their value defined here.
|
||||
public static final int FILTER_APPS_POWER_WHITELIST = 0;
|
||||
public static final int FILTER_APPS_POWER_WHITELIST_ALL = 1;
|
||||
public static final int FILTER_APPS_ALL = 2;
|
||||
public static final int FILTER_APPS_ENABLED = 3;
|
||||
public static final int FILTER_APPS_INSTANT = 4;
|
||||
public static final int FILTER_APPS_DISABLED = 5;
|
||||
public static final int FILTER_APPS_RECENT = 6;
|
||||
public static final int FILTER_APPS_FREQUENT = 7;
|
||||
public static final int FILTER_APPS_RECENT = 2;
|
||||
public static final int FILTER_APPS_FREQUENT = 3;
|
||||
public static final int FILTER_APPS_ALL = 4;
|
||||
public static final int FILTER_APPS_ENABLED = 5;
|
||||
public static final int FILTER_APPS_INSTANT = 6;
|
||||
public static final int FILTER_APPS_DISABLED = 7;
|
||||
public static final int FILTER_APPS_PERSONAL = 8;
|
||||
public static final int FILTER_APPS_WORK = 9;
|
||||
public static final int FILTER_APPS_USAGE_ACCESS = 10;
|
||||
|
||||
@@ -476,7 +476,7 @@ public class ManageApplications extends InstrumentedFragment
|
||||
mFilterAdapter.enableFilter(FILTER_APPS_RECENT);
|
||||
mFilterAdapter.enableFilter(FILTER_APPS_FREQUENT);
|
||||
mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED);
|
||||
mFilterAdapter.disableFilter(FILTER_APPS_ALL);
|
||||
mFilterAdapter.enableFilter(FILTER_APPS_ALL);
|
||||
}
|
||||
if (mListType == LIST_TYPE_HIGH_POWER) {
|
||||
mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL);
|
||||
@@ -1089,12 +1089,16 @@ public class ManageApplications extends InstrumentedFragment
|
||||
mAppFilter = appFilter;
|
||||
|
||||
// Notification filters require resorting the list
|
||||
if (FILTER_APPS_FREQUENT == appFilter.getFilterType()) {
|
||||
rebuild(R.id.sort_order_frequent_notification);
|
||||
} else if (FILTER_APPS_RECENT == appFilter.getFilterType()) {
|
||||
rebuild(R.id.sort_order_recent_notification);
|
||||
} else if (FILTER_APPS_BLOCKED == appFilter.getFilterType()) {
|
||||
rebuild(R.id.sort_order_alpha);
|
||||
if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
|
||||
if (FILTER_APPS_FREQUENT == appFilter.getFilterType()) {
|
||||
rebuild(R.id.sort_order_frequent_notification);
|
||||
} else if (FILTER_APPS_RECENT == appFilter.getFilterType()) {
|
||||
rebuild(R.id.sort_order_recent_notification);
|
||||
} else if (FILTER_APPS_BLOCKED == appFilter.getFilterType()) {
|
||||
rebuild(R.id.sort_order_alpha);
|
||||
} else {
|
||||
rebuild(R.id.sort_order_alpha);
|
||||
}
|
||||
} else {
|
||||
rebuild();
|
||||
}
|
||||
|
||||
@@ -40,20 +40,13 @@ public class ZenAccessController extends BasePreferenceController {
|
||||
|
||||
private static final String TAG = "ZenAccessController";
|
||||
|
||||
private final ActivityManager mActivityManager;
|
||||
|
||||
public ZenAccessController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return isSupported(mActivityManager) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
public static boolean isSupported(ActivityManager activityManager) {
|
||||
return !activityManager.isLowRamDevice();
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
public static Set<String> getPackagesRequestingNotificationPolicyAccess() {
|
||||
|
||||
@@ -50,9 +50,6 @@ public class ZenAccessDetails extends AppInfoWithHeader implements
|
||||
@Override
|
||||
protected boolean refreshUi() {
|
||||
final Context context = getContext();
|
||||
if (!ZenAccessController.isSupported(context.getSystemService(ActivityManager.class))) {
|
||||
return false;
|
||||
}
|
||||
// If this app didn't declare this permission in their manifest, don't bother showing UI.
|
||||
final Set<String> needAccessApps =
|
||||
ZenAccessController.getPackagesRequestingNotificationPolicyAccess();
|
||||
|
||||
@@ -53,9 +53,6 @@ public class ZenAccessSettingObserverMixin extends ContentObserver implements Li
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
if (!ZenAccessController.isSupported(mContext.getSystemService(ActivityManager.class))) {
|
||||
return;
|
||||
}
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(
|
||||
Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES),
|
||||
@@ -69,9 +66,6 @@ public class ZenAccessSettingObserverMixin extends ContentObserver implements Li
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
if (!ZenAccessController.isSupported(mContext.getSystemService(ActivityManager.class))) {
|
||||
return;
|
||||
}
|
||||
mContext.getContentResolver().unregisterContentObserver(this /* observer */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.biometrics.face;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
|
||||
/**
|
||||
* Confirmation dialog shown to users with accessibility enabled who are trying to start the
|
||||
* non-accessibility enrollment flow.
|
||||
*/
|
||||
public class FaceEnrollAccessibilityDialog extends InstrumentedDialogFragment {
|
||||
private AlertDialog.OnClickListener mPositiveButtonListener;
|
||||
|
||||
/**
|
||||
* @return new instance of the dialog
|
||||
*/
|
||||
public static FaceEnrollAccessibilityDialog newInstance() {
|
||||
return new FaceEnrollAccessibilityDialog();
|
||||
}
|
||||
|
||||
public void setPositiveButtonListener(AlertDialog.OnClickListener listener) {
|
||||
mPositiveButtonListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
final int titleResId =
|
||||
R.string.security_settings_face_enroll_education_accessibility_dialog_message;
|
||||
final int negativeButtonResId =
|
||||
R.string.security_settings_face_enroll_education_accessibility_dialog_negative;
|
||||
final int positiveButtonResId =
|
||||
R.string.security_settings_face_enroll_education_accessibility_dialog_positive;
|
||||
|
||||
builder.setMessage(titleResId)
|
||||
.setNegativeButton(negativeButtonResId, (dialog, which) -> {
|
||||
dialog.cancel();
|
||||
})
|
||||
.setPositiveButton(positiveButtonResId, (dialog, which) -> {
|
||||
mPositiveButtonListener.onClick(dialog, which);
|
||||
});
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.FACE_ENROLL_INTRO;
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
private Intent mResultIntent;
|
||||
private TextView mDescriptionText;
|
||||
private boolean mNextClicked;
|
||||
private boolean mAccessibilityEnabled;
|
||||
|
||||
private CompoundButton.OnCheckedChangeListener mSwitchDiversityListener =
|
||||
new CompoundButton.OnCheckedChangeListener() {
|
||||
@@ -123,13 +124,12 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
.setTheme(R.style.SudGlifButton_Primary)
|
||||
.build();
|
||||
|
||||
boolean accessibilityEnabled = false;
|
||||
final AccessibilityManager accessibilityManager = getApplicationContext().getSystemService(
|
||||
AccessibilityManager.class);
|
||||
if (accessibilityManager != null) {
|
||||
// Add additional check for touch exploration. This prevents other accessibility
|
||||
// features such as Live Transcribe from defaulting to the accessibility setup.
|
||||
accessibilityEnabled = accessibilityManager.isEnabled()
|
||||
mAccessibilityEnabled = accessibilityManager.isEnabled()
|
||||
&& accessibilityManager.isTouchExplorationEnabled();
|
||||
}
|
||||
mFooterBarMixin.setPrimaryButton(footerButton);
|
||||
@@ -147,7 +147,7 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
mSwitchDiversity.getSwitch().toggle();
|
||||
});
|
||||
|
||||
if (accessibilityEnabled) {
|
||||
if (mAccessibilityEnabled) {
|
||||
accessibilityButton.callOnClick();
|
||||
}
|
||||
}
|
||||
@@ -194,9 +194,20 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
if (mResultIntent != null) {
|
||||
intent.putExtras(mResultIntent);
|
||||
}
|
||||
mNextClicked = true;
|
||||
|
||||
intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, !mSwitchDiversity.isChecked());
|
||||
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
||||
|
||||
if (!mSwitchDiversity.isChecked() && mAccessibilityEnabled) {
|
||||
FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance();
|
||||
dialog.setPositiveButtonListener((dialog1, which) -> {
|
||||
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
||||
mNextClicked = true;
|
||||
});
|
||||
dialog.show(getSupportFragmentManager(), FaceEnrollAccessibilityDialog.class.getName());
|
||||
} else {
|
||||
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
||||
mNextClicked = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void onSkipButtonClick(View view) {
|
||||
|
||||
@@ -52,8 +52,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
* BluetoothDevicePreference is the preference type used to display each remote
|
||||
* Bluetooth device in the Bluetooth Settings screen.
|
||||
*/
|
||||
public final class BluetoothDevicePreference extends GearPreference implements
|
||||
CachedBluetoothDevice.Callback {
|
||||
public final class BluetoothDevicePreference extends GearPreference {
|
||||
private static final String TAG = "BluetoothDevicePref";
|
||||
|
||||
private static int sDimAlpha = Integer.MIN_VALUE;
|
||||
@@ -77,10 +76,20 @@ public final class BluetoothDevicePreference extends GearPreference implements
|
||||
private AlertDialog mDisconnectDialog;
|
||||
private String contentDescription = null;
|
||||
private boolean mHideSecondTarget = false;
|
||||
private boolean mIsCallbackRemoved = false;
|
||||
@VisibleForTesting
|
||||
boolean mNeedNotifyHierarchyChanged = false;
|
||||
/* Talk-back descriptions for various BT icons */
|
||||
Resources mResources;
|
||||
final BluetoothDevicePreferenceCallback mCallback;
|
||||
|
||||
private class BluetoothDevicePreferenceCallback implements CachedBluetoothDevice.Callback {
|
||||
|
||||
@Override
|
||||
public void onDeviceAttributesChanged() {
|
||||
onPreferenceAttributesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice,
|
||||
boolean showDeviceWithoutNames, @SortType int type) {
|
||||
@@ -96,11 +105,12 @@ public final class BluetoothDevicePreference extends GearPreference implements
|
||||
}
|
||||
|
||||
mCachedDevice = cachedDevice;
|
||||
mCachedDevice.registerCallback(this);
|
||||
mCallback = new BluetoothDevicePreferenceCallback();
|
||||
mCachedDevice.registerCallback(mCallback);
|
||||
mCurrentTime = System.currentTimeMillis();
|
||||
mType = type;
|
||||
|
||||
onDeviceAttributesChanged();
|
||||
onPreferenceAttributesChanged();
|
||||
}
|
||||
|
||||
public void setNeedNotifyHierarchyChanged(boolean needNotifyHierarchyChanged) {
|
||||
@@ -127,13 +137,35 @@ public final class BluetoothDevicePreference extends GearPreference implements
|
||||
@Override
|
||||
protected void onPrepareForRemoval() {
|
||||
super.onPrepareForRemoval();
|
||||
mCachedDevice.unregisterCallback(this);
|
||||
if (!mIsCallbackRemoved) {
|
||||
mCachedDevice.unregisterCallback(mCallback);
|
||||
mIsCallbackRemoved = true;
|
||||
}
|
||||
if (mDisconnectDialog != null) {
|
||||
mDisconnectDialog.dismiss();
|
||||
mDisconnectDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttached() {
|
||||
super.onAttached();
|
||||
if (mIsCallbackRemoved) {
|
||||
mCachedDevice.registerCallback(mCallback);
|
||||
mIsCallbackRemoved = false;
|
||||
}
|
||||
onPreferenceAttributesChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetached() {
|
||||
super.onDetached();
|
||||
if (!mIsCallbackRemoved) {
|
||||
mCachedDevice.unregisterCallback(mCallback);
|
||||
mIsCallbackRemoved = true;
|
||||
}
|
||||
}
|
||||
|
||||
public CachedBluetoothDevice getBluetoothDevice() {
|
||||
return mCachedDevice;
|
||||
}
|
||||
@@ -142,7 +174,7 @@ public final class BluetoothDevicePreference extends GearPreference implements
|
||||
mHideSecondTarget = hideSecondTarget;
|
||||
}
|
||||
|
||||
public void onDeviceAttributesChanged() {
|
||||
private void onPreferenceAttributesChanged() {
|
||||
/*
|
||||
* The preference framework takes care of making sure the value has
|
||||
* changed before proceeding. It will also call notifyChanged() if
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.bluetooth;
|
||||
|
||||
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
@@ -192,6 +193,6 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
|
||||
if (mLaunchPackage != null && mLaunchClass != null) {
|
||||
intent.setClassName(mLaunchPackage, mLaunchClass);
|
||||
}
|
||||
getActivity().sendBroadcast(intent);
|
||||
getActivity().sendBroadcast(intent, Manifest.permission.BLUETOOTH_ADMIN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.connecteddevice.DevicePreferenceCallback;
|
||||
import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
@@ -42,13 +43,15 @@ public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater
|
||||
|
||||
private static final String PREF_KEY = "saved_bt";
|
||||
|
||||
private final boolean mDisplayConnected;
|
||||
|
||||
@VisibleForTesting
|
||||
BluetoothAdapter mBluetoothAdapter;
|
||||
|
||||
public SavedBluetoothDeviceUpdater(Context context, DashboardFragment fragment,
|
||||
DevicePreferenceCallback devicePreferenceCallback) {
|
||||
super(context, fragment, devicePreferenceCallback);
|
||||
|
||||
mDisplayConnected = (fragment instanceof PreviouslyConnectedDeviceDashboardFragment);
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
|
||||
@@ -101,7 +104,8 @@ public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater
|
||||
", is connected : " + device.isConnected() + ", is profile connected : "
|
||||
+ cachedDevice.isConnected());
|
||||
}
|
||||
return device.getBondState() == BluetoothDevice.BOND_BONDED && !device.isConnected();
|
||||
return device.getBondState() == BluetoothDevice.BOND_BONDED
|
||||
&& (mDisplayConnected || !device.isConnected());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,6 +113,9 @@ public class SavedBluetoothDeviceUpdater extends BluetoothDeviceUpdater
|
||||
mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
|
||||
final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference)
|
||||
.getBluetoothDevice();
|
||||
if (device.isConnected()) {
|
||||
return device.setActive();
|
||||
}
|
||||
device.connect();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
package com.android.settings.connecteddevice;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
@@ -32,6 +36,9 @@ public class PreviouslyConnectedDeviceDashboardFragment extends DashboardFragmen
|
||||
private static final String TAG = "PreConnectedDeviceFrag";
|
||||
static final String KEY_PREVIOUSLY_CONNECTED_DEVICES = "saved_device_list";
|
||||
|
||||
@VisibleForTesting
|
||||
BluetoothAdapter mBluetoothAdapter;
|
||||
|
||||
@Override
|
||||
public int getHelpResource() {
|
||||
return R.string.help_url_previously_connected_devices;
|
||||
@@ -52,12 +59,32 @@ public class PreviouslyConnectedDeviceDashboardFragment extends DashboardFragmen
|
||||
return SettingsEnums.PREVIOUSLY_CONNECTED_DEVICES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
use(SavedDeviceGroupController.class).init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
enableBluetoothIfNecessary();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void enableBluetoothIfNecessary() {
|
||||
if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
|
||||
mBluetoothAdapter.enable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For Search.
|
||||
*/
|
||||
|
||||
@@ -15,14 +15,22 @@
|
||||
*/
|
||||
package com.android.settings.connecteddevice;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.bluetooth.BluetoothDevicePreference;
|
||||
import com.android.settings.bluetooth.BluetoothDeviceUpdater;
|
||||
import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater;
|
||||
import com.android.settings.connecteddevice.dock.DockUpdater;
|
||||
@@ -33,21 +41,47 @@ import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PreviouslyConnectedDevicePreferenceController extends BasePreferenceController
|
||||
implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback {
|
||||
|
||||
private static final String TAG = "PreviouslyDevicePreController";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private static final int MAX_DEVICE_NUM = 3;
|
||||
private static final int DOCK_DEVICE_INDEX = 9;
|
||||
private static final String KEY_SEE_ALL = "previously_connected_devices_see_all";
|
||||
|
||||
private final List<Preference> mDevicesList = new ArrayList<>();
|
||||
private final List<Preference> mDockDevicesList = new ArrayList<>();
|
||||
|
||||
private PreferenceGroup mPreferenceGroup;
|
||||
private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
|
||||
private DockUpdater mSavedDockUpdater;
|
||||
private int mPreferenceSize;
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
|
||||
@VisibleForTesting
|
||||
Preference mSeeAllPreference;
|
||||
@VisibleForTesting
|
||||
IntentFilter mIntentFilter;
|
||||
|
||||
@VisibleForTesting
|
||||
BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updatePreferenceVisibility();
|
||||
}
|
||||
};
|
||||
|
||||
public PreviouslyConnectedDevicePreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
|
||||
mSavedDockUpdater = FeatureFactory.getFactory(
|
||||
context).getDockUpdaterFeatureProvider().getSavedDockUpdater(context, this);
|
||||
mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,7 +96,8 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreferenceGroup = screen.findPreference(getPreferenceKey());
|
||||
mPreferenceGroup.setVisible(false);
|
||||
mSeeAllPreference = mPreferenceGroup.findPreference(KEY_SEE_ALL);
|
||||
updatePreferenceVisibility();
|
||||
|
||||
if (isAvailable()) {
|
||||
final Context context = screen.getContext();
|
||||
@@ -75,12 +110,14 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc
|
||||
public void onStart() {
|
||||
mBluetoothDeviceUpdater.registerCallback();
|
||||
mSavedDockUpdater.registerCallback();
|
||||
mContext.registerReceiver(mReceiver, mIntentFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
mBluetoothDeviceUpdater.unregisterCallback();
|
||||
mSavedDockUpdater.unregisterCallback();
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
public void init(DashboardFragment fragment) {
|
||||
@@ -90,18 +127,77 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc
|
||||
|
||||
@Override
|
||||
public void onDeviceAdded(Preference preference) {
|
||||
mPreferenceSize++;
|
||||
if (mPreferenceSize <= MAX_DEVICE_NUM) {
|
||||
mPreferenceGroup.addPreference(preference);
|
||||
final List<BluetoothDevice> bluetoothDevices =
|
||||
mBluetoothAdapter.getMostRecentlyConnectedDevices();
|
||||
final int index = preference instanceof BluetoothDevicePreference
|
||||
? bluetoothDevices.indexOf(((BluetoothDevicePreference) preference)
|
||||
.getBluetoothDevice().getDevice()) : DOCK_DEVICE_INDEX;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDeviceAdded() " + preference.getTitle() + ", index of : " + index);
|
||||
for (BluetoothDevice device : bluetoothDevices) {
|
||||
Log.d(TAG, "onDeviceAdded() most recently device : " + device.getName());
|
||||
}
|
||||
}
|
||||
updatePreferenceVisiblity();
|
||||
addPreference(index, preference);
|
||||
updatePreferenceVisibility();
|
||||
}
|
||||
|
||||
private void addPreference(int index, Preference preference) {
|
||||
if (preference instanceof BluetoothDevicePreference) {
|
||||
if (mDevicesList.size() >= index) {
|
||||
mDevicesList.add(index, preference);
|
||||
} else {
|
||||
mDevicesList.add(preference);
|
||||
}
|
||||
} else {
|
||||
mDockDevicesList.add(preference);
|
||||
}
|
||||
addPreference();
|
||||
}
|
||||
|
||||
private void addPreference() {
|
||||
mPreferenceGroup.removeAll();
|
||||
mPreferenceGroup.addPreference(mSeeAllPreference);
|
||||
final int size = getDeviceListSize();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addPreference() add device : " + mDevicesList.get(i).getTitle());
|
||||
}
|
||||
mDevicesList.get(i).setOrder(i);
|
||||
mPreferenceGroup.addPreference(mDevicesList.get(i));
|
||||
}
|
||||
if (mDockDevicesList.size() > 0) {
|
||||
for (int i = 0; i < getDockDeviceListSize(MAX_DEVICE_NUM - size); i++) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addPreference() add dock device : "
|
||||
+ mDockDevicesList.get(i).getTitle());
|
||||
}
|
||||
mDockDevicesList.get(i).setOrder(DOCK_DEVICE_INDEX);
|
||||
mPreferenceGroup.addPreference(mDockDevicesList.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getDeviceListSize() {
|
||||
return mDevicesList.size() >= MAX_DEVICE_NUM
|
||||
? MAX_DEVICE_NUM : mDevicesList.size();
|
||||
}
|
||||
|
||||
private int getDockDeviceListSize(int availableSize) {
|
||||
return mDockDevicesList.size() >= availableSize
|
||||
? availableSize : mDockDevicesList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceRemoved(Preference preference) {
|
||||
mPreferenceSize--;
|
||||
mPreferenceGroup.removePreference(preference);
|
||||
updatePreferenceVisiblity();
|
||||
if (preference instanceof BluetoothDevicePreference) {
|
||||
mDevicesList.remove(preference);
|
||||
} else {
|
||||
mDockDevicesList.remove(preference);
|
||||
}
|
||||
|
||||
addPreference();
|
||||
updatePreferenceVisibility();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -120,7 +216,12 @@ public class PreviouslyConnectedDevicePreferenceController extends BasePreferenc
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updatePreferenceVisiblity() {
|
||||
mPreferenceGroup.setVisible(mPreferenceSize > 0);
|
||||
void updatePreferenceVisibility() {
|
||||
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
|
||||
mSeeAllPreference.setSummary("");
|
||||
} else {
|
||||
mSeeAllPreference.setSummary(
|
||||
mContext.getString(R.string.connected_device_see_all_summary));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,9 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
|
||||
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
|
||||
final String titleFromUri = TileUtils.getTextFromUri(
|
||||
mContext, uri, providerMap, META_DATA_PREFERENCE_TITLE);
|
||||
ThreadUtils.postOnMainThread(() -> preference.setTitle(titleFromUri));
|
||||
if (!TextUtils.equals(titleFromUri, preference.getTitle())) {
|
||||
ThreadUtils.postOnMainThread(() -> preference.setTitle(titleFromUri));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -277,7 +279,9 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
|
||||
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
|
||||
final String summaryFromUri = TileUtils.getTextFromUri(
|
||||
mContext, uri, providerMap, META_DATA_PREFERENCE_SUMMARY);
|
||||
ThreadUtils.postOnMainThread(() -> preference.setSummary(summaryFromUri));
|
||||
if (!TextUtils.equals(summaryFromUri, preference.getSummary())) {
|
||||
ThreadUtils.postOnMainThread(() -> preference.setSummary(summaryFromUri));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import static android.service.quicksettings.TileService.ACTION_QS_TILE_PREFERENCES;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.bluetooth.BluetoothA2dp;
|
||||
@@ -23,12 +25,14 @@ import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothCodecStatus;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -41,6 +45,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
||||
import com.android.settings.development.autofill.AutofillLoggingLevelPreferenceController;
|
||||
import com.android.settings.development.autofill.AutofillResetOptionsPreferenceController;
|
||||
@@ -52,6 +57,7 @@ import com.android.settings.development.bluetooth.BluetoothCodecDialogPreference
|
||||
import com.android.settings.development.bluetooth.BluetoothHDAudioPreferenceController;
|
||||
import com.android.settings.development.bluetooth.BluetoothQualityDialogPreferenceController;
|
||||
import com.android.settings.development.bluetooth.BluetoothSampleRateDialogPreferenceController;
|
||||
import com.android.settings.development.qstile.DevelopmentTiles;
|
||||
import com.android.settings.development.storage.SharedDataPreferenceController;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.widget.SwitchBar;
|
||||
@@ -199,11 +205,42 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
// Restore UI state based on whether developer options is enabled
|
||||
if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext())) {
|
||||
enableDeveloperOptions();
|
||||
handleQsTileLongPressActionIfAny();
|
||||
} else {
|
||||
disableDeveloperOptions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Long-pressing a developer options quick settings tile will by default (see
|
||||
* QS_TILE_PREFERENCES in the manifest) take you to the developer options page.
|
||||
* Some tiles may want to go into their own page within the developer options.
|
||||
*/
|
||||
private void handleQsTileLongPressActionIfAny() {
|
||||
Intent intent = getActivity().getIntent();
|
||||
if (intent == null || !TextUtils.equals(ACTION_QS_TILE_PREFERENCES, intent.getAction())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Developer options started from qstile long-press");
|
||||
final ComponentName componentName = (ComponentName) intent.getParcelableExtra(
|
||||
Intent.EXTRA_COMPONENT_NAME);
|
||||
if (componentName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DevelopmentTiles.WirelessDebugging.class.getName().equals(
|
||||
componentName.getClassName()) && getDevelopmentOptionsController(
|
||||
WirelessDebuggingPreferenceController.class).isAvailable()) {
|
||||
Log.d(TAG, "Long press from wireless debugging qstile");
|
||||
new SubSettingLauncher(getContext())
|
||||
.setDestination(WirelessDebuggingFragment.class.getName())
|
||||
.setSourceMetricsCategory(SettingsEnums.SETTINGS_ADB_WIRELESS)
|
||||
.launch();
|
||||
}
|
||||
// Add other qstiles here
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package com.android.settings.development.graphicsdriver;
|
||||
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_DEFAULT;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_OFF;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.UPDATABLE_DRIVER_DEFAULT;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.UPDATABLE_DRIVER_OFF;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -62,7 +62,7 @@ public class GraphicsDriverAppPreferenceController extends BasePreferenceControl
|
||||
private final ContentResolver mContentResolver;
|
||||
private final String mPreferenceTitle;
|
||||
private final String mPreferenceDefault;
|
||||
private final String mPreferenceGameDriver;
|
||||
private final String mPreferenceProductionDriver;
|
||||
private final String mPreferencePrereleaseDriver;
|
||||
private final String mPreferenceSystem;
|
||||
@VisibleForTesting
|
||||
@@ -88,8 +88,8 @@ public class GraphicsDriverAppPreferenceController extends BasePreferenceControl
|
||||
final Resources resources = context.getResources();
|
||||
mPreferenceTitle = resources.getString(R.string.graphics_driver_app_preference_title);
|
||||
mPreferenceDefault = resources.getString(R.string.graphics_driver_app_preference_default);
|
||||
mPreferenceGameDriver =
|
||||
resources.getString(R.string.graphics_driver_app_preference_game_driver);
|
||||
mPreferenceProductionDriver =
|
||||
resources.getString(R.string.graphics_driver_app_preference_production_driver);
|
||||
mPreferencePrereleaseDriver =
|
||||
resources.getString(R.string.graphics_driver_app_preference_prerelease_driver);
|
||||
mPreferenceSystem = resources.getString(R.string.graphics_driver_app_preference_system);
|
||||
@@ -101,19 +101,21 @@ public class GraphicsDriverAppPreferenceController extends BasePreferenceControl
|
||||
mAppInfos = getAppInfos(context);
|
||||
|
||||
mDevOptInApps =
|
||||
getGlobalSettingsString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_IN_APPS);
|
||||
getGlobalSettingsString(mContentResolver,
|
||||
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS);
|
||||
mDevPrereleaseOptInApps = getGlobalSettingsString(
|
||||
mContentResolver, Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS);
|
||||
mContentResolver, Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS);
|
||||
mDevOptOutApps =
|
||||
getGlobalSettingsString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_OUT_APPS);
|
||||
getGlobalSettingsString(mContentResolver,
|
||||
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)
|
||||
&& (Settings.Global.getInt(mContentResolver,
|
||||
Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT)
|
||||
!= GAME_DRIVER_OFF)
|
||||
Settings.Global.UPDATABLE_DRIVER_ALL_APPS, UPDATABLE_DRIVER_DEFAULT)
|
||||
!= UPDATABLE_DRIVER_OFF)
|
||||
? AVAILABLE
|
||||
: CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
@@ -157,7 +159,7 @@ public class GraphicsDriverAppPreferenceController extends BasePreferenceControl
|
||||
mDevOptInApps.remove(packageName);
|
||||
mDevPrereleaseOptInApps.remove(packageName);
|
||||
mDevOptOutApps.add(packageName);
|
||||
} else if (value.equals(mPreferenceGameDriver)) {
|
||||
} else if (value.equals(mPreferenceProductionDriver)) {
|
||||
mDevOptInApps.add(packageName);
|
||||
mDevPrereleaseOptInApps.remove(packageName);
|
||||
mDevOptOutApps.remove(packageName);
|
||||
@@ -174,13 +176,15 @@ public class GraphicsDriverAppPreferenceController extends BasePreferenceControl
|
||||
listPref.setSummary(value);
|
||||
|
||||
// Push the updated Sets for stable/prerelease opt-in and opt-out apps to
|
||||
// corresponding Settings.Global.GAME_DRIVER(_PRERELEASE)?_OPT_(IN|OUT)_APPS
|
||||
Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_IN_APPS,
|
||||
// corresponding Settings.Global.UPDATABLE_DRIVER_[PRODUCTION|PRERELEASE]_OPT_(IN|OUT)_APPS
|
||||
Settings.Global.putString(mContentResolver,
|
||||
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS,
|
||||
String.join(",", mDevOptInApps));
|
||||
Settings.Global.putString(mContentResolver,
|
||||
Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS,
|
||||
Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS,
|
||||
String.join(",", mDevPrereleaseOptInApps));
|
||||
Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_OUT_APPS,
|
||||
Settings.Global.putString(mContentResolver,
|
||||
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS,
|
||||
String.join(",", mDevOptOutApps));
|
||||
|
||||
return true;
|
||||
@@ -251,7 +255,7 @@ public class GraphicsDriverAppPreferenceController extends BasePreferenceControl
|
||||
listPreference.setEntryValues(mEntryList);
|
||||
|
||||
// Initialize preference default and summary with the opt in/out choices
|
||||
// from Settings.Global.GAME_DRIVER(_PRERELEASE)?_OPT_(IN|OUT)_APPS
|
||||
// from Settings.Global.UPDATABLE_DRIVER_[PRODUCTION|PRERELEASE]_OPT_[IN|OUT]_APPS
|
||||
if (mDevOptOutApps.contains(packageName)) {
|
||||
listPreference.setValue(mPreferenceSystem);
|
||||
listPreference.setSummary(mPreferenceSystem);
|
||||
@@ -259,8 +263,8 @@ public class GraphicsDriverAppPreferenceController extends BasePreferenceControl
|
||||
listPreference.setValue(mPreferencePrereleaseDriver);
|
||||
listPreference.setSummary(mPreferencePrereleaseDriver);
|
||||
} else if (mDevOptInApps.contains(packageName)) {
|
||||
listPreference.setValue(mPreferenceGameDriver);
|
||||
listPreference.setSummary(mPreferenceGameDriver);
|
||||
listPreference.setValue(mPreferenceProductionDriver);
|
||||
listPreference.setSummary(mPreferenceProductionDriver);
|
||||
} else {
|
||||
listPreference.setValue(mPreferenceDefault);
|
||||
listPreference.setSummary(mPreferenceDefault);
|
||||
|
||||
@@ -52,7 +52,7 @@ public class GraphicsDriverContentObserver extends ContentObserver {
|
||||
*/
|
||||
public void register(ContentResolver contentResolver) {
|
||||
contentResolver.registerContentObserver(
|
||||
Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_ALL_APPS), false, this);
|
||||
Settings.Global.getUriFor(Settings.Global.UPDATABLE_DRIVER_ALL_APPS), false, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
/**
|
||||
* Dashboard for Game Driver preferences.
|
||||
* Dashboard for Graphics Driver preferences.
|
||||
*/
|
||||
@SearchIndexable
|
||||
public class GraphicsDriverDashboard extends DashboardFragment {
|
||||
@@ -39,7 +39,7 @@ public class GraphicsDriverDashboard extends DashboardFragment {
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.SETTINGS_GAME_DRIVER_DASHBOARD;
|
||||
return SettingsEnums.SETTINGS_GRAPHICS_DRIVER_DASHBOARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,24 +46,24 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Controller of global switch to enable Game Driver for all Apps.
|
||||
* Controller of global switch to enable updatable driver for all Apps.
|
||||
*/
|
||||
public class GraphicsDriverEnableForAllAppsPreferenceController extends BasePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener,
|
||||
GraphicsDriverContentObserver.OnGraphicsDriverContentChangedListener,
|
||||
LifecycleObserver, OnStart, OnStop {
|
||||
|
||||
public static final int GAME_DRIVER_DEFAULT = 0;
|
||||
public static final int GAME_DRIVER_ALL_APPS = 1;
|
||||
public static final int GAME_DRIVER_PRERELEASE_ALL_APPS = 2;
|
||||
public static final int GAME_DRIVER_OFF = 3;
|
||||
public static final String PROPERTY_GFX_DRIVER_GAME = "ro.gfx.driver.0";
|
||||
public static final int UPDATABLE_DRIVER_DEFAULT = 0;
|
||||
public static final int UPDATABLE_DRIVER_PRODUCTION_ALL_APPS = 1;
|
||||
public static final int UPDATABLE_DRIVER_PRERELEASE_ALL_APPS = 2;
|
||||
public static final int UPDATABLE_DRIVER_OFF = 3;
|
||||
public static final String PROPERTY_GFX_DRIVER_PRODUCTION = "ro.gfx.driver.0";
|
||||
public static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1";
|
||||
|
||||
private final Context mContext;
|
||||
private final ContentResolver mContentResolver;
|
||||
private final String mPreferenceDefault;
|
||||
private final String mPreferenceGameDriver;
|
||||
private final String mPreferenceProductionDriver;
|
||||
private final String mPreferencePrereleaseDriver;
|
||||
@VisibleForTesting
|
||||
CharSequence[] mEntryList;
|
||||
@@ -79,8 +79,8 @@ public class GraphicsDriverEnableForAllAppsPreferenceController extends BasePref
|
||||
|
||||
final Resources resources = context.getResources();
|
||||
mPreferenceDefault = resources.getString(R.string.graphics_driver_app_preference_default);
|
||||
mPreferenceGameDriver =
|
||||
resources.getString(R.string.graphics_driver_app_preference_game_driver);
|
||||
mPreferenceProductionDriver =
|
||||
resources.getString(R.string.graphics_driver_app_preference_production_driver);
|
||||
mPreferencePrereleaseDriver =
|
||||
resources.getString(R.string.graphics_driver_app_preference_prerelease_driver);
|
||||
mEntryList = constructEntryList(mContext, false);
|
||||
@@ -92,8 +92,9 @@ public class GraphicsDriverEnableForAllAppsPreferenceController extends BasePref
|
||||
public int getAvailabilityStatus() {
|
||||
return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)
|
||||
&& (Settings.Global.getInt(mContentResolver,
|
||||
Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT)
|
||||
!= GAME_DRIVER_OFF)
|
||||
Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
|
||||
UPDATABLE_DRIVER_DEFAULT)
|
||||
!= UPDATABLE_DRIVER_OFF)
|
||||
? AVAILABLE
|
||||
: CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
@@ -122,11 +123,12 @@ public class GraphicsDriverEnableForAllAppsPreferenceController extends BasePref
|
||||
final ListPreference listPref = (ListPreference) preference;
|
||||
listPref.setVisible(isAvailable());
|
||||
final int currentChoice = Settings.Global.getInt(
|
||||
mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT);
|
||||
if (currentChoice == GAME_DRIVER_ALL_APPS) {
|
||||
listPref.setValue(mPreferenceGameDriver);
|
||||
listPref.setSummary(mPreferenceGameDriver);
|
||||
} else if (currentChoice == GAME_DRIVER_PRERELEASE_ALL_APPS) {
|
||||
mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
|
||||
UPDATABLE_DRIVER_DEFAULT);
|
||||
if (currentChoice == UPDATABLE_DRIVER_PRODUCTION_ALL_APPS) {
|
||||
listPref.setValue(mPreferenceProductionDriver);
|
||||
listPref.setSummary(mPreferenceProductionDriver);
|
||||
} else if (currentChoice == UPDATABLE_DRIVER_PRERELEASE_ALL_APPS) {
|
||||
listPref.setValue(mPreferencePrereleaseDriver);
|
||||
listPref.setSummary(mPreferencePrereleaseDriver);
|
||||
} else {
|
||||
@@ -140,21 +142,22 @@ public class GraphicsDriverEnableForAllAppsPreferenceController extends BasePref
|
||||
final ListPreference listPref = (ListPreference) preference;
|
||||
final String value = newValue.toString();
|
||||
final int currentChoice = Settings.Global.getInt(
|
||||
mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT);
|
||||
mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
|
||||
UPDATABLE_DRIVER_DEFAULT);
|
||||
final int userChoice;
|
||||
if (value.equals(mPreferenceGameDriver)) {
|
||||
userChoice = GAME_DRIVER_ALL_APPS;
|
||||
if (value.equals(mPreferenceProductionDriver)) {
|
||||
userChoice = UPDATABLE_DRIVER_PRODUCTION_ALL_APPS;
|
||||
} else if (value.equals(mPreferencePrereleaseDriver)) {
|
||||
userChoice = GAME_DRIVER_PRERELEASE_ALL_APPS;
|
||||
userChoice = UPDATABLE_DRIVER_PRERELEASE_ALL_APPS;
|
||||
} else {
|
||||
userChoice = GAME_DRIVER_DEFAULT;
|
||||
userChoice = UPDATABLE_DRIVER_DEFAULT;
|
||||
}
|
||||
listPref.setValue(value);
|
||||
listPref.setSummary(value);
|
||||
|
||||
if (userChoice != currentChoice) {
|
||||
Settings.Global.putInt(
|
||||
mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, userChoice);
|
||||
mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS, userChoice);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -172,7 +175,8 @@ public class GraphicsDriverEnableForAllAppsPreferenceController extends BasePref
|
||||
final Resources resources = context.getResources();
|
||||
final String prereleaseDriverPackageName =
|
||||
SystemProperties.get(PROPERTY_GFX_DRIVER_PRERELEASE);
|
||||
final String gameDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER_GAME);
|
||||
final String productionDriverPackageName =
|
||||
SystemProperties.get(PROPERTY_GFX_DRIVER_PRODUCTION);
|
||||
|
||||
List<CharSequence> entryList = new ArrayList<>();
|
||||
entryList.add(resources.getString(R.string.graphics_driver_app_preference_default));
|
||||
@@ -182,9 +186,10 @@ public class GraphicsDriverEnableForAllAppsPreferenceController extends BasePref
|
||||
entryList.add(resources.getString(
|
||||
R.string.graphics_driver_app_preference_prerelease_driver));
|
||||
}
|
||||
if (!TextUtils.isEmpty(gameDriverPackageName)
|
||||
&& hasDriverPackage(pm, gameDriverPackageName)) {
|
||||
entryList.add(resources.getString(R.string.graphics_driver_app_preference_game_driver));
|
||||
if (!TextUtils.isEmpty(productionDriverPackageName)
|
||||
&& hasDriverPackage(pm, productionDriverPackageName)) {
|
||||
entryList.add(resources.getString(
|
||||
R.string.graphics_driver_app_preference_production_driver));
|
||||
}
|
||||
if (withSystem) {
|
||||
entryList.add(resources.getString(R.string.graphics_driver_app_preference_system));
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package com.android.settings.development.graphicsdriver;
|
||||
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_DEFAULT;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_OFF;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.UPDATABLE_DRIVER_DEFAULT;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.UPDATABLE_DRIVER_OFF;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -58,8 +58,9 @@ public class GraphicsDriverFooterPreferenceController extends BasePreferenceCont
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return Settings.Global.getInt(
|
||||
mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT)
|
||||
== GAME_DRIVER_OFF
|
||||
mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
|
||||
UPDATABLE_DRIVER_DEFAULT)
|
||||
== UPDATABLE_DRIVER_OFF
|
||||
? AVAILABLE_UNSEARCHABLE
|
||||
: CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
package com.android.settings.development.graphicsdriver;
|
||||
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_ALL_APPS;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_DEFAULT;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_OFF;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_PRERELEASE_ALL_APPS;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.UPDATABLE_DRIVER_DEFAULT;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.UPDATABLE_DRIVER_OFF;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.UPDATABLE_DRIVER_PRERELEASE_ALL_APPS;
|
||||
import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.UPDATABLE_DRIVER_PRODUCTION_ALL_APPS;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -36,7 +36,7 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||
import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
||||
|
||||
/**
|
||||
* Controller of global switch bar used to fully turn off Game Driver.
|
||||
* Controller of global switch bar used to fully turn off updatable driver.
|
||||
*/
|
||||
public class GraphicsDriverGlobalSwitchBarController
|
||||
implements SwitchWidgetController.OnSwitchChangeListener,
|
||||
@@ -61,8 +61,9 @@ public class GraphicsDriverGlobalSwitchBarController
|
||||
DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context));
|
||||
mSwitchWidgetController.setChecked(
|
||||
Settings.Global.getInt(
|
||||
mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT)
|
||||
!= GAME_DRIVER_OFF);
|
||||
mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
|
||||
UPDATABLE_DRIVER_DEFAULT)
|
||||
!= UPDATABLE_DRIVER_OFF);
|
||||
mSwitchWidgetController.setListener(this);
|
||||
}
|
||||
|
||||
@@ -81,21 +82,22 @@ public class GraphicsDriverGlobalSwitchBarController
|
||||
@Override
|
||||
public boolean onSwitchToggled(boolean isChecked) {
|
||||
final int graphicsDriverGlobalOption = Settings.Global.getInt(
|
||||
mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT);
|
||||
mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
|
||||
UPDATABLE_DRIVER_DEFAULT);
|
||||
|
||||
if (isChecked
|
||||
&& (graphicsDriverGlobalOption == GAME_DRIVER_DEFAULT
|
||||
|| graphicsDriverGlobalOption == GAME_DRIVER_ALL_APPS
|
||||
|| graphicsDriverGlobalOption == GAME_DRIVER_PRERELEASE_ALL_APPS)) {
|
||||
&& (graphicsDriverGlobalOption == UPDATABLE_DRIVER_DEFAULT
|
||||
|| graphicsDriverGlobalOption == UPDATABLE_DRIVER_PRODUCTION_ALL_APPS
|
||||
|| graphicsDriverGlobalOption == UPDATABLE_DRIVER_PRERELEASE_ALL_APPS)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isChecked && graphicsDriverGlobalOption == GAME_DRIVER_OFF) {
|
||||
if (!isChecked && graphicsDriverGlobalOption == UPDATABLE_DRIVER_OFF) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Settings.Global.putInt(mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS,
|
||||
isChecked ? GAME_DRIVER_DEFAULT : GAME_DRIVER_OFF);
|
||||
Settings.Global.putInt(mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
|
||||
isChecked ? UPDATABLE_DRIVER_DEFAULT : UPDATABLE_DRIVER_OFF);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -104,7 +106,8 @@ public class GraphicsDriverGlobalSwitchBarController
|
||||
public void onGraphicsDriverContentChanged() {
|
||||
mSwitchWidgetController.setChecked(
|
||||
Settings.Global.getInt(
|
||||
mContentResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT)
|
||||
!= GAME_DRIVER_OFF);
|
||||
mContentResolver, Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
|
||||
UPDATABLE_DRIVER_DEFAULT)
|
||||
!= UPDATABLE_DRIVER_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,26 +18,29 @@ package com.android.settings.display.darkmode;
|
||||
import android.content.Context;
|
||||
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Formats LocalTime to the locale time string format
|
||||
*/
|
||||
public class TimeFormatter {
|
||||
private final Context mContext;
|
||||
private final DateTimeFormatter mFormatter;
|
||||
private final java.text.DateFormat mFormatter;
|
||||
public TimeFormatter(Context context) {
|
||||
mContext = context;
|
||||
Locale locale = mContext.getResources().getConfiguration().locale;
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
mFormatter = DateTimeFormatter.ofPattern("hh:mm a", locale);
|
||||
mFormatter = android.text.format.DateFormat.getTimeFormat(context);
|
||||
mFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
public String of(LocalTime time) {
|
||||
return mFormatter.format(time);
|
||||
final Calendar c = Calendar.getInstance();
|
||||
c.setTimeZone(mFormatter.getTimeZone());
|
||||
c.set(Calendar.HOUR_OF_DAY, time.getHour());
|
||||
c.set(Calendar.MINUTE, time.getMinute());
|
||||
c.set(Calendar.SECOND, 0);
|
||||
c.set(Calendar.MILLISECOND, 0);
|
||||
return mFormatter.format(c.getTime());
|
||||
}
|
||||
|
||||
public boolean is24HourFormat() {
|
||||
|
||||
@@ -34,6 +34,7 @@ import androidx.preference.PreferenceScreen;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.widget.EntityHeaderController;
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
@@ -49,6 +50,8 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
|
||||
@VisibleForTesting
|
||||
static final String KEY_BATTERY_HEADER = "battery_header";
|
||||
|
||||
@VisibleForTesting
|
||||
BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
|
||||
@VisibleForTesting
|
||||
BatteryMeterView mBatteryMeterView;
|
||||
@VisibleForTesting
|
||||
@@ -66,6 +69,8 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
|
||||
public BatteryHeaderPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
mPowerManager = context.getSystemService(PowerManager.class);
|
||||
mBatteryStatusFeatureProvider = FeatureFactory.getFactory(context)
|
||||
.getBatteryStatusFeatureProvider(context);
|
||||
}
|
||||
|
||||
public void setActivity(Activity activity) {
|
||||
@@ -107,10 +112,12 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
|
||||
|
||||
public void updateHeaderPreference(BatteryInfo info) {
|
||||
mBatteryPercentText.setText(formatBatteryPercentageText(info.batteryLevel));
|
||||
if (info.remainingLabel == null) {
|
||||
mSummary1.setText(info.statusLabel);
|
||||
} else {
|
||||
mSummary1.setText(info.remainingLabel);
|
||||
if (!mBatteryStatusFeatureProvider.triggerBatteryStatusUpdate(this, info)) {
|
||||
if (info.remainingLabel == null) {
|
||||
mSummary1.setText(info.statusLabel);
|
||||
} else {
|
||||
mSummary1.setText(info.remainingLabel);
|
||||
}
|
||||
}
|
||||
|
||||
mBatteryMeterView.setBatteryLevel(info.batteryLevel);
|
||||
@@ -118,6 +125,13 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
|
||||
mBatteryMeterView.setPowerSave(mPowerManager.isPowerSaveMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback which receives text for the summary line.
|
||||
*/
|
||||
public void updateBatteryStatus(String statusLabel) {
|
||||
mSummary1.setText(statusLabel);
|
||||
}
|
||||
|
||||
public void quickUpdateHeaderPreference() {
|
||||
Intent batteryBroadcast = mContext.registerReceiver(null,
|
||||
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.fuelgauge;
|
||||
|
||||
/**
|
||||
* Feature Provider used to retrieve battery status
|
||||
*/
|
||||
public interface BatteryStatusFeatureProvider {
|
||||
|
||||
/**
|
||||
* Trigger a battery status update; return false if built-in status should be used.
|
||||
*/
|
||||
boolean triggerBatteryStatusUpdate(
|
||||
BatteryHeaderPreferenceController batteryHeaderPreferenceController, BatteryInfo info);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.fuelgauge;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Used to override battery status string in Battery Header.
|
||||
*/
|
||||
public class BatteryStatusFeatureProviderImpl implements BatteryStatusFeatureProvider {
|
||||
|
||||
protected Context mContext;
|
||||
|
||||
public BatteryStatusFeatureProviderImpl(Context context) {
|
||||
mContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean triggerBatteryStatusUpdate(
|
||||
BatteryHeaderPreferenceController batteryHeaderPreferenceController, BatteryInfo info) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,9 @@ public interface ContextualCardFeatureProvider {
|
||||
/** Get contextual cards from the card provider */
|
||||
Cursor getContextualCards();
|
||||
|
||||
/** Get the default contextual card to display */
|
||||
ContextualCard getDefaultContextualCard();
|
||||
|
||||
/**
|
||||
* Mark a specific {@link ContextualCard} as dismissed with dismissal signal in the database
|
||||
* to indicate that the card has been dismissed.
|
||||
|
||||
@@ -54,6 +54,11 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextualCard getDefaultContextualCard() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int markCardAsDismissed(Context context, String cardName) {
|
||||
final SQLiteDatabase db = CardDatabaseHelper.getInstance(mContext).getWritableDatabase();
|
||||
|
||||
@@ -50,7 +50,7 @@ import java.util.stream.Collectors;
|
||||
public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>> {
|
||||
|
||||
@VisibleForTesting
|
||||
static final int DEFAULT_CARD_COUNT = 3;
|
||||
static final int DEFAULT_CARD_COUNT = 1;
|
||||
@VisibleForTesting
|
||||
static final String CONTEXTUAL_CARD_COUNT = "contextual_card_count";
|
||||
static final int CARD_CONTENT_LOADER_ID = 1;
|
||||
@@ -131,7 +131,7 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
|
||||
final List<ContextualCard> visibleCards = new ArrayList<>();
|
||||
final List<ContextualCard> hiddenCards = new ArrayList<>();
|
||||
|
||||
final int maxCardCount = getCardCount();
|
||||
final int maxCardCount = getCardCount(mContext);
|
||||
eligibleCards.forEach(card -> {
|
||||
if (card.getCategory() != STICKY_VALUE) {
|
||||
return;
|
||||
@@ -164,14 +164,23 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
|
||||
SettingsEnums.ACTION_CONTEXTUAL_CARD_NOT_SHOW,
|
||||
ContextualCardLogUtils.buildCardListLog(hiddenCards));
|
||||
}
|
||||
|
||||
// Add a default card if no other visible cards
|
||||
if (visibleCards.isEmpty() && maxCardCount == 1) {
|
||||
final ContextualCard defaultCard = FeatureFactory.getFactory(mContext)
|
||||
.getContextualCardFeatureProvider(mContext).getDefaultContextualCard();
|
||||
if (defaultCard != null) {
|
||||
Log.i(TAG, "Default card: " + defaultCard.getSliceUri());
|
||||
visibleCards.add(defaultCard);
|
||||
}
|
||||
}
|
||||
return visibleCards;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
int getCardCount() {
|
||||
static int getCardCount(Context context) {
|
||||
// Return the card count if Settings.Global has KEY_CONTEXTUAL_CARD_COUNT key,
|
||||
// otherwise return the default one.
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
return Settings.Global.getInt(context.getContentResolver(),
|
||||
CONTEXTUAL_CARD_COUNT, DEFAULT_CARD_COUNT);
|
||||
}
|
||||
|
||||
|
||||
@@ -122,6 +122,10 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
||||
Log.w(TAG, "Legacy suggestion contextual card enabled, skipping contextual cards.");
|
||||
return;
|
||||
}
|
||||
if (ContextualCardLoader.getCardCount(mContext) <= 0) {
|
||||
Log.w(TAG, "Card count is zero, skipping contextual cards.");
|
||||
return;
|
||||
}
|
||||
mStartTime = System.currentTimeMillis();
|
||||
final CardContentLoaderCallbacks cardContentLoaderCallbacks =
|
||||
new CardContentLoaderCallbacks(mContext);
|
||||
@@ -239,7 +243,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
||||
final MetricsFeatureProvider metricsFeatureProvider =
|
||||
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||
|
||||
//navigate back to the homepage, screen rotate or after card dismissal
|
||||
// navigate back to the homepage, screen rotate or after card dismissal
|
||||
if (!mIsFirstLaunch) {
|
||||
onContextualCardUpdated(cardsToKeep.stream()
|
||||
.collect(groupingBy(ContextualCard::getCardType)));
|
||||
@@ -262,8 +266,17 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
||||
SettingsEnums.ACTION_CONTEXTUAL_CARD_LOAD_TIMEOUT,
|
||||
SettingsEnums.SETTINGS_HOMEPAGE,
|
||||
null /* key */, (int) loadTime /* value */);
|
||||
|
||||
// display a card on timeout if the one-card space is pre-allocated
|
||||
if (!cards.isEmpty() && ContextualCardLoader.getCardCount(mContext) == 1) {
|
||||
onContextualCardUpdated(cards.stream()
|
||||
.collect(groupingBy(ContextualCard::getCardType)));
|
||||
metricsFeatureProvider.action(mContext,
|
||||
SettingsEnums.ACTION_CONTEXTUAL_CARD_SHOW,
|
||||
ContextualCardLogUtils.buildCardListLog(cards));
|
||||
}
|
||||
}
|
||||
//only log homepage display upon a fresh launch
|
||||
// only log homepage display upon a fresh launch
|
||||
final long totalTime = System.currentTimeMillis() - mStartTime;
|
||||
metricsFeatureProvider.action(mContext,
|
||||
SettingsEnums.ACTION_CONTEXTUAL_HOME_SHOW, (int) totalTime);
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package com.android.settings.homepage.contextualcards;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -131,7 +134,22 @@ public class ContextualCardsAdapter extends RecyclerView.Adapter<RecyclerView.Vi
|
||||
diffResult.dispatchUpdatesTo(this);
|
||||
}
|
||||
|
||||
if (mRecyclerView != null && previouslyEmpty && !nowEmpty) {
|
||||
if (mRecyclerView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When no card gets displayed either because a card's condition no longer meets
|
||||
// or when it's dismissed, the height should be rearranged.
|
||||
if (mContextualCards.isEmpty()) {
|
||||
final ViewGroup.LayoutParams params = mRecyclerView.getLayoutParams();
|
||||
if (params.height != WRAP_CONTENT) {
|
||||
Log.d(TAG, "mContextualCards is empty. Set the RV to wrap_content");
|
||||
params.height = WRAP_CONTENT;
|
||||
mRecyclerView.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
if (previouslyEmpty && !nowEmpty) {
|
||||
// Adding items to empty list, should animate.
|
||||
mRecyclerView.scheduleLayoutAnimation();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings.homepage.contextualcards;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
|
||||
import static com.android.settings.homepage.contextualcards.ContextualCardsAdapter.SPAN_COUNT;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
@@ -34,6 +36,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
@@ -105,8 +108,20 @@ public class ContextualCardsFragment extends InstrumentedFragment implements
|
||||
final View rootView = inflater.inflate(R.layout.settings_homepage, container, false);
|
||||
mCardsContainer = rootView.findViewById(R.id.card_container);
|
||||
mLayoutManager = new GridLayoutManager(getActivity(), SPAN_COUNT,
|
||||
GridLayoutManager.VERTICAL, false /* reverseLayout */);
|
||||
GridLayoutManager.VERTICAL, false /* reverseLayout */) {
|
||||
@Override
|
||||
public void onLayoutCompleted(RecyclerView.State state) {
|
||||
super.onLayoutCompleted(state);
|
||||
// Once cards finish laying out, make the RV back to wrap content for flexibility.
|
||||
final ViewGroup.LayoutParams params = mCardsContainer.getLayoutParams();
|
||||
if (params.height != WRAP_CONTENT) {
|
||||
params.height = WRAP_CONTENT;
|
||||
mCardsContainer.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
};
|
||||
mCardsContainer.setLayoutManager(mLayoutManager);
|
||||
preAllocateHeight(context);
|
||||
mContextualCardsAdapter = new ContextualCardsAdapter(context, this /* lifecycleOwner */,
|
||||
mContextualCardManager);
|
||||
mCardsContainer.setItemAnimator(null);
|
||||
@@ -159,6 +174,25 @@ public class ContextualCardsFragment extends InstrumentedFragment implements
|
||||
FeatureFactory.getFactory(context).getSlicesFeatureProvider().newUiSession();
|
||||
}
|
||||
|
||||
private void preAllocateHeight(Context context) {
|
||||
final int cardCount = ContextualCardLoader.getCardCount(context);
|
||||
if (cardCount != 1) {
|
||||
// only pre-allocate space when card count is one
|
||||
Log.d(TAG, "Skip height pre-allocating. card count = " + cardCount);
|
||||
return;
|
||||
}
|
||||
|
||||
final int preAllocatedHeight = getResources().getDimensionPixelSize(
|
||||
R.dimen.contextual_card_preallocated_height);
|
||||
if (preAllocatedHeight == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ViewGroup.LayoutParams params = mCardsContainer.getLayoutParams();
|
||||
params.height = preAllocatedHeight;
|
||||
mCardsContainer.setLayoutParams(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receiver for updating UI session when home key or recent app key is pressed.
|
||||
*/
|
||||
|
||||
@@ -47,7 +47,6 @@ import com.android.settings.homepage.contextualcards.CardContentProvider;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardRenderer;
|
||||
import com.android.settings.homepage.contextualcards.ControllerRendererPool;
|
||||
import com.android.settings.homepage.contextualcards.slices.SliceFullCardRendererHelper.SliceViewHolder;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -105,7 +104,7 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, Life
|
||||
|
||||
// Show cached slice first before slice binding completed to avoid jank.
|
||||
if (holder.getItemViewType() != VIEW_TYPE_HALF_WIDTH) {
|
||||
((SliceViewHolder) holder).sliceView.setSlice(card.getSlice());
|
||||
mFullCardHelper.bindView(holder, card, card.getSlice());
|
||||
}
|
||||
|
||||
LiveData<Slice> sliceLiveData = mSliceLiveDataMap.get(uri);
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.session.MediaController;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
@@ -62,8 +63,12 @@ public class MediaOutputIndicatorSlice implements CustomSliceable {
|
||||
com.android.internal.R.drawable.ic_settings_bluetooth);
|
||||
final CharSequence title = mContext.getString(R.string.media_output_label_title,
|
||||
Utils.getApplicationLabel(mContext, getWorker().getPackageName()));
|
||||
final int requestCode = TextUtils.isEmpty(getWorker().getPackageName())
|
||||
? 0
|
||||
: getWorker().getPackageName().hashCode();
|
||||
final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext,
|
||||
0 /* requestCode */, getMediaOutputSliceIntent(), FLAG_UPDATE_CURRENT);
|
||||
requestCode,
|
||||
getMediaOutputSliceIntent(), FLAG_UPDATE_CURRENT);
|
||||
final SliceAction primarySliceAction = SliceAction.createDeeplink(
|
||||
primaryActionIntent, icon, ListBuilder.ICON_IMAGE, title);
|
||||
@ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
|
||||
|
||||
@@ -106,10 +106,15 @@ public class MediaOutputSlice implements CustomSliceable {
|
||||
final MediaDevice connectedDevice = worker.getCurrentConnectedMediaDevice();
|
||||
if (devices.size() == 1) {
|
||||
// Zero state
|
||||
for (MediaDevice device : devices) {
|
||||
addRow(device, device, listBuilder);
|
||||
final MediaDevice device = devices.iterator().next();
|
||||
addRow(device, device, listBuilder);
|
||||
// Add "pair new" only when local output device exists
|
||||
final int type = device.getDeviceType();
|
||||
if (type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE
|
||||
|| type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
|
||||
|| type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE) {
|
||||
listBuilder.addRow(getPairNewRow());
|
||||
}
|
||||
listBuilder.addRow(getPairNewRow());
|
||||
} else {
|
||||
final boolean isTouched = worker.getIsTouched();
|
||||
// Fix the last top device when user press device to transfer.
|
||||
@@ -252,9 +257,11 @@ public class MediaOutputSlice implements CustomSliceable {
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
|
||||
getWorker().getPackageName());
|
||||
|
||||
final int requestCode = TextUtils.isEmpty(getWorker().getPackageName())
|
||||
? 0
|
||||
: getWorker().getPackageName().hashCode();
|
||||
return SliceAction.createDeeplink(
|
||||
PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */),
|
||||
PendingIntent.getActivity(mContext, requestCode, intent, 0 /* flags */),
|
||||
IconCompat.createWithResource(mContext, R.drawable.ic_add_blue_24dp),
|
||||
ListBuilder.ICON_IMAGE,
|
||||
mContext.getText(R.string.add));
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.media;
|
||||
|
||||
import static android.app.slice.Slice.EXTRA_RANGE_VALUE;
|
||||
import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
|
||||
|
||||
import static com.android.settings.slices.CustomSliceRegistry.REMOTE_MEDIA_SLICE_URI;
|
||||
|
||||
@@ -24,11 +25,15 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.media.RoutingSessionInfo;
|
||||
import android.net.Uri;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.builders.ListBuilder;
|
||||
@@ -59,6 +64,9 @@ public class RemoteMediaSlice implements CustomSliceable {
|
||||
|
||||
private MediaDeviceUpdateWorker mWorker;
|
||||
|
||||
@VisibleForTesting
|
||||
MediaRouter2Manager mRouterManager;
|
||||
|
||||
public RemoteMediaSlice(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
@@ -80,6 +88,9 @@ public class RemoteMediaSlice implements CustomSliceable {
|
||||
Log.e(TAG, "Unable to get the slice worker.");
|
||||
return listBuilder.build();
|
||||
}
|
||||
if (mRouterManager == null) {
|
||||
mRouterManager = MediaRouter2Manager.getInstance(mContext);
|
||||
}
|
||||
// Only displaying remote devices
|
||||
final List<RoutingSessionInfo> infos = getWorker().getActiveRemoteMediaDevice();
|
||||
if (infos.isEmpty()) {
|
||||
@@ -91,7 +102,6 @@ public class RemoteMediaSlice implements CustomSliceable {
|
||||
R.drawable.ic_volume_remote);
|
||||
// To create an empty icon to indent the row
|
||||
final IconCompat emptyIcon = createEmptyIcon();
|
||||
int requestCode = 0;
|
||||
for (RoutingSessionInfo info : infos) {
|
||||
final int maxVolume = info.getVolumeMax();
|
||||
if (maxVolume <= 0) {
|
||||
@@ -99,20 +109,32 @@ public class RemoteMediaSlice implements CustomSliceable {
|
||||
+ maxVolume);
|
||||
continue;
|
||||
}
|
||||
final CharSequence appName = Utils.getApplicationLabel(
|
||||
mContext, info.getClientPackageName());
|
||||
final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title,
|
||||
Utils.getApplicationLabel(mContext, info.getClientPackageName()));
|
||||
appName);
|
||||
listBuilder.addInputRange(new InputRangeBuilder()
|
||||
.setTitleItem(icon, ListBuilder.ICON_IMAGE)
|
||||
.setTitle(castVolume)
|
||||
.setInputAction(getSliderInputAction(requestCode++, info.getId()))
|
||||
.setInputAction(getSliderInputAction(info.getId().hashCode(), info.getId()))
|
||||
.setPrimaryAction(getSoundSettingAction(castVolume, icon, info.getId()))
|
||||
.setMax(maxVolume)
|
||||
.setValue(info.getVolume()));
|
||||
|
||||
final boolean isMediaOutputDisabled =
|
||||
Utils.isMediaOutputDisabled(mRouterManager, info.getClientPackageName());
|
||||
final SpannableString spannableTitle = new SpannableString(
|
||||
TextUtils.isEmpty(appName) ? "" : appName);
|
||||
spannableTitle.setSpan(new ForegroundColorSpan(
|
||||
Utils.getColorAttrDefaultColor(
|
||||
mContext, android.R.attr.textColorSecondary)), 0,
|
||||
spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
listBuilder.addRow(new ListBuilder.RowBuilder()
|
||||
.setTitle(outputTitle)
|
||||
.setTitle(isMediaOutputDisabled ? spannableTitle : outputTitle)
|
||||
.setSubtitle(info.getName())
|
||||
.setTitleItem(emptyIcon, ListBuilder.ICON_IMAGE)
|
||||
.setPrimaryAction(getMediaOutputSliceAction(info.getClientPackageName())));
|
||||
.setPrimaryAction(getMediaOutputSliceAction(
|
||||
info.getClientPackageName(), isMediaOutputDisabled)));
|
||||
}
|
||||
return listBuilder.build();
|
||||
}
|
||||
@@ -145,15 +167,19 @@ public class RemoteMediaSlice implements CustomSliceable {
|
||||
return primarySliceAction;
|
||||
}
|
||||
|
||||
private SliceAction getMediaOutputSliceAction(String packageName) {
|
||||
private SliceAction getMediaOutputSliceAction(
|
||||
String packageName, boolean isMediaOutputDisabled) {
|
||||
final Intent intent = new Intent()
|
||||
.setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
|
||||
.setAction(isMediaOutputDisabled
|
||||
? ""
|
||||
: MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, packageName);
|
||||
final IconCompat icon = IconCompat.createWithResource(mContext,
|
||||
R.drawable.ic_volume_remote);
|
||||
final int requestCode = TextUtils.isEmpty(packageName) ? 0 : packageName.hashCode();
|
||||
final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext,
|
||||
0 /* requestCode */, intent, 0 /* flags */);
|
||||
requestCode, intent, 0 /* flags */);
|
||||
final SliceAction primarySliceAction = SliceAction.createDeeplink(
|
||||
primaryActionIntent, icon, ListBuilder.ICON_IMAGE,
|
||||
mContext.getString(R.string.media_output_label_title,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.network;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
/**
|
||||
* {@link BasePreferenceController} that shows Adaptive connectivity on/off state.
|
||||
*/
|
||||
public class AdaptiveConnectivityPreferenceController extends BasePreferenceController {
|
||||
|
||||
public AdaptiveConnectivityPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return mContext.getResources().getBoolean(R.bool.config_show_adaptive_connectivity)
|
||||
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 1) == 1
|
||||
? mContext.getString(R.string.switch_on_text)
|
||||
: mContext.getString(R.string.switch_off_text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.network;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
/**
|
||||
* Adaptive connectivity is a feature which automatically manages network connections.
|
||||
*/
|
||||
@SearchIndexable
|
||||
public class AdaptiveConnectivitySettings extends DashboardFragment {
|
||||
|
||||
private static final String TAG = "AdaptiveConnectivitySettings";
|
||||
|
||||
private static final String KEY_ADAPTIVE_CONNECTIVITY_PREFERENCE = "adaptive_connectivity";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.ADAPTIVE_CONNECTIVITY_CATEGORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.adaptive_connectivity_settings;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.adaptive_connectivity_settings);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.network;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
|
||||
/**
|
||||
* {@link TogglePreferenceController} that controls whether Adaptive connectivity option is enabled.
|
||||
*/
|
||||
public class AdaptiveConnectivityTogglePreferenceController extends TogglePreferenceController {
|
||||
|
||||
public AdaptiveConnectivityTogglePreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 1) == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
|
||||
isChecked ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -280,7 +280,8 @@ public class MobileNetworkUtils {
|
||||
String.format("showEuiccSettings: esimIgnoredDevice: %b, enabledEsimUiByDefault: "
|
||||
+ "%b, euiccProvisioned: %b, inDeveloperMode: %b.",
|
||||
esimIgnoredDevice, enabledEsimUiByDefault, euiccProvisioned, inDeveloperMode));
|
||||
return (inDeveloperMode || euiccProvisioned
|
||||
return (euiccProvisioned
|
||||
|| (!esimIgnoredDevice && inDeveloperMode)
|
||||
|| (!esimIgnoredDevice && enabledEsimUiByDefault
|
||||
&& isCurrentCountrySupported(context)));
|
||||
}
|
||||
|
||||
@@ -126,10 +126,8 @@ public class NetworkOperatorPreference extends Preference {
|
||||
final CellSignalStrength signalStrength = getCellSignalStrength(mCellInfo);
|
||||
final int level = signalStrength != null ? signalStrength.getLevel() : LEVEL_NONE;
|
||||
if (DBG) Log.d(TAG, "refresh level: " + String.valueOf(level));
|
||||
if (mLevel != level) {
|
||||
mLevel = level;
|
||||
updateIcon(mLevel);
|
||||
}
|
||||
mLevel = level;
|
||||
updateIcon(mLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,6 +55,13 @@ public class NrDisabledInDsdsFooterPreferenceController extends BasePreferenceCo
|
||||
super.updateState(preference);
|
||||
|
||||
if (preference != null) {
|
||||
// This is necessary to ensure that setting the title to the spannable string returned
|
||||
// by getFooterText will be accepted. Internally, setTitle does an equality check on
|
||||
// the spannable string being set to the text already set on the preference. That
|
||||
// equality check apparently only takes into account the raw text and not and spannables
|
||||
// that are part of the text. So we clear the title before applying the spannable
|
||||
// footer to ensure it is accepted.
|
||||
preference.setTitle("");
|
||||
preference.setTitle(getFooterText());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ package com.android.settings.network.telephony;
|
||||
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
/**
|
||||
* Contains hidden constants copied from the platform.
|
||||
*/
|
||||
@@ -211,6 +209,12 @@ public class TelephonyConstants {
|
||||
* Copied from {@link android.telephony.RadioAccessFamily}
|
||||
*/
|
||||
public static class RadioAccessFamily {
|
||||
/**
|
||||
* TODO: get rid of RAF definition in RadioAccessFamily and
|
||||
* use {@link TelephonyManager.NetworkTypeBitMask}
|
||||
* TODO: public definition {@link TelephonyManager.NetworkTypeBitMask} is long.
|
||||
* TODO: Convert from int * to long everywhere including HAL definitions.
|
||||
*/
|
||||
// 2G
|
||||
public static final int RAF_UNKNOWN = (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN;
|
||||
public static final int RAF_GSM = (int) TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
|
||||
@@ -246,9 +250,7 @@ public class TelephonyConstants {
|
||||
public static final int WCDMA = HS | RAF_UMTS;
|
||||
// 4G
|
||||
public static final int LTE = RAF_LTE | RAF_LTE_CA;
|
||||
|
||||
// 5G
|
||||
public static final int NR = RAF_NR;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ public abstract class AdjustVolumeRestrictedPreferenceController extends
|
||||
filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
|
||||
filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
|
||||
filter.addAction(AudioManager.MASTER_MUTE_CHANGED_ACTION);
|
||||
filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.notification;
|
||||
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
@@ -81,7 +82,8 @@ public class BubbleNotificationPreferenceController extends TogglePreferenceCont
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
ActivityManager am = mContext.getSystemService(ActivityManager.class);
|
||||
return am.isLowRamDevice() ? UNSUPPORTED_ON_DEVICE : AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.notification;
|
||||
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
@@ -48,7 +49,8 @@ public class BubbleSummaryNotificationPreferenceController extends BasePreferenc
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
ActivityManager am = mContext.getSystemService(ActivityManager.class);
|
||||
return am.isLowRamDevice() ? UNSUPPORTED_ON_DEVICE : AVAILABLE;
|
||||
}
|
||||
|
||||
private boolean areBubblesEnabled() {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.notification;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.media.RoutingSessionInfo;
|
||||
import android.text.TextUtils;
|
||||
|
||||
@@ -57,6 +58,8 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem
|
||||
|
||||
@VisibleForTesting
|
||||
LocalMediaManager mLocalMediaManager;
|
||||
@VisibleForTesting
|
||||
MediaRouter2Manager mRouterManager;
|
||||
|
||||
public RemoteVolumeGroupController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
@@ -65,6 +68,7 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem
|
||||
mLocalMediaManager.registerCallback(this);
|
||||
mLocalMediaManager.startScan();
|
||||
}
|
||||
mRouterManager = MediaRouter2Manager.getInstance(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,8 +115,10 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem
|
||||
if (mPreferenceCategory.findPreference(info.getId()) != null) {
|
||||
continue;
|
||||
}
|
||||
final CharSequence appName = Utils.getApplicationLabel(
|
||||
mContext, info.getClientPackageName());
|
||||
final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title,
|
||||
Utils.getApplicationLabel(mContext, info.getClientPackageName()));
|
||||
appName);
|
||||
// Add slider
|
||||
final RemoteVolumeSeekBarPreference seekBarPreference =
|
||||
new RemoteVolumeSeekBarPreference(mContext);
|
||||
@@ -125,10 +131,13 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem
|
||||
seekBarPreference.setIcon(R.drawable.ic_volume_remote);
|
||||
mPreferenceCategory.addPreference(seekBarPreference);
|
||||
// Add output indicator
|
||||
final boolean isMediaOutputDisabled = Utils.isMediaOutputDisabled(
|
||||
mRouterManager, info.getClientPackageName());
|
||||
final Preference preference = new Preference(mContext);
|
||||
preference.setKey(SWITCHER_PREFIX + info.getId());
|
||||
preference.setTitle(outputTitle);
|
||||
preference.setTitle(isMediaOutputDisabled ? appName : outputTitle);
|
||||
preference.setSummary(info.getName());
|
||||
preference.setEnabled(!isMediaOutputDisabled);
|
||||
mPreferenceCategory.addPreference(preference);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult
|
||||
private RingtonePreference mRequestPreference;
|
||||
private UpdatableListPreferenceDialogFragment mDialogFragment;
|
||||
private String mHfpOutputControllerKey;
|
||||
private String mVibrationPreferencesKey = "vibration_preference_screen";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -127,6 +128,10 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
if (TextUtils.equals(mVibrationPreferencesKey, preference.getKey())) {
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
return;
|
||||
}
|
||||
final int metricsCategory;
|
||||
if (mHfpOutputControllerKey.equals(preference.getKey())) {
|
||||
metricsCategory = SettingsEnums.DIALOG_SWITCH_HFP_DEVICES;
|
||||
@@ -310,4 +315,4 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult
|
||||
mDialogFragment.onListPreferenceUpdated(preference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,10 @@ public abstract class VolumeSeekBarPreferenceController extends
|
||||
return mHelper.getMinVolume(getAudioStream());
|
||||
}
|
||||
|
||||
protected abstract int getAudioStream();
|
||||
/**
|
||||
* @return the audio stream type
|
||||
*/
|
||||
public abstract int getAudioStream();
|
||||
|
||||
protected abstract int getMuteIcon();
|
||||
|
||||
|
||||
@@ -106,8 +106,11 @@ public class BlockPreferenceController extends NotificationPreferenceController
|
||||
// It's always safe to override the importance if it's meant to be blocked or if
|
||||
// it was blocked and we are unblocking it.
|
||||
if (blocked || originalImportance == IMPORTANCE_NONE) {
|
||||
final int importance = blocked ? IMPORTANCE_NONE
|
||||
: isDefaultChannel() ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_DEFAULT;
|
||||
final int importance = blocked
|
||||
? IMPORTANCE_NONE
|
||||
: isDefaultChannel()
|
||||
? IMPORTANCE_UNSPECIFIED
|
||||
: mChannel.getOriginalImportance();
|
||||
mChannel.setImportance(importance);
|
||||
saveChannel();
|
||||
}
|
||||
|
||||
@@ -45,8 +45,6 @@ public class BubblePreference extends Preference implements View.OnClickListener
|
||||
private int mSelectedPreference;
|
||||
|
||||
private Context mContext;
|
||||
private Drawable mSelectedBackground;
|
||||
private Drawable mUnselectedBackground;
|
||||
|
||||
private ButtonViewHolder mBubbleAllButton;
|
||||
private ButtonViewHolder mBubbleSelectedButton;
|
||||
@@ -72,8 +70,6 @@ public class BubblePreference extends Preference implements View.OnClickListener
|
||||
mHelper = new RestrictedPreferenceHelper(context, this, attrs);
|
||||
mHelper.useAdminDisabledSummary(true);
|
||||
mContext = context;
|
||||
mSelectedBackground = mContext.getDrawable(R.drawable.button_border_selected);
|
||||
mUnselectedBackground = mContext.getDrawable(R.drawable.button_border_unselected);
|
||||
setLayoutResource(R.layout.bubble_preference);
|
||||
}
|
||||
|
||||
@@ -167,7 +163,9 @@ public class BubblePreference extends Preference implements View.OnClickListener
|
||||
}
|
||||
|
||||
void setSelected(Context context, boolean selected) {
|
||||
mView.setBackground(selected ? mSelectedBackground : mUnselectedBackground);
|
||||
mView.setBackground(mContext.getDrawable(selected
|
||||
? R.drawable.button_border_selected
|
||||
: R.drawable.button_border_unselected));
|
||||
mView.setSelected(selected);
|
||||
|
||||
ColorStateList stateList = selected
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.settings.notification.app;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
@@ -77,7 +78,7 @@ public class BubblePreferenceController extends NotificationPreferenceController
|
||||
if (isDefaultChannel()) {
|
||||
return true;
|
||||
} else {
|
||||
return mAppRow != null;
|
||||
return mAppRow != null && mAppRow.bubblePreference != BUBBLE_PREFERENCE_NONE;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -139,7 +140,8 @@ public class BubblePreferenceController extends NotificationPreferenceController
|
||||
}
|
||||
|
||||
private boolean isGloballyEnabled() {
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
ActivityManager am = mContext.getSystemService(ActivityManager.class);
|
||||
return !am.isLowRamDevice() && Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
@@ -99,7 +100,8 @@ public class BubbleSummaryPreferenceController extends NotificationPreferenceCon
|
||||
}
|
||||
|
||||
private boolean isGloballyEnabled() {
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
ActivityManager am = mContext.getSystemService(ActivityManager.class);
|
||||
return !am.isLowRamDevice() && Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, ON) == ON;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,17 +207,14 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
|
||||
channelPref.setOnPreferenceChangeListener(
|
||||
(preference, o) -> {
|
||||
boolean value = (Boolean) o;
|
||||
int importance = value ? IMPORTANCE_LOW : IMPORTANCE_NONE;
|
||||
int importance = value ? channel.getOriginalImportance() : IMPORTANCE_NONE;
|
||||
channel.setImportance(importance);
|
||||
channel.lockFields(
|
||||
NotificationChannel.USER_LOCKED_IMPORTANCE);
|
||||
channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
|
||||
MasterSwitchPreference channelPref1 = (MasterSwitchPreference) preference;
|
||||
channelPref1.setIcon(null);
|
||||
if (channel.getImportance() > IMPORTANCE_LOW) {
|
||||
channelPref1.setIcon(getAlertingIcon());
|
||||
}
|
||||
toggleBehaviorIconState(channelPref1.getIcon(),
|
||||
importance != IMPORTANCE_NONE);
|
||||
mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, channel);
|
||||
|
||||
return true;
|
||||
@@ -234,24 +231,6 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
|
||||
return icon;
|
||||
}
|
||||
|
||||
private void toggleBehaviorIconState(Drawable icon, boolean enabled) {
|
||||
if (icon == null) return;
|
||||
|
||||
LayerDrawable layerDrawable = (LayerDrawable) icon;
|
||||
GradientDrawable background =
|
||||
(GradientDrawable) layerDrawable.findDrawableByLayerId(R.id.back);
|
||||
|
||||
if (background == null) return;
|
||||
|
||||
if (enabled) {
|
||||
background.clearColorFilter();
|
||||
} else {
|
||||
background.setColorFilter(new BlendModeColorFilter(
|
||||
mContext.getColor(R.color.material_grey_300),
|
||||
BlendMode.SRC_IN));
|
||||
}
|
||||
}
|
||||
|
||||
protected void onGroupBlockStateChanged(NotificationChannelGroup group) {
|
||||
if (group == null) {
|
||||
return;
|
||||
|
||||
@@ -50,8 +50,6 @@ public class ConversationPriorityPreference extends Preference {
|
||||
private View mAlertButton;
|
||||
private View mPriorityButton;
|
||||
private Context mContext;
|
||||
Drawable selectedBackground;
|
||||
Drawable unselectedBackground;
|
||||
private static final int BUTTON_ANIM_TIME_MS = 100;
|
||||
|
||||
public ConversationPriorityPreference(Context context, AttributeSet attrs,
|
||||
@@ -77,8 +75,6 @@ public class ConversationPriorityPreference extends Preference {
|
||||
|
||||
private void init(Context context) {
|
||||
mContext = context;
|
||||
selectedBackground = mContext.getDrawable(R.drawable.button_border_selected);
|
||||
unselectedBackground = mContext.getDrawable(R.drawable.button_border_unselected);
|
||||
setLayoutResource(R.layout.notif_priority_conversation_preference);
|
||||
}
|
||||
|
||||
@@ -148,86 +144,43 @@ public class ConversationPriorityPreference extends Preference {
|
||||
TransitionManager.beginDelayedTransition(parent, transition);
|
||||
}
|
||||
|
||||
ColorStateList colorAccent = getAccentTint();
|
||||
ColorStateList colorNormal = getRegularTint();
|
||||
ImageView silenceIcon = parent.findViewById(R.id.silence_icon);
|
||||
TextView silenceLabel = parent.findViewById(R.id.silence_label);
|
||||
TextView silenceSummary = parent.findViewById(R.id.silence_summary);
|
||||
ImageView alertIcon = parent.findViewById(R.id.alert_icon);
|
||||
TextView alertLabel = parent.findViewById(R.id.alert_label);
|
||||
TextView alertSummary = parent.findViewById(R.id.alert_summary);
|
||||
ImageView priorityIcon = parent.findViewById(R.id.priority_icon);
|
||||
TextView priorityLabel = parent.findViewById(R.id.priority_label);
|
||||
TextView prioritySummary = parent.findViewById(R.id.priority_summary);
|
||||
|
||||
if (importance <= IMPORTANCE_LOW && importance > IMPORTANCE_UNSPECIFIED) {
|
||||
alertSummary.setVisibility(GONE);
|
||||
alertIcon.setImageTintList(colorNormal);
|
||||
alertLabel.setTextColor(colorNormal);
|
||||
|
||||
prioritySummary.setVisibility(GONE);
|
||||
priorityIcon.setImageTintList(colorNormal);
|
||||
priorityLabel.setTextColor(colorNormal);
|
||||
|
||||
silenceIcon.setImageTintList(colorAccent);
|
||||
silenceLabel.setTextColor(colorAccent);
|
||||
silenceSummary.setVisibility(VISIBLE);
|
||||
|
||||
mAlertButton.setBackground(unselectedBackground);
|
||||
mPriorityButton.setBackground(unselectedBackground);
|
||||
mSilenceButton.setBackground(selectedBackground);
|
||||
// a11y service won't always read the newly appearing text in the right order if the
|
||||
// selection happens too soon (readback happens on a different thread as layout). post
|
||||
// the selection to make that conflict less likely
|
||||
parent.post(() -> {
|
||||
mSilenceButton.setSelected(true);
|
||||
mAlertButton.setSelected(false);
|
||||
mPriorityButton.setSelected(false);
|
||||
});
|
||||
setSelected(mPriorityButton, false);
|
||||
setSelected(mAlertButton, false);
|
||||
setSelected(mSilenceButton, true);
|
||||
} else {
|
||||
if (isPriority) {
|
||||
alertSummary.setVisibility(GONE);
|
||||
alertIcon.setImageTintList(colorNormal);
|
||||
alertLabel.setTextColor(colorNormal);
|
||||
|
||||
prioritySummary.setVisibility(VISIBLE);
|
||||
priorityIcon.setImageTintList(colorAccent);
|
||||
priorityLabel.setTextColor(colorAccent);
|
||||
|
||||
silenceIcon.setImageTintList(colorNormal);
|
||||
silenceLabel.setTextColor(colorNormal);
|
||||
silenceSummary.setVisibility(GONE);
|
||||
|
||||
mAlertButton.setBackground(unselectedBackground);
|
||||
mPriorityButton.setBackground(selectedBackground);
|
||||
mSilenceButton.setBackground(unselectedBackground);
|
||||
parent.post(() -> {
|
||||
mSilenceButton.setSelected(false);
|
||||
mAlertButton.setSelected(false);
|
||||
mPriorityButton.setSelected(true);
|
||||
});
|
||||
setSelected(mPriorityButton, true);
|
||||
setSelected(mAlertButton, false);
|
||||
setSelected(mSilenceButton, false);
|
||||
} else {
|
||||
alertSummary.setVisibility(VISIBLE);
|
||||
alertIcon.setImageTintList(colorAccent);
|
||||
alertLabel.setTextColor(colorAccent);
|
||||
|
||||
prioritySummary.setVisibility(GONE);
|
||||
priorityIcon.setImageTintList(colorNormal);
|
||||
priorityLabel.setTextColor(colorNormal);
|
||||
|
||||
silenceIcon.setImageTintList(colorNormal);
|
||||
silenceLabel.setTextColor(colorNormal);
|
||||
silenceSummary.setVisibility(GONE);
|
||||
|
||||
mAlertButton.setBackground(selectedBackground);
|
||||
mPriorityButton.setBackground(unselectedBackground);
|
||||
mSilenceButton.setBackground(unselectedBackground);
|
||||
parent.post(() -> {
|
||||
mSilenceButton.setSelected(false);
|
||||
mAlertButton.setSelected(true);
|
||||
mPriorityButton.setSelected(false);
|
||||
});
|
||||
setSelected(mPriorityButton, false);
|
||||
setSelected(mAlertButton, true);
|
||||
setSelected(mSilenceButton, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setSelected(View view, boolean selected) {
|
||||
ColorStateList colorAccent = getAccentTint();
|
||||
ColorStateList colorNormal = getRegularTint();
|
||||
|
||||
ImageView icon = view.findViewById(R.id.icon);
|
||||
TextView label = view.findViewById(R.id.label);
|
||||
TextView summary = view.findViewById(R.id.summary);
|
||||
|
||||
icon.setImageTintList(selected ? colorAccent : colorNormal);
|
||||
label.setTextColor(selected ? colorAccent : colorNormal);
|
||||
summary.setVisibility(selected ? VISIBLE : GONE);
|
||||
|
||||
view.setBackground(mContext.getDrawable(selected
|
||||
? R.drawable.button_border_selected
|
||||
: R.drawable.button_border_unselected));
|
||||
// a11y service won't always read the newly appearing text in the right order if the
|
||||
// selection happens too soon (readback happens on a different thread as layout). post
|
||||
// the selection to make that conflict less likely
|
||||
view.post(() -> {
|
||||
view.setSelected(selected);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,21 +155,22 @@ public class NotificationHistoryActivity extends Activity {
|
||||
|
||||
final View container = viewForPackage.findViewById(R.id.notification_list);
|
||||
container.setVisibility(View.GONE);
|
||||
ImageButton expand = viewForPackage.findViewById(R.id.expand);
|
||||
expand.setContentDescription(container.getVisibility() == View.VISIBLE
|
||||
View header = viewForPackage.findViewById(R.id.app_header);
|
||||
ImageView expand = viewForPackage.findViewById(R.id.expand);
|
||||
header.setStateDescription(container.getVisibility() == View.VISIBLE
|
||||
? getString(R.string.condition_expand_hide)
|
||||
: getString(R.string.condition_expand_show));
|
||||
int finalI = i;
|
||||
expand.setOnClickListener(v -> {
|
||||
header.setOnClickListener(v -> {
|
||||
container.setVisibility(container.getVisibility() == View.VISIBLE
|
||||
? View.GONE : View.VISIBLE);
|
||||
expand.setImageResource(container.getVisibility() == View.VISIBLE
|
||||
? R.drawable.ic_expand_less
|
||||
: com.android.internal.R.drawable.ic_expand_more);
|
||||
expand.setContentDescription(container.getVisibility() == View.VISIBLE
|
||||
header.setStateDescription(container.getVisibility() == View.VISIBLE
|
||||
? getString(R.string.condition_expand_hide)
|
||||
: getString(R.string.condition_expand_show));
|
||||
expand.sendAccessibilityEvent(TYPE_VIEW_ACCESSIBILITY_FOCUSED);
|
||||
header.sendAccessibilityEvent(TYPE_VIEW_ACCESSIBILITY_FOCUSED);
|
||||
mUiEventLogger.logWithPosition(
|
||||
(container.getVisibility() == View.VISIBLE)
|
||||
? NotificationHistoryEvent.NOTIFICATION_HISTORY_PACKAGE_HISTORY_OPEN
|
||||
|
||||
@@ -33,7 +33,6 @@ public class NotificationHistoryViewHolder extends RecyclerView.ViewHolder {
|
||||
NotificationHistoryViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mTime = itemView.findViewById(R.id.timestamp);
|
||||
mTime.setShowRelativeTime(true);
|
||||
mTitle = itemView.findViewById(R.id.title);
|
||||
mSummary = itemView.findViewById(R.id.text);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class NotificationSbnViewHolder extends RecyclerView.ViewHolder {
|
||||
: NotificationHistoryActivity.NotificationHistoryEvent
|
||||
.NOTIFICATION_HISTORY_RECENT_ITEM_CLICK,
|
||||
uid, pkg, instanceId, position);
|
||||
if (pi != null) {
|
||||
if (pi != null && isPendingIntentValid) {
|
||||
try {
|
||||
pi.send();
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.android.settings.bluetooth.BluetoothFeatureProvider;
|
||||
import com.android.settings.dashboard.DashboardFeatureProvider;
|
||||
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
||||
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
|
||||
import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.gestures.AssistGestureFeatureProvider;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
||||
@@ -103,6 +104,12 @@ public abstract class FeatureFactory {
|
||||
|
||||
public abstract PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context);
|
||||
|
||||
/**
|
||||
* Retrieve implementation for Battery Status feature.
|
||||
*/
|
||||
public abstract BatteryStatusFeatureProvider getBatteryStatusFeatureProvider(
|
||||
Context context);
|
||||
|
||||
public abstract DashboardFeatureProvider getDashboardFeatureProvider(Context context);
|
||||
|
||||
public abstract DockUpdaterFeatureProvider getDockUpdaterFeatureProvider();
|
||||
|
||||
@@ -42,6 +42,8 @@ import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
||||
import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl;
|
||||
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
|
||||
import com.android.settings.enterprise.EnterprisePrivacyFeatureProviderImpl;
|
||||
import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
|
||||
import com.android.settings.fuelgauge.BatteryStatusFeatureProviderImpl;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProviderImpl;
|
||||
import com.android.settings.gestures.AssistGestureFeatureProvider;
|
||||
@@ -78,6 +80,7 @@ public class FeatureFactoryImpl extends FeatureFactory {
|
||||
private SecurityFeatureProvider mSecurityFeatureProvider;
|
||||
private SuggestionFeatureProvider mSuggestionFeatureProvider;
|
||||
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
||||
private BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
|
||||
private AssistGestureFeatureProvider mAssistGestureFeatureProvider;
|
||||
private UserFeatureProvider mUserFeatureProvider;
|
||||
private SlicesFeatureProvider mSlicesFeatureProvider;
|
||||
@@ -110,6 +113,15 @@ public class FeatureFactoryImpl extends FeatureFactory {
|
||||
return mPowerUsageFeatureProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatteryStatusFeatureProvider getBatteryStatusFeatureProvider(Context context) {
|
||||
if (mBatteryStatusFeatureProvider == null) {
|
||||
mBatteryStatusFeatureProvider = new BatteryStatusFeatureProviderImpl(
|
||||
context.getApplicationContext());
|
||||
}
|
||||
return mBatteryStatusFeatureProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DashboardFeatureProvider getDashboardFeatureProvider(Context context) {
|
||||
if (mDashboardFeatureProvider == null) {
|
||||
|
||||
@@ -25,11 +25,7 @@ import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
@@ -109,8 +105,7 @@ public class MediaOutputPanel implements PanelContent, LocalMediaManager.DeviceC
|
||||
@Override
|
||||
public IconCompat getIcon() {
|
||||
if (mMediaController == null) {
|
||||
return IconCompat.createWithResource(mContext, R.drawable.ic_media_stream).setTint(
|
||||
Utils.getColorAccentDefaultColor(mContext));
|
||||
return null;
|
||||
}
|
||||
final MediaMetadata metadata = mMediaController.getMetadata();
|
||||
if (metadata != null) {
|
||||
@@ -124,25 +119,6 @@ public class MediaOutputPanel implements PanelContent, LocalMediaManager.DeviceC
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "Media meta data does not contain icon information");
|
||||
return getPackageIcon();
|
||||
}
|
||||
|
||||
private IconCompat getPackageIcon() {
|
||||
try {
|
||||
final Drawable drawable = mContext.getPackageManager().getApplicationIcon(mPackageName);
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return IconCompat.createWithBitmap(((BitmapDrawable) drawable).getBitmap());
|
||||
}
|
||||
final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
|
||||
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return IconCompat.createWithBitmap(bitmap);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Package is not found. Unable to get package icon.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,10 +93,12 @@ public class PanelFragment extends Fragment {
|
||||
private String mPanelClosedKey;
|
||||
private LinearLayout mPanelHeader;
|
||||
private ImageView mTitleIcon;
|
||||
private LinearLayout mTitleGroup;
|
||||
private TextView mHeaderTitle;
|
||||
private TextView mHeaderSubtitle;
|
||||
private int mMaxHeight;
|
||||
private View mFooterDivider;
|
||||
private boolean mPanelCreating;
|
||||
|
||||
private final Map<Uri, LiveData<Slice>> mSliceLiveData = new LinkedHashMap<>();
|
||||
|
||||
@@ -127,6 +129,7 @@ public class PanelFragment extends Fragment {
|
||||
if (mPanelSlices != null) {
|
||||
mPanelSlices.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
mPanelCreating = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -140,6 +143,7 @@ public class PanelFragment extends Fragment {
|
||||
mLayoutView.getViewTreeObserver()
|
||||
.addOnGlobalLayoutListener(mPanelLayoutListener);
|
||||
mMaxHeight = getResources().getDimensionPixelSize(R.dimen.output_switcher_slice_max_height);
|
||||
mPanelCreating = true;
|
||||
createPanelContent();
|
||||
return mLayoutView;
|
||||
}
|
||||
@@ -153,6 +157,7 @@ public class PanelFragment extends Fragment {
|
||||
* Call createPanelContent() once animation end.
|
||||
*/
|
||||
void updatePanelWithAnimation() {
|
||||
mPanelCreating = true;
|
||||
final View panelContent = mLayoutView.findViewById(R.id.panel_container);
|
||||
final AnimatorSet animatorSet = buildAnimatorSet(mLayoutView,
|
||||
0.0f /* startY */, panelContent.getHeight() /* endY */,
|
||||
@@ -171,6 +176,10 @@ public class PanelFragment extends Fragment {
|
||||
animatorSet.start();
|
||||
}
|
||||
|
||||
boolean isPanelCreating() {
|
||||
return mPanelCreating;
|
||||
}
|
||||
|
||||
private void createPanelContent() {
|
||||
final FragmentActivity activity = getActivity();
|
||||
if (activity == null) {
|
||||
@@ -181,6 +190,7 @@ public class PanelFragment extends Fragment {
|
||||
activity.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
final ViewGroup.LayoutParams params = mLayoutView.getLayoutParams();
|
||||
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
mLayoutView.setLayoutParams(params);
|
||||
@@ -191,6 +201,7 @@ public class PanelFragment extends Fragment {
|
||||
mTitleView = mLayoutView.findViewById(R.id.panel_title);
|
||||
mPanelHeader = mLayoutView.findViewById(R.id.panel_header);
|
||||
mTitleIcon = mLayoutView.findViewById(R.id.title_icon);
|
||||
mTitleGroup = mLayoutView.findViewById(R.id.title_group);
|
||||
mHeaderTitle = mLayoutView.findViewById(R.id.header_title);
|
||||
mHeaderSubtitle = mLayoutView.findViewById(R.id.header_subtitle);
|
||||
mFooterDivider = mLayoutView.findViewById(R.id.footer_divider);
|
||||
@@ -228,26 +239,13 @@ public class PanelFragment extends Fragment {
|
||||
|
||||
final IconCompat icon = mPanel.getIcon();
|
||||
final CharSequence title = mPanel.getTitle();
|
||||
if (icon == null) {
|
||||
|
||||
if (icon != null || mPanel.getViewType() == PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON) {
|
||||
enablePanelHeader(icon, title);
|
||||
} else {
|
||||
mTitleView.setVisibility(View.VISIBLE);
|
||||
mPanelHeader.setVisibility(View.GONE);
|
||||
mTitleView.setText(title);
|
||||
} else {
|
||||
mTitleView.setVisibility(View.GONE);
|
||||
mPanelHeader.setVisibility(View.VISIBLE);
|
||||
mPanelHeader.setAccessibilityPaneTitle(title);
|
||||
mTitleIcon.setImageIcon(icon.toIcon(getContext()));
|
||||
mHeaderTitle.setText(title);
|
||||
mHeaderSubtitle.setText(mPanel.getSubTitle());
|
||||
if (mPanel.getHeaderIconIntent() != null) {
|
||||
mTitleIcon.setOnClickListener(getHeaderIconListener());
|
||||
mTitleIcon.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
} else {
|
||||
final int size = getResources().getDimensionPixelSize(
|
||||
R.dimen.output_switcher_panel_icon_size);
|
||||
mTitleIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size));
|
||||
}
|
||||
}
|
||||
|
||||
if (mPanel.getViewType() == PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON) {
|
||||
@@ -281,6 +279,29 @@ public class PanelFragment extends Fragment {
|
||||
0 /* value */);
|
||||
}
|
||||
|
||||
private void enablePanelHeader(IconCompat icon, CharSequence title) {
|
||||
mTitleView.setVisibility(View.GONE);
|
||||
mPanelHeader.setVisibility(View.VISIBLE);
|
||||
mPanelHeader.setAccessibilityPaneTitle(title);
|
||||
mHeaderTitle.setText(title);
|
||||
mHeaderSubtitle.setText(mPanel.getSubTitle());
|
||||
if (icon != null) {
|
||||
mTitleGroup.setVisibility(View.VISIBLE);
|
||||
mTitleIcon.setImageIcon(icon.toIcon(getContext()));
|
||||
if (mPanel.getHeaderIconIntent() != null) {
|
||||
mTitleIcon.setOnClickListener(getHeaderIconListener());
|
||||
mTitleIcon.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
} else {
|
||||
final int size = getResources().getDimensionPixelSize(
|
||||
R.dimen.output_switcher_panel_icon_size);
|
||||
mTitleIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size));
|
||||
}
|
||||
} else {
|
||||
mTitleGroup.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAllSlices() {
|
||||
mSliceLiveData.clear();
|
||||
final List<Uri> sliceUris = mPanel.getSlices();
|
||||
@@ -477,7 +498,13 @@ public class PanelFragment extends Fragment {
|
||||
@Override
|
||||
public void onHeaderChanged() {
|
||||
ThreadUtils.postOnMainThread(() -> {
|
||||
mTitleIcon.setImageIcon(mPanel.getIcon().toIcon(getContext()));
|
||||
final IconCompat icon = mPanel.getIcon();
|
||||
if (icon != null) {
|
||||
mTitleIcon.setImageIcon(icon.toIcon(getContext()));
|
||||
mTitleGroup.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mTitleGroup.setVisibility(View.GONE);
|
||||
}
|
||||
mHeaderTitle.setText(mPanel.getTitle());
|
||||
mHeaderSubtitle.setText(mPanel.getSubTitle());
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.android.settingslib.media.MediaOutputSliceConstants.EXTRA_PACK
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.Window;
|
||||
@@ -41,12 +42,14 @@ import com.android.settings.core.HideNonSystemOverlayMixin;
|
||||
*/
|
||||
public class SettingsPanelActivity extends FragmentActivity {
|
||||
|
||||
private final String TAG = "panel_activity";
|
||||
private static final String TAG = "SettingsPanelActivity";
|
||||
|
||||
@VisibleForTesting
|
||||
final Bundle mBundle = new Bundle();
|
||||
@VisibleForTesting
|
||||
boolean mForceCreation = false;
|
||||
@VisibleForTesting
|
||||
PanelFragment mPanelFragment;
|
||||
|
||||
/**
|
||||
* Key specifying which Panel the app is requesting.
|
||||
@@ -87,7 +90,9 @@ public class SettingsPanelActivity extends FragmentActivity {
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mForceCreation = true;
|
||||
if (mPanelFragment != null && !mPanelFragment.isPanelCreating()) {
|
||||
mForceCreation = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,10 +109,10 @@ public class SettingsPanelActivity extends FragmentActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
final String action = callingIntent.getAction();
|
||||
// We will use it once media output switch panel support remote device.
|
||||
final String mediaPackageName = callingIntent.getStringExtra(EXTRA_PACKAGE_NAME);
|
||||
|
||||
mBundle.putString(KEY_PANEL_TYPE_ARGUMENT, callingIntent.getAction());
|
||||
mBundle.putString(KEY_PANEL_TYPE_ARGUMENT, action);
|
||||
mBundle.putString(KEY_CALLING_PACKAGE_NAME, getCallingPackage());
|
||||
mBundle.putString(KEY_MEDIA_PACKAGE_NAME, mediaPackageName);
|
||||
|
||||
@@ -116,9 +121,21 @@ public class SettingsPanelActivity extends FragmentActivity {
|
||||
|
||||
// If fragment already exists and visible, we will need to update panel with animation.
|
||||
if (!shouldForceCreation && fragment != null && fragment instanceof PanelFragment) {
|
||||
final PanelFragment panelFragment = (PanelFragment) fragment;
|
||||
panelFragment.setArguments(mBundle);
|
||||
panelFragment.updatePanelWithAnimation();
|
||||
mPanelFragment = (PanelFragment) fragment;
|
||||
if (mPanelFragment.isPanelCreating()) {
|
||||
Log.w(TAG, "A panel is creating, skip " + action);
|
||||
return;
|
||||
}
|
||||
|
||||
final Bundle bundle = fragment.getArguments();
|
||||
if (bundle != null
|
||||
&& TextUtils.equals(action, bundle.getString(KEY_PANEL_TYPE_ARGUMENT))) {
|
||||
Log.w(TAG, "Panel is showing the same action, skip " + action);
|
||||
return;
|
||||
}
|
||||
|
||||
mPanelFragment.setArguments(new Bundle(mBundle));
|
||||
mPanelFragment.updatePanelWithAnimation();
|
||||
} else {
|
||||
setContentView(R.layout.settings_panel);
|
||||
|
||||
@@ -127,9 +144,9 @@ public class SettingsPanelActivity extends FragmentActivity {
|
||||
window.setGravity(Gravity.BOTTOM);
|
||||
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT);
|
||||
final PanelFragment panelFragment = new PanelFragment();
|
||||
panelFragment.setArguments(mBundle);
|
||||
fragmentManager.beginTransaction().add(R.id.main_content, panelFragment).commit();
|
||||
mPanelFragment = new PanelFragment();
|
||||
mPanelFragment.setArguments(new Bundle(mBundle));
|
||||
fragmentManager.beginTransaction().add(R.id.main_content, mPanelFragment).commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_
|
||||
import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS;
|
||||
import static android.provider.Settings.EXTRA_ENABLE_MMS_DATA_REQUEST_REASON;
|
||||
import static android.provider.Settings.EXTRA_SUB_ID;
|
||||
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
|
||||
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE;
|
||||
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
|
||||
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
|
||||
@@ -209,6 +210,7 @@ public class SimSelectNotification extends BroadcastReceiver {
|
||||
.setContentText(resources.getText(R.string.sim_notification_summary))
|
||||
.setAutoCancel(true);
|
||||
Intent resultIntent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
|
||||
resultIntent.setPackage(SETTINGS_PACKAGE_NAME);
|
||||
resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, resultIntent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
@@ -216,6 +216,16 @@ public class CustomSliceRegistry {
|
||||
.appendPath("ring_volume")
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Full {@link Uri} for the all volume Slices.
|
||||
*/
|
||||
public static final Uri VOLUME_SLICES_URI = new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
||||
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
||||
.appendPath("volume_slices")
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Full {@link Uri} for the Wifi Calling Slice.
|
||||
*/
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.bluetooth.BluetoothSliceBuilder;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.notification.VolumeSeekBarPreferenceController;
|
||||
import com.android.settings.notification.zen.ZenModeSliceBuilder;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.SliceBroadcastRelay;
|
||||
@@ -184,7 +185,10 @@ public class SettingsSliceProvider extends SliceProvider {
|
||||
|
||||
@Override
|
||||
public void onSliceUnpinned(Uri sliceUri) {
|
||||
SliceBroadcastRelay.unregisterReceivers(getContext(), sliceUri);
|
||||
final Context context = getContext();
|
||||
if (!VolumeSliceHelper.unregisterUri(context, sliceUri)) {
|
||||
SliceBroadcastRelay.unregisterReceivers(context, sliceUri);
|
||||
}
|
||||
ThreadUtils.postOnMainThread(() -> stopBackgroundWorker(sliceUri));
|
||||
}
|
||||
|
||||
@@ -390,7 +394,13 @@ public class SettingsSliceProvider extends SliceProvider {
|
||||
|
||||
final IntentFilter filter = controller.getIntentFilter();
|
||||
if (filter != null) {
|
||||
registerIntentToUri(filter, uri);
|
||||
if (controller instanceof VolumeSeekBarPreferenceController) {
|
||||
// Register volume slices to a broadcast relay to reduce unnecessary UI updates
|
||||
VolumeSliceHelper.registerIntentToUri(getContext(), filter, uri,
|
||||
((VolumeSeekBarPreferenceController) controller).getAudioStream());
|
||||
} else {
|
||||
registerIntentToUri(filter, uri);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadUtils.postOnMainThread(() -> startBackgroundWorker(controller, uri));
|
||||
|
||||
127
src/com/android/settings/slices/VolumeSliceHelper.java
Normal file
127
src/com/android/settings/slices/VolumeSliceHelper.java
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.slices;
|
||||
|
||||
import static com.android.settings.slices.CustomSliceRegistry.VOLUME_SLICES_URI;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settingslib.SliceBroadcastRelay;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This helper is to handle the broadcasts of volume slices
|
||||
*/
|
||||
public class VolumeSliceHelper {
|
||||
|
||||
private static final String TAG = "VolumeSliceHelper";
|
||||
|
||||
@VisibleForTesting
|
||||
static Map<Uri, Integer> sRegisteredUri = new ArrayMap<>();
|
||||
@VisibleForTesting
|
||||
static IntentFilter sIntentFilter;
|
||||
|
||||
static void registerIntentToUri(Context context, IntentFilter intentFilter, Uri sliceUri,
|
||||
int audioStream) {
|
||||
Log.d(TAG, "Registering uri for broadcast relay: " + sliceUri);
|
||||
synchronized (sRegisteredUri) {
|
||||
if (sRegisteredUri.isEmpty()) {
|
||||
SliceBroadcastRelay.registerReceiver(context, VOLUME_SLICES_URI,
|
||||
VolumeSliceRelayReceiver.class, intentFilter);
|
||||
sIntentFilter = intentFilter;
|
||||
}
|
||||
sRegisteredUri.put(sliceUri, audioStream);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean unregisterUri(Context context, Uri sliceUri) {
|
||||
if (!sRegisteredUri.containsKey(sliceUri)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Unregistering uri broadcast relay: " + sliceUri);
|
||||
synchronized (sRegisteredUri) {
|
||||
sRegisteredUri.remove(sliceUri);
|
||||
if (sRegisteredUri.isEmpty()) {
|
||||
sIntentFilter = null;
|
||||
SliceBroadcastRelay.unregisterReceivers(context, VOLUME_SLICES_URI);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void onReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
if (sIntentFilter == null || action == null || !sIntentFilter.hasAction(action)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String uriString = intent.getStringExtra(SliceBroadcastRelay.EXTRA_URI);
|
||||
if (uriString == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Uri uri = Uri.parse(uriString);
|
||||
if (!VOLUME_SLICES_URI.equals(ContentProvider.getUriWithoutUserId(uri))) {
|
||||
Log.w(TAG, "Invalid uri: " + uriString);
|
||||
return;
|
||||
}
|
||||
|
||||
if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
|
||||
handleVolumeChanged(context, intent);
|
||||
} else if (AudioManager.STREAM_MUTE_CHANGED_ACTION.equals(action)
|
||||
|| AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
|
||||
handleStreamChanged(context, intent);
|
||||
} else {
|
||||
notifyAllStreamsChanged(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleVolumeChanged(Context context, Intent intent) {
|
||||
final int vol = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
|
||||
final int prevVol = intent.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
|
||||
if (vol != prevVol) {
|
||||
handleStreamChanged(context, intent);
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleStreamChanged(Context context, Intent intent) {
|
||||
final int inputType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
|
||||
for (Map.Entry<Uri, Integer> entry : sRegisteredUri.entrySet()) {
|
||||
if (entry.getValue() == inputType) {
|
||||
context.getContentResolver().notifyChange(entry.getKey(), null /* observer */);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void notifyAllStreamsChanged(Context context) {
|
||||
sRegisteredUri.forEach((uri, audioStream) -> {
|
||||
context.getContentResolver().notifyChange(uri, null /* observer */);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.slices;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Receives broadcasts to notify that Settings volume Slices are potentially stale.
|
||||
*/
|
||||
public class VolumeSliceRelayReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
VolumeSliceHelper.onReceive(context, intent);
|
||||
}
|
||||
}
|
||||
@@ -35,12 +35,12 @@ public class MediaControlsPreferenceController extends TogglePreferenceControlle
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
int val = Settings.Secure.getInt(mContext.getContentResolver(), MEDIA_CONTROLS_RESUME, 1);
|
||||
return val == 0;
|
||||
return val == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
int val = isChecked ? 0 : 1;
|
||||
int val = isChecked ? 1 : 0;
|
||||
return Settings.Secure.putInt(mContext.getContentResolver(), MEDIA_CONTROLS_RESUME, val);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ import android.os.IBinder;
|
||||
import android.os.UserManager;
|
||||
import android.security.Credentials;
|
||||
import android.security.KeyStore;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.SpannableString;
|
||||
@@ -80,7 +82,9 @@ import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -159,11 +163,11 @@ public class WifiConfigController implements TextWatcher,
|
||||
private String mMultipleCertSetString;
|
||||
private String mUseSystemCertsString;
|
||||
private String mDoNotProvideEapUserCertString;
|
||||
private String mDoNotValidateEapServerString;
|
||||
|
||||
private ScrollView mDialogContainer;
|
||||
private Spinner mSecuritySpinner;
|
||||
private Spinner mEapMethodSpinner;
|
||||
@VisibleForTesting Spinner mEapMethodSpinner;
|
||||
@VisibleForTesting Spinner mEapSimSpinner; // For EAP-SIM, EAP-AKA and EAP-AKA-PRIME.
|
||||
private Spinner mEapCaCertSpinner;
|
||||
private Spinner mEapOcspSpinner;
|
||||
private TextView mEapDomainView;
|
||||
@@ -209,6 +213,8 @@ public class WifiConfigController implements TextWatcher,
|
||||
|
||||
private final WifiManager mWifiManager;
|
||||
|
||||
private final List<SubscriptionInfo> mActiveSubscriptionInfos = new ArrayList<>();
|
||||
|
||||
public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint,
|
||||
int mode) {
|
||||
this (parent, view, accessPoint, mode, true /* requestFocus */);
|
||||
@@ -264,8 +270,6 @@ public class WifiConfigController implements TextWatcher,
|
||||
mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs);
|
||||
mDoNotProvideEapUserCertString =
|
||||
mContext.getString(R.string.wifi_do_not_provide_eap_user_cert);
|
||||
mDoNotValidateEapServerString =
|
||||
mContext.getString(R.string.wifi_do_not_validate_eap_server);
|
||||
|
||||
mSsidScanButton = (ImageButton) mView.findViewById(R.id.ssid_scanner_button);
|
||||
mDialogContainer = mView.findViewById(R.id.dialog_scrollview);
|
||||
@@ -554,12 +558,10 @@ public class WifiConfigController implements TextWatcher,
|
||||
// Disallow submit if the user has not selected a CA certificate for an EAP network
|
||||
// configuration.
|
||||
enabled = false;
|
||||
}
|
||||
if (caCertSelection.equals(mUseSystemCertsString)
|
||||
&& mEapDomainView != null
|
||||
} else if (mEapDomainView != null
|
||||
&& mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
|
||||
&& TextUtils.isEmpty(mEapDomainView.getText().toString())) {
|
||||
// Disallow submit if the user chooses to use system certificates for EAP server
|
||||
// Disallow submit if the user chooses to use a certificate for EAP server
|
||||
// validation, but does not provide a domain.
|
||||
enabled = false;
|
||||
}
|
||||
@@ -577,7 +579,6 @@ public class WifiConfigController implements TextWatcher,
|
||||
}
|
||||
|
||||
void showWarningMessagesIfAppropriate() {
|
||||
mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.GONE);
|
||||
mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.GONE);
|
||||
mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE);
|
||||
mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.GONE);
|
||||
@@ -590,19 +591,11 @@ public class WifiConfigController implements TextWatcher,
|
||||
}
|
||||
if (mEapCaCertSpinner != null
|
||||
&& mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
|
||||
String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
|
||||
if (caCertSelection.equals(mDoNotValidateEapServerString)) {
|
||||
// Display warning if user chooses not to validate the EAP server with a
|
||||
// user-supplied CA certificate in an EAP network configuration.
|
||||
mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (caCertSelection.equals(mUseSystemCertsString)
|
||||
&& mEapDomainView != null
|
||||
if (mEapDomainView != null
|
||||
&& mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
|
||||
&& TextUtils.isEmpty(mEapDomainView.getText().toString())) {
|
||||
// Display warning if user chooses to use pre-installed public CA certificates
|
||||
// without restricting the server domain that these certificates can be used to
|
||||
// validate.
|
||||
// Display warning if user chooses to use a certificate without restricting the
|
||||
// server domain that these certificates can be used to validate.
|
||||
mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@@ -736,8 +729,7 @@ public class WifiConfigController implements TextWatcher,
|
||||
config.enterpriseConfig.setCaCertificateAliases(null);
|
||||
config.enterpriseConfig.setCaPath(null);
|
||||
config.enterpriseConfig.setDomainSuffixMatch(mEapDomainView.getText().toString());
|
||||
if (caCert.equals(mUnspecifiedCertString)
|
||||
|| caCert.equals(mDoNotValidateEapServerString)) {
|
||||
if (caCert.equals(mUnspecifiedCertString)) {
|
||||
// ca_cert already set to null, so do nothing.
|
||||
} else if (caCert.equals(mUseSystemCertsString)) {
|
||||
config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH);
|
||||
@@ -771,8 +763,7 @@ public class WifiConfigController implements TextWatcher,
|
||||
}
|
||||
|
||||
// Only set OCSP option if there is a valid CA certificate.
|
||||
if (caCert.equals(mUnspecifiedCertString)
|
||||
|| caCert.equals(mDoNotValidateEapServerString)) {
|
||||
if (caCert.equals(mUnspecifiedCertString)) {
|
||||
config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_NONE);
|
||||
} else {
|
||||
config.enterpriseConfig.setOcsp(mEapOcspSpinner.getSelectedItemPosition());
|
||||
@@ -825,6 +816,12 @@ public class WifiConfigController implements TextWatcher,
|
||||
return null;
|
||||
}
|
||||
|
||||
if (config.enterpriseConfig.isAuthenticationSimBased()
|
||||
&& mActiveSubscriptionInfos.size() > 0) {
|
||||
config.carrierId = mActiveSubscriptionInfos
|
||||
.get(mEapSimSpinner.getSelectedItemPosition()).getCarrierId();
|
||||
}
|
||||
|
||||
config.setIpConfiguration(
|
||||
new IpConfiguration(mIpAssignment, mProxySettings,
|
||||
mStaticIpConfiguration, mHttpProxy));
|
||||
@@ -1013,6 +1010,7 @@ public class WifiConfigController implements TextWatcher,
|
||||
initiateEnterpriseNetworkUi = true;
|
||||
mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method);
|
||||
mEapMethodSpinner.setOnItemSelectedListener(this);
|
||||
mEapSimSpinner = (Spinner) mView.findViewById(R.id.sim);
|
||||
mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
|
||||
mPhase2Spinner.setOnItemSelectedListener(this);
|
||||
mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert);
|
||||
@@ -1049,18 +1047,20 @@ public class WifiConfigController implements TextWatcher,
|
||||
}
|
||||
|
||||
if (refreshCertificates) {
|
||||
loadSims();
|
||||
|
||||
loadCertificates(
|
||||
mEapCaCertSpinner,
|
||||
Credentials.CA_CERTIFICATE,
|
||||
mDoNotValidateEapServerString,
|
||||
false,
|
||||
true);
|
||||
null /* noCertificateString */,
|
||||
false /* showMultipleCerts */,
|
||||
true /* showUsePreinstalledCertOption */);
|
||||
loadCertificates(
|
||||
mEapUserCertSpinner,
|
||||
Credentials.USER_PRIVATE_KEY,
|
||||
mDoNotProvideEapUserCertString,
|
||||
false,
|
||||
false);
|
||||
false /* showMultipleCerts */,
|
||||
false /* showUsePreinstalledCertOption */);
|
||||
// To avoid the user connects to a non-secure network unexpectedly,
|
||||
// request using system trusted certificates by default
|
||||
// unless the user explicitly chooses "Do not validate" or other
|
||||
@@ -1070,9 +1070,10 @@ public class WifiConfigController implements TextWatcher,
|
||||
|
||||
// Modifying an existing network
|
||||
if (initiateEnterpriseNetworkUi && mAccessPoint != null && mAccessPoint.isSaved()) {
|
||||
WifiEnterpriseConfig enterpriseConfig = mAccessPoint.getConfig().enterpriseConfig;
|
||||
int eapMethod = enterpriseConfig.getEapMethod();
|
||||
int phase2Method = enterpriseConfig.getPhase2Method();
|
||||
final WifiConfiguration wifiConfig = mAccessPoint.getConfig();
|
||||
final WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig;
|
||||
final int eapMethod = enterpriseConfig.getEapMethod();
|
||||
final int phase2Method = enterpriseConfig.getPhase2Method();
|
||||
mEapMethodSpinner.setSelection(eapMethod);
|
||||
showEapFieldsByMethod(eapMethod);
|
||||
switch (eapMethod) {
|
||||
@@ -1120,12 +1121,22 @@ public class WifiConfigController implements TextWatcher,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (enterpriseConfig.isAuthenticationSimBased()) {
|
||||
for (int i = 0; i < mActiveSubscriptionInfos.size(); i++) {
|
||||
if (wifiConfig.carrierId == mActiveSubscriptionInfos.get(i).getCarrierId()) {
|
||||
mEapSimSpinner.setSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
|
||||
setSelection(mEapCaCertSpinner, mUseSystemCertsString);
|
||||
} else {
|
||||
String[] caCerts = enterpriseConfig.getCaCertificateAliases();
|
||||
if (caCerts == null) {
|
||||
setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString);
|
||||
setSelection(mEapCaCertSpinner, mUnspecifiedCertString);
|
||||
} else if (caCerts.length == 1) {
|
||||
setSelection(mEapCaCertSpinner, caCerts[0]);
|
||||
} else {
|
||||
@@ -1133,9 +1144,9 @@ public class WifiConfigController implements TextWatcher,
|
||||
loadCertificates(
|
||||
mEapCaCertSpinner,
|
||||
Credentials.CA_CERTIFICATE,
|
||||
mDoNotValidateEapServerString,
|
||||
true,
|
||||
true);
|
||||
null /* noCertificateString */,
|
||||
true /* showMultipleCerts */,
|
||||
true /* showUsePreinstalledCertOption */);
|
||||
setSelection(mEapCaCertSpinner, mMultipleCertSetString);
|
||||
}
|
||||
}
|
||||
@@ -1208,6 +1219,7 @@ public class WifiConfigController implements TextWatcher,
|
||||
mView.findViewById(R.id.l_ocsp).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
|
||||
|
||||
Context context = mConfigUi.getContext();
|
||||
switch (eapMethod) {
|
||||
@@ -1218,12 +1230,14 @@ public class WifiConfigController implements TextWatcher,
|
||||
setDomainInvisible();
|
||||
setAnonymousIdentInvisible();
|
||||
setUserCertInvisible();
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
|
||||
break;
|
||||
case WIFI_EAP_METHOD_TLS:
|
||||
mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE);
|
||||
setPhase2Invisible();
|
||||
setAnonymousIdentInvisible();
|
||||
setPasswordInvisible();
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
|
||||
break;
|
||||
case WIFI_EAP_METHOD_PEAP:
|
||||
// Reset adapter if needed
|
||||
@@ -1245,6 +1259,7 @@ public class WifiConfigController implements TextWatcher,
|
||||
mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
|
||||
setUserCertInvisible();
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
|
||||
break;
|
||||
case WIFI_EAP_METHOD_SIM:
|
||||
case WIFI_EAP_METHOD_AKA:
|
||||
@@ -1262,8 +1277,7 @@ public class WifiConfigController implements TextWatcher,
|
||||
|
||||
if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
|
||||
String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
|
||||
if (eapCertSelection.equals(mDoNotValidateEapServerString)
|
||||
|| eapCertSelection.equals(mUnspecifiedCertString)) {
|
||||
if (eapCertSelection.equals(mUnspecifiedCertString)) {
|
||||
// Domain suffix matching is not relevant if the user hasn't chosen a CA
|
||||
// certificate yet, or chooses not to validate the EAP server.
|
||||
setDomainInvisible();
|
||||
@@ -1282,11 +1296,13 @@ public class WifiConfigController implements TextWatcher,
|
||||
mEapIdentityView.setText("");
|
||||
mView.findViewById(R.id.l_identity).setVisibility(View.GONE);
|
||||
setPasswordInvisible();
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1449,6 +1465,44 @@ public class WifiConfigController implements TextWatcher,
|
||||
return KeyStore.getInstance();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void loadSims() {
|
||||
List<SubscriptionInfo> activeSubscriptionInfos = mContext
|
||||
.getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList();
|
||||
if (activeSubscriptionInfos == null) {
|
||||
activeSubscriptionInfos = Collections.EMPTY_LIST;
|
||||
}
|
||||
mActiveSubscriptionInfos.clear();
|
||||
|
||||
// De-duplicates active subscriptions and caches in mActiveSubscriptionInfos.
|
||||
for (SubscriptionInfo newInfo : activeSubscriptionInfos) {
|
||||
for (SubscriptionInfo cachedInfo : mActiveSubscriptionInfos) {
|
||||
if (newInfo.getCarrierId() == cachedInfo.getCarrierId()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mActiveSubscriptionInfos.add(newInfo);
|
||||
}
|
||||
|
||||
// Shows disabled 'No SIM' when there is no active subscription.
|
||||
if (mActiveSubscriptionInfos.size() == 0) {
|
||||
final String[] noSim = new String[]{mContext.getString(R.string.wifi_no_sim_card)};
|
||||
mEapSimSpinner.setAdapter(getSpinnerAdapter(noSim));
|
||||
mEapSimSpinner.setSelection(0 /* position */);
|
||||
mEapSimSpinner.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Shows display name of each active subscription.
|
||||
final String[] displayNames = mActiveSubscriptionInfos.stream().map(
|
||||
SubscriptionInfo::getDisplayName).toArray(String[]::new);
|
||||
mEapSimSpinner.setAdapter(getSpinnerAdapter(displayNames));
|
||||
mEapSimSpinner.setSelection(0 /* position */);
|
||||
if (displayNames.length == 1) {
|
||||
mEapSimSpinner.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void loadCertificates(
|
||||
Spinner spinner,
|
||||
@@ -1485,7 +1539,8 @@ public class WifiConfigController implements TextWatcher,
|
||||
}).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
if (mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B) {
|
||||
if (!TextUtils.isEmpty(noCertificateString)
|
||||
&& mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B) {
|
||||
certs.add(noCertificateString);
|
||||
}
|
||||
|
||||
@@ -1713,7 +1768,8 @@ public class WifiConfigController implements TextWatcher,
|
||||
mContext.getResources().getStringArray(contentStringArrayResId));
|
||||
}
|
||||
|
||||
private ArrayAdapter<CharSequence> getSpinnerAdapter(
|
||||
@VisibleForTesting
|
||||
ArrayAdapter<CharSequence> getSpinnerAdapter(
|
||||
String[] contentStringArray) {
|
||||
ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>(mContext,
|
||||
android.R.layout.simple_spinner_item, contentStringArray);
|
||||
|
||||
@@ -35,6 +35,8 @@ import android.os.IBinder;
|
||||
import android.os.UserManager;
|
||||
import android.security.Credentials;
|
||||
import android.security.KeyStore;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.SpannableString;
|
||||
@@ -80,7 +82,9 @@ import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -156,11 +160,11 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
private String mMultipleCertSetString;
|
||||
private String mUseSystemCertsString;
|
||||
private String mDoNotProvideEapUserCertString;
|
||||
private String mDoNotValidateEapServerString;
|
||||
|
||||
private ScrollView mDialogContainer;
|
||||
private Spinner mSecuritySpinner;
|
||||
private Spinner mEapMethodSpinner;
|
||||
@VisibleForTesting Spinner mEapMethodSpinner;
|
||||
@VisibleForTesting Spinner mEapSimSpinner; // For EAP-SIM, EAP-AKA and EAP-AKA-PRIME.
|
||||
private Spinner mEapCaCertSpinner;
|
||||
private Spinner mEapOcspSpinner;
|
||||
private TextView mEapDomainView;
|
||||
@@ -205,6 +209,8 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
|
||||
private final WifiManager mWifiManager;
|
||||
|
||||
private final List<SubscriptionInfo> mActiveSubscriptionInfos = new ArrayList<>();
|
||||
|
||||
public WifiConfigController2(WifiConfigUiBase2 parent, View view, WifiEntry wifiEntry,
|
||||
int mode) {
|
||||
mConfigUi = parent;
|
||||
@@ -253,8 +259,6 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs);
|
||||
mDoNotProvideEapUserCertString =
|
||||
mContext.getString(R.string.wifi_do_not_provide_eap_user_cert);
|
||||
mDoNotValidateEapServerString =
|
||||
mContext.getString(R.string.wifi_do_not_validate_eap_server);
|
||||
|
||||
mSsidScanButton = (ImageButton) mView.findViewById(R.id.ssid_scanner_button);
|
||||
mDialogContainer = mView.findViewById(R.id.dialog_scrollview);
|
||||
@@ -533,12 +537,10 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
// Disallow submit if the user has not selected a CA certificate for an EAP network
|
||||
// configuration.
|
||||
enabled = false;
|
||||
}
|
||||
if (caCertSelection.equals(mUseSystemCertsString)
|
||||
&& mEapDomainView != null
|
||||
} else if (mEapDomainView != null
|
||||
&& mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
|
||||
&& TextUtils.isEmpty(mEapDomainView.getText().toString())) {
|
||||
// Disallow submit if the user chooses to use system certificates for EAP server
|
||||
// Disallow submit if the user chooses to use a certificate for EAP server
|
||||
// validation, but does not provide a domain.
|
||||
enabled = false;
|
||||
}
|
||||
@@ -556,7 +558,6 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
}
|
||||
|
||||
void showWarningMessagesIfAppropriate() {
|
||||
mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.GONE);
|
||||
mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.GONE);
|
||||
mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE);
|
||||
mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.GONE);
|
||||
@@ -569,19 +570,11 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
}
|
||||
if (mEapCaCertSpinner != null
|
||||
&& mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
|
||||
String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
|
||||
if (caCertSelection.equals(mDoNotValidateEapServerString)) {
|
||||
// Display warning if user chooses not to validate the EAP server with a
|
||||
// user-supplied CA certificate in an EAP network configuration.
|
||||
mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (caCertSelection.equals(mUseSystemCertsString)
|
||||
&& mEapDomainView != null
|
||||
if (mEapDomainView != null
|
||||
&& mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
|
||||
&& TextUtils.isEmpty(mEapDomainView.getText().toString())) {
|
||||
// Display warning if user chooses to use pre-installed public CA certificates
|
||||
// without restricting the server domain that these certificates can be used to
|
||||
// validate.
|
||||
// Display warning if user chooses to use a certificate without restricting the
|
||||
// server domain that these certificates can be used to validate.
|
||||
mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@@ -714,12 +707,17 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
break;
|
||||
}
|
||||
|
||||
if (config.enterpriseConfig.isAuthenticationSimBased()
|
||||
&& mActiveSubscriptionInfos.size() > 0) {
|
||||
config.carrierId = mActiveSubscriptionInfos
|
||||
.get(mEapSimSpinner.getSelectedItemPosition()).getCarrierId();
|
||||
}
|
||||
|
||||
String caCert = (String) mEapCaCertSpinner.getSelectedItem();
|
||||
config.enterpriseConfig.setCaCertificateAliases(null);
|
||||
config.enterpriseConfig.setCaPath(null);
|
||||
config.enterpriseConfig.setDomainSuffixMatch(mEapDomainView.getText().toString());
|
||||
if (caCert.equals(mUnspecifiedCertString)
|
||||
|| caCert.equals(mDoNotValidateEapServerString)) {
|
||||
if (caCert.equals(mUnspecifiedCertString)) {
|
||||
// ca_cert already set to null, so do nothing.
|
||||
} else if (caCert.equals(mUseSystemCertsString)) {
|
||||
config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH);
|
||||
@@ -752,8 +750,7 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
}
|
||||
|
||||
// Only set OCSP option if there is a valid CA certificate.
|
||||
if (caCert.equals(mUnspecifiedCertString)
|
||||
|| caCert.equals(mDoNotValidateEapServerString)) {
|
||||
if (caCert.equals(mUnspecifiedCertString)) {
|
||||
config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_NONE);
|
||||
} else {
|
||||
config.enterpriseConfig.setOcsp(mEapOcspSpinner.getSelectedItemPosition());
|
||||
@@ -994,6 +991,7 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
initiateEnterpriseNetworkUi = true;
|
||||
mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method);
|
||||
mEapMethodSpinner.setOnItemSelectedListener(this);
|
||||
mEapSimSpinner = (Spinner) mView.findViewById(R.id.sim);
|
||||
mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
|
||||
mPhase2Spinner.setOnItemSelectedListener(this);
|
||||
mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert);
|
||||
@@ -1031,18 +1029,20 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
}
|
||||
|
||||
if (refreshCertificates) {
|
||||
loadSims();
|
||||
|
||||
loadCertificates(
|
||||
mEapCaCertSpinner,
|
||||
Credentials.CA_CERTIFICATE,
|
||||
mDoNotValidateEapServerString,
|
||||
false,
|
||||
true);
|
||||
null /* noCertificateString */,
|
||||
false /* showMultipleCerts */,
|
||||
true /* showUsePreinstalledCertOption */);
|
||||
loadCertificates(
|
||||
mEapUserCertSpinner,
|
||||
Credentials.USER_PRIVATE_KEY,
|
||||
mDoNotProvideEapUserCertString,
|
||||
false,
|
||||
false);
|
||||
false /* showMultipleCerts */,
|
||||
false /* showUsePreinstalledCertOption */);
|
||||
// To avoid the user connects to a non-secure network unexpectedly,
|
||||
// request using system trusted certificates by default
|
||||
// unless the user explicitly chooses "Do not validate" or other
|
||||
@@ -1052,10 +1052,10 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
|
||||
// Modifying an existing network
|
||||
if (initiateEnterpriseNetworkUi && mWifiEntry != null && mWifiEntry.isSaved()) {
|
||||
WifiEnterpriseConfig enterpriseConfig = mWifiEntry.getWifiConfiguration()
|
||||
.enterpriseConfig;
|
||||
int eapMethod = enterpriseConfig.getEapMethod();
|
||||
int phase2Method = enterpriseConfig.getPhase2Method();
|
||||
final WifiConfiguration wifiConfig = mWifiEntry.getWifiConfiguration();
|
||||
final WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig;
|
||||
final int eapMethod = enterpriseConfig.getEapMethod();
|
||||
final int phase2Method = enterpriseConfig.getPhase2Method();
|
||||
mEapMethodSpinner.setSelection(eapMethod);
|
||||
showEapFieldsByMethod(eapMethod);
|
||||
switch (eapMethod) {
|
||||
@@ -1103,12 +1103,22 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (enterpriseConfig.isAuthenticationSimBased()) {
|
||||
for (int i = 0; i < mActiveSubscriptionInfos.size(); i++) {
|
||||
if (wifiConfig.carrierId == mActiveSubscriptionInfos.get(i).getCarrierId()) {
|
||||
mEapSimSpinner.setSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
|
||||
setSelection(mEapCaCertSpinner, mUseSystemCertsString);
|
||||
} else {
|
||||
String[] caCerts = enterpriseConfig.getCaCertificateAliases();
|
||||
if (caCerts == null) {
|
||||
setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString);
|
||||
setSelection(mEapCaCertSpinner, mUnspecifiedCertString);
|
||||
} else if (caCerts.length == 1) {
|
||||
setSelection(mEapCaCertSpinner, caCerts[0]);
|
||||
} else {
|
||||
@@ -1116,9 +1126,9 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
loadCertificates(
|
||||
mEapCaCertSpinner,
|
||||
Credentials.CA_CERTIFICATE,
|
||||
mDoNotValidateEapServerString,
|
||||
true,
|
||||
true);
|
||||
null /* noCertificateString */,
|
||||
true /* showMultipleCerts */,
|
||||
true /* showUsePreinstalledCertOption */);
|
||||
setSelection(mEapCaCertSpinner, mMultipleCertSetString);
|
||||
}
|
||||
}
|
||||
@@ -1191,6 +1201,7 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
mView.findViewById(R.id.l_ocsp).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
|
||||
|
||||
Context context = mConfigUi.getContext();
|
||||
switch (eapMethod) {
|
||||
@@ -1201,12 +1212,14 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
setDomainInvisible();
|
||||
setAnonymousIdentInvisible();
|
||||
setUserCertInvisible();
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
|
||||
break;
|
||||
case WIFI_EAP_METHOD_TLS:
|
||||
mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE);
|
||||
setPhase2Invisible();
|
||||
setAnonymousIdentInvisible();
|
||||
setPasswordInvisible();
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
|
||||
break;
|
||||
case WIFI_EAP_METHOD_PEAP:
|
||||
// Reset adapter if needed
|
||||
@@ -1228,6 +1241,7 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
|
||||
setUserCertInvisible();
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
|
||||
break;
|
||||
case WIFI_EAP_METHOD_SIM:
|
||||
case WIFI_EAP_METHOD_AKA:
|
||||
@@ -1245,8 +1259,7 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
|
||||
if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
|
||||
String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
|
||||
if (eapCertSelection.equals(mDoNotValidateEapServerString)
|
||||
|| eapCertSelection.equals(mUnspecifiedCertString)) {
|
||||
if (eapCertSelection.equals(mUnspecifiedCertString)) {
|
||||
// Domain suffix matching is not relevant if the user hasn't chosen a CA
|
||||
// certificate yet, or chooses not to validate the EAP server.
|
||||
setDomainInvisible();
|
||||
@@ -1265,11 +1278,13 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
mEapIdentityView.setText("");
|
||||
mView.findViewById(R.id.l_identity).setVisibility(View.GONE);
|
||||
setPasswordInvisible();
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
|
||||
mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1432,6 +1447,44 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
return KeyStore.getInstance();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void loadSims() {
|
||||
List<SubscriptionInfo> activeSubscriptionInfos = mContext
|
||||
.getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList();
|
||||
if (activeSubscriptionInfos == null) {
|
||||
activeSubscriptionInfos = Collections.EMPTY_LIST;
|
||||
}
|
||||
mActiveSubscriptionInfos.clear();
|
||||
|
||||
// De-duplicates active subscriptions and caches in mActiveSubscriptionInfos.
|
||||
for (SubscriptionInfo newInfo : activeSubscriptionInfos) {
|
||||
for (SubscriptionInfo cachedInfo : mActiveSubscriptionInfos) {
|
||||
if (newInfo.getCarrierId() == cachedInfo.getCarrierId()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mActiveSubscriptionInfos.add(newInfo);
|
||||
}
|
||||
|
||||
// Shows disabled 'No SIM' when there is no active subscription.
|
||||
if (mActiveSubscriptionInfos.size() == 0) {
|
||||
final String[] noSim = new String[]{mContext.getString(R.string.wifi_no_sim_card)};
|
||||
mEapSimSpinner.setAdapter(getSpinnerAdapter(noSim));
|
||||
mEapSimSpinner.setSelection(0 /* position */);
|
||||
mEapSimSpinner.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Shows display name of each active subscription.
|
||||
final String[] displayNames = mActiveSubscriptionInfos.stream().map(
|
||||
SubscriptionInfo::getDisplayName).toArray(String[]::new);
|
||||
mEapSimSpinner.setAdapter(getSpinnerAdapter(displayNames));
|
||||
mEapSimSpinner.setSelection(0 /* position */);
|
||||
if (displayNames.length == 1) {
|
||||
mEapSimSpinner.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void loadCertificates(
|
||||
Spinner spinner,
|
||||
@@ -1468,7 +1521,8 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
}).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
if (mWifiEntrySecurity != WifiEntry.SECURITY_EAP_SUITE_B) {
|
||||
if (!TextUtils.isEmpty(noCertificateString)
|
||||
&& mWifiEntrySecurity != WifiEntry.SECURITY_EAP_SUITE_B) {
|
||||
certs.add(noCertificateString);
|
||||
}
|
||||
|
||||
@@ -1696,7 +1750,8 @@ public class WifiConfigController2 implements TextWatcher,
|
||||
mContext.getResources().getStringArray(contentStringArrayResId));
|
||||
}
|
||||
|
||||
private ArrayAdapter<CharSequence> getSpinnerAdapter(
|
||||
@VisibleForTesting
|
||||
ArrayAdapter<CharSequence> getSpinnerAdapter(
|
||||
String[] contentStringArray) {
|
||||
ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>(mContext,
|
||||
android.R.layout.simple_spinner_item, contentStringArray);
|
||||
|
||||
@@ -17,32 +17,20 @@
|
||||
package com.android.settings.wifi;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkScoreManager;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.SimpleClock;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
|
||||
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.wifi.WifiEntryPreference;
|
||||
import com.android.wifitrackerlib.WifiEntry;
|
||||
import com.android.wifitrackerlib.WifiPickerTracker;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.ZoneOffset;
|
||||
import com.android.settingslib.wifi.AccessPoint;
|
||||
import com.android.settingslib.wifi.AccessPointPreference;
|
||||
import com.android.settingslib.wifi.WifiTracker;
|
||||
import com.android.settingslib.wifi.WifiTrackerFactory;
|
||||
|
||||
// TODO(b/151133650): Replace AbstractPreferenceController with BasePreferenceController.
|
||||
/**
|
||||
@@ -50,28 +38,21 @@ import java.time.ZoneOffset;
|
||||
* controller class when there is a wifi connection present.
|
||||
*/
|
||||
public class WifiConnectionPreferenceController extends AbstractPreferenceController implements
|
||||
WifiPickerTracker.WifiPickerTrackerCallback {
|
||||
WifiTracker.WifiListener {
|
||||
|
||||
private static final String TAG = "WifiConnPrefCtrl";
|
||||
|
||||
private static final String KEY = "active_wifi_connection";
|
||||
|
||||
// Max age of tracked WifiEntries.
|
||||
private static final long MAX_SCAN_AGE_MILLIS = 15_000;
|
||||
// Interval between initiating WifiPickerTracker scans.
|
||||
private static final long SCAN_INTERVAL_MILLIS = 10_000;
|
||||
|
||||
private UpdateListener mUpdateListener;
|
||||
private Context mPrefContext;
|
||||
private String mPreferenceGroupKey;
|
||||
private PreferenceGroup mPreferenceGroup;
|
||||
@VisibleForTesting
|
||||
public WifiPickerTracker mWifiPickerTracker;
|
||||
private WifiEntryPreference mPreference;
|
||||
private WifiTracker mWifiTracker;
|
||||
private AccessPointPreference mPreference;
|
||||
private AccessPointPreference.UserBadgeCache mBadgeCache;
|
||||
private int order;
|
||||
private int mMetricsCategory;
|
||||
// Worker thread used for WifiPickerTracker work.
|
||||
private HandlerThread mWorkerThread;
|
||||
|
||||
/**
|
||||
* Used to notify a parent controller that this controller has changed in availability, or has
|
||||
@@ -99,34 +80,16 @@ public class WifiConnectionPreferenceController extends AbstractPreferenceContro
|
||||
super(context);
|
||||
mUpdateListener = updateListener;
|
||||
mPreferenceGroupKey = preferenceGroupKey;
|
||||
mWifiTracker = WifiTrackerFactory.create(context, this, lifecycle, true /* includeSaved */,
|
||||
true /* includeScans */);
|
||||
this.order = order;
|
||||
mMetricsCategory = metricsCategory;
|
||||
|
||||
mWorkerThread = new HandlerThread(
|
||||
TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
|
||||
Process.THREAD_PRIORITY_BACKGROUND);
|
||||
mWorkerThread.start();
|
||||
final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
|
||||
@Override
|
||||
public long millis() {
|
||||
return SystemClock.elapsedRealtime();
|
||||
}
|
||||
};
|
||||
mWifiPickerTracker = new WifiPickerTracker(lifecycle, context,
|
||||
context.getSystemService(WifiManager.class),
|
||||
context.getSystemService(ConnectivityManager.class),
|
||||
context.getSystemService(NetworkScoreManager.class),
|
||||
new Handler(Looper.getMainLooper()),
|
||||
mWorkerThread.getThreadHandler(),
|
||||
elapsedRealtimeClock,
|
||||
MAX_SCAN_AGE_MILLIS,
|
||||
SCAN_INTERVAL_MILLIS,
|
||||
this);
|
||||
mBadgeCache = new AccessPointPreference.UserBadgeCache(context.getPackageManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return mWifiPickerTracker.getConnectedWifiEntry() != null;
|
||||
return mWifiTracker.isConnected() && getCurrentAccessPoint() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,69 +105,74 @@ public class WifiConnectionPreferenceController extends AbstractPreferenceContro
|
||||
update();
|
||||
}
|
||||
|
||||
private void updatePreference(WifiEntry wifiEntry) {
|
||||
private AccessPoint getCurrentAccessPoint() {
|
||||
for (AccessPoint accessPoint : mWifiTracker.getAccessPoints()) {
|
||||
if (accessPoint.isActive()) {
|
||||
return accessPoint;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updatePreference(AccessPoint accessPoint) {
|
||||
if (mPreference != null) {
|
||||
mPreferenceGroup.removePreference(mPreference);
|
||||
mPreference = null;
|
||||
}
|
||||
if (wifiEntry == null || mPrefContext == null) {
|
||||
if (accessPoint == null) {
|
||||
return;
|
||||
}
|
||||
if (mPrefContext != null) {
|
||||
mPreference = new AccessPointPreference(accessPoint, mPrefContext, mBadgeCache,
|
||||
R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */);
|
||||
mPreference.setKey(KEY);
|
||||
mPreference.refresh();
|
||||
mPreference.setOrder(order);
|
||||
|
||||
mPreference = new WifiEntryPreference(mPrefContext, wifiEntry);
|
||||
mPreference.setKey(KEY);
|
||||
mPreference.refresh();
|
||||
mPreference.setOrder(order);
|
||||
mPreference.setOnPreferenceClickListener(pref -> {
|
||||
final Bundle args = new Bundle();
|
||||
args.putString(WifiNetworkDetailsFragment2.KEY_CHOSEN_WIFIENTRY_KEY,
|
||||
wifiEntry.getKey());
|
||||
new SubSettingLauncher(mPrefContext)
|
||||
.setTitleRes(R.string.pref_title_network_details)
|
||||
.setDestination(WifiNetworkDetailsFragment2.class.getName())
|
||||
.setArguments(args)
|
||||
.setSourceMetricsCategory(mMetricsCategory)
|
||||
.launch();
|
||||
return true;
|
||||
});
|
||||
mPreferenceGroup.addPreference(mPreference);
|
||||
mPreference.setOnPreferenceClickListener(pref -> {
|
||||
Bundle args = new Bundle();
|
||||
mPreference.getAccessPoint().saveWifiState(args);
|
||||
new SubSettingLauncher(mPrefContext)
|
||||
.setTitleRes(R.string.pref_title_network_details)
|
||||
.setDestination(WifiNetworkDetailsFragment.class.getName())
|
||||
.setArguments(args)
|
||||
.setSourceMetricsCategory(mMetricsCategory)
|
||||
.launch();
|
||||
return true;
|
||||
});
|
||||
mPreferenceGroup.addPreference(mPreference);
|
||||
}
|
||||
}
|
||||
|
||||
private void update() {
|
||||
final WifiEntry connectedWifiEntry = mWifiPickerTracker.getConnectedWifiEntry();
|
||||
if (connectedWifiEntry == null) {
|
||||
AccessPoint connectedAccessPoint = null;
|
||||
if (mWifiTracker.isConnected()) {
|
||||
connectedAccessPoint = getCurrentAccessPoint();
|
||||
}
|
||||
if (connectedAccessPoint == null) {
|
||||
updatePreference(null);
|
||||
} else {
|
||||
if (mPreference == null || !mPreference.getWifiEntry().equals(connectedWifiEntry)) {
|
||||
updatePreference(connectedWifiEntry);
|
||||
} else if (mPreference != null) {
|
||||
mPreference.refresh();
|
||||
}
|
||||
if (mPreference == null || !mPreference.getAccessPoint().equals(connectedAccessPoint)) {
|
||||
updatePreference(connectedAccessPoint);
|
||||
} else if (mPreference != null) {
|
||||
mPreference.refresh();
|
||||
}
|
||||
}
|
||||
mUpdateListener.onChildrenUpdated();
|
||||
}
|
||||
|
||||
/** Called when the state of Wifi has changed. */
|
||||
@Override
|
||||
public void onWifiStateChanged() {
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the results when data changes.
|
||||
*/
|
||||
@Override
|
||||
public void onWifiEntriesChanged() {
|
||||
public void onWifiStateChanged(int state) {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNumSavedSubscriptionsChanged() {
|
||||
// Do nothing.
|
||||
public void onConnectedChanged() {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNumSavedNetworksChanged() {
|
||||
// Do nothing.
|
||||
public void onAccessPointsChanged() {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.wifi.ScanResult;
|
||||
import android.net.wifi.SoftApConfiguration;
|
||||
import android.net.wifi.WifiConfiguration;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
@@ -38,8 +39,6 @@ public class WifiUtils {
|
||||
|
||||
private static final int SSID_ASCII_MIN_LENGTH = 1;
|
||||
private static final int SSID_ASCII_MAX_LENGTH = 32;
|
||||
private static final int PASSWORD_MIN_LENGTH = 8;
|
||||
private static final int PASSWORD_MAX_LENGTH = 63;
|
||||
|
||||
|
||||
public static boolean isSSIDTooLong(String ssid) {
|
||||
@@ -56,13 +55,17 @@ public class WifiUtils {
|
||||
return ssid.length() < SSID_ASCII_MIN_LENGTH;
|
||||
}
|
||||
|
||||
public static boolean isHotspotPasswordValid(String password) {
|
||||
if (TextUtils.isEmpty(password)) {
|
||||
/**
|
||||
* Check if the WPA2-PSK hotspot password is valid.
|
||||
*/
|
||||
public static boolean isHotspotWpa2PasswordValid(String password) {
|
||||
final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
|
||||
try {
|
||||
configBuilder.setPassphrase(password, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int length = password.length();
|
||||
return length >= PASSWORD_MIN_LENGTH && length <= PASSWORD_MAX_LENGTH;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,8 +23,10 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.AsyncQueryHandler;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -41,9 +43,14 @@ import android.net.NetworkRequest;
|
||||
import android.net.NetworkUtils;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.Uri;
|
||||
import android.net.wifi.WifiConfiguration;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Handler;
|
||||
import android.provider.Telephony.CarrierId;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.FeatureFlagUtils;
|
||||
import android.util.Log;
|
||||
@@ -98,6 +105,7 @@ import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -133,6 +141,8 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
|
||||
@VisibleForTesting
|
||||
static final String KEY_SSID_PREF = "ssid";
|
||||
@VisibleForTesting
|
||||
static final String KEY_EAP_SIM_SUBSCRIPTION_PREF = "eap_sim_subscription";
|
||||
@VisibleForTesting
|
||||
static final String KEY_MAC_ADDRESS_PREF = "mac_address";
|
||||
@VisibleForTesting
|
||||
static final String KEY_IP_ADDRESS_PREF = "ip_address";
|
||||
@@ -156,6 +166,7 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
|
||||
private NetworkInfo mNetworkInfo;
|
||||
private NetworkCapabilities mNetworkCapabilities;
|
||||
private int mRssiSignalLevel = -1;
|
||||
@VisibleForTesting boolean mShowX; // Shows the Wi-Fi signal icon of Pie+x when it's true.
|
||||
private String[] mSignalStr;
|
||||
private WifiInfo mWifiInfo;
|
||||
private final WifiManager mWifiManager;
|
||||
@@ -170,6 +181,7 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
|
||||
private Preference mFrequencyPref;
|
||||
private Preference mSecurityPref;
|
||||
private Preference mSsidPref;
|
||||
private Preference mEapSimSubscriptionPref;
|
||||
private Preference mMacAddressPref;
|
||||
private Preference mIpAddressPref;
|
||||
private Preference mGatewayPref;
|
||||
@@ -187,6 +199,35 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
|
||||
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
|
||||
.clearCapabilities().addTransportType(TRANSPORT_WIFI).build();
|
||||
|
||||
private CarrierIdAsyncQueryHandler mCarrierIdAsyncQueryHandler;
|
||||
private static final int TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY = 1;
|
||||
private static final int COLUMN_CARRIER_NAME = 0;
|
||||
|
||||
private class CarrierIdAsyncQueryHandler extends AsyncQueryHandler {
|
||||
|
||||
private CarrierIdAsyncQueryHandler(Context context) {
|
||||
super(context.getContentResolver());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
|
||||
if (token == TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY) {
|
||||
if (mContext == null || cursor == null || !cursor.moveToFirst()) {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
mEapSimSubscriptionPref.setSummary(R.string.wifi_require_sim_card_to_connect);
|
||||
return;
|
||||
}
|
||||
mEapSimSubscriptionPref.setSummary(mContext.getString(
|
||||
R.string.wifi_require_specific_sim_card_to_connect,
|
||||
cursor.getString(COLUMN_CARRIER_NAME)));
|
||||
cursor.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must be run on the UI thread since it directly manipulates UI state.
|
||||
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
|
||||
@Override
|
||||
@@ -336,6 +377,7 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
|
||||
mSecurityPref = screen.findPreference(KEY_SECURITY_PREF);
|
||||
|
||||
mSsidPref = screen.findPreference(KEY_SSID_PREF);
|
||||
mEapSimSubscriptionPref = screen.findPreference(KEY_EAP_SIM_SUBSCRIPTION_PREF);
|
||||
mMacAddressPref = screen.findPreference(KEY_MAC_ADDRESS_PREF);
|
||||
mIpAddressPref = screen.findPreference(KEY_IP_ADDRESS_PREF);
|
||||
mGatewayPref = screen.findPreference(KEY_GATEWAY_PREF);
|
||||
@@ -507,12 +549,14 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
|
||||
refreshIpLayerInfo();
|
||||
// SSID Pref
|
||||
refreshSsid();
|
||||
// EAP SIM subscription
|
||||
refreshEapSimSubscription();
|
||||
// MAC Address Pref
|
||||
refreshMacAddress();
|
||||
}
|
||||
|
||||
private void refreshRssiViews() {
|
||||
int signalLevel = mWifiEntry.getLevel();
|
||||
final int signalLevel = mWifiEntry.getLevel();
|
||||
|
||||
// Disappears signal view if not in range. e.g. for saved networks.
|
||||
if (signalLevel == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
|
||||
@@ -521,11 +565,14 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRssiSignalLevel == signalLevel) {
|
||||
final boolean showX = mWifiEntry.shouldShowXLevelIcon();
|
||||
|
||||
if (mRssiSignalLevel == signalLevel && mShowX == showX) {
|
||||
return;
|
||||
}
|
||||
mRssiSignalLevel = signalLevel;
|
||||
Drawable wifiIcon = mIconInjector.getIcon(mRssiSignalLevel);
|
||||
mShowX = showX;
|
||||
Drawable wifiIcon = mIconInjector.getIcon(mShowX, mRssiSignalLevel);
|
||||
|
||||
if (mEntityHeaderController != null) {
|
||||
mEntityHeaderController
|
||||
@@ -630,6 +677,62 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshEapSimSubscription() {
|
||||
mEapSimSubscriptionPref.setVisible(false);
|
||||
|
||||
if (mWifiEntry.getSecurity() != WifiEntry.SECURITY_EAP) {
|
||||
return;
|
||||
}
|
||||
final WifiConfiguration config = mWifiEntry.getWifiConfiguration();
|
||||
if (config == null || config.enterpriseConfig == null) {
|
||||
return;
|
||||
}
|
||||
if (!config.enterpriseConfig.isAuthenticationSimBased()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEapSimSubscriptionPref.setVisible(true);
|
||||
|
||||
// Checks if the SIM subscription is active.
|
||||
final List<SubscriptionInfo> activeSubscriptionInfos = mContext
|
||||
.getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList();
|
||||
final int defaultDataSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId();
|
||||
if (activeSubscriptionInfos != null) {
|
||||
for (SubscriptionInfo subscriptionInfo : activeSubscriptionInfos) {
|
||||
if (config.carrierId == subscriptionInfo.getCarrierId()) {
|
||||
mEapSimSubscriptionPref.setSummary(subscriptionInfo.getDisplayName());
|
||||
return;
|
||||
}
|
||||
|
||||
// When it's UNKNOWN_CARRIER_ID, devices connects it with the SIM subscription of
|
||||
// defaultDataSubscriptionId.
|
||||
if (config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID
|
||||
&& defaultDataSubscriptionId == subscriptionInfo.getSubscriptionId()) {
|
||||
mEapSimSubscriptionPref.setSummary(subscriptionInfo.getDisplayName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
|
||||
mEapSimSubscriptionPref.setSummary(R.string.wifi_no_related_sim_card);
|
||||
return;
|
||||
}
|
||||
|
||||
// The Wi-Fi network has specified carrier id, query carrier name from CarrierIdProvider.
|
||||
if (mCarrierIdAsyncQueryHandler == null) {
|
||||
mCarrierIdAsyncQueryHandler = new CarrierIdAsyncQueryHandler(mContext);
|
||||
}
|
||||
mCarrierIdAsyncQueryHandler.cancelOperation(TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY);
|
||||
mCarrierIdAsyncQueryHandler.startQuery(TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY,
|
||||
null /* cookie */,
|
||||
CarrierId.All.CONTENT_URI,
|
||||
new String[]{CarrierId.CARRIER_NAME},
|
||||
CarrierId.CARRIER_ID + "=?",
|
||||
new String[] {Integer.toString(config.carrierId)},
|
||||
null /* orderBy */);
|
||||
}
|
||||
|
||||
private void refreshMacAddress() {
|
||||
final String macAddress = mWifiEntry.getMacAddress();
|
||||
if (TextUtils.isEmpty(macAddress)) {
|
||||
@@ -907,8 +1010,8 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public Drawable getIcon(int level) {
|
||||
return mContext.getDrawable(Utils.getWifiIconResource(level)).mutate();
|
||||
public Drawable getIcon(boolean showX, int level) {
|
||||
return mContext.getDrawable(Utils.getWifiIconResource(showX, level)).mutate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.net.NetworkInfo.State;
|
||||
import android.net.Uri;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
@@ -44,6 +45,9 @@ import com.android.settingslib.wifi.AccessPoint;
|
||||
*/
|
||||
public class ContextualWifiSlice extends WifiSlice {
|
||||
|
||||
@VisibleForTesting
|
||||
static final String CONTEXTUAL_WIFI_EXPANDABLE = "contextual_wifi_expandable";
|
||||
|
||||
@VisibleForTesting
|
||||
static final int COLLAPSED_ROW_COUNT = 0;
|
||||
|
||||
@@ -63,13 +67,17 @@ public class ContextualWifiSlice extends WifiSlice {
|
||||
|
||||
@Override
|
||||
public Slice getSlice() {
|
||||
final long currentUiSession = FeatureFactory.getFactory(mContext)
|
||||
.getSlicesFeatureProvider().getUiSessionToken();
|
||||
if (currentUiSession != sActiveUiSession) {
|
||||
sActiveUiSession = currentUiSession;
|
||||
sApRowCollapsed = hasWorkingNetwork();
|
||||
} else if (!mWifiManager.isWifiEnabled()) {
|
||||
sApRowCollapsed = false;
|
||||
if (isExpandable()) {
|
||||
final long currentUiSession = FeatureFactory.getFactory(mContext)
|
||||
.getSlicesFeatureProvider().getUiSessionToken();
|
||||
if (currentUiSession != sActiveUiSession) {
|
||||
sActiveUiSession = currentUiSession;
|
||||
sApRowCollapsed = hasWorkingNetwork();
|
||||
} else if (!mWifiManager.isWifiEnabled()) {
|
||||
sApRowCollapsed = false;
|
||||
}
|
||||
} else {
|
||||
sApRowCollapsed = true;
|
||||
}
|
||||
return super.getSlice();
|
||||
}
|
||||
@@ -87,12 +95,18 @@ public class ContextualWifiSlice extends WifiSlice {
|
||||
protected ListBuilder.RowBuilder getHeaderRow(boolean isWifiEnabled, AccessPoint accessPoint) {
|
||||
final ListBuilder.RowBuilder builder = super.getHeaderRow(isWifiEnabled, accessPoint);
|
||||
builder.setTitleItem(getHeaderIcon(isWifiEnabled, accessPoint), ListBuilder.ICON_IMAGE);
|
||||
if (sApRowCollapsed) {
|
||||
if (sApRowCollapsed && isWifiEnabled) {
|
||||
builder.setSubtitle(getSubtitle(accessPoint));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private boolean isExpandable() {
|
||||
// Return whether this slice can be expandable.
|
||||
return Settings.Global.getInt(mContext.getContentResolver(), CONTEXTUAL_WIFI_EXPANDABLE, 0)
|
||||
!= 0;
|
||||
}
|
||||
|
||||
private IconCompat getHeaderIcon(boolean isWifiEnabled, AccessPoint accessPoint) {
|
||||
final Drawable drawable;
|
||||
final int tint;
|
||||
|
||||
@@ -118,7 +118,7 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer
|
||||
|
||||
@Override
|
||||
public boolean isTextValid(String value) {
|
||||
return WifiUtils.isHotspotPasswordValid(value);
|
||||
return WifiUtils.isHotspotWpa2PasswordValid(value);
|
||||
}
|
||||
|
||||
private static String generateRandomPassword() {
|
||||
|
||||
Reference in New Issue
Block a user