Browse Source

Add custom Activity to show libraries licenses.

As Google's library for showing licences isn't open-source, this commit
reimplements its main Activity. This is in prevision to having an OSS
flavor of fenix.

We chose to not introduce dependencies to third-party libraries
such as AboutLibraries for now, and we'll stick to using Google's gradle
plugin for the dependencies extraction.

Fixes #7584

See also #162
master
Gilbert Gilb's 2 years ago
committed by liuche
parent
commit
2743c37b40
  1. 1
      app/build.gradle
  2. 9
      app/src/main/AndroidManifest.xml
  3. 10
      app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt
  4. 120
      app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesActivity.kt
  5. 25
      app/src/main/res/layout/about_libraries_activity.xml
  6. 45
      app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesActivityTest.kt
  7. 2
      buildSrc/src/main/java/Dependencies.kt

1
app/build.gradle

@ -330,7 +330,6 @@ dependencies {
implementation Deps.androidx_coordinatorlayout
implementation Deps.sentry
implementation Deps.osslicenses_library
implementation Deps.leanplum_core
implementation Deps.leanplum_fcm

9
app/src/main/AndroidManifest.xml

@ -228,13 +228,8 @@
<activity android:name=".settings.account.AuthIntentReceiverActivity"
android:exported="false" />
<activity android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:exported="false"
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"/>
<activity android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
android:exported="false"
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"/>
<activity android:name=".settings.about.AboutLibrariesActivity"
android:exported="false" />
<service android:name=".media.MediaService"
android:exported="false" />

10
app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt

@ -13,7 +13,6 @@ import android.view.ViewGroup
import androidx.core.content.pm.PackageInfoCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import kotlinx.android.synthetic.main.fragment_about.*
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.BuildConfig
@ -168,13 +167,8 @@ class AboutFragment : Fragment(), AboutPageListener {
}
private fun openLibrariesPage() {
startActivity(Intent(context, OssLicensesMenuActivity::class.java))
OssLicensesMenuActivity.setActivityTitle(
getString(
R.string.open_source_licenses_title,
appName
)
)
val intent = Intent(requireContext(), AboutLibrariesActivity::class.java)
startActivity(intent)
}
override fun onAboutItemClicked(item: AboutItem) {

120
app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesActivity.kt

@ -0,0 +1,120 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.settings.about
import android.graphics.Typeface
import android.os.Bundle
import android.text.util.Linkify
import android.widget.ArrayAdapter
import android.widget.ListView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import org.mozilla.fenix.R
import java.nio.charset.Charset
import java.util.Locale
/**
* Displays the licenses of all the libraries used by Fenix.
*
* This is a re-implementation of play-services-oss-licenses library.
* We can't use the official implementation in the OSS flavor of Fenix
* because it is proprietary and closed-source.
*
* There are popular FLOSS alternatives to Google's plugin and library
* such as AboutLibraries (https://github.com/mikepenz/AboutLibraries)
* but we considered the risk of introducing such third-party dependency
* to Fenix too high. Therefore, we use Google's gradle plugin to
* extract the dependencies and their licenses, and this activity
* to show the extracted licenses to the end-user.
*/
class AboutLibrariesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val appName = getString(R.string.app_name)
title = getString(R.string.open_source_licenses_title, appName)
setContentView(R.layout.about_libraries_activity)
setSupportActionBar(findViewById(R.id.toolbar))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
setupLibrariesListView()
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
private fun setupLibrariesListView() {
val libraries = parseLibraries()
val listView = findViewById<ListView>(R.id.about_libraries_listview)
listView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, libraries)
listView.setOnItemClickListener { _, _, position, _ ->
showLicenseDialog(libraries[position])
}
}
private fun parseLibraries(): List<LibraryItem> {
/*
The gradle plugin "oss-licenses-plugin" creates two "raw" resources:
- third_party_licenses which is the binary concatenation of all the licenses text for
all the libraries. License texts can either be an URL to a license file or just the
raw text of the license.
- third_party_licenses_metadata which contains one dependency per line formatted in
the following way: "[start_offset]:[length] [name]"
[start_offset] : first byte in third_party_licenses that contains the license
text for this library.
[length] : length of the license text for this library in
third_party_licenses.
[name] : either the name of the library, or its artifact name.
See https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin
*/
val licensesData = resources
.openRawResource(R.raw.third_party_licenses)
.readBytes()
val licensesMetadataReader = resources
.openRawResource(R.raw.third_party_license_metadata)
.bufferedReader()
return licensesMetadataReader.use { reader -> reader.readLines() }.map { line ->
val (section, name) = line.split(" ", limit = 2)
val (startOffset, length) = section.split(":", limit = 2).map(String::toInt)
val licenseData = licensesData.sliceArray(startOffset until startOffset + length)
val licenseText = licenseData.toString(Charset.forName("UTF-8"))
LibraryItem(name, licenseText)
}.sortedBy { item -> item.name.toLowerCase(Locale.ROOT) }
}
private fun showLicenseDialog(libraryItem: LibraryItem) {
val dialog = AlertDialog.Builder(this)
.setTitle(libraryItem.name)
.setMessage(libraryItem.license)
.create()
dialog.show()
val textView = dialog.findViewById<TextView>(android.R.id.message)!!
Linkify.addLinks(textView, Linkify.ALL)
textView.linksClickable = true
textView.textSize = LICENSE_TEXT_SIZE
textView.typeface = Typeface.MONOSPACE
}
companion object {
private const val LICENSE_TEXT_SIZE = 10F
}
}
private class LibraryItem(val name: String, val license: String) {
override fun toString(): String {
return name
}
}

25
app/src/main/res/layout/about_libraries_activity.xml

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/about_libraries"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:context="org.mozilla.fenix.settings.about.AboutLibrariesActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar" />
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/about_libraries_listview" />
</RelativeLayout>

45
app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesActivityTest.kt

@ -0,0 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.settings.about
import android.widget.ListView
import android.widget.TextView
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.robolectric.Robolectric
import org.robolectric.Shadows.shadowOf
import org.robolectric.shadows.ShadowAlertDialog
@RunWith(FenixRobolectricTestRunner::class)
class AboutLibrariesActivityTest {
@Test
fun `activity should display licenses`() {
val activity = Robolectric.buildActivity(AboutLibrariesActivity::class.java).create().get()
val listView = activity.findViewById<ListView>(R.id.about_libraries_listview)
assertTrue(0 < listView.count)
}
@Test
fun `item click should open license dialog`() {
val activity = Robolectric.buildActivity(AboutLibrariesActivity::class.java).create().get()
val listView = activity.findViewById<ListView>(R.id.about_libraries_listview)
val listViewShadow = shadowOf(listView)
listViewShadow.clickFirstItemContainingText("org.mozilla.geckoview:geckoview")
val alertDialogShadow = ShadowAlertDialog.getLatestDialog()
assertTrue(alertDialogShadow.isShowing)
val alertDialogText = alertDialogShadow
.findViewById<TextView>(android.R.id.message)
.text
.toString()
assertTrue(alertDialogText.contains("MPL"))
}
}

2
buildSrc/src/main/java/Dependencies.kt

@ -10,7 +10,6 @@ object Versions {
const val leakcanary = "2.4"
const val leanplum = "5.4.0"
const val osslicenses_plugin = "0.9.5"
const val osslicenses_library = "17.0.0"
const val detekt = "1.9.1"
const val androidx_appcompat = "1.2.0-rc01"
@ -59,7 +58,6 @@ object Deps {
const val allopen = "org.jetbrains.kotlin:kotlin-allopen:${Versions.kotlin}"
const val osslicenses_plugin = "com.google.android.gms:oss-licenses-plugin:${Versions.osslicenses_plugin}"
const val osslicenses_library = "com.google.android.gms:play-services-oss-licenses:${Versions.osslicenses_library}"
const val mozilla_concept_engine = "org.mozilla.components:concept-engine:${Versions.mozilla_android_components}"
const val mozilla_concept_menu = "org.mozilla.components:concept-menu:${Versions.mozilla_android_components}"

Loading…
Cancel
Save