Files
app_Settings/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
Colin Cross 807e861105 Use if instead of switch for resources
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
2019-05-13 13:42:01 -07:00

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