Loading res/layout/private_dns_mode_dialog.xml +45 −32 Original line number Diff line number Diff line Loading @@ -14,33 +14,38 @@ limitations under the License. --> <RadioGroup <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/private_dns_radio_group" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="8dip"> android:orientation="vertical" android:padding="8dp"> <RadioGroup android:id="@+id/private_dns_radio_group" android:layout_width="match_parent" android:layout_height="wrap_content"> <RadioButton android:id="@+id/private_dns_mode_off" android:text="@string/private_dns_mode_off" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip"/> android:layout_margin="8dp"/> <RadioButton android:id="@+id/private_dns_mode_opportunistic" android:text="@string/private_dns_mode_opportunistic" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip"/> android:layout_margin="8dp"/> <RadioButton android:id="@+id/private_dns_mode_provider" android:text="@string/private_dns_mode_provider" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip"/> android:layout_margin="8dp"/> <EditText android:id="@+id/private_dns_mode_provider_hostname" Loading @@ -50,7 +55,15 @@ android:inputType="textFilter|textUri" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="40dip" android:layout_marginEnd="8dip"/> android:layout_marginStart="40dp" android:layout_marginEnd="8dp"/> </RadioGroup> <TextView android:id="@+id/private_dns_help_info" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:paddingStart="16dp" android:textAppearance="?android:attr/textAppearanceSmall"/> </LinearLayout> res/values/strings.xml +3 −1 Original line number Diff line number Diff line Loading @@ -2274,7 +2274,8 @@ <string name="emergency_address_title">Emergency Address</string> <!-- Summary of Update Emergency Address preference, explaining usage of emergency address [CHAR LIMIT=NONE] --> <string name="emergency_address_summary">Used as your location when you make an emergency call over Wi\u2011Fi</string> <!-- Message of private dns that provides a help link. [CHAR LIMIT=NONE] --> <string name="private_dns_help_message"><annotation id="url">Learn more</annotation> about Private DNS features</string> <!-- Sound and alerts settings --> <skip/> Loading Loading @@ -6346,6 +6347,7 @@ <string name="help_url_icc_lock" translatable="false"></string> <string name="help_uri_process_stats_summary" translatable="false"></string> <string name="help_uri_process_stats_apps" translatable="false"></string> <string name="help_uri_private_dns" translatable="false"></string> <!-- User account title [CHAR LIMIT=30] --> <string name="user_account_title">Account for content</string> Loading src/com/android/settings/fingerprint/FingerprintSettings.java +26 −78 Original line number Diff line number Diff line Loading @@ -25,12 +25,10 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; Loading @@ -41,12 +39,7 @@ import android.support.v7.preference.Preference.OnPreferenceClickListener; import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceViewHolder; import android.text.Annotation; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.URLSpan; import android.util.Log; import android.view.View; import android.view.WindowManager; Loading @@ -61,6 +54,7 @@ import com.android.settings.Utils; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.utils.AnnotationSpan; import com.android.settingslib.HelpUtils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; Loading Loading @@ -103,6 +97,9 @@ public class FingerprintSettings extends SubSettings { private static final long LOCKOUT_DURATION = 30000; // time we have to wait for fp to reset, ms public static final String ANNOTATION_URL = "url"; public static final String ANNOTATION_ADMIN_DETAILS = "admin_details"; public static final String KEY_FINGERPRINT_SETTINGS = "fingerprint_settings"; @Override Loading Loading @@ -162,6 +159,20 @@ public class FingerprintSettings extends SubSettings { private FingerprintRemoveSidecar mRemovalSidecar; private HashMap<Integer, String> mFingerprintsRenaming; final AnnotationSpan.LinkInfo mUrlLinkInfo = new AnnotationSpan.LinkInfo( ANNOTATION_URL, (view) -> { final Context context = view.getContext(); Intent intent = HelpUtils.getHelpIntent(context, getString(getHelpResource()), context.getClass().getName()); if (intent != null) { try { view.startActivityForResult(intent, 0); } catch (ActivityNotFoundException e) { Log.w(TAG, "Activity was not found for intent, " + intent.toString()); } } }); FingerprintAuthenticateSidecar.Listener mAuthenticateListener = new FingerprintAuthenticateSidecar.Listener() { @Override Loading Loading @@ -346,10 +357,15 @@ public class FingerprintSettings extends SubSettings { final FooterPreference pref = mFooterPreferenceMixin.createFooterPreference(); final EnforcedAdmin admin = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled( activity, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId); pref.setTitle(LearnMoreSpan.linkify(getText(admin != null ? R.string.security_settings_fingerprint_enroll_disclaimer_lockscreen_disabled final AnnotationSpan.LinkInfo adminLinkInfo = new AnnotationSpan.LinkInfo( ANNOTATION_ADMIN_DETAILS, (view) -> { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(activity, admin); }); pref.setTitle(AnnotationSpan.linkify(getText(admin != null ? R.string .security_settings_fingerprint_enroll_disclaimer_lockscreen_disabled : R.string.security_settings_fingerprint_enroll_disclaimer), getString(getHelpResource()), admin)); mUrlLinkInfo, adminLinkInfo)); } protected void removeFingerprintPreference(int fingerprintId) { Loading Loading @@ -906,74 +922,6 @@ public class FingerprintSettings extends SubSettings { } } private static class LearnMoreSpan extends URLSpan { private static final String TAG = "LearnMoreSpan"; private static final Typeface TYPEFACE_MEDIUM = Typeface.create("sans-serif-medium", Typeface.NORMAL); private static final String ANNOTATION_URL = "url"; private static final String ANNOTATION_ADMIN_DETAILS = "admin_details"; private EnforcedAdmin mEnforcedAdmin = null; private LearnMoreSpan(String url) { super(url); } private LearnMoreSpan(EnforcedAdmin admin) { super((String) null); mEnforcedAdmin = admin; } @Override public void onClick(View widget) { Context ctx = widget.getContext(); if (mEnforcedAdmin != null) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(ctx, mEnforcedAdmin); } else { Intent intent = HelpUtils.getHelpIntent(ctx, getURL(), ctx.getClass().getName()); if (intent == null) { Log.w(LearnMoreSpan.TAG, "Null help intent."); return; } try { widget.startActivityForResult(intent, 0); } catch (ActivityNotFoundException e) { Log.w(FingerprintSettingsFragment.TAG, "Actvity was not found for intent, " + intent.toString()); } } } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); ds.setTypeface(TYPEFACE_MEDIUM); } public static CharSequence linkify(CharSequence rawText, String uri, EnforcedAdmin admin) { SpannableString msg = new SpannableString(rawText); Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class); SpannableStringBuilder builder = new SpannableStringBuilder(msg); for (Annotation annotation : spans) { final String key = annotation.getValue(); int start = msg.getSpanStart(annotation); int end = msg.getSpanEnd(annotation); LearnMoreSpan link = null; if (ANNOTATION_URL.equals(key)) { link = new LearnMoreSpan(uri); } else if (ANNOTATION_ADMIN_DETAILS.equals(key)) { link = new LearnMoreSpan(admin); } if (link != null) { builder.setSpan(link, start, end, msg.getSpanFlags(link)); } } return builder; } } /** * @deprecated in favor of new SecuritySettings. */ Loading src/com/android/settings/network/PrivateDnsModeDialogFragment.java +33 −3 Original line number Diff line number Diff line Loading @@ -22,22 +22,30 @@ import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME import android.app.AlertDialog; import android.app.Dialog; import android.app.FragmentManager; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.VisibleForTesting; import android.text.Editable; import android.text.TextWatcher; import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.utils.AnnotationSpan; import com.android.settingslib.HelpUtils; import java.util.HashMap; import java.util.Map; Loading @@ -48,6 +56,8 @@ import java.util.Map; public class PrivateDnsModeDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener, RadioGroup.OnCheckedChangeListener, TextWatcher { public static final String ANNOTATION_URL = "url"; private static final String TAG = "PrivateDnsModeDialogFragment"; // DNS_MODE -> RadioButton id private static final Map<String, Integer> PRIVATE_DNS_MAP; Loading @@ -73,6 +83,21 @@ public class PrivateDnsModeDialogFragment extends InstrumentedDialogFragment imp @VisibleForTesting String mMode; private final AnnotationSpan.LinkInfo mUrlLinkInfo = new AnnotationSpan.LinkInfo( ANNOTATION_URL, (widget) -> { final Context context = widget.getContext(); final Intent intent = HelpUtils.getHelpIntent(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()); } } }); public static void show(FragmentManager fragmentManager) { if (fragmentManager.findFragmentByTag(TAG) == null) { final PrivateDnsModeDialogFragment fragment = new PrivateDnsModeDialogFragment(); Loading Loading @@ -112,25 +137,30 @@ public class PrivateDnsModeDialogFragment extends InstrumentedDialogFragment imp 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()); helpTextView.setText(AnnotationSpan.linkify( context.getText(R.string.private_dns_help_message), mUrlLinkInfo)); return view; } @Override public void onClick(DialogInterface dialog, int which) { //TODO(b/34953048): add metric action if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { // Only clickable if hostname is valid, so we could save it safely Settings.Global.putString(getContext().getContentResolver(), HOSTNAME_KEY, mEditText.getText().toString()); } mMetricsFeatureProvider.action(getContext(), MetricsProto.MetricsEvent.ACTION_PRIVATE_DNS_MODE, mMode); Settings.Global.putString(getContext().getContentResolver(), MODE_KEY, mMode); } @Override public int getMetricsCategory() { //TODO(b/68030013): add metric id return 0; return MetricsProto.MetricsEvent.DIALOG_PRIVATE_DNS; } @Override Loading src/com/android/settings/utils/AnnotationSpan.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.text.Annotation; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.style.URLSpan; import android.view.View; /** * This class is used to add {@link View.OnClickListener} for the text been wrapped by * annotation. */ public class AnnotationSpan extends URLSpan { private final View.OnClickListener mClickListener; private AnnotationSpan(View.OnClickListener lsn) { super((String) null); mClickListener = lsn; } @Override public void onClick(View widget) { if (mClickListener != null) { mClickListener.onClick(widget); } } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); } public static CharSequence linkify(CharSequence rawText, LinkInfo... linkInfos) { SpannableString msg = new SpannableString(rawText); Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class); SpannableStringBuilder builder = new SpannableStringBuilder(msg); for (Annotation annotation : spans) { final String key = annotation.getValue(); int start = msg.getSpanStart(annotation); int end = msg.getSpanEnd(annotation); AnnotationSpan link = null; for (LinkInfo linkInfo : linkInfos) { if (linkInfo.annotation.equals(key)) { link = new AnnotationSpan(linkInfo.listener); break; } } if (link != null) { builder.setSpan(link, start, end, msg.getSpanFlags(link)); } } return builder; } /** * Data class to store the annotation and the click action */ public static class LinkInfo { public final String annotation; public final View.OnClickListener listener; public LinkInfo(String annotation, View.OnClickListener listener) { this.annotation = annotation; this.listener = listener; } } } Loading
res/layout/private_dns_mode_dialog.xml +45 −32 Original line number Diff line number Diff line Loading @@ -14,33 +14,38 @@ limitations under the License. --> <RadioGroup <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/private_dns_radio_group" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="8dip"> android:orientation="vertical" android:padding="8dp"> <RadioGroup android:id="@+id/private_dns_radio_group" android:layout_width="match_parent" android:layout_height="wrap_content"> <RadioButton android:id="@+id/private_dns_mode_off" android:text="@string/private_dns_mode_off" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip"/> android:layout_margin="8dp"/> <RadioButton android:id="@+id/private_dns_mode_opportunistic" android:text="@string/private_dns_mode_opportunistic" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip"/> android:layout_margin="8dp"/> <RadioButton android:id="@+id/private_dns_mode_provider" android:text="@string/private_dns_mode_provider" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip"/> android:layout_margin="8dp"/> <EditText android:id="@+id/private_dns_mode_provider_hostname" Loading @@ -50,7 +55,15 @@ android:inputType="textFilter|textUri" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="40dip" android:layout_marginEnd="8dip"/> android:layout_marginStart="40dp" android:layout_marginEnd="8dp"/> </RadioGroup> <TextView android:id="@+id/private_dns_help_info" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:paddingStart="16dp" android:textAppearance="?android:attr/textAppearanceSmall"/> </LinearLayout>
res/values/strings.xml +3 −1 Original line number Diff line number Diff line Loading @@ -2274,7 +2274,8 @@ <string name="emergency_address_title">Emergency Address</string> <!-- Summary of Update Emergency Address preference, explaining usage of emergency address [CHAR LIMIT=NONE] --> <string name="emergency_address_summary">Used as your location when you make an emergency call over Wi\u2011Fi</string> <!-- Message of private dns that provides a help link. [CHAR LIMIT=NONE] --> <string name="private_dns_help_message"><annotation id="url">Learn more</annotation> about Private DNS features</string> <!-- Sound and alerts settings --> <skip/> Loading Loading @@ -6346,6 +6347,7 @@ <string name="help_url_icc_lock" translatable="false"></string> <string name="help_uri_process_stats_summary" translatable="false"></string> <string name="help_uri_process_stats_apps" translatable="false"></string> <string name="help_uri_private_dns" translatable="false"></string> <!-- User account title [CHAR LIMIT=30] --> <string name="user_account_title">Account for content</string> Loading
src/com/android/settings/fingerprint/FingerprintSettings.java +26 −78 Original line number Diff line number Diff line Loading @@ -25,12 +25,10 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; Loading @@ -41,12 +39,7 @@ import android.support.v7.preference.Preference.OnPreferenceClickListener; import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceViewHolder; import android.text.Annotation; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.URLSpan; import android.util.Log; import android.view.View; import android.view.WindowManager; Loading @@ -61,6 +54,7 @@ import com.android.settings.Utils; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.utils.AnnotationSpan; import com.android.settingslib.HelpUtils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; Loading Loading @@ -103,6 +97,9 @@ public class FingerprintSettings extends SubSettings { private static final long LOCKOUT_DURATION = 30000; // time we have to wait for fp to reset, ms public static final String ANNOTATION_URL = "url"; public static final String ANNOTATION_ADMIN_DETAILS = "admin_details"; public static final String KEY_FINGERPRINT_SETTINGS = "fingerprint_settings"; @Override Loading Loading @@ -162,6 +159,20 @@ public class FingerprintSettings extends SubSettings { private FingerprintRemoveSidecar mRemovalSidecar; private HashMap<Integer, String> mFingerprintsRenaming; final AnnotationSpan.LinkInfo mUrlLinkInfo = new AnnotationSpan.LinkInfo( ANNOTATION_URL, (view) -> { final Context context = view.getContext(); Intent intent = HelpUtils.getHelpIntent(context, getString(getHelpResource()), context.getClass().getName()); if (intent != null) { try { view.startActivityForResult(intent, 0); } catch (ActivityNotFoundException e) { Log.w(TAG, "Activity was not found for intent, " + intent.toString()); } } }); FingerprintAuthenticateSidecar.Listener mAuthenticateListener = new FingerprintAuthenticateSidecar.Listener() { @Override Loading Loading @@ -346,10 +357,15 @@ public class FingerprintSettings extends SubSettings { final FooterPreference pref = mFooterPreferenceMixin.createFooterPreference(); final EnforcedAdmin admin = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled( activity, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId); pref.setTitle(LearnMoreSpan.linkify(getText(admin != null ? R.string.security_settings_fingerprint_enroll_disclaimer_lockscreen_disabled final AnnotationSpan.LinkInfo adminLinkInfo = new AnnotationSpan.LinkInfo( ANNOTATION_ADMIN_DETAILS, (view) -> { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(activity, admin); }); pref.setTitle(AnnotationSpan.linkify(getText(admin != null ? R.string .security_settings_fingerprint_enroll_disclaimer_lockscreen_disabled : R.string.security_settings_fingerprint_enroll_disclaimer), getString(getHelpResource()), admin)); mUrlLinkInfo, adminLinkInfo)); } protected void removeFingerprintPreference(int fingerprintId) { Loading Loading @@ -906,74 +922,6 @@ public class FingerprintSettings extends SubSettings { } } private static class LearnMoreSpan extends URLSpan { private static final String TAG = "LearnMoreSpan"; private static final Typeface TYPEFACE_MEDIUM = Typeface.create("sans-serif-medium", Typeface.NORMAL); private static final String ANNOTATION_URL = "url"; private static final String ANNOTATION_ADMIN_DETAILS = "admin_details"; private EnforcedAdmin mEnforcedAdmin = null; private LearnMoreSpan(String url) { super(url); } private LearnMoreSpan(EnforcedAdmin admin) { super((String) null); mEnforcedAdmin = admin; } @Override public void onClick(View widget) { Context ctx = widget.getContext(); if (mEnforcedAdmin != null) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(ctx, mEnforcedAdmin); } else { Intent intent = HelpUtils.getHelpIntent(ctx, getURL(), ctx.getClass().getName()); if (intent == null) { Log.w(LearnMoreSpan.TAG, "Null help intent."); return; } try { widget.startActivityForResult(intent, 0); } catch (ActivityNotFoundException e) { Log.w(FingerprintSettingsFragment.TAG, "Actvity was not found for intent, " + intent.toString()); } } } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); ds.setTypeface(TYPEFACE_MEDIUM); } public static CharSequence linkify(CharSequence rawText, String uri, EnforcedAdmin admin) { SpannableString msg = new SpannableString(rawText); Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class); SpannableStringBuilder builder = new SpannableStringBuilder(msg); for (Annotation annotation : spans) { final String key = annotation.getValue(); int start = msg.getSpanStart(annotation); int end = msg.getSpanEnd(annotation); LearnMoreSpan link = null; if (ANNOTATION_URL.equals(key)) { link = new LearnMoreSpan(uri); } else if (ANNOTATION_ADMIN_DETAILS.equals(key)) { link = new LearnMoreSpan(admin); } if (link != null) { builder.setSpan(link, start, end, msg.getSpanFlags(link)); } } return builder; } } /** * @deprecated in favor of new SecuritySettings. */ Loading
src/com/android/settings/network/PrivateDnsModeDialogFragment.java +33 −3 Original line number Diff line number Diff line Loading @@ -22,22 +22,30 @@ import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME import android.app.AlertDialog; import android.app.Dialog; import android.app.FragmentManager; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.VisibleForTesting; import android.text.Editable; import android.text.TextWatcher; import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.utils.AnnotationSpan; import com.android.settingslib.HelpUtils; import java.util.HashMap; import java.util.Map; Loading @@ -48,6 +56,8 @@ import java.util.Map; public class PrivateDnsModeDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener, RadioGroup.OnCheckedChangeListener, TextWatcher { public static final String ANNOTATION_URL = "url"; private static final String TAG = "PrivateDnsModeDialogFragment"; // DNS_MODE -> RadioButton id private static final Map<String, Integer> PRIVATE_DNS_MAP; Loading @@ -73,6 +83,21 @@ public class PrivateDnsModeDialogFragment extends InstrumentedDialogFragment imp @VisibleForTesting String mMode; private final AnnotationSpan.LinkInfo mUrlLinkInfo = new AnnotationSpan.LinkInfo( ANNOTATION_URL, (widget) -> { final Context context = widget.getContext(); final Intent intent = HelpUtils.getHelpIntent(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()); } } }); public static void show(FragmentManager fragmentManager) { if (fragmentManager.findFragmentByTag(TAG) == null) { final PrivateDnsModeDialogFragment fragment = new PrivateDnsModeDialogFragment(); Loading Loading @@ -112,25 +137,30 @@ public class PrivateDnsModeDialogFragment extends InstrumentedDialogFragment imp 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()); helpTextView.setText(AnnotationSpan.linkify( context.getText(R.string.private_dns_help_message), mUrlLinkInfo)); return view; } @Override public void onClick(DialogInterface dialog, int which) { //TODO(b/34953048): add metric action if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { // Only clickable if hostname is valid, so we could save it safely Settings.Global.putString(getContext().getContentResolver(), HOSTNAME_KEY, mEditText.getText().toString()); } mMetricsFeatureProvider.action(getContext(), MetricsProto.MetricsEvent.ACTION_PRIVATE_DNS_MODE, mMode); Settings.Global.putString(getContext().getContentResolver(), MODE_KEY, mMode); } @Override public int getMetricsCategory() { //TODO(b/68030013): add metric id return 0; return MetricsProto.MetricsEvent.DIALOG_PRIVATE_DNS; } @Override Loading
src/com/android/settings/utils/AnnotationSpan.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.text.Annotation; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.style.URLSpan; import android.view.View; /** * This class is used to add {@link View.OnClickListener} for the text been wrapped by * annotation. */ public class AnnotationSpan extends URLSpan { private final View.OnClickListener mClickListener; private AnnotationSpan(View.OnClickListener lsn) { super((String) null); mClickListener = lsn; } @Override public void onClick(View widget) { if (mClickListener != null) { mClickListener.onClick(widget); } } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); } public static CharSequence linkify(CharSequence rawText, LinkInfo... linkInfos) { SpannableString msg = new SpannableString(rawText); Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class); SpannableStringBuilder builder = new SpannableStringBuilder(msg); for (Annotation annotation : spans) { final String key = annotation.getValue(); int start = msg.getSpanStart(annotation); int end = msg.getSpanEnd(annotation); AnnotationSpan link = null; for (LinkInfo linkInfo : linkInfos) { if (linkInfo.annotation.equals(key)) { link = new AnnotationSpan(linkInfo.listener); break; } } if (link != null) { builder.setSpan(link, start, end, msg.getSpanFlags(link)); } } return builder; } /** * Data class to store the annotation and the click action */ public static class LinkInfo { public final String annotation; public final View.OnClickListener listener; public LinkInfo(String annotation, View.OnClickListener listener) { this.annotation = annotation; this.listener = listener; } } }