Surface third-party licenses
Change-Id: If6958dc39e3aa305108b40f84c7383fe4ae1a546
Fixes: 110150995
Fixes: 112270784
Test: In head unit
diff --git a/src/com/android/car/settings/common/ListItemSettingsFragment.java b/src/com/android/car/settings/common/ListItemSettingsFragment.java
index 01867af..3c77495 100644
--- a/src/com/android/car/settings/common/ListItemSettingsFragment.java
+++ b/src/com/android/car/settings/common/ListItemSettingsFragment.java
@@ -16,11 +16,15 @@
package com.android.car.settings.common;
+import android.content.Context;
import android.os.Bundle;
+import android.view.View;
+import androidx.annotation.StringRes;
import androidx.car.widget.ListItemAdapter;
import androidx.car.widget.ListItemProvider;
import androidx.car.widget.PagedListView;
+import androidx.car.widget.TextListItem;
import com.android.car.settings.R;
import com.android.car.settings.suggestions.SuggestionListItem;
@@ -73,4 +77,14 @@
* Gets ListItemProvider that should provide items to show up in the list.
*/
public abstract ListItemProvider getItemProvider();
+
+ protected TextListItem createSimpleListItem(@StringRes int titleResId,
+ View.OnClickListener onClickListener) {
+ Context context = requireContext();
+ TextListItem item = new TextListItem(context);
+ item.setTitle(context.getString(titleResId));
+ item.setSupplementalIcon(R.drawable.ic_chevron_right, /* showDivider= */ false);
+ item.setOnClickListener(onClickListener);
+ return item;
+ }
}
diff --git a/src/com/android/car/settings/system/LegalInformationFragment.java b/src/com/android/car/settings/system/LegalInformationFragment.java
new file mode 100644
index 0000000..f6150c7
--- /dev/null
+++ b/src/com/android/car/settings/system/LegalInformationFragment.java
@@ -0,0 +1,79 @@
+/*
+ * 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.car.settings.system;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemProvider;
+import androidx.car.widget.TextListItem;
+
+import com.android.car.settings.R;
+import com.android.car.settings.common.ListItemSettingsFragment;
+
+import java.util.ArrayList;
+
+/**
+ * Fragment showing legal information.
+ */
+public class LegalInformationFragment extends ListItemSettingsFragment {
+ private static final String ACTION_WEBVIEW_LICENSE = "android.settings.WEBVIEW_LICENSE";
+
+ /**
+ * Factory method for creating the fragment.
+ */
+ public static LegalInformationFragment newInstance() {
+ LegalInformationFragment fragment = new LegalInformationFragment();
+ Bundle bundle = ListItemSettingsFragment.getBundle();
+ bundle.putInt(EXTRA_TITLE_ID, R.string.legal_information);
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ @Override
+ public ListItemProvider getItemProvider() {
+ return new ListItemProvider.ListProvider(getListItems());
+ }
+
+ private ArrayList<ListItem> getListItems() {
+ ArrayList<ListItem> listItems = new ArrayList<>();
+
+ listItems.add(createSystemWebviewLicensesListItem());
+ listItems.add(createThirdPartyLicensesListItem());
+
+ return listItems;
+ }
+
+ private TextListItem createSystemWebviewLicensesListItem() {
+ Context context = requireContext();
+ return createSimpleListItem(R.string.webview_license_title, v -> {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_WEBVIEW_LICENSE);
+ context.startActivity(intent);
+ });
+ }
+
+ private TextListItem createThirdPartyLicensesListItem() {
+ Context context = requireContext();
+ return createSimpleListItem(R.string.settings_license_activity_title, v -> {
+ Intent intent = new Intent(context, ThirdPartyLicensesActivity.class);
+ context.startActivity(intent);
+ });
+ }
+}
diff --git a/src/com/android/car/settings/system/LicenseHtmlGeneratorFromXml.java b/src/com/android/car/settings/system/LicenseHtmlGeneratorFromXml.java
new file mode 100644
index 0000000..8002ac1
--- /dev/null
+++ b/src/com/android/car/settings/system/LicenseHtmlGeneratorFromXml.java
@@ -0,0 +1,288 @@
+/*
+ * 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.car.settings.system;
+
+import android.text.TextUtils;
+import android.util.Xml;
+
+import com.android.car.settings.common.Logger;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * The utility class that generate a license html file from xml files.
+ * All the HTML snippets and logic are copied from build/make/tools/generate-notice-files.py.
+ */
+class LicenseHtmlGeneratorFromXml {
+ private static final Logger LOG = new Logger(LicenseHtmlGeneratorFromXml.class);
+
+ private static final String TAG_ROOT = "licenses";
+ private static final String TAG_FILE_NAME = "file-name";
+ private static final String TAG_FILE_CONTENT = "file-content";
+ private static final String ATTR_CONTENT_ID = "contentId";
+ private static final String HTML_HEAD_STRING =
+ "<html><head>\n"
+ + "<style type=\"text/css\">\n"
+ + "body { padding: 0; font-family: sans-serif; }\n"
+ + ".same-license { background-color: #eeeeee;\n"
+ + " border-top: 20px solid white;\n"
+ + " padding: 10px; }\n"
+ + ".label { font-weight: bold; }\n"
+ + ".file-list { margin-left: 1em; color: blue; }\n"
+ + "</style>\n"
+ + "</head>"
+ + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">"
+ + "\n"
+ + "<div class=\"toc\">\n"
+ + "<ul>";
+
+ private static final String HTML_MIDDLE_STRING =
+ "</ul>\n"
+ + "</div><!-- table of contents -->\n"
+ + "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">";
+
+ private static final String HTML_REAR_STRING =
+ "</table></body></html>";
+
+ private final List<File> mXmlFiles;
+
+ /*
+ * A map from a file name to a content id (MD5 sum of file content) for its license.
+ * For example, "/system/priv-app/TeleService/TeleService.apk" maps to
+ * "9645f39e9db895a4aa6e02cb57294595". Here "9645f39e9db895a4aa6e02cb57294595" is a MD5 sum
+ * of the content of packages/services/Telephony/MODULE_LICENSE_APACHE2.
+ */
+ private final Map<String, String> mFileNameToContentIdMap = new HashMap();
+
+ /*
+ * A map from a content id (MD5 sum of file content) to a license file content.
+ * For example, "9645f39e9db895a4aa6e02cb57294595" maps to the content string of
+ * packages/services/Telephony/MODULE_LICENSE_APACHE2. Here "9645f39e9db895a4aa6e02cb57294595"
+ * is a MD5 sum of the file content.
+ */
+ private final Map<String, String> mContentIdToFileContentMap = new HashMap();
+
+ static class ContentIdAndFileNames {
+ final String mContentId;
+ final List<String> mFileNameList = new ArrayList();
+
+ ContentIdAndFileNames(String contentId) {
+ mContentId = contentId;
+ }
+ }
+
+ private LicenseHtmlGeneratorFromXml(List<File> xmlFiles) {
+ mXmlFiles = xmlFiles;
+ }
+
+ public static boolean generateHtml(List<File> xmlFiles, File outputFile) {
+ LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles);
+ return genertor.generateHtml(outputFile);
+ }
+
+ private boolean generateHtml(File outputFile) {
+ for (File xmlFile : mXmlFiles) {
+ parse(xmlFile);
+ }
+
+ if (mFileNameToContentIdMap.isEmpty() || mContentIdToFileContentMap.isEmpty()) {
+ return false;
+ }
+
+ PrintWriter writer = null;
+ try {
+ writer = new PrintWriter(outputFile);
+
+ generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer);
+
+ writer.flush();
+ writer.close();
+ return true;
+ } catch (FileNotFoundException | SecurityException e) {
+ LOG.e("Failed to generate " + outputFile, e);
+
+ if (writer != null) {
+ writer.close();
+ }
+ return false;
+ }
+ }
+
+ private void parse(File xmlFile) {
+ if (xmlFile == null || !xmlFile.exists() || xmlFile.length() == 0) {
+ return;
+ }
+
+ InputStreamReader in = null;
+ try {
+ if (xmlFile.getName().endsWith(".gz")) {
+ in = new InputStreamReader(new GZIPInputStream(new FileInputStream(xmlFile)));
+ } else {
+ in = new FileReader(xmlFile);
+ }
+
+ parse(in, mFileNameToContentIdMap, mContentIdToFileContentMap);
+
+ in.close();
+ } catch (XmlPullParserException | IOException e) {
+ LOG.e("Failed to parse " + xmlFile, e);
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException ie) {
+ LOG.w("Failed to close " + xmlFile);
+ }
+ }
+ }
+ }
+
+ /*
+ * Parses an input stream and fills a map from a file name to a content id for its license
+ * and a map from a content id to a license file content.
+ *
+ * Following xml format is expected from the input stream.
+ *
+ * <licenses>
+ * <file-name contentId="content_id_of_license1">file1</file-name>
+ * <file-name contentId="content_id_of_license2">file2</file-name>
+ * ...
+ * <file-content contentId="content_id_of_license1">license1 file contents</file-content>
+ * <file-content contentId="content_id_of_license2">license2 file contents</file-content>
+ * ...
+ * </licenses>
+ */
+ private static void parse(InputStreamReader in, Map<String, String> outFileNameToContentIdMap,
+ Map<String, String> outContentIdToFileContentMap)
+ throws XmlPullParserException, IOException {
+ Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in);
+ parser.nextTag();
+
+ parser.require(XmlPullParser.START_TAG, "", TAG_ROOT);
+
+ int state = parser.getEventType();
+ while (state != XmlPullParser.END_DOCUMENT) {
+ if (state == XmlPullParser.START_TAG) {
+ if (TAG_FILE_NAME.equals(parser.getName())) {
+ String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
+ if (!TextUtils.isEmpty(contentId)) {
+ String fileName = readText(parser).trim();
+ if (!TextUtils.isEmpty(fileName)) {
+ fileNameToContentIdMap.put(fileName, contentId);
+ }
+ }
+ } else if (TAG_FILE_CONTENT.equals(parser.getName())) {
+ String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
+ if (!TextUtils.isEmpty(contentId)
+ && !outContentIdToFileContentMap.containsKey(contentId)
+ && !contentIdToFileContentMap.containsKey(contentId)) {
+ String fileContent = readText(parser);
+ if (!TextUtils.isEmpty(fileContent)) {
+ contentIdToFileContentMap.put(contentId, fileContent);
+ }
+ }
+ }
+ }
+
+ state = parser.next();
+ }
+ outFileNameToContentIdMap.putAll(fileNameToContentIdMap);
+ outContentIdToFileContentMap.putAll(contentIdToFileContentMap);
+ }
+
+ private static String readText(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ StringBuffer result = new StringBuffer();
+ int state = parser.next();
+ while (state == XmlPullParser.TEXT) {
+ result.append(parser.getText());
+ state = parser.next();
+ }
+ return result.toString();
+ }
+
+ private static void generateHtml(Map<String, String> fileNameToContentIdMap,
+ Map<String, String> contentIdToFileContentMap, PrintWriter writer) {
+ List<String> fileNameList = new ArrayList();
+ fileNameList.addAll(fileNameToContentIdMap.keySet());
+ Collections.sort(fileNameList);
+
+ writer.println(HTML_HEAD_STRING);
+
+ int count = 0;
+ Map<String, Integer> contentIdToOrderMap = new HashMap();
+ List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
+
+ // Prints all the file list with a link to its license file content.
+ for (String fileName : fileNameList) {
+ String contentId = fileNameToContentIdMap.get(fileName);
+ // Assigns an id to a newly referred license file content.
+ if (!contentIdToOrderMap.containsKey(contentId)) {
+ contentIdToOrderMap.put(contentId, count);
+
+ // An index in contentIdAndFileNamesList is the order of each element.
+ contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+ count++;
+ }
+
+ int id = contentIdToOrderMap.get(contentId);
+ contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
+ writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+ }
+
+ writer.println(HTML_MIDDLE_STRING);
+
+ count = 0;
+ // Prints all contents of the license files in order of id.
+ for (ContentIdAndFileNames contentIdAndFileNames : contentIdAndFileNamesList) {
+ writer.format("<tr id=\"id%d\"><td class=\"same-license\">\n", count);
+ writer.println("<div class=\"label\">Notices for file(s):</div>");
+ writer.println("<div class=\"file-list\">");
+ for (String fileName : contentIdAndFileNames.mFileNameList) {
+ writer.format("%s <br/>\n", fileName);
+ }
+ writer.println("</div><!-- file-list -->");
+ writer.println("<pre class=\"license-text\">");
+ writer.println(contentIdToFileContentMap.get(
+ contentIdAndFileNames.mContentId));
+ writer.println("</pre><!-- license-text -->");
+ writer.println("</td></tr><!-- same-license -->");
+
+ count++;
+ }
+
+ writer.println(HTML_REAR_STRING);
+ }
+}
diff --git a/src/com/android/car/settings/system/LicenseHtmlLoader.java b/src/com/android/car/settings/system/LicenseHtmlLoader.java
new file mode 100644
index 0000000..8bdf9f7
--- /dev/null
+++ b/src/com/android/car/settings/system/LicenseHtmlLoader.java
@@ -0,0 +1,103 @@
+/*
+ * 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.car.settings.system;
+
+import android.content.Context;
+
+import com.android.car.settings.common.Logger;
+import com.android.car.settingslib.loader.AsyncLoader;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * LicenseHtmlLoader is a loader which loads a license html file from default license xml files.
+ */
+public class LicenseHtmlLoader extends AsyncLoader<File> {
+ private static final Logger LOG = new Logger(LicenseHtmlLoader.class);
+
+ private static final String[] DEFAULT_LICENSE_XML_PATHS = {
+ "/system/etc/NOTICE.xml.gz",
+ "/vendor/etc/NOTICE.xml.gz",
+ "/odm/etc/NOTICE.xml.gz",
+ "/oem/etc/NOTICE.xml.gz"};
+ private static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
+
+ private final Context mContext;
+
+ public LicenseHtmlLoader(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public File loadInBackground() {
+ return generateHtmlFromDefaultXmlFiles();
+ }
+
+ private File generateHtmlFromDefaultXmlFiles() {
+ final List<File> xmlFiles = getVaildXmlFiles();
+ if (xmlFiles.isEmpty()) {
+ LOG.e("No notice file exists.");
+ return null;
+ }
+
+ File cachedHtmlFile = getCachedHtmlFile();
+ if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
+ || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+ return cachedHtmlFile;
+ }
+
+ return null;
+ }
+
+ private List<File> getVaildXmlFiles() {
+ final List<File> xmlFiles = new ArrayList();
+ for (final String xmlPath : DEFAULT_LICENSE_XML_PATHS) {
+ File file = new File(xmlPath);
+ if (file.exists() && file.length() != 0) {
+ xmlFiles.add(file);
+ }
+ }
+ return xmlFiles;
+ }
+
+ private File getCachedHtmlFile() {
+ return new File(mContext.getCacheDir(), NOTICE_HTML_FILE_NAME);
+ }
+
+ private boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
+ boolean outdated = true;
+ if (cachedHtmlFile.exists() && cachedHtmlFile.length() != 0) {
+ outdated = false;
+ for (File file : xmlFiles) {
+ if (cachedHtmlFile.lastModified() < file.lastModified()) {
+ outdated = true;
+ break;
+ }
+ }
+ }
+ return outdated;
+ }
+
+ private boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
+ return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
+ }
+}
+
+
diff --git a/src/com/android/car/settings/system/SystemSettingsFragment.java b/src/com/android/car/settings/system/SystemSettingsFragment.java
index 161e907..a378c9d 100644
--- a/src/com/android/car/settings/system/SystemSettingsFragment.java
+++ b/src/com/android/car/settings/system/SystemSettingsFragment.java
@@ -46,9 +46,6 @@
private static final String ACTION_SYSTEM_UPDATE_SETTINGS =
"android.settings.SYSTEM_UPDATE_SETTINGS";
- private static final String ACTION_SETTING_VIEW_LICENSE =
- "android.settings.WEBVIEW_LICENSE";
-
private ListItemProvider mItemProvider;
public static SystemSettingsFragment getInstance() {
@@ -135,11 +132,9 @@
legalInfoItem.setPrimaryActionIcon(
R.drawable.ic_settings_about, /* useLargeIcon= */ false);
legalInfoItem.setSupplementalIcon(R.drawable.ic_chevron_right, /* showDivider= */ false);
- legalInfoItem.setOnClickListener(v -> {
- Intent intent = new Intent();
- intent.setAction(ACTION_SETTING_VIEW_LICENSE);
- context.startActivity(intent);
- });
+ legalInfoItem.setOnClickListener(v ->
+ getFragmentController().launchFragment(LegalInformationFragment.newInstance())
+ );
return legalInfoItem;
}
diff --git a/src/com/android/car/settings/system/ThirdPartyLicensesActivity.java b/src/com/android/car/settings/system/ThirdPartyLicensesActivity.java
new file mode 100644
index 0000000..571e7b7
--- /dev/null
+++ b/src/com/android/car/settings/system/ThirdPartyLicensesActivity.java
@@ -0,0 +1,147 @@
+/*
+ * 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.car.settings.system;
+
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import androidx.core.content.FileProvider;
+import androidx.fragment.app.FragmentActivity;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+
+import com.android.car.settings.R;
+import com.android.car.settings.common.Logger;
+
+import java.io.File;
+
+/**
+ * The activity that displays third-party licenses.
+ */
+public class ThirdPartyLicensesActivity extends FragmentActivity implements
+ LoaderManager.LoaderCallbacks<File> {
+ private static final Logger LOG = new Logger(ThirdPartyLicensesActivity.class);
+ private static final int LOADER_ID_LICENSE_HTML_LOADER = 0;
+ private static final String DEFAULT_LICENSE_PATH = "/system/etc/NOTICE.html.gz";
+ private static final String PROPERTY_LICENSE_PATH = "ro.config.license_path";
+ private static final String FILE_PROVIDER_AUTHORITY = "com.android.settings.files";
+ private static final String HTML_VIEWER_PACKAGE = "com.android.htmlviewer";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final String licenseHtmlPath =
+ SystemProperties.get(PROPERTY_LICENSE_PATH, DEFAULT_LICENSE_PATH);
+ if (isFilePathValid(licenseHtmlPath)) {
+ showSelectedFile(licenseHtmlPath);
+ } else {
+ showHtmlFromDefaultXmlFiles();
+ }
+ }
+
+ @Override
+ public Loader<File> onCreateLoader(int id, Bundle args) {
+ return new LicenseHtmlLoader(this);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<File> loader, File generatedHtmlFile) {
+ showGeneratedHtmlFile(generatedHtmlFile);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<File> loader) {
+ }
+
+ private void showSelectedFile(final String path) {
+ if (TextUtils.isEmpty(path)) {
+ LOG.e("The system property for the license file is empty");
+ showErrorAndFinish();
+ return;
+ }
+
+ final File file = new File(path);
+ if (!isFileValid(file)) {
+ LOG.e("License file " + path + " does not exist");
+ showErrorAndFinish();
+ return;
+ }
+ showHtmlFromUri(Uri.fromFile(file));
+ }
+
+ private void showErrorAndFinish() {
+ Toast.makeText(this, R.string.settings_license_activity_unavailable, Toast.LENGTH_LONG)
+ .show();
+ finish();
+ }
+
+ private void showHtmlFromUri(Uri uri) {
+ // Kick off external viewer due to WebView security restrictions; we
+ // carefully point it at HTMLViewer, since it offers to decompress
+ // before viewing.
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(uri, "text/html");
+ intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.settings_license_activity_title));
+ if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setPackage(HTML_VIEWER_PACKAGE);
+
+ try {
+ startActivity(intent);
+ finish();
+ } catch (ActivityNotFoundException e) {
+ LOG.e("Failed to find viewer", e);
+ showErrorAndFinish();
+ }
+ }
+
+ private void showHtmlFromDefaultXmlFiles() {
+ LoaderManager.getInstance(this).initLoader(LOADER_ID_LICENSE_HTML_LOADER, Bundle.EMPTY,
+ this);
+ }
+
+ private void showGeneratedHtmlFile(File generatedHtmlFile) {
+ if (generatedHtmlFile != null) {
+ LOG.i("File size: " + generatedHtmlFile.length());
+ showHtmlFromUri(getUriFromGeneratedHtmlFile(generatedHtmlFile));
+ } else {
+ LOG.e("Failed to generate.");
+ showErrorAndFinish();
+ }
+ }
+
+ private Uri getUriFromGeneratedHtmlFile(File generatedHtmlFile) {
+ return FileProvider.getUriForFile(this, FILE_PROVIDER_AUTHORITY, generatedHtmlFile);
+ }
+
+ private boolean isFilePathValid(final String path) {
+ return !TextUtils.isEmpty(path) && isFileValid(new File(path));
+ }
+
+ private boolean isFileValid(final File file) {
+ return file.exists() && file.length() != 0;
+ }
+}