Converting to Soong will move some code from directly compiled into the app to compiled into an Android library and then shared between the app and the tests. This will cause resource IDs in the library to become non-final, which means they can no longer be used in case statements. Convert affect case statements to if blocks. Test: m RunSettingsRoboTests Change-Id: I25742a374f06d3fa4decbfc0d223a350acc50881
276 lines
10 KiB
Java
276 lines
10 KiB
Java
/*
|
|
* 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.network;
|
|
|
|
import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
|
|
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
|
|
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
|
|
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
|
|
|
|
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
|
|
|
import android.app.settings.SettingsEnums;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.net.NetworkUtils;
|
|
import android.os.UserHandle;
|
|
import android.os.UserManager;
|
|
import android.provider.Settings;
|
|
import android.text.Editable;
|
|
import android.text.TextWatcher;
|
|
import android.text.method.LinkMovementMethod;
|
|
import android.util.AttributeSet;
|
|
import android.util.Log;
|
|
import android.view.View;
|
|
import android.widget.Button;
|
|
import android.widget.EditText;
|
|
import android.widget.RadioGroup;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.appcompat.app.AlertDialog;
|
|
import androidx.preference.PreferenceViewHolder;
|
|
|
|
import com.android.settings.R;
|
|
import com.android.settings.overlay.FeatureFactory;
|
|
import com.android.settings.utils.AnnotationSpan;
|
|
import com.android.settingslib.CustomDialogPreferenceCompat;
|
|
import com.android.settingslib.HelpUtils;
|
|
import com.android.settingslib.RestrictedLockUtils;
|
|
import com.android.settingslib.RestrictedLockUtilsInternal;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Dialog to set the Private DNS
|
|
*/
|
|
public class PrivateDnsModeDialogPreference extends CustomDialogPreferenceCompat implements
|
|
DialogInterface.OnClickListener, RadioGroup.OnCheckedChangeListener, TextWatcher {
|
|
|
|
public static final String ANNOTATION_URL = "url";
|
|
|
|
private static final String TAG = "PrivateDnsModeDialog";
|
|
// DNS_MODE -> RadioButton id
|
|
private static final Map<String, Integer> PRIVATE_DNS_MAP;
|
|
|
|
static {
|
|
PRIVATE_DNS_MAP = new HashMap<>();
|
|
PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OFF, R.id.private_dns_mode_off);
|
|
PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OPPORTUNISTIC, R.id.private_dns_mode_opportunistic);
|
|
PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, R.id.private_dns_mode_provider);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static final String MODE_KEY = Settings.Global.PRIVATE_DNS_MODE;
|
|
@VisibleForTesting
|
|
static final String HOSTNAME_KEY = Settings.Global.PRIVATE_DNS_SPECIFIER;
|
|
|
|
public static String getModeFromSettings(ContentResolver cr) {
|
|
String mode = Settings.Global.getString(cr, MODE_KEY);
|
|
if (!PRIVATE_DNS_MAP.containsKey(mode)) {
|
|
mode = Settings.Global.getString(cr, Settings.Global.PRIVATE_DNS_DEFAULT_MODE);
|
|
}
|
|
return PRIVATE_DNS_MAP.containsKey(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
|
|
}
|
|
|
|
public static String getHostnameFromSettings(ContentResolver cr) {
|
|
return Settings.Global.getString(cr, HOSTNAME_KEY);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
EditText mEditText;
|
|
@VisibleForTesting
|
|
RadioGroup mRadioGroup;
|
|
@VisibleForTesting
|
|
String mMode;
|
|
|
|
public PrivateDnsModeDialogPreference(Context context) {
|
|
super(context);
|
|
initialize();
|
|
}
|
|
|
|
public PrivateDnsModeDialogPreference(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
initialize();
|
|
}
|
|
|
|
public PrivateDnsModeDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
super(context, attrs, defStyleAttr);
|
|
initialize();
|
|
}
|
|
|
|
public PrivateDnsModeDialogPreference(Context context, AttributeSet attrs, int defStyleAttr,
|
|
int defStyleRes) {
|
|
super(context, attrs, defStyleAttr, defStyleRes);
|
|
initialize();
|
|
}
|
|
|
|
private final AnnotationSpan.LinkInfo mUrlLinkInfo = new AnnotationSpan.LinkInfo(
|
|
ANNOTATION_URL, (widget) -> {
|
|
final Context context = widget.getContext();
|
|
final Intent intent = HelpUtils.getHelpIntent(context,
|
|
context.getString(R.string.help_uri_private_dns),
|
|
context.getClass().getName());
|
|
if (intent != null) {
|
|
try {
|
|
widget.startActivityForResult(intent, 0);
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.w(TAG, "Activity was not found for intent, " + intent.toString());
|
|
}
|
|
}
|
|
});
|
|
|
|
private void initialize() {
|
|
// Add the "Restricted" icon resource so that if the preference is disabled by the
|
|
// admin, an information button will be shown.
|
|
setWidgetLayoutResource(R.layout.restricted_icon);
|
|
}
|
|
|
|
@Override
|
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
|
super.onBindViewHolder(holder);
|
|
if (isDisabledByAdmin()) {
|
|
// If the preference is disabled by the admin, set the inner item as enabled so
|
|
// it could act as a click target. The preference itself will have been disabled
|
|
// by the controller.
|
|
holder.itemView.setEnabled(true);
|
|
}
|
|
|
|
final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
|
|
if (restrictedIcon != null) {
|
|
// Show the "Restricted" icon if, and only if, the preference was disabled by
|
|
// the admin.
|
|
restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onBindDialogView(View view) {
|
|
final Context context = getContext();
|
|
final ContentResolver contentResolver = context.getContentResolver();
|
|
|
|
mMode = getModeFromSettings(context.getContentResolver());
|
|
|
|
mEditText = view.findViewById(R.id.private_dns_mode_provider_hostname);
|
|
mEditText.addTextChangedListener(this);
|
|
mEditText.setText(getHostnameFromSettings(contentResolver));
|
|
|
|
mRadioGroup = view.findViewById(R.id.private_dns_radio_group);
|
|
mRadioGroup.setOnCheckedChangeListener(this);
|
|
mRadioGroup.check(PRIVATE_DNS_MAP.getOrDefault(mMode, R.id.private_dns_mode_opportunistic));
|
|
|
|
final TextView helpTextView = view.findViewById(R.id.private_dns_help_info);
|
|
helpTextView.setMovementMethod(LinkMovementMethod.getInstance());
|
|
final Intent helpIntent = HelpUtils.getHelpIntent(context,
|
|
context.getString(R.string.help_uri_private_dns),
|
|
context.getClass().getName());
|
|
final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(context,
|
|
ANNOTATION_URL, helpIntent);
|
|
if (linkInfo.isActionable()) {
|
|
helpTextView.setText(AnnotationSpan.linkify(
|
|
context.getText(R.string.private_dns_help_message), linkInfo));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
if (which == DialogInterface.BUTTON_POSITIVE) {
|
|
final Context context = getContext();
|
|
if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
|
|
// Only clickable if hostname is valid, so we could save it safely
|
|
Settings.Global.putString(context.getContentResolver(), HOSTNAME_KEY,
|
|
mEditText.getText().toString());
|
|
}
|
|
|
|
FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
|
|
SettingsEnums.ACTION_PRIVATE_DNS_MODE, mMode);
|
|
Settings.Global.putString(context.getContentResolver(), MODE_KEY, mMode);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCheckedChanged(RadioGroup group, int checkedId) {
|
|
if (checkedId == R.id.private_dns_mode_off) {
|
|
mMode = PRIVATE_DNS_MODE_OFF;
|
|
} else if (checkedId == R.id.private_dns_mode_opportunistic) {
|
|
mMode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
|
|
} else if (checkedId == R.id.private_dns_mode_provider) {
|
|
mMode = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
|
|
}
|
|
updateDialogInfo();
|
|
}
|
|
|
|
@Override
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
}
|
|
|
|
@Override
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
}
|
|
|
|
@Override
|
|
public void afterTextChanged(Editable s) {
|
|
updateDialogInfo();
|
|
}
|
|
|
|
@Override
|
|
public void performClick() {
|
|
EnforcedAdmin enforcedAdmin = getEnforcedAdmin();
|
|
|
|
if (enforcedAdmin == null) {
|
|
// If the restriction is not restricted by admin, continue as usual.
|
|
super.performClick();
|
|
} else {
|
|
// Show a dialog explaining to the user why they cannot change the preference.
|
|
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), enforcedAdmin);
|
|
}
|
|
}
|
|
|
|
private EnforcedAdmin getEnforcedAdmin() {
|
|
return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
|
|
getContext(), UserManager.DISALLOW_CONFIG_PRIVATE_DNS, UserHandle.myUserId());
|
|
}
|
|
|
|
private boolean isDisabledByAdmin() {
|
|
return getEnforcedAdmin() != null;
|
|
}
|
|
|
|
private Button getSaveButton() {
|
|
final AlertDialog dialog = (AlertDialog) getDialog();
|
|
if (dialog == null) {
|
|
return null;
|
|
}
|
|
return dialog.getButton(DialogInterface.BUTTON_POSITIVE);
|
|
}
|
|
|
|
private void updateDialogInfo() {
|
|
final boolean modeProvider = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mMode);
|
|
if (mEditText != null) {
|
|
mEditText.setEnabled(modeProvider);
|
|
}
|
|
final Button saveButton = getSaveButton();
|
|
if (saveButton != null) {
|
|
saveButton.setEnabled(modeProvider
|
|
? NetworkUtils.isWeaklyValidatedHostname(mEditText.getText().toString())
|
|
: true);
|
|
}
|
|
}
|
|
}
|