1c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak/*
2c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * Copyright 2017, The Android Open Source Project
3c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak *
4c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * Licensed under the Apache License, Version 2.0 (the "License");
5c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * you may not use this file except in compliance with the License.
6c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * You may obtain a copy of the License at
7c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak *
8c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak *      http://www.apache.org/licenses/LICENSE-2.0
9c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak *
10c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * Unless required by applicable law or agreed to in writing, software
11c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * distributed under the License is distributed on an "AS IS" BASIS,
12c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * See the License for the specific language governing permissions and
14c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * limitations under the License.
15c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak */
16c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakpackage com.android.managedprovisioning.common;
17c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
18c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
19c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
20c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport static com.android.internal.util.Preconditions.checkNotNull;
21c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport static com.android.internal.util.Preconditions.checkStringNotEmpty;
22c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
23c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport android.content.Intent;
24c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport android.text.Html;
25c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport android.text.SpannableStringBuilder;
26c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport android.text.Spanned;
27c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport android.text.style.ClickableSpan;
28c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport android.text.style.URLSpan;
29c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
30c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakimport com.android.managedprovisioning.preprovisioning.WebActivity;
31c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
32c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak/**
33c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak * Parses HTML text using {@link Html} and sets URL links to be handled by {@link WebActivity}
34c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak */
35c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzakpublic class HtmlToSpannedParser {
36c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    private static final int HTML_MODE = Html.FROM_HTML_MODE_COMPACT;
37c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
38c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    private final ClickableSpanFactory mClickableSpanFactory;
39c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    private final UrlIntentFactory mUrlIntentFactory;
40c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
41c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    /**
42c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak     * Default constructor
43c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak     *
44c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak     * @param clickableSpanFactory Factory of {@link ClickableSpan} objects for urls
45c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak     * @param urlIntentFactory Factory of {@link Intent} objects for handling urls
46c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak     */
47c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    public HtmlToSpannedParser(ClickableSpanFactory clickableSpanFactory,
48c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak            UrlIntentFactory urlIntentFactory) {
49c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        mClickableSpanFactory = checkNotNull(clickableSpanFactory);
50c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        mUrlIntentFactory = checkNotNull(urlIntentFactory);
51c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    }
52c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
53c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    /**
54c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak     * See {@link Html#fromHtml(String, int)} for caveats regarding limited HTML support
55c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak     */
56c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    public Spanned parseHtml(String htmlContent) {
57c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        Spanned spanned = Html.fromHtml(checkStringNotEmpty(htmlContent), HTML_MODE);
58c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        if (spanned == null) {
59c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak            return null;
60c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        }
61c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
62c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        // Make html <a> tags open WebActivity
63c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        SpannableStringBuilder result = new SpannableStringBuilder(spanned);
64c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
65c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        URLSpan[] urlSpans = result.getSpans(0, result.length(), URLSpan.class);
66c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        for (URLSpan urlSpan : urlSpans) {
67c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak            Intent intent = mUrlIntentFactory.create(urlSpan.getURL());
68c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak            if (intent != null) {
69c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak                int spanStart = result.getSpanStart(urlSpan);
70c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak                int spanEnd = result.getSpanEnd(urlSpan);
71c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak                result.setSpan(mClickableSpanFactory.create(intent), spanStart, spanEnd,
72c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak                        SPAN_EXCLUSIVE_EXCLUSIVE);
73c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak                result.removeSpan(urlSpan);
74c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak            }
75c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        }
76c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
77c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        return result;
78c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    }
79c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak
80c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    /**
81c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak     * Allows to specify an intent to handle URLs
82c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak     */
83c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    public interface UrlIntentFactory {
84c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        /**
85c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak         * Creates an {@link Intent} based on a passed in {@link String} url
86c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak         */
87c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak        Intent create(String url);
88c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak    }
89c5565bff08cbfb102f8f0cd93edfa0d3c24d9b98Jakub Gielzak}