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}