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