DO NOT MERGE - qt-qpr1-dev-plus-aosp-without-vendor@5915889 into stage-aosp-master
Bug: 142003500 Change-Id: Ibae6b218a616a93cfda05dd77a5ea6f3a2dc4cea
This commit is contained in:
@@ -160,7 +160,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
// TODO(b/37313605): Re-enable once this controller supports instant apps
|
||||
return isInstantApp() || isSystemModule() ? DISABLED_FOR_USER : AVAILABLE;
|
||||
return mFinishing || isInstantApp() || isSystemModule() ? DISABLED_FOR_USER : AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -189,7 +189,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
if (isAvailable() && !mFinishing) {
|
||||
if (isAvailable()) {
|
||||
mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction(
|
||||
mActivity, UserManager.DISALLOW_APPS_CONTROL, mUserId);
|
||||
mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
import com.google.android.setupdesign.span.LinkSpan;
|
||||
import com.google.android.setupdesign.template.RequireScrollMixin;
|
||||
|
||||
public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@@ -50,7 +51,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.skip_label)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_no_thanks)
|
||||
.setListener(this::onSkipButtonClick)
|
||||
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
@@ -59,7 +60,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
} else {
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_cancel)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_no_thanks)
|
||||
.setListener(this::onCancelButtonClick)
|
||||
.setButtonType(FooterButton.ButtonType.CANCEL)
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
@@ -67,14 +68,25 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
);
|
||||
}
|
||||
|
||||
mFooterBarMixin.setPrimaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.wizard_next)
|
||||
.setListener(this::onNextButtonClick)
|
||||
.setButtonType(FooterButton.ButtonType.NEXT)
|
||||
.setTheme(R.style.SudGlifButton_Primary)
|
||||
.build()
|
||||
);
|
||||
FooterButton.Builder nextButtonBuilder = new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_agree)
|
||||
.setButtonType(FooterButton.ButtonType.NEXT)
|
||||
.setTheme(R.style.SudGlifButton_Primary);
|
||||
if (maxFacesEnrolled()) {
|
||||
nextButtonBuilder.setListener(this::onNextButtonClick);
|
||||
mFooterBarMixin.setPrimaryButton(nextButtonBuilder.build());
|
||||
} else {
|
||||
final FooterButton agreeButton = nextButtonBuilder.build();
|
||||
mFooterBarMixin.setPrimaryButton(agreeButton);
|
||||
final RequireScrollMixin requireScrollMixin = getLayout().getMixin(
|
||||
RequireScrollMixin.class);
|
||||
requireScrollMixin.requireScrollWithButton(this, agreeButton,
|
||||
R.string.security_settings_face_enroll_introduction_more,
|
||||
button -> {
|
||||
onNextButtonClick(button);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,13 +146,22 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
return findViewById(R.id.error_text);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int checkMaxEnrolled() {
|
||||
private boolean maxFacesEnrolled() {
|
||||
if (mFaceManager != null) {
|
||||
final int max = getResources().getInteger(
|
||||
com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
|
||||
final int numEnrolledFaces = mFaceManager.getEnrolledFaces(mUserId).size();
|
||||
if (numEnrolledFaces >= max) {
|
||||
return numEnrolledFaces >= max;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Refactor this to something that conveys it is used for getting a string ID.
|
||||
@Override
|
||||
protected int checkMaxEnrolled() {
|
||||
if (mFaceManager != null) {
|
||||
if (maxFacesEnrolled()) {
|
||||
return R.string.face_intro_error_max;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -47,10 +47,10 @@ public class FaceSettingsLockscreenBypassPreferenceController
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
boolean defaultValue = mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_faceAuthDismissesKeyguard);
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, defaultValue ? 1 : 0) != 0;
|
||||
int defaultValue = mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_faceAuthDismissesKeyguard) ? 1 : 0;
|
||||
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
||||
Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, defaultValue, getUserId()) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,7 +81,8 @@ public class FaceSettingsLockscreenBypassPreferenceController
|
||||
}
|
||||
|
||||
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
|
||||
return mFaceManager.hasEnrolledTemplates() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
|
||||
return mFaceManager.hasEnrolledTemplates(getUserId())
|
||||
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
|
||||
} else {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,6 @@ import com.android.settings.nfc.AndroidBeam;
|
||||
import com.android.settings.nfc.PaymentSettings;
|
||||
import com.android.settings.notification.AppBubbleNotificationSettings;
|
||||
import com.android.settings.notification.AppNotificationSettings;
|
||||
import com.android.settings.notification.ChannelGroupNotificationSettings;
|
||||
import com.android.settings.notification.ChannelNotificationSettings;
|
||||
import com.android.settings.notification.ConfigureNotificationSettings;
|
||||
import com.android.settings.notification.NotificationAccessSettings;
|
||||
@@ -238,7 +237,6 @@ public class SettingsGateway {
|
||||
AppNotificationSettings.class.getName(),
|
||||
NotificationAssistantPicker.class.getName(),
|
||||
ChannelNotificationSettings.class.getName(),
|
||||
ChannelGroupNotificationSettings.class.getName(),
|
||||
ApnSettings.class.getName(),
|
||||
ApnEditor.class.getName(),
|
||||
WifiCallingSettings.class.getName(),
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
@@ -42,7 +40,7 @@ public class BubbleGlobalPreferenceController extends DeveloperOptionsPreference
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return NOTIFICATION_BUBBLES;
|
||||
return Settings.Global.NOTIFICATION_BUBBLES;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,12 +62,12 @@ public class BubbleGlobalPreferenceController extends DeveloperOptionsPreference
|
||||
}
|
||||
|
||||
private boolean isEnabled() {
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, OFF) == ON;
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.NOTIFICATION_BUBBLES, OFF) == ON;
|
||||
}
|
||||
|
||||
private void writeSetting(boolean isEnabled) {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, isEnabled ? ON : OFF);
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.NOTIFICATION_BUBBLES, isEnabled ? ON : OFF);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.settings.display;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.display.AmbientDisplayConfiguration;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
@@ -29,6 +30,7 @@ public class AmbientDisplayAlwaysOnPreferenceController extends TogglePreference
|
||||
private final int OFF = 0;
|
||||
|
||||
private static final int MY_USER = UserHandle.myUserId();
|
||||
private static final String PROP_AWARE_AVAILABLE = "ro.vendor.aware_available";
|
||||
|
||||
private AmbientDisplayConfiguration mConfig;
|
||||
private OnPreferenceChangedCallback mCallback;
|
||||
@@ -43,7 +45,9 @@ public class AmbientDisplayAlwaysOnPreferenceController extends TogglePreference
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return isAvailable(getConfig()) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
return isAvailable(getConfig())
|
||||
&& !SystemProperties.getBoolean(PROP_AWARE_AVAILABLE, false) ?
|
||||
AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.om.IOverlayManager;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.provider.SearchIndexableResource;
|
||||
@@ -48,6 +47,7 @@ import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.utils.CandidateInfoExtra;
|
||||
import com.android.settings.widget.RadioButtonPickerFragment;
|
||||
import com.android.settings.widget.RadioButtonPreference;
|
||||
import com.android.settings.widget.RadioButtonPreferenceWithExtraWidget;
|
||||
@@ -150,12 +150,12 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment {
|
||||
@Override
|
||||
public void bindPreferenceExtra(RadioButtonPreference pref,
|
||||
String key, CandidateInfo info, String defaultKey, String systemDefaultKey) {
|
||||
if (!(info instanceof NavModeCandidateInfo)
|
||||
if (!(info instanceof CandidateInfoExtra)
|
||||
|| !(pref instanceof RadioButtonPreferenceWithExtraWidget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pref.setSummary(((NavModeCandidateInfo) info).loadSummary());
|
||||
pref.setSummary(((CandidateInfoExtra) info).loadSummary());
|
||||
|
||||
RadioButtonPreferenceWithExtraWidget p = (RadioButtonPreferenceWithExtraWidget) pref;
|
||||
if (info.getKey() == KEY_SYSTEM_NAV_GESTURAL) {
|
||||
@@ -175,25 +175,25 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment {
|
||||
@Override
|
||||
protected List<? extends CandidateInfo> getCandidates() {
|
||||
final Context c = getContext();
|
||||
List<NavModeCandidateInfo> candidates = new ArrayList<>();
|
||||
List<CandidateInfoExtra> candidates = new ArrayList<>();
|
||||
|
||||
if (SystemNavigationPreferenceController.isOverlayPackageAvailable(c,
|
||||
NAV_BAR_MODE_GESTURAL_OVERLAY)) {
|
||||
candidates.add(new NavModeCandidateInfo(
|
||||
candidates.add(new CandidateInfoExtra(
|
||||
c.getText(R.string.edge_to_edge_navigation_title),
|
||||
c.getText(R.string.edge_to_edge_navigation_summary),
|
||||
KEY_SYSTEM_NAV_GESTURAL, true /* enabled */));
|
||||
}
|
||||
if (SystemNavigationPreferenceController.isOverlayPackageAvailable(c,
|
||||
NAV_BAR_MODE_2BUTTON_OVERLAY)) {
|
||||
candidates.add(new NavModeCandidateInfo(
|
||||
candidates.add(new CandidateInfoExtra(
|
||||
c.getText(R.string.swipe_up_to_switch_apps_title),
|
||||
c.getText(R.string.swipe_up_to_switch_apps_summary),
|
||||
KEY_SYSTEM_NAV_2BUTTONS, true /* enabled */));
|
||||
}
|
||||
if (SystemNavigationPreferenceController.isOverlayPackageAvailable(c,
|
||||
NAV_BAR_MODE_3BUTTON_OVERLAY)) {
|
||||
candidates.add(new NavModeCandidateInfo(
|
||||
candidates.add(new CandidateInfoExtra(
|
||||
c.getText(R.string.legacy_navigation_title),
|
||||
c.getText(R.string.legacy_navigation_summary),
|
||||
KEY_SYSTEM_NAV_3BUTTONS, true /* enabled */));
|
||||
@@ -325,39 +325,6 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment {
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1;
|
||||
}
|
||||
|
||||
static class NavModeCandidateInfo extends CandidateInfo {
|
||||
private final CharSequence mLabel;
|
||||
private final CharSequence mSummary;
|
||||
private final String mKey;
|
||||
|
||||
NavModeCandidateInfo(CharSequence label, CharSequence summary, String key,
|
||||
boolean enabled) {
|
||||
super(enabled);
|
||||
mLabel = label;
|
||||
mSummary = summary;
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence loadLabel() {
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public CharSequence loadSummary() {
|
||||
return mSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable loadIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return mKey;
|
||||
}
|
||||
}
|
||||
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.homepage.contextualcards;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.hardware.face.Face;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.app.AlertActivity;
|
||||
import com.android.internal.app.AlertController;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.homepage.contextualcards.slices.FaceSetupSlice;
|
||||
|
||||
/**
|
||||
* This class is used to show a popup dialog for {@link FaceSetupSlice}.
|
||||
*/
|
||||
public class FaceReEnrollDialog extends AlertActivity implements
|
||||
DialogInterface.OnClickListener {
|
||||
|
||||
private static final String TAG = "FaceReEnrollDialog";
|
||||
|
||||
private static final String BIOMETRIC_ENROLL_ACTION = "android.settings.BIOMETRIC_ENROLL";
|
||||
|
||||
private FaceManager mFaceManager;
|
||||
/**
|
||||
* The type of re-enrollment that has been requested,
|
||||
* see {@link Settings.Secure#FACE_UNLOCK_RE_ENROLL} for more details.
|
||||
*/
|
||||
private int mReEnrollType;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final AlertController.AlertParams alertParams = mAlertParams;
|
||||
alertParams.mTitle = getText(
|
||||
R.string.security_settings_face_enroll_improve_face_alert_title);
|
||||
alertParams.mMessage = getText(
|
||||
R.string.security_settings_face_enroll_improve_face_alert_body);
|
||||
alertParams.mPositiveButtonText = getText(R.string.storage_menu_set_up);
|
||||
alertParams.mNegativeButtonText = getText(R.string.cancel);
|
||||
alertParams.mPositiveButtonListener = this;
|
||||
|
||||
mFaceManager = Utils.getFaceManagerOrNull(getApplicationContext());
|
||||
|
||||
final Context context = getApplicationContext();
|
||||
mReEnrollType = FaceSetupSlice.getReEnrollSetting(context, getUserId());
|
||||
|
||||
Log.d(TAG, "ReEnroll Type : " + mReEnrollType);
|
||||
if (mReEnrollType == FaceSetupSlice.FACE_RE_ENROLL_SUGGESTED) {
|
||||
// setupAlert will actually display the popup dialog.
|
||||
setupAlert();
|
||||
} else if (mReEnrollType == FaceSetupSlice.FACE_RE_ENROLL_REQUIRED) {
|
||||
// in this case we are skipping the popup dialog and directly going to the
|
||||
// re enrollment flow. A grey overlay will appear to indicate that we are
|
||||
// transitioning.
|
||||
removeFaceAndReEnroll();
|
||||
} else {
|
||||
Log.d(TAG, "Error unsupported flow for : " + mReEnrollType);
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
removeFaceAndReEnroll();
|
||||
}
|
||||
|
||||
public void removeFaceAndReEnroll() {
|
||||
final int userId = getUserId();
|
||||
if (mFaceManager == null || !mFaceManager.hasEnrolledTemplates(userId)) {
|
||||
finish();
|
||||
}
|
||||
mFaceManager.remove(new Face("", 0, 0), userId, new FaceManager.RemovalCallback() {
|
||||
@Override
|
||||
public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
|
||||
super.onRemovalError(face, errMsgId, errString);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemovalSucceeded(Face face, int remaining) {
|
||||
super.onRemovalSucceeded(face, remaining);
|
||||
if (remaining != 0) {
|
||||
return;
|
||||
}
|
||||
// Send user to the enroll flow.
|
||||
final Intent reEnroll = new Intent(BIOMETRIC_ENROLL_ACTION);
|
||||
final Context context = getApplicationContext();
|
||||
|
||||
try {
|
||||
startActivity(reEnroll);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to startActivity");
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.slice.Slice;
|
||||
@@ -72,11 +73,12 @@ public class ContextualAdaptiveSleepSlice implements CustomSliceable {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Display the contextual card only if all the following 3 conditions hold:
|
||||
// 1. The Screen Attention is enabled in Settings.
|
||||
// Display the contextual card only if all the following 4 conditions hold:
|
||||
// 1. The Screen Attention is available in Settings.
|
||||
// 2. The device is not recently set up.
|
||||
// 3. Current user hasn't opened Screen Attention's settings page before.
|
||||
if (isSettingsAvailable() && !isUserInteracted() && !isRecentlySetup()) {
|
||||
// 4. Screen Attention is off.
|
||||
if (isSettingsAvailable() && !isUserInteracted() && !isRecentlySetup() && !isTurnedOn()) {
|
||||
final IconCompat icon = IconCompat.createWithResource(mContext,
|
||||
R.drawable.ic_settings_adaptive_sleep);
|
||||
final CharSequence title = mContext.getText(R.string.adaptive_sleep_title);
|
||||
@@ -122,6 +124,14 @@ public class ContextualAdaptiveSleepSlice implements CustomSliceable {
|
||||
return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if the feature is turned on for the device, otherwise {@code false}
|
||||
*/
|
||||
private boolean isTurnedOn() {
|
||||
return Settings.System.getInt(
|
||||
mContext.getContentResolver(), Settings.System.ADAPTIVE_SLEEP, 0) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if the current user has opened the Screen Attention settings page
|
||||
* before, otherwise {@code false}.
|
||||
|
||||
@@ -17,17 +17,14 @@
|
||||
package com.android.settings.homepage.contextualcards.slices;
|
||||
|
||||
|
||||
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.net.Uri;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.slice.Slice;
|
||||
@@ -39,14 +36,40 @@ import com.android.settings.R;
|
||||
import com.android.settings.SubSettings;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.biometrics.face.FaceStatusPreferenceController;
|
||||
import com.android.settings.homepage.contextualcards.FaceReEnrollDialog;
|
||||
import com.android.settings.security.SecuritySettings;
|
||||
import com.android.settings.slices.CustomSliceRegistry;
|
||||
import com.android.settings.slices.CustomSliceable;
|
||||
import com.android.settings.slices.SliceBuilderUtils;
|
||||
|
||||
/**
|
||||
* This class is used for showing re-enroll suggestions in the Settings page. By either having an
|
||||
* un-enrolled user or setting {@link Settings.Secure#FACE_UNLOCK_RE_ENROLL} to one of the
|
||||
* states listed in {@link Settings.Secure} the slice will change its text and potentially show
|
||||
* a {@link FaceReEnrollDialog}.
|
||||
*/
|
||||
public class FaceSetupSlice implements CustomSliceable {
|
||||
|
||||
private final Context mContext;
|
||||
/**
|
||||
* If a user currently is not enrolled then this class will show a recommendation to
|
||||
* enroll their face.
|
||||
*/
|
||||
private FaceManager mFaceManager;
|
||||
|
||||
/**
|
||||
* Various states the {@link FaceSetupSlice} can be in,
|
||||
* See {@link Settings.Secure#FACE_UNLOCK_RE_ENROLL} for more details.
|
||||
*/
|
||||
|
||||
// No re-enrollment.
|
||||
public static final int FACE_NO_RE_ENROLL_REQUIRED = 0;
|
||||
// Re enrollment is suggested.
|
||||
public static final int FACE_RE_ENROLL_SUGGESTED = 1;
|
||||
// Re enrollment is required after a set time period.
|
||||
public static final int FACE_RE_ENROLL_AFTER_TIMEOUT = 2;
|
||||
// Re enrollment is required immediately.
|
||||
public static final int FACE_RE_ENROLL_REQUIRED = 3;
|
||||
|
||||
public FaceSetupSlice(Context context) {
|
||||
mContext = context;
|
||||
@@ -54,21 +77,45 @@ public class FaceSetupSlice implements CustomSliceable {
|
||||
|
||||
@Override
|
||||
public Slice getSlice() {
|
||||
final FaceManager faceManager = Utils.getFaceManagerOrNull(mContext);
|
||||
if (faceManager == null || faceManager.hasEnrolledTemplates(UserHandle.myUserId())) {
|
||||
return null;
|
||||
mFaceManager = Utils.getFaceManagerOrNull(mContext);
|
||||
if (mFaceManager == null) {
|
||||
return new ListBuilder(mContext, CustomSliceRegistry.FACE_ENROLL_SLICE_URI,
|
||||
ListBuilder.INFINITY).setIsError(true).build();
|
||||
}
|
||||
|
||||
final int userId = UserHandle.myUserId();
|
||||
final boolean hasEnrolledTemplates = mFaceManager.hasEnrolledTemplates(userId);
|
||||
final int shouldReEnroll = FaceSetupSlice.getReEnrollSetting(mContext, userId);
|
||||
|
||||
CharSequence title = "";
|
||||
CharSequence subtitle = "";
|
||||
|
||||
// Set the title and subtitle according to the different states, the icon and layout will
|
||||
// stay the same.
|
||||
if (!hasEnrolledTemplates) {
|
||||
title = mContext.getText(R.string.security_settings_face_settings_enroll);
|
||||
subtitle = mContext.getText(
|
||||
R.string.security_settings_face_settings_context_subtitle);
|
||||
} else if (shouldReEnroll == FACE_RE_ENROLL_SUGGESTED) {
|
||||
title = mContext.getText(
|
||||
R.string.security_settings_face_enroll_should_re_enroll_title);
|
||||
subtitle = mContext.getText(
|
||||
R.string.security_settings_face_enroll_should_re_enroll_subtitle);
|
||||
} else if (shouldReEnroll == FACE_RE_ENROLL_REQUIRED) {
|
||||
title = mContext.getText(
|
||||
R.string.security_settings_face_enroll_must_re_enroll_title);
|
||||
subtitle = mContext.getText(
|
||||
R.string.security_settings_face_enroll_must_re_enroll_subtitle);
|
||||
} else {
|
||||
return new ListBuilder(mContext, CustomSliceRegistry.FACE_ENROLL_SLICE_URI,
|
||||
ListBuilder.INFINITY).setIsError(true).build();
|
||||
}
|
||||
|
||||
final CharSequence title = mContext.getText(
|
||||
R.string.security_settings_face_settings_enroll);
|
||||
final ListBuilder listBuilder = new ListBuilder(mContext,
|
||||
CustomSliceRegistry.FACE_ENROLL_SLICE_URI, ListBuilder.INFINITY)
|
||||
.setAccentColor(Utils.getColorAccentDefaultColor(mContext));
|
||||
final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_face_24dp);
|
||||
return listBuilder
|
||||
.addRow(buildRowBuilder(title,
|
||||
mContext.getText(R.string.security_settings_face_settings_context_subtitle),
|
||||
icon, mContext, getIntent()))
|
||||
return listBuilder.addRow(buildRowBuilder(title, subtitle, icon, mContext, getIntent()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -79,12 +126,18 @@ public class FaceSetupSlice implements CustomSliceable {
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
|
||||
SecuritySettings.class.getName(),
|
||||
FaceStatusPreferenceController.KEY_FACE_SETTINGS,
|
||||
mContext.getText(R.string.security_settings_face_settings_enroll).toString(),
|
||||
SettingsEnums.SLICE)
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName());
|
||||
final boolean hasEnrolledTemplates = mFaceManager.hasEnrolledTemplates(
|
||||
UserHandle.myUserId());
|
||||
if (!hasEnrolledTemplates) {
|
||||
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
|
||||
SecuritySettings.class.getName(),
|
||||
FaceStatusPreferenceController.KEY_FACE_SETTINGS,
|
||||
mContext.getText(R.string.security_settings_face_settings_enroll).toString(),
|
||||
SettingsEnums.SLICE)
|
||||
.setClassName(mContext.getPackageName(), SubSettings.class.getName());
|
||||
} else {
|
||||
return new Intent(mContext, FaceReEnrollDialog.class);
|
||||
}
|
||||
}
|
||||
|
||||
private static RowBuilder buildRowBuilder(CharSequence title, CharSequence subTitle,
|
||||
@@ -98,4 +151,10 @@ public class FaceSetupSlice implements CustomSliceable {
|
||||
.setSubtitle(subTitle)
|
||||
.setPrimaryAction(primarySliceAction);
|
||||
}
|
||||
|
||||
public static int getReEnrollSetting(Context context, int userId) {
|
||||
return Settings.Secure.getIntForUser(context.getContentResolver(),
|
||||
Settings.Secure.FACE_UNLOCK_RE_ENROLL, FACE_NO_RE_ENROLL_REQUIRED, userId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
import static android.app.NotificationManager.IMPORTANCE_NONE;
|
||||
import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
|
||||
|
||||
import static com.android.settings.notification.NotificationSettingsBase.ARG_FROM_SETTINGS;
|
||||
import static com.android.settings.notification.ChannelListPreferenceController.ARG_FROM_SETTINGS;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.NotificationChannel;
|
||||
@@ -110,7 +110,7 @@ public class NotificationChannelSlice implements CustomSliceable {
|
||||
*
|
||||
* Note:
|
||||
* When the sent count of notification channels is the same, follow the sorting mechanism from
|
||||
* {@link com.android.settings.notification.NotificationSettingsBase#mChannelComparator}.
|
||||
* {@link com.android.settings.notification.ChannelListPreferenceController}.
|
||||
* Since slice view only shows displayable notification channels, so those deleted ones are
|
||||
* excluded from the comparison here.
|
||||
*/
|
||||
@@ -257,7 +257,6 @@ public class NotificationChannelSlice implements CustomSliceable {
|
||||
channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
|
||||
channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPackageName);
|
||||
channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
|
||||
channelArgs.putBoolean(ARG_FROM_SETTINGS, true);
|
||||
|
||||
final Intent channelIntent = new SubSettingLauncher(mContext)
|
||||
.setDestination(ChannelNotificationSettings.class.getName())
|
||||
|
||||
@@ -135,7 +135,10 @@ public class ApnEditor extends SettingsPreferenceFragment
|
||||
private int mBearerInitialVal = 0;
|
||||
private String mMvnoTypeStr;
|
||||
private String mMvnoMatchDataStr;
|
||||
private String[] mReadOnlyApnTypes;
|
||||
@VisibleForTesting
|
||||
String[] mReadOnlyApnTypes;
|
||||
@VisibleForTesting
|
||||
String[] mDefaultApnTypes;
|
||||
private String[] mReadOnlyApnFields;
|
||||
private boolean mReadOnlyApn;
|
||||
private Uri mCarrierUri;
|
||||
@@ -189,7 +192,8 @@ public class ApnEditor extends SettingsPreferenceFragment
|
||||
private static final int MMSPROXY_INDEX = 12;
|
||||
private static final int MMSPORT_INDEX = 13;
|
||||
private static final int AUTH_TYPE_INDEX = 14;
|
||||
private static final int TYPE_INDEX = 15;
|
||||
@VisibleForTesting
|
||||
static final int TYPE_INDEX = 15;
|
||||
private static final int PROTOCOL_INDEX = 16;
|
||||
@VisibleForTesting
|
||||
static final int CARRIER_ENABLED_INDEX = 17;
|
||||
@@ -250,12 +254,17 @@ public class ApnEditor extends SettingsPreferenceFragment
|
||||
mReadOnlyApnTypes = b.getStringArray(
|
||||
CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
|
||||
if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)) {
|
||||
for (String apnType : mReadOnlyApnTypes) {
|
||||
Log.d(TAG, "onCreate: read only APN type: " + apnType);
|
||||
}
|
||||
Log.d(TAG,
|
||||
"onCreate: read only APN type: " + Arrays.toString(mReadOnlyApnTypes));
|
||||
}
|
||||
mReadOnlyApnFields = b.getStringArray(
|
||||
CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY);
|
||||
|
||||
mDefaultApnTypes = b.getStringArray(
|
||||
CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY);
|
||||
if (!ArrayUtils.isEmpty(mDefaultApnTypes)) {
|
||||
Log.d(TAG, "onCreate: default apn types: " + Arrays.toString(mDefaultApnTypes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1150,17 +1159,24 @@ public class ApnEditor extends SettingsPreferenceFragment
|
||||
return sNotSet.equals(value) ? null : value;
|
||||
}
|
||||
|
||||
private String getUserEnteredApnType() {
|
||||
@VisibleForTesting
|
||||
String getUserEnteredApnType() {
|
||||
// if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY"
|
||||
// but if user enter empty type, map it just for default
|
||||
String userEnteredApnType = mApnType.getText();
|
||||
if (userEnteredApnType != null) userEnteredApnType = userEnteredApnType.trim();
|
||||
if ((TextUtils.isEmpty(userEnteredApnType)
|
||||
|| PhoneConstants.APN_TYPE_ALL.equals(userEnteredApnType))
|
||||
&& !ArrayUtils.isEmpty(mReadOnlyApnTypes)) {
|
||||
String[] apnTypeList = PhoneConstants.APN_TYPES;
|
||||
if (TextUtils.isEmpty(userEnteredApnType) && !ArrayUtils.isEmpty(mDefaultApnTypes)) {
|
||||
apnTypeList = mDefaultApnTypes;
|
||||
}
|
||||
|
||||
StringBuilder editableApnTypes = new StringBuilder();
|
||||
List<String> readOnlyApnTypes = Arrays.asList(mReadOnlyApnTypes);
|
||||
boolean first = true;
|
||||
for (String apnType : PhoneConstants.APN_TYPES) {
|
||||
for (String apnType : apnTypeList) {
|
||||
// add APN type if it is not read-only and is not wild-cardable
|
||||
if (!readOnlyApnTypes.contains(apnType)
|
||||
&& !apnType.equals(PhoneConstants.APN_TYPE_IA)
|
||||
|
||||
@@ -37,6 +37,7 @@ import androidx.preference.PreferenceScreen;
|
||||
import com.android.settings.core.FeatureFlags;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.network.telephony.MobileNetworkActivity;
|
||||
import com.android.settings.network.telephony.MobileNetworkUtils;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
import com.android.settingslib.Utils;
|
||||
@@ -162,6 +163,6 @@ public class MobileNetworkPreferenceController extends AbstractPreferenceControl
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return mTelephonyManager.getNetworkOperatorName();
|
||||
return MobileNetworkUtils.getCurrentCarrierNameForDisplay(mContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,4 +536,60 @@ public class MobileNetworkUtils {
|
||||
icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
|
||||
return icons;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is migrated from
|
||||
* {@link android.telephony.TelephonyManager.getNetworkOperatorName}. Which provides
|
||||
*
|
||||
* 1. Better support under multi-SIM environment.
|
||||
* 2. Similar design which aligned with operator name displayed in status bar
|
||||
*/
|
||||
public static CharSequence getCurrentCarrierNameForDisplay(Context context, int subId) {
|
||||
SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
|
||||
if (sm != null) {
|
||||
SubscriptionInfo subInfo = getSubscriptionInfo(sm, subId);
|
||||
if (subInfo != null) {
|
||||
return subInfo.getCarrierName();
|
||||
}
|
||||
}
|
||||
return getOperatorNameFromTelephonyManager(context);
|
||||
}
|
||||
|
||||
public static CharSequence getCurrentCarrierNameForDisplay(Context context) {
|
||||
SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
|
||||
if (sm != null) {
|
||||
int subId = sm.getDefaultSubscriptionId();
|
||||
SubscriptionInfo subInfo = getSubscriptionInfo(sm, subId);
|
||||
if (subInfo != null) {
|
||||
return subInfo.getCarrierName();
|
||||
}
|
||||
}
|
||||
return getOperatorNameFromTelephonyManager(context);
|
||||
}
|
||||
|
||||
private static SubscriptionInfo getSubscriptionInfo(SubscriptionManager subManager,
|
||||
int subId) {
|
||||
List<SubscriptionInfo> subInfos = subManager.getAccessibleSubscriptionInfoList();
|
||||
if (subInfos == null) {
|
||||
subInfos = subManager.getActiveSubscriptionInfoList();
|
||||
}
|
||||
if (subInfos == null) {
|
||||
return null;
|
||||
}
|
||||
for (SubscriptionInfo subInfo : subInfos) {
|
||||
if (subInfo.getSubscriptionId() == subId) {
|
||||
return subInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getOperatorNameFromTelephonyManager(Context context) {
|
||||
TelephonyManager tm =
|
||||
(TelephonyManager) context.getSystemService(TelephonyManager.class);
|
||||
if (tm == null) {
|
||||
return null;
|
||||
}
|
||||
return tm.getNetworkOperatorName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,9 +310,8 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
* 1. use {@code ServiceState#getNetworkRegistrationInfoList()} to get the currently
|
||||
* registered cellIdentity, wrap it into a CellInfo;
|
||||
* 2. set the signal strength level as strong;
|
||||
* 3. use {@link TelephonyManager#getNetworkOperatorName()} to get the title of the
|
||||
* previously connected network operator, since the CellIdentity got from step 1 only has
|
||||
* PLMN.
|
||||
* 3. get the title of the previously connected network operator, since the CellIdentity
|
||||
* got from step 1 only has PLMN.
|
||||
* - If the device has no data, we will remove the connected network operators list from the
|
||||
* screen.
|
||||
*/
|
||||
@@ -333,7 +332,8 @@ public class NetworkSelectSettings extends DashboardFragment {
|
||||
if (cellInfo != null) {
|
||||
NetworkOperatorPreference pref = new NetworkOperatorPreference(
|
||||
cellInfo, getPrefContext(), mForbiddenPlmns, mShow4GForLTE);
|
||||
pref.setTitle(mTelephonyManager.getNetworkOperatorName());
|
||||
pref.setTitle(MobileNetworkUtils.getCurrentCarrierNameForDisplay(
|
||||
getPrefContext(), mSubId));
|
||||
pref.setSummary(R.string.network_connected);
|
||||
// Update the signal strength icon, since the default signalStrength value would be
|
||||
// zero (it would be quite confusing why the connected network has no signal)
|
||||
|
||||
@@ -75,7 +75,7 @@ public class OpenNetworkSelectPagePreferenceController extends
|
||||
public CharSequence getSummary() {
|
||||
final ServiceState ss = mTelephonyManager.getServiceState();
|
||||
if (ss != null && ss.getState() == ServiceState.STATE_IN_SERVICE) {
|
||||
return mTelephonyManager.getNetworkOperatorName();
|
||||
return MobileNetworkUtils.getCurrentCarrierNameForDisplay(mContext, mSubId);
|
||||
} else {
|
||||
return mContext.getString(R.string.network_disconnected);
|
||||
}
|
||||
@@ -108,4 +108,4 @@ public class OpenNetworkSelectPagePreferenceController extends
|
||||
public void onNetworkSelectModeChanged() {
|
||||
updateState(mPreference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,46 +16,34 @@
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.widget.MasterSwitchPreference;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
/** These settings are per app, so should not be returned in global search results. */
|
||||
public class AppNotificationSettings extends NotificationSettingsBase {
|
||||
private static final String TAG = "AppNotificationSettings";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private static String KEY_GENERAL_CATEGORY = "categories";
|
||||
private static String KEY_ADVANCED_CATEGORY = "app_advanced";
|
||||
private static String KEY_BADGE = "badge";
|
||||
private static String KEY_APP_LINK = "app_link";
|
||||
private static String KEY_BUBBLE = "bubble_link_pref";
|
||||
private static String[] LEGACY_NON_ADVANCED_KEYS = {KEY_BADGE, KEY_APP_LINK, KEY_BUBBLE};
|
||||
|
||||
private List<NotificationChannelGroup> mChannelGroupList;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.NOTIFICATION_APP_NOTIFICATION;
|
||||
@@ -91,26 +79,6 @@ public class AppNotificationSettings extends NotificationSettingsBase {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mShowLegacyChannelConfig) {
|
||||
// Load channel settings
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... unused) {
|
||||
mChannelGroupList = mBackend.getGroups(mPkg, mUid).getList();
|
||||
Collections.sort(mChannelGroupList, mChannelGroupComparator);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void unused) {
|
||||
if (getHost() == null) {
|
||||
return;
|
||||
}
|
||||
populateList();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
for (NotificationPreferenceController controller : mControllers) {
|
||||
controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
|
||||
controller.displayPreference(getPreferenceScreen());
|
||||
@@ -154,125 +122,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
|
||||
mControllers.add(new NotificationsOffPreferenceController(context));
|
||||
mControllers.add(new DeletedChannelsPreferenceController(context, mBackend));
|
||||
mControllers.add(new BubbleSummaryPreferenceController(context, mBackend));
|
||||
mControllers.add(new ChannelListPreferenceController(context, mBackend));
|
||||
return new ArrayList<>(mControllers);
|
||||
}
|
||||
|
||||
private void populateList() {
|
||||
if (!mDynamicPreferences.isEmpty()) {
|
||||
for (Preference p : mDynamicPreferences) {
|
||||
getPreferenceScreen().removePreference(p);
|
||||
}
|
||||
mDynamicPreferences.clear();
|
||||
}
|
||||
if (mChannelGroupList.isEmpty()) {
|
||||
PreferenceCategory groupCategory = new PreferenceCategory(getPrefContext());
|
||||
groupCategory.setTitle(R.string.notification_channels);
|
||||
groupCategory.setKey(KEY_GENERAL_CATEGORY);
|
||||
getPreferenceScreen().addPreference(groupCategory);
|
||||
mDynamicPreferences.add(groupCategory);
|
||||
|
||||
Preference empty = new Preference(getPrefContext());
|
||||
empty.setTitle(R.string.no_channels);
|
||||
empty.setEnabled(false);
|
||||
groupCategory.addPreference(empty);
|
||||
} else {
|
||||
populateGroupList();
|
||||
mImportanceListener.onImportanceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void populateGroupList() {
|
||||
for (NotificationChannelGroup group : mChannelGroupList) {
|
||||
PreferenceCategory groupCategory = new PreferenceCategory(getPrefContext());
|
||||
groupCategory.setOrderingAsAdded(true);
|
||||
getPreferenceScreen().addPreference(groupCategory);
|
||||
mDynamicPreferences.add(groupCategory);
|
||||
if (group.getId() == null) {
|
||||
if (mChannelGroupList.size() > 1) {
|
||||
groupCategory.setTitle(R.string.notification_channels_other);
|
||||
}
|
||||
groupCategory.setKey(KEY_GENERAL_CATEGORY);
|
||||
} else {
|
||||
groupCategory.setTitle(group.getName());
|
||||
groupCategory.setKey(group.getId());
|
||||
populateGroupToggle(groupCategory, group);
|
||||
}
|
||||
if (!group.isBlocked()) {
|
||||
final List<NotificationChannel> channels = group.getChannels();
|
||||
Collections.sort(channels, mChannelComparator);
|
||||
int N = channels.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationChannel channel = channels.get(i);
|
||||
populateSingleChannelPrefs(groupCategory, channel, group.isBlocked());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void populateGroupToggle(final PreferenceGroup parent,
|
||||
NotificationChannelGroup group) {
|
||||
RestrictedSwitchPreference preference = new RestrictedSwitchPreference(getPrefContext());
|
||||
preference.setTitle(R.string.notification_switch_label);
|
||||
preference.setEnabled(mSuspendedAppsAdmin == null
|
||||
&& isChannelGroupBlockable(group));
|
||||
preference.setChecked(!group.isBlocked());
|
||||
preference.setOnPreferenceClickListener(preference1 -> {
|
||||
final boolean allowGroup = ((SwitchPreference) preference1).isChecked();
|
||||
group.setBlocked(!allowGroup);
|
||||
mBackend.updateChannelGroup(mAppRow.pkg, mAppRow.uid, group);
|
||||
|
||||
onGroupBlockStateChanged(group);
|
||||
return true;
|
||||
});
|
||||
|
||||
parent.addPreference(preference);
|
||||
}
|
||||
|
||||
private Comparator<NotificationChannelGroup> mChannelGroupComparator =
|
||||
new Comparator<NotificationChannelGroup>() {
|
||||
|
||||
@Override
|
||||
public int compare(NotificationChannelGroup left, NotificationChannelGroup right) {
|
||||
// Non-grouped channels (in placeholder group with a null id) come last
|
||||
if (left.getId() == null && right.getId() != null) {
|
||||
return 1;
|
||||
} else if (right.getId() == null && left.getId() != null) {
|
||||
return -1;
|
||||
}
|
||||
return left.getId().compareTo(right.getId());
|
||||
}
|
||||
};
|
||||
|
||||
protected void onGroupBlockStateChanged(NotificationChannelGroup group) {
|
||||
if (group == null) {
|
||||
return;
|
||||
}
|
||||
PreferenceGroup groupGroup = (
|
||||
PreferenceGroup) getPreferenceScreen().findPreference(group.getId());
|
||||
|
||||
if (groupGroup != null) {
|
||||
if (group.isBlocked()) {
|
||||
List<Preference> toRemove = new ArrayList<>();
|
||||
int childCount = groupGroup.getPreferenceCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Preference pref = groupGroup.getPreference(i);
|
||||
if (pref instanceof MasterSwitchPreference) {
|
||||
toRemove.add(pref);
|
||||
}
|
||||
}
|
||||
for (Preference pref : toRemove) {
|
||||
groupGroup.removePreference(pref);
|
||||
}
|
||||
} else {
|
||||
final List<NotificationChannel> channels = group.getChannels();
|
||||
Collections.sort(channels, mChannelComparator);
|
||||
int N = channels.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationChannel channel = channels.get(i);
|
||||
populateSingleChannelPrefs(groupGroup, channel, group.isBlocked());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
@@ -113,7 +113,7 @@ public class BubblePreferenceController extends NotificationPreferenceController
|
||||
}
|
||||
|
||||
private boolean isGloballyEnabled() {
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public class BubblePreferenceController extends NotificationPreferenceController
|
||||
backend.setAllowBubbles(pkg, uid, false);
|
||||
// changing the global settings will cause the observer on the host page to reload
|
||||
// correct preference state
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF);
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public class BubblePreferenceController extends NotificationPreferenceController
|
||||
backend.setAllowBubbles(pkg, uid, true);
|
||||
// changing the global settings will cause the observer on the host page to reload
|
||||
// correct preference state
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
@@ -100,7 +100,7 @@ public class BubbleSummaryPreferenceController extends NotificationPreferenceCon
|
||||
}
|
||||
|
||||
private boolean isGloballyEnabled() {
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ChannelGroupNotificationSettings extends NotificationSettingsBase {
|
||||
private static final String TAG = "ChannelGroupSettings";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.NOTIFICATION_CHANNEL_GROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (mAppRow == null || mChannelGroup == null) {
|
||||
Log.w(TAG, "Missing package or uid or packageinfo or group");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
populateChannelList();
|
||||
for (NotificationPreferenceController controller : mControllers) {
|
||||
controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
|
||||
controller.displayPreference(getPreferenceScreen());
|
||||
}
|
||||
updatePreferenceStates();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.notification_group_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
mControllers = new ArrayList<>();
|
||||
mControllers.add(new HeaderPreferenceController(context, this));
|
||||
mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
|
||||
mControllers.add(new AppLinkPreferenceController(context));
|
||||
mControllers.add(new NotificationsOffPreferenceController(context));
|
||||
mControllers.add(new DescriptionPreferenceController(context));
|
||||
return new ArrayList<>(mControllers);
|
||||
}
|
||||
|
||||
private void populateChannelList() {
|
||||
if (!mDynamicPreferences.isEmpty()) {
|
||||
// If there's anything in mDynamicPreferences, we've called populateChannelList twice.
|
||||
// Clear out existing channels and log.
|
||||
Log.w(TAG, "Notification channel group posted twice to settings - old size " +
|
||||
mDynamicPreferences.size() + ", new size " + mDynamicPreferences.size());
|
||||
for (Preference p : mDynamicPreferences) {
|
||||
getPreferenceScreen().removePreference(p);
|
||||
}
|
||||
}
|
||||
if (mChannelGroup.getChannels().isEmpty()) {
|
||||
Preference empty = new Preference(getPrefContext());
|
||||
empty.setTitle(R.string.no_channels);
|
||||
empty.setEnabled(false);
|
||||
getPreferenceScreen().addPreference(empty);
|
||||
mDynamicPreferences.add(empty);
|
||||
|
||||
} else {
|
||||
final List<NotificationChannel> channels = mChannelGroup.getChannels();
|
||||
Collections.sort(channels, mChannelComparator);
|
||||
for (NotificationChannel channel : channels) {
|
||||
mDynamicPreferences.add(populateSingleChannelPrefs(
|
||||
getPreferenceScreen(), channel, mChannelGroup.isBlocked()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
import static android.app.NotificationManager.IMPORTANCE_NONE;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.graphics.BlendMode;
|
||||
import android.graphics.BlendModeColorFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.applications.AppInfoBase;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.widget.MasterSwitchPreference;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
public class ChannelListPreferenceController extends NotificationPreferenceController {
|
||||
|
||||
private static final String KEY = "channels";
|
||||
private static String KEY_GENERAL_CATEGORY = "categories";
|
||||
public static final String ARG_FROM_SETTINGS = "fromSettings";
|
||||
|
||||
private List<NotificationChannelGroup> mChannelGroupList;
|
||||
private PreferenceCategory mPreference;
|
||||
|
||||
public ChannelListPreferenceController(Context context, NotificationBackend backend) {
|
||||
super(context, backend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
if (mAppRow == null) {
|
||||
return false;
|
||||
}
|
||||
if (mAppRow.banned) {
|
||||
return false;
|
||||
}
|
||||
if (mChannel != null) {
|
||||
if (mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)
|
||||
|| NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
mPreference = (PreferenceCategory) preference;
|
||||
// Load channel settings
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... unused) {
|
||||
mChannelGroupList = mBackend.getGroups(mAppRow.pkg, mAppRow.uid).getList();
|
||||
Collections.sort(mChannelGroupList, mChannelGroupComparator);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void unused) {
|
||||
if (mContext == null) {
|
||||
return;
|
||||
}
|
||||
populateList();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void populateList() {
|
||||
// TODO: if preference has children, compare with newly loaded list
|
||||
mPreference.removeAll();
|
||||
|
||||
if (mChannelGroupList.isEmpty()) {
|
||||
PreferenceCategory groupCategory = new PreferenceCategory(mContext);
|
||||
groupCategory.setTitle(R.string.notification_channels);
|
||||
groupCategory.setKey(KEY_GENERAL_CATEGORY);
|
||||
mPreference.addPreference(groupCategory);
|
||||
|
||||
Preference empty = new Preference(mContext);
|
||||
empty.setTitle(R.string.no_channels);
|
||||
empty.setEnabled(false);
|
||||
groupCategory.addPreference(empty);
|
||||
} else {
|
||||
populateGroupList();
|
||||
}
|
||||
}
|
||||
|
||||
private void populateGroupList() {
|
||||
for (NotificationChannelGroup group : mChannelGroupList) {
|
||||
PreferenceCategory groupCategory = new PreferenceCategory(mContext);
|
||||
groupCategory.setOrderingAsAdded(true);
|
||||
mPreference.addPreference(groupCategory);
|
||||
if (group.getId() == null) {
|
||||
if (mChannelGroupList.size() > 1) {
|
||||
groupCategory.setTitle(R.string.notification_channels_other);
|
||||
}
|
||||
groupCategory.setKey(KEY_GENERAL_CATEGORY);
|
||||
} else {
|
||||
groupCategory.setTitle(group.getName());
|
||||
groupCategory.setKey(group.getId());
|
||||
populateGroupToggle(groupCategory, group);
|
||||
}
|
||||
if (!group.isBlocked()) {
|
||||
final List<NotificationChannel> channels = group.getChannels();
|
||||
Collections.sort(channels, mChannelComparator);
|
||||
int N = channels.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationChannel channel = channels.get(i);
|
||||
populateSingleChannelPrefs(groupCategory, channel, group.isBlocked());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void populateGroupToggle(final PreferenceGroup parent,
|
||||
NotificationChannelGroup group) {
|
||||
RestrictedSwitchPreference preference =
|
||||
new RestrictedSwitchPreference(mContext);
|
||||
preference.setTitle(R.string.notification_switch_label);
|
||||
preference.setEnabled(mAdmin == null
|
||||
&& isChannelGroupBlockable(group));
|
||||
preference.setChecked(!group.isBlocked());
|
||||
preference.setOnPreferenceClickListener(preference1 -> {
|
||||
final boolean allowGroup = ((SwitchPreference) preference1).isChecked();
|
||||
group.setBlocked(!allowGroup);
|
||||
mBackend.updateChannelGroup(mAppRow.pkg, mAppRow.uid, group);
|
||||
|
||||
onGroupBlockStateChanged(group);
|
||||
return true;
|
||||
});
|
||||
|
||||
parent.addPreference(preference);
|
||||
}
|
||||
|
||||
protected Preference populateSingleChannelPrefs(PreferenceGroup parent,
|
||||
final NotificationChannel channel, final boolean groupBlocked) {
|
||||
MasterSwitchPreference channelPref = new MasterSwitchPreference(mContext);
|
||||
channelPref.setSwitchEnabled(mAdmin == null
|
||||
&& isChannelBlockable(channel)
|
||||
&& isChannelConfigurable(channel)
|
||||
&& !groupBlocked);
|
||||
channelPref.setIcon(null);
|
||||
if (channel.getImportance() > IMPORTANCE_LOW) {
|
||||
channelPref.setIcon(getAlertingIcon());
|
||||
}
|
||||
channelPref.setIconSize(MasterSwitchPreference.ICON_SIZE_SMALL);
|
||||
channelPref.setKey(channel.getId());
|
||||
channelPref.setTitle(channel.getName());
|
||||
channelPref.setSummary(NotificationBackend.getSentSummary(
|
||||
mContext, mAppRow.sentByChannel.get(channel.getId()), false));
|
||||
channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
|
||||
Bundle channelArgs = new Bundle();
|
||||
channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mAppRow.uid);
|
||||
channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mAppRow.pkg);
|
||||
channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
|
||||
channelArgs.putBoolean(ARG_FROM_SETTINGS, true);
|
||||
channelPref.setIntent(new SubSettingLauncher(mContext)
|
||||
.setDestination(ChannelNotificationSettings.class.getName())
|
||||
.setArguments(channelArgs)
|
||||
.setTitleRes(R.string.notification_channel_title)
|
||||
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_APP_NOTIFICATION)
|
||||
.toIntent());
|
||||
|
||||
channelPref.setOnPreferenceChangeListener(
|
||||
(preference, o) -> {
|
||||
boolean value = (Boolean) o;
|
||||
int importance = value ? IMPORTANCE_LOW : IMPORTANCE_NONE;
|
||||
channel.setImportance(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;
|
||||
});
|
||||
if (parent.findPreference(channelPref.getKey()) == null) {
|
||||
parent.addPreference(channelPref);
|
||||
}
|
||||
return channelPref;
|
||||
}
|
||||
|
||||
private Drawable getAlertingIcon() {
|
||||
Drawable icon = mContext.getDrawable(R.drawable.ic_notifications_alert);
|
||||
icon.setTintList(Utils.getColorAccent(mContext));
|
||||
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;
|
||||
}
|
||||
PreferenceGroup groupGroup = mPreference.findPreference(group.getId());
|
||||
|
||||
if (groupGroup != null) {
|
||||
if (group.isBlocked()) {
|
||||
List<Preference> toRemove = new ArrayList<>();
|
||||
int childCount = groupGroup.getPreferenceCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Preference pref = groupGroup.getPreference(i);
|
||||
if (pref instanceof MasterSwitchPreference) {
|
||||
toRemove.add(pref);
|
||||
}
|
||||
}
|
||||
for (Preference pref : toRemove) {
|
||||
groupGroup.removePreference(pref);
|
||||
}
|
||||
} else {
|
||||
final List<NotificationChannel> channels = group.getChannels();
|
||||
Collections.sort(channels, mChannelComparator);
|
||||
int N = channels.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationChannel channel = channels.get(i);
|
||||
populateSingleChannelPrefs(groupGroup, channel, group.isBlocked());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Comparator<NotificationChannelGroup> mChannelGroupComparator =
|
||||
new Comparator<NotificationChannelGroup>() {
|
||||
|
||||
@Override
|
||||
public int compare(NotificationChannelGroup left, NotificationChannelGroup right) {
|
||||
// Non-grouped channels (in placeholder group with a null id) come last
|
||||
if (left.getId() == null && right.getId() != null) {
|
||||
return 1;
|
||||
} else if (right.getId() == null && left.getId() != null) {
|
||||
return -1;
|
||||
}
|
||||
return left.getId().compareTo(right.getId());
|
||||
}
|
||||
};
|
||||
|
||||
protected Comparator<NotificationChannel> mChannelComparator =
|
||||
(left, right) -> {
|
||||
if (left.isDeleted() != right.isDeleted()) {
|
||||
return Boolean.compare(left.isDeleted(), right.isDeleted());
|
||||
} else if (left.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
|
||||
// Uncategorized/miscellaneous legacy channel goes last
|
||||
return 1;
|
||||
} else if (right.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return left.getId().compareTo(right.getId());
|
||||
};
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import static com.android.settings.notification.ChannelListPreferenceController.ARG_FROM_SETTINGS;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ public class GlobalBubblePermissionObserverMixin extends ContentObserver {
|
||||
|
||||
public void onStart() {
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(
|
||||
Settings.Secure.NOTIFICATION_BUBBLES),
|
||||
Settings.Global.getUriFor(
|
||||
Settings.Global.NOTIFICATION_BUBBLES),
|
||||
false /* notifyForDescendants */,
|
||||
this /* observer */);
|
||||
}
|
||||
|
||||
@@ -113,25 +113,40 @@ public abstract class NotificationPreferenceController extends AbstractPreferenc
|
||||
}
|
||||
|
||||
protected boolean isChannelBlockable() {
|
||||
if (mChannel != null && mAppRow != null) {
|
||||
if (mChannel.isImportanceLockedByCriticalDeviceFunction()
|
||||
|| mChannel.isImportanceLockedByOEM()) {
|
||||
return mChannel.getImportance() == IMPORTANCE_NONE;
|
||||
return isChannelBlockable(mChannel);
|
||||
}
|
||||
|
||||
protected boolean isChannelBlockable(NotificationChannel channel) {
|
||||
if (channel != null && mAppRow != null) {
|
||||
if (channel.isImportanceLockedByCriticalDeviceFunction()
|
||||
|| channel.isImportanceLockedByOEM()) {
|
||||
return channel.getImportance() == IMPORTANCE_NONE;
|
||||
}
|
||||
|
||||
return mChannel.isBlockableSystem() || !mAppRow.systemApp
|
||||
|| mChannel.getImportance() == IMPORTANCE_NONE;
|
||||
return channel.isBlockableSystem() || !mAppRow.systemApp
|
||||
|| channel.getImportance() == IMPORTANCE_NONE;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isChannelConfigurable(NotificationChannel channel) {
|
||||
if (channel != null && mAppRow != null) {
|
||||
return !channel.isImportanceLockedByOEM();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isChannelGroupBlockable() {
|
||||
if (mChannelGroup != null && mAppRow != null) {
|
||||
return isChannelGroupBlockable(mChannelGroup);
|
||||
}
|
||||
|
||||
protected boolean isChannelGroupBlockable(NotificationChannelGroup group) {
|
||||
if (group != null && mAppRow != null) {
|
||||
if (!mAppRow.systemApp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return mChannelGroup.isBlocked();
|
||||
return group.isBlocked();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ import java.util.List;
|
||||
abstract public class NotificationSettingsBase extends DashboardFragment {
|
||||
private static final String TAG = "NotifiSettingsBase";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
public static final String ARG_FROM_SETTINGS = "fromSettings";
|
||||
|
||||
protected PackageManager mPm;
|
||||
protected NotificationBackend mBackend = new NotificationBackend();
|
||||
@@ -88,7 +87,6 @@ abstract public class NotificationSettingsBase extends DashboardFragment {
|
||||
protected boolean mListeningToPackageRemove;
|
||||
|
||||
protected List<NotificationPreferenceController> mControllers = new ArrayList<>();
|
||||
protected List<Preference> mDynamicPreferences = new ArrayList<>();
|
||||
protected ImportanceListener mImportanceListener = new ImportanceListener();
|
||||
|
||||
protected Intent mIntent;
|
||||
@@ -126,7 +124,6 @@ abstract public class NotificationSettingsBase extends DashboardFragment {
|
||||
mSuspendedAppsAdmin = RestrictedLockUtilsInternal.checkIfApplicationIsSuspended(
|
||||
mContext, mPkg, mUserId);
|
||||
|
||||
|
||||
loadChannel();
|
||||
loadAppRow();
|
||||
loadChannelGroup();
|
||||
@@ -280,135 +277,6 @@ abstract public class NotificationSettingsBase extends DashboardFragment {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Drawable getAlertingIcon() {
|
||||
Drawable icon = getContext().getDrawable(R.drawable.ic_notifications_alert);
|
||||
icon.setTintList(Utils.getColorAccent(getContext()));
|
||||
return icon;
|
||||
}
|
||||
|
||||
protected Preference populateSingleChannelPrefs(PreferenceGroup parent,
|
||||
final NotificationChannel channel, final boolean groupBlocked) {
|
||||
MasterSwitchPreference channelPref = new MasterSwitchPreference(getPrefContext());
|
||||
channelPref.setSwitchEnabled(mSuspendedAppsAdmin == null
|
||||
&& isChannelBlockable(channel)
|
||||
&& isChannelConfigurable(channel)
|
||||
&& !groupBlocked);
|
||||
channelPref.setIcon(null);
|
||||
if (channel.getImportance() > IMPORTANCE_LOW) {
|
||||
channelPref.setIcon(getAlertingIcon());
|
||||
}
|
||||
channelPref.setIconSize(MasterSwitchPreference.ICON_SIZE_SMALL);
|
||||
channelPref.setKey(channel.getId());
|
||||
channelPref.setTitle(channel.getName());
|
||||
channelPref.setSummary(NotificationBackend.getSentSummary(
|
||||
mContext, mAppRow.sentByChannel.get(channel.getId()), false));
|
||||
channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
|
||||
Bundle channelArgs = new Bundle();
|
||||
channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
|
||||
channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
|
||||
channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
|
||||
channelArgs.putBoolean(ARG_FROM_SETTINGS, true);
|
||||
channelPref.setIntent(new SubSettingLauncher(getActivity())
|
||||
.setDestination(ChannelNotificationSettings.class.getName())
|
||||
.setArguments(channelArgs)
|
||||
.setTitleRes(R.string.notification_channel_title)
|
||||
.setSourceMetricsCategory(getMetricsCategory())
|
||||
.toIntent());
|
||||
|
||||
channelPref.setOnPreferenceChangeListener(
|
||||
(preference, o) -> {
|
||||
boolean value = (Boolean) o;
|
||||
int importance = value ? IMPORTANCE_LOW : IMPORTANCE_NONE;
|
||||
channel.setImportance(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(mPkg, mUid, channel);
|
||||
|
||||
return true;
|
||||
});
|
||||
if (parent.findPreference(channelPref.getKey()) == null) {
|
||||
parent.addPreference(channelPref);
|
||||
}
|
||||
return channelPref;
|
||||
}
|
||||
|
||||
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 boolean isChannelConfigurable(NotificationChannel channel) {
|
||||
if (channel != null && mAppRow != null) {
|
||||
return !channel.isImportanceLockedByOEM();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isChannelBlockable(NotificationChannel channel) {
|
||||
if (channel != null && mAppRow != null) {
|
||||
if (!mAppRow.systemApp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (channel.isImportanceLockedByCriticalDeviceFunction()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (channel.isImportanceLockedByOEM()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return channel.isBlockableSystem()
|
||||
|| channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isChannelGroupBlockable(NotificationChannelGroup group) {
|
||||
if (group != null && mAppRow != null) {
|
||||
if (!mAppRow.systemApp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return group.isBlocked();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void setVisible(Preference p, boolean visible) {
|
||||
setVisible(getPreferenceScreen(), p, visible);
|
||||
}
|
||||
|
||||
protected void setVisible(PreferenceGroup parent, Preference p, boolean visible) {
|
||||
final boolean isVisible = parent.findPreference(p.getKey()) != null;
|
||||
if (isVisible == visible) return;
|
||||
if (visible) {
|
||||
parent.addPreference(p);
|
||||
} else {
|
||||
parent.removePreference(p);
|
||||
}
|
||||
}
|
||||
|
||||
protected void startListeningToPackageRemove() {
|
||||
if (mListeningToPackageRemove) {
|
||||
return;
|
||||
@@ -445,20 +313,6 @@ abstract public class NotificationSettingsBase extends DashboardFragment {
|
||||
}
|
||||
};
|
||||
|
||||
protected Comparator<NotificationChannel> mChannelComparator =
|
||||
(left, right) -> {
|
||||
if (left.isDeleted() != right.isDeleted()) {
|
||||
return Boolean.compare(left.isDeleted(), right.isDeleted());
|
||||
} else if (left.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
|
||||
// Uncategorized/miscellaneous legacy channel goes last
|
||||
return 1;
|
||||
} else if (right.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return left.getId().compareTo(right.getId());
|
||||
};
|
||||
|
||||
protected class ImportanceListener {
|
||||
protected void onImportanceChanged() {
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
@@ -466,20 +320,6 @@ abstract public class NotificationSettingsBase extends DashboardFragment {
|
||||
controller.displayPreference(screen);
|
||||
}
|
||||
updatePreferenceStates();
|
||||
|
||||
boolean hideDynamicFields = false;
|
||||
if (mAppRow == null || mAppRow.banned) {
|
||||
hideDynamicFields = true;
|
||||
} else {
|
||||
if (mChannel != null) {
|
||||
hideDynamicFields = mChannel.getImportance() == IMPORTANCE_NONE;
|
||||
} else if (mChannelGroup != null) {
|
||||
hideDynamicFields = mChannelGroup.isBlocked();
|
||||
}
|
||||
}
|
||||
for (Preference preference : mDynamicPreferences) {
|
||||
setVisible(getPreferenceScreen(), preference, !hideDynamicFields);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,14 +266,17 @@ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceC
|
||||
Bundle args = new Bundle();
|
||||
args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkgName);
|
||||
args.putInt(AppInfoBase.ARG_PACKAGE_UID, appEntry.info.uid);
|
||||
pref.setIntent(new SubSettingLauncher(mHost.getActivity())
|
||||
.setDestination(AppNotificationSettings.class.getName())
|
||||
.setTitleRes(R.string.notifications_title)
|
||||
.setArguments(args)
|
||||
.setUserHandle(new UserHandle(UserHandle.getUserId(appEntry.info.uid)))
|
||||
.setSourceMetricsCategory(
|
||||
SettingsEnums.MANAGE_APPLICATIONS_NOTIFICATIONS)
|
||||
.toIntent());
|
||||
pref.setOnPreferenceClickListener(preference -> {
|
||||
new SubSettingLauncher(mHost.getActivity())
|
||||
.setDestination(AppNotificationSettings.class.getName())
|
||||
.setTitleRes(R.string.notifications_title)
|
||||
.setArguments(args)
|
||||
.setUserHandle(new UserHandle(UserHandle.getUserId(appEntry.info.uid)))
|
||||
.setSourceMetricsCategory(
|
||||
SettingsEnums.MANAGE_APPLICATIONS_NOTIFICATIONS)
|
||||
.launch();
|
||||
return true;
|
||||
});
|
||||
pref.setSwitchEnabled(mNotificationBackend.isBlockable(mContext, appEntry.info));
|
||||
pref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
boolean blocked = !(Boolean) newValue;
|
||||
|
||||
@@ -46,6 +46,7 @@ import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.HelpTrampoline;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
import com.android.settings.network.telephony.MobileNetworkActivity;
|
||||
@@ -292,10 +293,9 @@ public class SimSelectNotification extends BroadcastReceiver {
|
||||
.setAutoCancel(true);
|
||||
|
||||
// Create the pending intent that will lead to the helper page.
|
||||
Intent resultIntent = HelpUtils.getHelpIntent(
|
||||
context,
|
||||
context.getString(R.string.help_uri_sim_combination_warning),
|
||||
context.getClass().getName());
|
||||
Intent resultIntent = new Intent(context, HelpTrampoline.class);
|
||||
resultIntent.putExtra(Intent.EXTRA_TEXT, "help_uri_sim_combination_warning");
|
||||
|
||||
PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, resultIntent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
builder.setContentIntent(resultPendingIntent);
|
||||
|
||||
54
src/com/android/settings/utils/CandidateInfoExtra.java
Normal file
54
src/com/android/settings/utils/CandidateInfoExtra.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.utils;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.android.settingslib.widget.CandidateInfo;
|
||||
|
||||
public class CandidateInfoExtra extends CandidateInfo {
|
||||
private final CharSequence mLabel;
|
||||
private final CharSequence mSummary;
|
||||
private final String mKey;
|
||||
|
||||
public CandidateInfoExtra(CharSequence label, CharSequence summary, String key,
|
||||
boolean enabled) {
|
||||
super(enabled);
|
||||
mLabel = label;
|
||||
mSummary = summary;
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence loadLabel() {
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public CharSequence loadSummary() {
|
||||
return mSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable loadIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return mKey;
|
||||
}
|
||||
}
|
||||
@@ -340,6 +340,7 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
|
||||
// Throws error dialog.
|
||||
final NetworkRequestErrorDialogFragment fragment = NetworkRequestErrorDialogFragment
|
||||
.newInstance();
|
||||
fragment.setRejectCallback(mUserSelectionCallback);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putSerializable(NetworkRequestErrorDialogFragment.DIALOG_TYPE, type);
|
||||
fragment.setArguments(bundle);
|
||||
|
||||
@@ -20,8 +20,10 @@ import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.R;
|
||||
@@ -36,6 +38,8 @@ public class NetworkRequestErrorDialogFragment extends InstrumentedDialogFragmen
|
||||
public static final String DIALOG_TYPE = "DIALOG_ERROR_TYPE";
|
||||
|
||||
public enum ERROR_DIALOG_TYPE {TIME_OUT, ABORT}
|
||||
@Nullable
|
||||
private NetworkRequestUserSelectionCallback mRejectCallback;
|
||||
|
||||
public static NetworkRequestErrorDialogFragment newInstance() {
|
||||
return new NetworkRequestErrorDialogFragment();
|
||||
@@ -49,7 +53,7 @@ public class NetworkRequestErrorDialogFragment extends InstrumentedDialogFragmen
|
||||
public void onCancel(@NonNull DialogInterface dialog) {
|
||||
super.onCancel(dialog);
|
||||
// Wants to finish the activity when user clicks back key or outside of the dialog.
|
||||
getActivity().finish();
|
||||
rejectNetworkRequestAndFinish();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,10 +69,12 @@ public class NetworkRequestErrorDialogFragment extends InstrumentedDialogFragmen
|
||||
builder.setMessage(R.string.network_connection_timeout_dialog_message)
|
||||
.setPositiveButton(R.string.network_connection_timeout_dialog_ok,
|
||||
(dialog, which) -> startScanningDialog())
|
||||
.setNegativeButton(R.string.cancel, (dialog, which) -> getActivity().finish());
|
||||
.setNegativeButton(R.string.cancel,
|
||||
(dialog, which) -> rejectNetworkRequestAndFinish());
|
||||
} else {
|
||||
builder.setMessage(R.string.network_connection_errorstate_dialog_message)
|
||||
.setPositiveButton(R.string.okay, (dialog, which) -> getActivity().finish());
|
||||
.setPositiveButton(R.string.okay,
|
||||
(dialog, which) -> rejectNetworkRequestAndFinish());
|
||||
}
|
||||
return builder.create();
|
||||
}
|
||||
@@ -78,9 +84,23 @@ public class NetworkRequestErrorDialogFragment extends InstrumentedDialogFragmen
|
||||
return SettingsEnums.WIFI_SCANNING_NEEDED_DIALOG;
|
||||
}
|
||||
|
||||
// Sets the callback for fragment to reject this request.
|
||||
public void setRejectCallback(NetworkRequestUserSelectionCallback rejectCallback) {
|
||||
mRejectCallback = rejectCallback;
|
||||
}
|
||||
|
||||
protected void startScanningDialog() {
|
||||
final NetworkRequestDialogFragment fragment = NetworkRequestDialogFragment.newInstance();
|
||||
fragment.show(getActivity().getSupportFragmentManager(),
|
||||
NetworkRequestErrorDialogFragment.class.getSimpleName());
|
||||
}
|
||||
|
||||
private void rejectNetworkRequestAndFinish() {
|
||||
if (getActivity() != null) {
|
||||
if (mRejectCallback != null) {
|
||||
mRejectCallback.reject();
|
||||
}
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,9 @@ public class WifiConfigController implements TextWatcher,
|
||||
private TextView mSsidView;
|
||||
|
||||
private Context mContext;
|
||||
private Integer mSecurityInPosition[];
|
||||
|
||||
@VisibleForTesting
|
||||
Integer mSecurityInPosition[];
|
||||
|
||||
private final WifiManager mWifiManager;
|
||||
|
||||
@@ -325,7 +327,7 @@ public class WifiConfigController implements TextWatcher,
|
||||
if ((!mAccessPoint.isSaved() && !mAccessPoint.isActive()
|
||||
&& !mAccessPoint.isPasspointConfig())
|
||||
|| mMode != WifiConfigUiBase.MODE_VIEW) {
|
||||
showSecurityFields();
|
||||
showSecurityFields(true /* refreshEapMethods */, true /* refreshCertificates */);
|
||||
showIpConfigFields();
|
||||
showProxyFields();
|
||||
final CheckBox advancedTogglebox =
|
||||
@@ -956,7 +958,7 @@ public class WifiConfigController implements TextWatcher,
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void showSecurityFields() {
|
||||
private void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) {
|
||||
if (mAccessPointSecurity == AccessPoint.SECURITY_NONE ||
|
||||
mAccessPointSecurity == AccessPoint.SECURITY_OWE ||
|
||||
mAccessPointSecurity == AccessPoint.SECURITY_OWE_TRANSITION) {
|
||||
@@ -985,19 +987,11 @@ public class WifiConfigController implements TextWatcher,
|
||||
}
|
||||
mView.findViewById(R.id.eap).setVisibility(View.VISIBLE);
|
||||
|
||||
boolean initiateEnterpriseNetworkUi = false;
|
||||
if (mEapMethodSpinner == null) {
|
||||
initiateEnterpriseNetworkUi = true;
|
||||
mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method);
|
||||
mEapMethodSpinner.setOnItemSelectedListener(this);
|
||||
if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_eap_sim_based_auth_supported)) {
|
||||
String[] eapMethods = mContext.getResources().getStringArray(
|
||||
R.array.eap_method_without_sim_auth);
|
||||
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(mContext,
|
||||
android.R.layout.simple_spinner_item, eapMethods);
|
||||
spinnerAdapter.setDropDownViewResource(
|
||||
android.R.layout.simple_spinner_dropdown_item);
|
||||
mEapMethodSpinner.setAdapter(spinnerAdapter);
|
||||
}
|
||||
mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
|
||||
mPhase2Spinner.setOnItemSelectedListener(this);
|
||||
mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert);
|
||||
@@ -1008,11 +1002,36 @@ public class WifiConfigController implements TextWatcher,
|
||||
mEapUserCertSpinner.setOnItemSelectedListener(this);
|
||||
mEapIdentityView = (TextView) mView.findViewById(R.id.identity);
|
||||
mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous);
|
||||
}
|
||||
|
||||
if (mAccessPoint != null && mAccessPoint.isCarrierAp()) {
|
||||
mEapMethodSpinner.setSelection(mAccessPoint.getCarrierApEapType());
|
||||
if (refreshEapMethods) {
|
||||
ArrayAdapter<CharSequence> eapMethodSpinnerAdapter;
|
||||
if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B) {
|
||||
eapMethodSpinnerAdapter = getSpinnerAdapter(R.array.wifi_eap_method);
|
||||
mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter);
|
||||
// WAP3-Enterprise 192-bit only allows EAP method TLS
|
||||
mEapMethodSpinner.setSelection(Eap.TLS);
|
||||
mEapMethodSpinner.setEnabled(false);
|
||||
} else if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_eap_sim_based_auth_supported)) {
|
||||
eapMethodSpinnerAdapter = getSpinnerAdapter(
|
||||
R.array.eap_method_without_sim_auth);
|
||||
mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter);
|
||||
mEapMethodSpinner.setEnabled(true);
|
||||
} else {
|
||||
eapMethodSpinnerAdapter = getSpinnerAdapter(R.array.wifi_eap_method);
|
||||
mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter);
|
||||
mEapMethodSpinner.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B
|
||||
&& mAccessPoint != null
|
||||
&& mAccessPoint.isCarrierAp()) {
|
||||
mEapMethodSpinner.setSelection(mAccessPoint.getCarrierApEapType());
|
||||
}
|
||||
|
||||
if (refreshCertificates) {
|
||||
loadCertificates(
|
||||
mEapCaCertSpinner,
|
||||
Credentials.CA_CERTIFICATE,
|
||||
@@ -1025,76 +1044,73 @@ public class WifiConfigController implements TextWatcher,
|
||||
mDoNotProvideEapUserCertString,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
// Modifying an existing network
|
||||
if (mAccessPoint != null && mAccessPoint.isSaved()) {
|
||||
WifiEnterpriseConfig enterpriseConfig = mAccessPoint.getConfig().enterpriseConfig;
|
||||
int eapMethod = enterpriseConfig.getEapMethod();
|
||||
int phase2Method = enterpriseConfig.getPhase2Method();
|
||||
mEapMethodSpinner.setSelection(eapMethod);
|
||||
showEapFieldsByMethod(eapMethod);
|
||||
switch (eapMethod) {
|
||||
case Eap.PEAP:
|
||||
switch (phase2Method) {
|
||||
case Phase2.NONE:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_NONE);
|
||||
break;
|
||||
case Phase2.MSCHAPV2:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_MSCHAPV2);
|
||||
break;
|
||||
case Phase2.GTC:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_GTC);
|
||||
break;
|
||||
case Phase2.SIM:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_SIM);
|
||||
break;
|
||||
case Phase2.AKA:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA);
|
||||
break;
|
||||
case Phase2.AKA_PRIME:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA_PRIME);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Invalid phase 2 method " + phase2Method);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mPhase2Spinner.setSelection(phase2Method);
|
||||
break;
|
||||
}
|
||||
if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
|
||||
setSelection(mEapCaCertSpinner, mUseSystemCertsString);
|
||||
} else {
|
||||
String[] caCerts = enterpriseConfig.getCaCertificateAliases();
|
||||
if (caCerts == null) {
|
||||
setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString);
|
||||
} else if (caCerts.length == 1) {
|
||||
setSelection(mEapCaCertSpinner, caCerts[0]);
|
||||
} else {
|
||||
// Reload the cert spinner with an extra "multiple certificates added" item.
|
||||
loadCertificates(
|
||||
mEapCaCertSpinner,
|
||||
Credentials.CA_CERTIFICATE,
|
||||
mDoNotValidateEapServerString,
|
||||
true,
|
||||
true);
|
||||
setSelection(mEapCaCertSpinner, mMultipleCertSetString);
|
||||
// Modifying an existing network
|
||||
if (initiateEnterpriseNetworkUi && mAccessPoint != null && mAccessPoint.isSaved()) {
|
||||
final WifiEnterpriseConfig enterpriseConfig = mAccessPoint.getConfig().enterpriseConfig;
|
||||
final int eapMethod = enterpriseConfig.getEapMethod();
|
||||
final int phase2Method = enterpriseConfig.getPhase2Method();
|
||||
mEapMethodSpinner.setSelection(eapMethod);
|
||||
showEapFieldsByMethod(eapMethod);
|
||||
switch (eapMethod) {
|
||||
case Eap.PEAP:
|
||||
switch (phase2Method) {
|
||||
case Phase2.NONE:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_NONE);
|
||||
break;
|
||||
case Phase2.MSCHAPV2:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_MSCHAPV2);
|
||||
break;
|
||||
case Phase2.GTC:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_GTC);
|
||||
break;
|
||||
case Phase2.SIM:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_SIM);
|
||||
break;
|
||||
case Phase2.AKA:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA);
|
||||
break;
|
||||
case Phase2.AKA_PRIME:
|
||||
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA_PRIME);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Invalid phase 2 method " + phase2Method);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mEapDomainView.setText(enterpriseConfig.getDomainSuffixMatch());
|
||||
String userCert = enterpriseConfig.getClientCertificateAlias();
|
||||
if (TextUtils.isEmpty(userCert)) {
|
||||
setSelection(mEapUserCertSpinner, mDoNotProvideEapUserCertString);
|
||||
} else {
|
||||
setSelection(mEapUserCertSpinner, userCert);
|
||||
}
|
||||
mEapIdentityView.setText(enterpriseConfig.getIdentity());
|
||||
mEapAnonymousView.setText(enterpriseConfig.getAnonymousIdentity());
|
||||
} else {
|
||||
mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
|
||||
showEapFieldsByMethod(mEapMethodSpinner.getSelectedItemPosition());
|
||||
break;
|
||||
default:
|
||||
mPhase2Spinner.setSelection(phase2Method);
|
||||
break;
|
||||
}
|
||||
if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
|
||||
setSelection(mEapCaCertSpinner, mUseSystemCertsString);
|
||||
} else {
|
||||
final String[] caCerts = enterpriseConfig.getCaCertificateAliases();
|
||||
if (caCerts == null) {
|
||||
setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString);
|
||||
} else if (caCerts.length == 1) {
|
||||
setSelection(mEapCaCertSpinner, caCerts[0]);
|
||||
} else {
|
||||
// Reload the cert spinner with an extra "multiple certificates added" item.
|
||||
loadCertificates(
|
||||
mEapCaCertSpinner,
|
||||
Credentials.CA_CERTIFICATE,
|
||||
mDoNotValidateEapServerString,
|
||||
true,
|
||||
true);
|
||||
setSelection(mEapCaCertSpinner, mMultipleCertSetString);
|
||||
}
|
||||
}
|
||||
mEapDomainView.setText(enterpriseConfig.getDomainSuffixMatch());
|
||||
final String userCert = enterpriseConfig.getClientCertificateAlias();
|
||||
if (TextUtils.isEmpty(userCert)) {
|
||||
setSelection(mEapUserCertSpinner, mDoNotProvideEapUserCertString);
|
||||
} else {
|
||||
setSelection(mEapUserCertSpinner, userCert);
|
||||
}
|
||||
mEapIdentityView.setText(enterpriseConfig.getIdentity());
|
||||
mEapAnonymousView.setText(enterpriseConfig.getAnonymousIdentity());
|
||||
} else {
|
||||
showEapFieldsByMethod(mEapMethodSpinner.getSelectedItemPosition());
|
||||
}
|
||||
@@ -1389,7 +1405,18 @@ public class WifiConfigController implements TextWatcher,
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "can't get the certificate list from KeyStore");
|
||||
}
|
||||
certs.add(noCertificateString);
|
||||
|
||||
if (mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B) {
|
||||
certs.add(noCertificateString);
|
||||
}
|
||||
|
||||
// If there are only mUnspecifiedCertString and one item to select, only shows the item
|
||||
if (certs.size() == 2) {
|
||||
certs.remove(mUnspecifiedCertString);
|
||||
spinner.setEnabled(false);
|
||||
} else {
|
||||
spinner.setEnabled(true);
|
||||
}
|
||||
|
||||
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
|
||||
context, android.R.layout.simple_spinner_item,
|
||||
@@ -1486,15 +1513,17 @@ public class WifiConfigController implements TextWatcher,
|
||||
if (parent == mSecuritySpinner) {
|
||||
// Convert menu position to actual Wi-Fi security type
|
||||
mAccessPointSecurity = mSecurityInPosition[position];
|
||||
showSecurityFields();
|
||||
showSecurityFields(true /* refreshEapMethods */, true /* refreshCertificates */);
|
||||
|
||||
if (WifiDppUtils.isSupportEnrolleeQrCodeScanner(mContext, mAccessPointSecurity)) {
|
||||
mSsidScanButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mSsidScanButton.setVisibility(View.GONE);
|
||||
}
|
||||
} else if (parent == mEapMethodSpinner || parent == mEapCaCertSpinner) {
|
||||
showSecurityFields();
|
||||
} else if (parent == mEapMethodSpinner) {
|
||||
showSecurityFields(false /* refreshEapMethods */, true /* refreshCertificates */);
|
||||
} else if (parent == mEapCaCertSpinner) {
|
||||
showSecurityFields(false /* refreshEapMethods */, false /* refreshCertificates */);
|
||||
} else if (parent == mPhase2Spinner
|
||||
&& mEapMethodSpinner.getSelectedItemPosition() == WIFI_EAP_METHOD_PEAP) {
|
||||
showPeapFields();
|
||||
@@ -1585,4 +1614,15 @@ public class WifiConfigController implements TextWatcher,
|
||||
((CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox))
|
||||
.setOnCheckedChangeListener(this);
|
||||
}
|
||||
|
||||
private ArrayAdapter<CharSequence> getSpinnerAdapter(
|
||||
int contentStringArrayResId) {
|
||||
final String[] eapMethods = mContext.getResources().getStringArray(
|
||||
contentStringArrayResId);
|
||||
final ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>(mContext,
|
||||
android.R.layout.simple_spinner_item, eapMethods);
|
||||
spinnerAdapter.setDropDownViewResource(
|
||||
android.R.layout.simple_spinner_dropdown_item);
|
||||
return spinnerAdapter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ public class WifiDppConfiguratorActivity extends InstrumentedActivity implements
|
||||
WifiDppUtils.getSecurityString(wifiConfiguration),
|
||||
wifiConfiguration.getPrintableSsid(),
|
||||
wifiConfiguration.preSharedKey,
|
||||
/* hiddenSsid */ false,
|
||||
wifiConfiguration.hiddenSSID,
|
||||
wifiConfiguration.networkId,
|
||||
/* isHotspot */ false);
|
||||
}
|
||||
|
||||
@@ -172,22 +172,25 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
|
||||
|
||||
// Adds all Wi-Fi networks in QR code to the set of configured networks and
|
||||
// connects to it if it's reachable.
|
||||
boolean hasReachableWifiNetwork = false;
|
||||
boolean hasHiddenOrReachableWifiNetwork = false;
|
||||
for (WifiConfiguration qrCodeWifiConfiguration : qrCodeWifiConfigurations) {
|
||||
final int id = wifiManager.addNetwork(qrCodeWifiConfiguration);
|
||||
if (id == -1) {
|
||||
continue;
|
||||
}
|
||||
wifiManager.enableNetwork(id, /* attemptConnect */ false);
|
||||
if (isReachableWifiNetwork(qrCodeWifiConfiguration)) {
|
||||
hasReachableWifiNetwork = true;
|
||||
// WifiTracker only contains a hidden SSID Wi-Fi network if it's saved.
|
||||
// We can't check if a hidden SSID Wi-Fi network is reachable in advance.
|
||||
if (qrCodeWifiConfiguration.hiddenSSID ||
|
||||
isReachableWifiNetwork(qrCodeWifiConfiguration)) {
|
||||
hasHiddenOrReachableWifiNetwork = true;
|
||||
mEnrolleeWifiConfiguration = qrCodeWifiConfiguration;
|
||||
wifiManager.connect(id,
|
||||
/* listener */ WifiDppQrCodeScannerFragment.this);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasReachableWifiNetwork == false) {
|
||||
if (!hasHiddenOrReachableWifiNetwork) {
|
||||
showErrorMessageAndRestartCamera(
|
||||
R.string.wifi_dpp_check_connection_try_again);
|
||||
return;
|
||||
|
||||
@@ -296,6 +296,7 @@ public class WifiDppUtils {
|
||||
if (!TextUtils.isEmpty(preSharedKey)) {
|
||||
intent.putExtra(EXTRA_WIFI_PRE_SHARED_KEY, preSharedKey);
|
||||
}
|
||||
intent.putExtra(EXTRA_WIFI_HIDDEN_SSID, wifiConfiguration.hiddenSSID);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -220,7 +220,7 @@ public class WifiNetworkListFragment extends SettingsPreferenceFragment implemen
|
||||
}
|
||||
final WifiNetworkConfig networkConfig = WifiNetworkConfig.getValidConfigOrNull(
|
||||
selectedAccessPoint.getSecurityString(/* concise */ true),
|
||||
wifiConfig.getPrintableSsid(), wifiConfig.preSharedKey, /* hiddenSsid */ false,
|
||||
wifiConfig.getPrintableSsid(), wifiConfig.preSharedKey, wifiConfig.hiddenSSID,
|
||||
wifiConfig.networkId, /* isHotspot */ false);
|
||||
if (mOnChooseNetworkListener != null) {
|
||||
mOnChooseNetworkListener.onChooseNetwork(networkConfig);
|
||||
|
||||
Reference in New Issue
Block a user