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:
Xin Li
2019-10-30 11:49:56 -07:00
393 changed files with 40468 additions and 47131 deletions

View File

@@ -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(

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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(),

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}
});
}
}

View File

@@ -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}.

View File

@@ -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);
}
}

View File

@@ -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())

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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)

View File

@@ -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);
}
}
}

View File

@@ -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());
}
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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()));
}
}
}
}

View File

@@ -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());
};
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 */);
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;

View File

@@ -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);

View 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;
}
}

View File

@@ -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);

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
}
/**

View File

@@ -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);