Add a "view licenses" button in settings dialog to view open source licenses.

- Followed the instructions here:
  https://developers.google.com/android/guides/opensource
- The plugin will automatically parse libs.versions.toml and store the licenses
  related data in the app bundle as resources.
- Use the activities in play-services-oss-licenses library to view the licenses.

PiperOrigin-RevId: 776826500
This commit is contained in:
Google AI Edge Gallery 2025-06-27 20:55:29 -07:00 committed by Copybara-Service
parent 323124a628
commit a58751eba4
7 changed files with 100 additions and 2 deletions

View file

@ -21,6 +21,7 @@ plugins {
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.protobuf)
alias(libs.plugins.hilt.application)
alias(libs.plugins.oss.licenses)
kotlin("kapt")
}
@ -95,6 +96,7 @@ dependencies {
implementation(libs.protobuf.javalite)
implementation(libs.hilt.android)
implementation(libs.hilt.navigation.compose)
implementation(libs.play.services.oss.licenses)
kapt(libs.hilt.android.compiler)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)

View file

@ -47,12 +47,18 @@
android:supportsRtl="true"
android:theme="@style/Theme.Gallery"
tools:targetApi="31">
<!--
android:configChanges="uiMode" tells the system don't destroy and
recreate the activity when UI mode changes (e.g. setting dark mode).
Instead, just recompose the view.
-->
<activity
android:name="com.google.ai.edge.gallery.MainActivity"
android:exported="true"
android:theme="@style/Theme.Gallery.SplashScreen"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"
android:configChanges="uiMode"
tools:ignore="DiscouragedApi,LockedOrientationActivity">
<!-- This is for putting the app into launcher -->
<intent-filter>
@ -71,6 +77,16 @@
</intent-filter>
</activity>
<!-- Set themes for activities that are used for viewing open source licenses -->
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:exported="true"
android:theme="@style/Theme.Gallery.OssLicenses" />
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
android:exported="true"
android:theme="@style/Theme.Gallery.OssLicenses" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"

View file

@ -16,6 +16,11 @@
package com.google.ai.edge.gallery.ui.home
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import android.app.UiModeManager
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -36,6 +41,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.CheckCircle
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@ -55,6 +61,7 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
@ -73,6 +80,7 @@ import kotlin.math.min
private val THEME_OPTIONS = listOf(Theme.THEME_AUTO, Theme.THEME_LIGHT, Theme.THEME_DARK)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsDialog(
curThemeOverride: Theme,
@ -127,6 +135,7 @@ fun SettingsDialog(
modifier = Modifier.verticalScroll(rememberScrollState()).weight(1f, fill = false),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
val context = LocalContext.current
// Theme switcher.
Column(modifier = Modifier.fillMaxWidth()) {
Text(
@ -147,6 +156,23 @@ fun SettingsDialog(
// Save to data store.
modelManagerViewModel.saveThemeOverride(theme)
// Update ui mode.
//
// This is necessary to make other Activities launched from MainActivity to have
// the correct theme.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val uiModeManager =
context.applicationContext.getSystemService(Context.UI_MODE_SERVICE)
as UiModeManager
if (theme == Theme.THEME_AUTO) {
uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_AUTO)
} else if (theme == Theme.THEME_LIGHT) {
uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_NO)
} else {
uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_YES)
}
}
},
checked = theme == selectedTheme,
label = { Text(themeLabel(theme)) },
@ -166,7 +192,7 @@ fun SettingsDialog(
)
// Show the start of the token.
val curHfToken = hfToken
if (curHfToken != null) {
if (curHfToken != null && curHfToken.accessToken.isNotEmpty()) {
Text(
curHfToken.accessToken.substring(0, min(16, curHfToken.accessToken.length)) + "...",
style = MaterialTheme.typography.bodyMedium,
@ -254,6 +280,24 @@ fun SettingsDialog(
}
}
}
// Third party licenses.
Column(modifier = Modifier.fillMaxWidth()) {
Text(
"Third-party libraries",
style = MaterialTheme.typography.titleSmall.copy(fontWeight = FontWeight.Bold),
)
OutlinedButton(
onClick = {
// Create an Intent to launch a license viewer that displays a list of
// third-party library names. Clicking a name will show its license content.
val intent = Intent(context, OssLicensesMenuActivity::class.java)
context.startActivity(intent)
}
) {
Text("View licenses")
}
}
}
// Button row.

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2025 Google LLC
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.
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.Gallery.OssLicenses" parent="Theme.AppCompat">
<item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item>
</style>
</resources>

View file

@ -15,10 +15,13 @@
limitations under the License.
-->
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.Gallery" parent="android:Theme.Material.Light.NoActionBar" />
<style name="Theme.Gallery.SplashScreen" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">#2A2A34</item>
<item name="postSplashScreenTheme">@style/Theme.Gallery</item>
</style>
<style name="Theme.Gallery.OssLicenses" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item>
</style>
</resources>

View file

@ -31,6 +31,8 @@ netOpenidAppauth = "0.11.1"
splashscreen = "1.2.0-beta01"
hilt = "2.56.2"
hiltNavigation = "1.2.0"
ossLicenses = "0.10.6"
playServicesOssLicenses = "17.1.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -73,6 +75,7 @@ hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt"
hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigation" }
hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
play-services-oss-licenses = { module = "com.google.android.gms:play-services-oss-licenses", version.ref = "playServicesOssLicenses"}
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
@ -81,3 +84,4 @@ kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "ko
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "serializationPlugin" }
protobuf = {id = "com.google.protobuf", version.ref = "protobuf"}
hilt-application = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
oss-licenses = {id = "com.google.android.gms.oss-licenses-plugin", version.ref = "ossLicenses"}

View file

@ -26,6 +26,13 @@ pluginManagement {
mavenCentral()
gradlePluginPortal()
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == "com.google.android.gms.oss-licenses-plugin") {
useModule("com.google.android.gms:oss-licenses-plugin:0.10.6")
}
}
}
}
dependencyResolutionManagement {