| /* |
| * 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.content.ActivityNotFoundException; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.text.Annotation; |
| import android.text.SpannableString; |
| import android.text.SpannableStringBuilder; |
| import android.text.TextPaint; |
| import android.text.style.URLSpan; |
| import android.util.Log; |
| import android.view.View; |
| |
| import java.util.Arrays; |
| import java.util.Comparator; |
| |
| /** |
| * 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(true); |
| } |
| |
| 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.mAnnotation.equals(key)) { |
| link = new AnnotationSpan(linkInfo.mListener); |
| break; |
| } |
| } |
| if (link != null) { |
| builder.setSpan(link, start, end, msg.getSpanFlags(link)); |
| } |
| } |
| return builder; |
| } |
| |
| /** |
| * get the text part without having text for link part |
| */ |
| public static CharSequence textWithoutLink(CharSequence encodedText) { |
| SpannableString msg = new SpannableString(encodedText); |
| Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class); |
| if (spans == null) { |
| return encodedText; |
| } |
| Arrays.sort(spans, Comparator.comparingInt(span -> -msg.getSpanStart(span))); |
| StringBuilder msgWithoutLink = new StringBuilder(msg.toString()); |
| for (Annotation span : spans) { |
| msgWithoutLink.delete(msg.getSpanStart(span), msg.getSpanEnd(span)); |
| } |
| return msgWithoutLink.toString(); |
| } |
| |
| /** |
| * Data class to store the annotation and the click action |
| */ |
| public static class LinkInfo { |
| private static final String TAG = "AnnotationSpan.LinkInfo"; |
| public static final String DEFAULT_ANNOTATION = "link"; |
| private final String mAnnotation; |
| private final Boolean mActionable; |
| private final View.OnClickListener mListener; |
| |
| public LinkInfo(String annotation, View.OnClickListener listener) { |
| mAnnotation = annotation; |
| mListener = listener; |
| mActionable = true; // assume actionable |
| } |
| |
| public LinkInfo(Context context, String annotation, Intent intent) { |
| mAnnotation = annotation; |
| if (intent != null) { |
| mActionable = context.getPackageManager() |
| .resolveActivity(intent, 0 /* flags */) != null; |
| } else { |
| mActionable = false; |
| } |
| if (!mActionable) { |
| mListener = null; |
| } else { |
| mListener = view -> { |
| try { |
| view.startActivityForResult(intent, 0); |
| } catch (ActivityNotFoundException e) { |
| Log.w(TAG, "Activity was not found for intent, " + intent); |
| } |
| }; |
| } |
| } |
| |
| public boolean isActionable() { |
| return mActionable; |
| } |
| } |
| } |