1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.support; 18 19 import android.accounts.Account; 20 import android.app.Activity; 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.app.DialogFragment; 24 import android.content.DialogInterface; 25 import android.os.Bundle; 26 import android.text.Annotation; 27 import android.text.Spannable; 28 import android.text.Spanned; 29 import android.text.TextPaint; 30 import android.text.TextUtils; 31 import android.text.style.URLSpan; 32 import android.view.LayoutInflater; 33 import android.view.View; 34 import android.widget.CheckBox; 35 import android.widget.TextView; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.internal.logging.nano.MetricsProto; 39 import com.android.settings.R; 40 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 41 import com.android.settings.overlay.FeatureFactory; 42 import com.android.settings.overlay.SupportFeatureProvider; 43 44 /** 45 * {@link DialogFragment} for support disclaimer. 46 */ 47 public final class SupportDisclaimerDialogFragment extends InstrumentedDialogFragment 48 implements DialogInterface.OnClickListener { 49 50 public static final String TAG = "SupportDisclaimerDialog"; 51 public static final String EXTRA_TYPE = "extra_type"; 52 public static final String EXTRA_ACCOUNT = "extra_account"; 53 newInstance(Account account, @SupportFeatureProvider.SupportType int type)54 public static SupportDisclaimerDialogFragment newInstance(Account account, 55 @SupportFeatureProvider.SupportType int type) { 56 final SupportDisclaimerDialogFragment fragment = new SupportDisclaimerDialogFragment(); 57 final Bundle bundle = new Bundle(2); 58 bundle.putParcelable(SupportDisclaimerDialogFragment.EXTRA_ACCOUNT, account); 59 bundle.putInt(SupportDisclaimerDialogFragment.EXTRA_TYPE, type); 60 fragment.setArguments(bundle); 61 return fragment; 62 } 63 64 @Override onCreateDialog(Bundle savedInstanceState)65 public Dialog onCreateDialog(Bundle savedInstanceState) { 66 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) 67 .setTitle(R.string.support_disclaimer_title) 68 .setPositiveButton(android.R.string.ok, this) 69 .setNegativeButton(android.R.string.cancel, this); 70 final View content = LayoutInflater.from(builder.getContext()) 71 .inflate(R.layout.support_disclaimer_content, null); 72 final TextView disclaimer = (TextView) content.findViewById(R.id.support_disclaimer_text); 73 final Activity activity = getActivity(); 74 final SupportFeatureProvider supportFeatureProvider = 75 FeatureFactory.getFactory(activity).getSupportFeatureProvider(activity); 76 77 // sets the two links that go to privacy policy and terms of service 78 disclaimer.setText(supportFeatureProvider.getDisclaimerStringResId()); 79 Spannable viewText = (Spannable) disclaimer.getText(); 80 stripUnderlines(viewText); 81 SystemInformationSpan.linkify(viewText, this); 82 // sets the link that launches a dialog to expose the signals we are sending 83 return builder 84 .setView(content) 85 .create(); 86 } 87 88 @Override onClick(DialogInterface dialog, int which)89 public void onClick(DialogInterface dialog, int which) { 90 if (which == Dialog.BUTTON_NEGATIVE) { 91 mMetricsFeatureProvider.action(getContext(), 92 MetricsProto.MetricsEvent.ACTION_SUPPORT_DISCLAIMER_CANCEL); 93 return; 94 } 95 final Activity activity = getActivity(); 96 final CheckBox doNotShow = 97 (CheckBox) getDialog().findViewById(R.id.support_disclaimer_do_not_show_again); 98 final boolean isChecked = doNotShow.isChecked(); 99 final SupportFeatureProvider supportFeatureProvider = 100 FeatureFactory.getFactory(activity).getSupportFeatureProvider(activity); 101 supportFeatureProvider.setShouldShowDisclaimerDialog(getContext(), !isChecked); 102 final Bundle bundle = getArguments(); 103 if (isChecked) { 104 mMetricsFeatureProvider.action(activity, 105 MetricsProto.MetricsEvent.ACTION_SKIP_DISCLAIMER_SELECTED); 106 } 107 mMetricsFeatureProvider.action(activity, 108 MetricsProto.MetricsEvent.ACTION_SUPPORT_DISCLAIMER_OK); 109 supportFeatureProvider.startSupport(getActivity(), 110 bundle.getParcelable(EXTRA_ACCOUNT), bundle.getInt(EXTRA_TYPE)); 111 } 112 113 @Override onCancel(DialogInterface dialog)114 public void onCancel(DialogInterface dialog) { 115 super.onCancel(dialog); 116 mMetricsFeatureProvider.action(getContext(), 117 MetricsProto.MetricsEvent.ACTION_SUPPORT_DISCLAIMER_CANCEL); 118 } 119 120 /** 121 * Removes the underlines of {@link android.text.style.URLSpan}s. 122 */ stripUnderlines(Spannable input)123 private static void stripUnderlines(Spannable input) { 124 final URLSpan[] urls = input.getSpans(0, input.length(), URLSpan.class); 125 126 for (URLSpan span : urls) { 127 final int start = input.getSpanStart(span); 128 final int end = input.getSpanEnd(span); 129 if (!TextUtils.isEmpty(span.getURL())) { 130 input.removeSpan(span); 131 input.setSpan(new NoUnderlineUrlSpan(span.getURL()), start, end, 132 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 133 } 134 } 135 } 136 137 @Override getMetricsCategory()138 public int getMetricsCategory() { 139 return MetricsProto.MetricsEvent.DIALOG_SUPPORT_DISCLAIMER; 140 } 141 142 /** 143 * A {@link URLSpan} that doesn't decorate the link with underline. 144 */ 145 public static class NoUnderlineUrlSpan extends URLSpan { 146 NoUnderlineUrlSpan(String url)147 public NoUnderlineUrlSpan(String url) { 148 super(url); 149 } 150 151 @Override updateDrawState(TextPaint ds)152 public void updateDrawState(TextPaint ds) { 153 super.updateDrawState(ds); 154 ds.setUnderlineText(false); 155 } 156 } 157 158 /** 159 * A {@link URLSpan} that opens a dialog when clicked 160 */ 161 public static class SystemInformationSpan extends URLSpan { 162 163 private static final String ANNOTATION_URL = "url"; 164 private final DialogFragment mDialog; 165 private SupportFeatureProvider mSupport; 166 SystemInformationSpan(DialogFragment parent)167 public SystemInformationSpan(DialogFragment parent) { 168 // sets the url to empty string so we can prevent the NoUnderlineUrlSpan from stripping 169 // this one 170 super(""); 171 mSupport = FeatureFactory.getFactory(parent.getContext()) 172 .getSupportFeatureProvider(parent.getContext()); 173 mDialog = parent; 174 } 175 176 @Override onClick(View widget)177 public void onClick(View widget) { 178 Activity activity = mDialog.getActivity(); 179 if (mSupport != null && activity != null) { 180 // launch the system info fragment 181 mSupport.launchSystemInfoFragment(mDialog.getArguments(), 182 activity.getFragmentManager()); 183 184 // dismiss this fragment 185 mDialog.dismiss(); 186 } 187 } 188 189 @Override updateDrawState(TextPaint ds)190 public void updateDrawState(TextPaint ds) { 191 super.updateDrawState(ds); 192 // remove underline 193 ds.setUnderlineText(false); 194 } 195 196 /** 197 * This method takes a string and turns it into a url span that will launch a 198 * SupportSystemInformationDialogFragment 199 * @param msg The text to turn into a link 200 * @param parent The dialog the text is in 201 * @return A CharSequence containing the original text content as a url 202 */ linkify(Spannable msg, DialogFragment parent)203 public static CharSequence linkify(Spannable msg, DialogFragment parent) { 204 Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class); 205 for (Annotation annotation : spans) { 206 int start = msg.getSpanStart(annotation); 207 int end = msg.getSpanEnd(annotation); 208 if (ANNOTATION_URL.equals(annotation.getValue())) { 209 SystemInformationSpan link = new SystemInformationSpan(parent); 210 msg.removeSpan(annotation); 211 msg.setSpan(link, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 212 } 213 } 214 return msg; 215 } 216 217 @VisibleForTesting setSupportProvider(SupportFeatureProvider prov)218 public void setSupportProvider(SupportFeatureProvider prov) { 219 mSupport = prov; 220 } 221 } 222 } 223