Snap for 15862157 from 9531c456b9a72b4a5e8a8d77074bff94d7db35b2 to car-apps-aosp-release Change-Id: I6e7a2ba9d6230b10158aaa8eff86449415e866a8
diff --git a/aaos-apps-gradle-project/attestation_manifest.json b/aaos-apps-gradle-project/attestation_manifest.json new file mode 100644 index 0000000..f9144bc --- /dev/null +++ b/aaos-apps-gradle-project/attestation_manifest.json
@@ -0,0 +1 @@ +[{"artifact_path": "DriverUI.apk", "attest_archive_contents": false}]
diff --git a/aaos-apps-gradle-project/copy_gradle_output.sh b/aaos-apps-gradle-project/copy_gradle_output.sh index b9ea07a..7989348 100755 --- a/aaos-apps-gradle-project/copy_gradle_output.sh +++ b/aaos-apps-gradle-project/copy_gradle_output.sh
@@ -65,6 +65,8 @@ check_status $? cp $GRADLE_OUTPUT_DIR/car-messenger-app/outputs/apk/prod/release/car-messenger-app-prod-release.apk $1/CarMessengerApp.apk check_status $? +cp $GRADLE_OUTPUT_DIR/car-messenger-app/outputs/apk/platformAosp/release/car-messenger-app-platformAosp-release.apk $1/CarMessengerApp_aosp_cert.apk +check_status $? cp $GRADLE_OUTPUT_DIR/PaintBooth/outputs/apk/unbundled/release/PaintBooth-unbundled-release.apk $1/PaintBooth.apk check_status $? cp $GRADLE_OUTPUT_DIR/oem-token-shared-lib/outputs/apk/unbundled/release/oem-token-shared-lib-unbundled-release.apk $1/oem-token-shared-lib.apk @@ -81,7 +83,7 @@ check_status $? cp $GRADLE_OUTPUT_DIR/car-bugreport-app/outputs/apk/platformAosp/release/car-bugreport-app-platformAosp-release.apk $1/CarBugReportApp_aosp_cert.apk check_status $? -cp $GRADLE_OUTPUT_DIR/driver-ui/outputs/apk/default/release/driver-ui-default-release-unsigned.apk $1/DriverUI.apk +cp $GRADLE_OUTPUT_DIR/driver-ui/outputs/apk/platformAosp/debug/driver-ui-platformAosp-debug.apk $1/DriverUI.apk check_status $? cp $GRADLE_OUTPUT_DIR/driver-ui/outputs/apk/platformAosp/release/driver-ui-platformAosp-release.apk $1/driver-ui-platform-release.apk check_status $? @@ -107,6 +109,8 @@ check_status $? cp $GRADLE_OUTPUT_DIR/car-sensitiveapplock-app/outputs/apk/platformGoogle/release/car-sensitiveapplock-app-platformGoogle-release.apk $1/SensitiveAppLock.apk check_status $? + cp $GRADLE_OUTPUT_DIR/car-messenger-app/outputs/apk/platformGoogle/release/car-messenger-app-platformGoogle-release.apk $1/CarMessengerApp_google_cert.apk + check_status $? fi # AARs @@ -280,3 +284,8 @@ $JAVA_HOME/bin/jar cvfM $1/jacoco-report-classes-all.jar -C $GRADLE_OUTPUT_DIR/jacoco . check_status $? + +# Copy attestation manifest for DriverUI.apk +cp "$SCRIPTS_DIR/attestation_manifest.json" "$1/attestation_manifest.json" +check_status $? +
diff --git a/aaos-apps-gradle-project/gradle.properties b/aaos-apps-gradle-project/gradle.properties index dbe5436..7722b45 100644 --- a/aaos-apps-gradle-project/gradle.properties +++ b/aaos-apps-gradle-project/gradle.properties
@@ -58,7 +58,7 @@ aaosApps.buildCfg.buildToolsVersion=36.0.0 aaosApps.buildCfg.defaultJdkToolchain=17 aaosApps.buildCfg.ndkVersion=27.0.12077973 -aaosApps.buildCfg.cmakeVersion=3.22.1 +aaosApps.buildCfg.cmakeVersion=4.1.2 # To prevent Jetifier issues with SDV Gateway related jars. android.jetifier.ignorelist=libsdvgatewayclient-java.jar
diff --git a/aaos-apps-gradle-project/gradle/libs.versions.toml b/aaos-apps-gradle-project/gradle/libs.versions.toml index 04b6a8f..a07fb57 100644 --- a/aaos-apps-gradle-project/gradle/libs.versions.toml +++ b/aaos-apps-gradle-project/gradle/libs.versions.toml
@@ -24,8 +24,8 @@ coilCompose = "2.4.0" composeBom = "2025.05.00" #Sets the versions for all AndroidX Compose dependencies coreTesting = "2.2.0" -designcompose = "0.37.1" -dexmaker-mockito = "2.28.4" +designcompose = "0.39.2" +dexmaker-mockito = "2.28.6" espressoCore = "3.6.1" glide = "4.12.0" googleApiClient = "1.33.2"
diff --git a/car-app-card-host-lib/app-card-host/src/androidTest/java/com/android/car/appcard/host/AppCardHostTest.kt b/car-app-card-host-lib/app-card-host/src/androidTest/java/com/android/car/appcard/host/AppCardHostTest.kt index ff944d2..c249e29 100644 --- a/car-app-card-host-lib/app-card-host/src/androidTest/java/com/android/car/appcard/host/AppCardHostTest.kt +++ b/car-app-card-host-lib/app-card-host/src/androidTest/java/com/android/car/appcard/host/AppCardHostTest.kt
@@ -43,8 +43,11 @@ import com.android.car.appcard.internal.AppCardTransport import com.android.car.appcard.util.ParcelableUtils import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.ListeningExecutorService +import com.google.common.util.concurrent.MoreExecutors import java.util.concurrent.Executor import java.util.concurrent.Phaser +import java.util.concurrent.ScheduledExecutorService import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -158,6 +161,7 @@ listener: AppCardTimer.UpdateReadyListener, updateRateMs: Int, fastUpdateRateMs: Int, + scheduledExecutorService: ScheduledExecutorService, ): AppCardTimer { actualUpdateReadyListener = listener actualUpdateRateMs = updateRateMs @@ -169,6 +173,14 @@ object : AppCardHost.UserProvider { override fun getCurrentUser() = 10 } + private val executorProvider = + object : AppCardHost.ExecutorProvider { + override fun getExecutorService(ipcThreadPoolSize: Int): ListeningExecutorService = + MoreExecutors.newDirectExecutorService() + + override fun getScheduledExecutorService(): ScheduledExecutorService = + mock<ScheduledExecutorService>() + } private lateinit var appCardHost: AppCardHost @Before @@ -185,6 +197,8 @@ responseExecutor, appCardTimerProvider, userProvider, + executorProvider, + 8, ) verify(packageManager)
diff --git a/car-app-card-host-lib/app-card-host/src/androidTest/java/com/android/car/appcard/host/AppCardTimerTest.kt b/car-app-card-host-lib/app-card-host/src/androidTest/java/com/android/car/appcard/host/AppCardTimerTest.kt index 5b712fd..d791b27 100644 --- a/car-app-card-host-lib/app-card-host/src/androidTest/java/com/android/car/appcard/host/AppCardTimerTest.kt +++ b/car-app-card-host-lib/app-card-host/src/androidTest/java/com/android/car/appcard/host/AppCardTimerTest.kt
@@ -19,18 +19,21 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.car.appcard.ImageAppCard import com.google.common.truth.Truth.assertThat -import java.util.Timer -import java.util.TimerTask +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.reset import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class AppCardTimerTest { @@ -47,12 +50,13 @@ } } private lateinit var appCardTimer: AppCardTimer - private val timer: Timer = mock<Timer>() - private val identifier = ApplicationIdentifier(TEST_AUTHORITY, TEST_PACKAGE) - private val timerFactory = - object : AppCardTimer.TimerFactory { - override fun getTimer() = timer + private val future: ScheduledFuture<*> = mock<ScheduledFuture<Any>>() + private val scheduledExecutorService: ScheduledExecutorService = + mock<ScheduledExecutorService> { + on { schedule(any(), any(), any()) } doReturn future + on { scheduleAtFixedRate(any(), any(), any(), any()) } doReturn future } + private val identifier = ApplicationIdentifier(TEST_AUTHORITY, TEST_PACKAGE) @Before fun setup() { @@ -61,15 +65,18 @@ updateReadyListener, TEST_UPDATE_RATE_MS, TEST_FAST_UPDATE_RATE_MS, - timerFactory, + scheduledExecutorService, ) } @Test fun testUpdateAppCard_imageAppCard_timerCancelled() { appCardTimer.updateAppCard(AppCardContainer(identifier, ImageAppCardUtility.imageAppCard)) + reset(future) - verify(timer).cancel() + appCardTimer.updateAppCard(AppCardContainer(identifier, ImageAppCardUtility.imageAppCard)) + + verify(future).cancel(eq(false)) } @Test @@ -78,8 +85,13 @@ AppCardContainer(identifier, ImageAppCardUtility.progressBarButtonCard) ) - val captor = argumentCaptor<TimerTask>() - verify(timer).schedule(captor.capture(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) + val captor = argumentCaptor<Runnable>() + verify(scheduledExecutorService) + .schedule( + captor.capture(), + eq(TEST_FAST_UPDATE_RATE_MS.toLong()), + eq(TimeUnit.MILLISECONDS), + ) captor.firstValue.run() assertThat( appCardTimer.isComponentReadyForUpdate( @@ -93,12 +105,13 @@ fun testUpdateAppCard_imageAppCard_withProgressBar_appCardTimerScheduled() { appCardTimer.updateAppCard(AppCardContainer(identifier, ImageAppCardUtility.imageAppCard)) - val captor = argumentCaptor<TimerTask>() - verify(timer) - .schedule( + val captor = argumentCaptor<Runnable>() + verify(scheduledExecutorService) + .scheduleAtFixedRate( captor.capture(), eq(TEST_UPDATE_RATE_MS.toLong()), eq(TEST_UPDATE_RATE_MS.toLong()), + eq(TimeUnit.MILLISECONDS), ) captor.firstValue.run() assertThat(actualIdentifier).isEqualTo(identifier) @@ -117,7 +130,8 @@ appCardTimer.updateAppCard(AppCardContainer(identifier, imageAppCard)) - verify(timer, never()).schedule(any<TimerTask>(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) + verify(scheduledExecutorService, never()) + .schedule(any<Runnable>(), eq(TEST_FAST_UPDATE_RATE_MS.toLong()), any()) } @Test @@ -127,12 +141,7 @@ @Test fun testIsComponentReadyForUpdate_existingComponent_returnTrue() { - appCardTimer.updateAppCard( - AppCardContainer(identifier, ImageAppCardUtility.progressBarButtonCard) - ) - val captor = argumentCaptor<TimerTask>() - verify(timer).schedule(captor.capture(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) - captor.firstValue.run() + setupProgressBarComponentReady() assertThat( appCardTimer.isComponentReadyForUpdate( @@ -144,16 +153,18 @@ @Test fun testDestroy_timerCancelled() { + appCardTimer.updateAppCard(AppCardContainer(identifier, ImageAppCardUtility.imageAppCard)) + appCardTimer.destroy() - verify(timer).cancel() + verify(future).cancel(eq(false)) } @Test fun testResetAppCardTimerAndRequestUpdate_noAppCard_timerCancelNotCalled() { appCardTimer.resetAppCardTimerAndRequestUpdate() - verify(timer, never()).cancel() + verify(future, never()).cancel(any()) } @Test @@ -167,40 +178,22 @@ fun testResetAppCardTimerAndRequestUpdate_noAppCard_timerScheduleNotCalled() { appCardTimer.resetAppCardTimerAndRequestUpdate() - verify(timer, never()).schedule(any<TimerTask>(), any<Long>()) + verify(scheduledExecutorService, never()).schedule(any<Runnable>(), any(), any()) } @Test fun testResetAppCardTimerAndRequestUpdate_appCard_timerCancelled() { appCardTimer.updateAppCard(AppCardContainer(identifier, ImageAppCardUtility.imageAppCard)) - reset(timer) + reset(future) appCardTimer.resetAppCardTimerAndRequestUpdate() - verify(timer).cancel() + verify(future).cancel(eq(false)) } @Test fun testResetAppCardTimerAndRequestUpdate_appCard_componentStatusFalse() { - appCardTimer.updateAppCard( - AppCardContainer(identifier, ImageAppCardUtility.progressBarButtonCard) - ) - assertThat( - appCardTimer.isComponentReadyForUpdate( - ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID - ) - ) - .isFalse() - val captor = argumentCaptor<TimerTask>() - verify(timer).schedule(captor.capture(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) - reset(timer) - captor.firstValue.run() - assertThat( - appCardTimer.isComponentReadyForUpdate( - ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID - ) - ) - .isTrue() + setupProgressBarComponentReady() appCardTimer.resetAppCardTimerAndRequestUpdate() @@ -214,30 +207,18 @@ @Test fun testResetAppCardTimerAndRequestUpdate_appCard_componentTimerTaskSetStatusToTrue() { - appCardTimer.updateAppCard( - AppCardContainer(identifier, ImageAppCardUtility.progressBarButtonCard) - ) - assertThat( - appCardTimer.isComponentReadyForUpdate( - ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID - ) - ) - .isFalse() - val captor = argumentCaptor<TimerTask>() - verify(timer).schedule(captor.capture(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) - reset(timer) - captor.firstValue.run() - assertThat( - appCardTimer.isComponentReadyForUpdate( - ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID - ) - ) - .isTrue() + setupProgressBarComponentReady() appCardTimer.resetAppCardTimerAndRequestUpdate() - verify(timer).schedule(captor.capture(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) - captor.secondValue.run() + val captor = argumentCaptor<Runnable>() + verify(scheduledExecutorService) + .schedule( + captor.capture(), + eq(TEST_FAST_UPDATE_RATE_MS.toLong()), + eq(TimeUnit.MILLISECONDS), + ) + captor.firstValue.run() assertThat( appCardTimer.isComponentReadyForUpdate( ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID @@ -249,16 +230,18 @@ @Test fun testResetAppCardTimerAndRequestUpdate_appCard_appCardTimerTaskCorrectlySet() { appCardTimer.updateAppCard(AppCardContainer(identifier, ImageAppCardUtility.imageAppCard)) - reset(timer) + reset(scheduledExecutorService) + reConfigureMockExecutor() appCardTimer.resetAppCardTimerAndRequestUpdate() - val captor = argumentCaptor<TimerTask>() - verify(timer) - .schedule( + val captor = argumentCaptor<Runnable>() + verify(scheduledExecutorService) + .scheduleAtFixedRate( captor.capture(), eq(TEST_UPDATE_RATE_MS.toLong()), eq(TEST_UPDATE_RATE_MS.toLong()), + eq(TimeUnit.MILLISECONDS), ) captor.firstValue.run() assertThat(actualIdentifier).isEqualTo(identifier) @@ -269,35 +252,23 @@ fun testComponentUpdate_nonExistingComponent_timerNotScheduled() { appCardTimer.componentUpdate(TEST_COMPONENT_ID) - verify(timer, never()).schedule(any<TimerTask>(), any<Long>()) + verify(scheduledExecutorService, never()).schedule(any<Runnable>(), any(), any()) } @Test fun testComponentUpdate_existingComponent_timerScheduled() { - appCardTimer.updateAppCard( - AppCardContainer(identifier, ImageAppCardUtility.progressBarButtonCard) - ) - assertThat( - appCardTimer.isComponentReadyForUpdate( - ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID - ) - ) - .isFalse() - val captor = argumentCaptor<TimerTask>() - verify(timer).schedule(captor.capture(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) - reset(timer) - captor.firstValue.run() - assertThat( - appCardTimer.isComponentReadyForUpdate( - ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID - ) - ) - .isTrue() + setupProgressBarComponentReady() appCardTimer.componentUpdate(ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID) - verify(timer).schedule(captor.capture(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) - captor.secondValue.run() + val captor = argumentCaptor<Runnable>() + verify(scheduledExecutorService) + .schedule( + captor.capture(), + eq(TEST_FAST_UPDATE_RATE_MS.toLong()), + eq(TimeUnit.MILLISECONDS), + ) + captor.firstValue.run() assertThat( appCardTimer.isComponentReadyForUpdate( ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID @@ -308,18 +279,35 @@ @Test fun testComponentUpdate_existingComponent_componentUpdateStatusIsFalse() { - appCardTimer.updateAppCard( - AppCardContainer(identifier, ImageAppCardUtility.progressBarButtonCard) - ) + setupProgressBarComponentReady() + + appCardTimer.componentUpdate(ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID) + + verify(scheduledExecutorService) + .schedule( + any<Runnable>(), + eq(TEST_FAST_UPDATE_RATE_MS.toLong()), + eq(TimeUnit.MILLISECONDS), + ) assertThat( appCardTimer.isComponentReadyForUpdate( ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID ) ) .isFalse() - val captor = argumentCaptor<TimerTask>() - verify(timer).schedule(captor.capture(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) - reset(timer) + } + + private fun setupProgressBarComponentReady() { + appCardTimer.updateAppCard( + AppCardContainer(identifier, ImageAppCardUtility.progressBarButtonCard) + ) + val captor = argumentCaptor<Runnable>() + verify(scheduledExecutorService) + .schedule( + captor.capture(), + eq(TEST_FAST_UPDATE_RATE_MS.toLong()), + eq(TimeUnit.MILLISECONDS), + ) captor.firstValue.run() assertThat( appCardTimer.isComponentReadyForUpdate( @@ -327,16 +315,15 @@ ) ) .isTrue() + reset(scheduledExecutorService) + reConfigureMockExecutor() + } - appCardTimer.componentUpdate(ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID) - - verify(timer).schedule(captor.capture(), eq(TEST_FAST_UPDATE_RATE_MS.toLong())) - assertThat( - appCardTimer.isComponentReadyForUpdate( - ImageAppCardUtility.TEST_PROGRESS_BAR_COMPONENT_ID - ) - ) - .isFalse() + private fun reConfigureMockExecutor() { + doReturn(future).whenever(scheduledExecutorService).schedule(any(), any(), any()) + doReturn(future) + .whenever(scheduledExecutorService) + .scheduleAtFixedRate(any(), any(), any(), any()) } companion object {
diff --git a/car-app-card-host-lib/app-card-host/src/main/java/com/android/car/appcard/host/AppCardHost.kt b/car-app-card-host-lib/app-card-host/src/main/java/com/android/car/appcard/host/AppCardHost.kt index e17f227..42bb821 100644 --- a/car-app-card-host-lib/app-card-host/src/main/java/com/android/car/appcard/host/AppCardHost.kt +++ b/car-app-card-host-lib/app-card-host/src/main/java/com/android/car/appcard/host/AppCardHost.kt
@@ -25,7 +25,6 @@ import android.content.IntentFilter import android.content.pm.PackageManager import android.content.pm.ProviderInfo -import android.os.Build import android.os.Bundle import android.os.DeadObjectException import android.os.UserHandle @@ -40,12 +39,14 @@ import com.android.car.appcard.internal.AppCardTransport import com.google.common.util.concurrent.FutureCallback import com.google.common.util.concurrent.Futures +import com.google.common.util.concurrent.ListeningExecutorService import com.google.common.util.concurrent.MoreExecutors import com.google.protobuf.InvalidProtocolBufferException import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentMap import java.util.concurrent.Executor import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService import java.util.function.Consumer class AppCardHost @@ -56,6 +57,8 @@ private val responseExecutor: Executor, private val timerProvider: AppCardTimerProvider, private val userProvider: UserProvider, + private val executorProvider: ExecutorProvider, + ipcThreadPoolSize: Int, ) : UpdateReadyListener, AppCardObserverCallback { constructor( @@ -63,6 +66,7 @@ updateRate: Int, fastUpdateRate: Int, responseExecutor: Executor, + ipcThreadPoolSize: Int = DEFAULT_IPC_THREAD_COUNT, ) : this( c, updateRate, @@ -73,16 +77,27 @@ listener: UpdateReadyListener, updateRateMs: Int, fastUpdateRateMs: Int, - ) = AppCardTimer(listener, updateRateMs, fastUpdateRateMs) + scheduledExecutorService: ScheduledExecutorService, + ) = AppCardTimer(listener, updateRateMs, fastUpdateRateMs, scheduledExecutorService) }, object : UserProvider { override fun getCurrentUser() = ActivityManager.getCurrentUser() }, + object : ExecutorProvider { + override fun getExecutorService(ipcThreadPoolSize: Int): ListeningExecutorService = + MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(ipcThreadPoolSize)) + + override fun getScheduledExecutorService(): ScheduledExecutorService = + Executors.newSingleThreadScheduledExecutor() + }, + ipcThreadPoolSize, ) private val listeners: MutableSet<AppCardListener> private val idBrokerMap: ConcurrentMap<ApplicationIdentifier, BrokerWrapper> - private val executorService = MoreExecutors.listeningDecorator(Executors.newWorkStealingPool()) + private val authorityToIdentifierMap: ConcurrentMap<String, ApplicationIdentifier> + private val executorService: ListeningExecutorService + private val scheduledExecutorService: ScheduledExecutorService private val packageManager: PackageManager private val contentResolver: ContentResolver private val brokerFactory: BrokerFactory @@ -100,6 +115,10 @@ listeners = HashSet() idBrokerMap = ConcurrentHashMap() + authorityToIdentifierMap = ConcurrentHashMap() + + executorService = executorProvider.getExecutorService(ipcThreadPoolSize) + scheduledExecutorService = executorProvider.getScheduledExecutorService() val expectedBrokerPermission = context.resources.getString(com.android.car.appcard.R.string.host_permission) @@ -113,6 +132,7 @@ ) brokerFactory.connectToAllCompatibleApplications().forEach { idBrokerMap[it.key] = BrokerWrapper(it.value, it.key) + authorityToIdentifierMap[it.key.authority] = it.key } setupAppChangeReceiver() @@ -161,6 +181,7 @@ synchronized(idBrokerMap) { brokerFactory.refreshCompatibleApplication(idBrokerMap.keys).forEach { idBrokerMap[it.key] = BrokerWrapper(it.value, it.key) + authorityToIdentifierMap[it.key.authority] = it.key } } } @@ -180,6 +201,7 @@ ) idBrokerMap.clear() + authorityToIdentifierMap.clear() } } @@ -192,6 +214,8 @@ synchronized(listeners) { listeners.clear() } closeAllApplicationConnections() + executorService.shutdownNow() + scheduledExecutorService.shutdownNow() } } @@ -326,6 +350,18 @@ brokerWrapper.activeAppCardMap.clear() idBrokerMap.remove(identifier) + authorityToIdentifierMap.remove(identifier.authority) + + synchronized(listeners) { + listeners.forEach( + Consumer { appCardListener: AppCardListener -> + appCardListener.onProviderRemoved( + identifier.packageName, + identifier.authority, + ) + } + ) + } } } @@ -680,96 +716,13 @@ listener = this, updateRateMs = updateRateMs, fastUpdateRateMs = fastUpdateRateMs, + scheduledExecutorService = scheduledExecutorService, ) timer.updateAppCard(AppCardContainer(identifier, appCard)) activeAppCardMap.put(appCard.id, ActiveAppCard(appCard, timer)) } } - private fun retrieveAppCardComponentUpdate( - brokerWrapper: BrokerWrapper, - activeAppCard: ActiveAppCard, - identifier: ApplicationIdentifier, - appCardId: String, - componentId: String, - ) { - synchronized(idBrokerMap) { - val broker = brokerWrapper.broker - val timer = activeAppCard.appCardTimer - val listenableFuture = - executorService.submit<AppCardComponentContainer> { - val bundle = Bundle() - bundle.apply { - putString(AppCardContentProvider.BUNDLE_KEY_APP_CARD_ID, appCardId) - putString( - AppCardContentProvider.BUNDLE_KEY_APP_CARD_COMPONENT_ID, - componentId, - ) - } - - val appCardTransport = - try { - broker.getAppCardTransport( - identifier, - errorId = "$appCardId#$componentId", - bundle, - AppCardMessageConstants.MSG_APP_CARD_COMPONENT_UPDATE, - ) - } catch (exception: Exception) { - if (exception is ContentProviderBroker.ContentProviderBrokerException) { - if (exception.cause is DeadObjectException) { - handleInvalidBroker(identifier) - } - } - - throw exception - } - - val component = - appCardTransport.component - ?: throw AppCardHostException( - AppCardMessageConstants.MSG_APP_CARD_COMPONENT_UPDATE, - id = "$appCardId#$componentId", - IllegalStateException("App card component missing from transport"), - ) - timer.componentUpdate(component.componentId) - - AppCardComponentContainer(identifier, appCardId, component) - } - - Futures.addCallback( - listenableFuture, - object : FutureCallback<AppCardComponentContainer> { - override fun onSuccess(appCardComponentContainer: AppCardComponentContainer) { - synchronized(listeners) { - listeners.forEach( - Consumer { appCardListener: AppCardListener -> - appCardListener.onComponentReceived(appCardComponentContainer) - } - ) - } - } - - override fun onFailure(throwable: Throwable) { - Log.e(TAG, throwable.message, throwable) - - synchronized(listeners) { - listeners.forEach( - Consumer { appCardListener: AppCardListener -> - appCardListener.onPackageCommunicationError( - identifier, - throwable, - ) - } - ) - } - } - }, - responseExecutor, - ) - } - } - private fun createUserContext(context: Context, userId: Int): Context { val flags = 0 return context.createContextAsUser(UserHandle.of(userId), flags) @@ -795,20 +748,20 @@ @VisibleForTesting internal fun handlePackageRemovedOrDisabled(packageName: String, authority: String?) { synchronized(idBrokerMap) { - var isRemoved = false - idBrokerMap.entries.removeIf { (key, value) -> - authority?.let { if (!key.containsAuthority(it)) return@removeIf false } + val wasRemoved = + idBrokerMap.entries.removeIf { (key, value) -> + authority?.let { if (!key.containsAuthority(it)) return@removeIf false } - if (!key.containsPackage(packageName)) return@removeIf false + if (!key.containsPackage(packageName)) return@removeIf false - value.broker.close() - value.activeAppCardMap.clear() - isRemoved = true + value.broker.close() + value.activeAppCardMap.clear() + authorityToIdentifierMap.remove(key.authority) - true - } + true + } - if (!isRemoved) return + if (!wasRemoved) return synchronized(listeners) { listeners.forEach( @@ -909,11 +862,26 @@ @VisibleForTesting internal fun handleUserChange() { synchronized(idBrokerMap) { + val identifiers = idBrokerMap.keys.toList() closeAllApplicationConnections() currentUser = userProvider.getCurrentUser() - idBrokerMap.clear() + + synchronized(listeners) { + identifiers.forEach { identifier -> + listeners.forEach( + Consumer { appCardListener: AppCardListener -> + appCardListener.onProviderRemoved( + identifier.packageName, + identifier.authority, + ) + } + ) + } + } + brokerFactory.connectToAllCompatibleApplications().forEach { idBrokerMap[it.key] = BrokerWrapper(it.value, it.key) + authorityToIdentifierMap[it.key.authority] = it.key } } } @@ -996,42 +964,127 @@ id: String, componentId: String, ) { - synchronized(idBrokerMap) { - val identifier = - idBrokerMap.keys - .stream() - .filter { i: ApplicationIdentifier -> i.containsAuthority(authority) } - .findFirst() + executorService.execute { + val updateInfo = + synchronized(idBrokerMap) { + val identifier = authorityToIdentifierMap[authority] ?: return@synchronized null - val isEmpty = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - identifier.isEmpty - } else { - !identifier.isPresent + val brokerWrapper = idBrokerMap[identifier] ?: return@synchronized null + val activeAppCard = + brokerWrapper.activeAppCardMap[id] ?: return@synchronized null + + if (!activeAppCard.appCardTimer.isComponentReadyForUpdate(componentId)) { + return@synchronized null + } + Triple(identifier, brokerWrapper, activeAppCard) } - if (isEmpty) return - val brokerWrapper = idBrokerMap[identifier.get()] ?: return - val cacheTimer = brokerWrapper.activeAppCardMap[id] ?: return + updateInfo?.let { (identifier, brokerWrapper, activeAppCard) -> + val broker = brokerWrapper.broker + val timer = activeAppCard.appCardTimer - if (!cacheTimer.appCardTimer.isComponentReadyForUpdate(componentId)) return + val bundle = Bundle() + bundle.apply { + putString(AppCardContentProvider.BUNDLE_KEY_APP_CARD_ID, id) + putString(AppCardContentProvider.BUNDLE_KEY_APP_CARD_COMPONENT_ID, componentId) + } - retrieveAppCardComponentUpdate( - brokerWrapper, - cacheTimer, - identifier.get(), - id, - componentId, - ) + try { + val appCardTransport = + broker.getAppCardTransport( + identifier, + errorId = "$id#$componentId", + bundle, + AppCardMessageConstants.MSG_APP_CARD_COMPONENT_UPDATE, + ) + + val component = + appCardTransport.component + ?: throw AppCardHostException( + AppCardMessageConstants.MSG_APP_CARD_COMPONENT_UPDATE, + id = "$id#$componentId", + IllegalStateException("App card component missing from transport"), + ) + + // Re-verify that the app card and its broker are still active after the + // long-running IPC call, ensuring that we use the most current state + // and avoid race conditions. + synchronized(idBrokerMap) { + val currentIdentifier = + authorityToIdentifierMap[authority] ?: return@synchronized + val currentBrokerWrapper = + idBrokerMap[currentIdentifier] ?: return@synchronized + val currentActiveCard = + currentBrokerWrapper.activeAppCardMap[id] ?: return@synchronized + + currentActiveCard.appCardTimer.componentUpdate(component.componentId) + + val appCardComponentContainer = + AppCardComponentContainer(currentIdentifier, id, component) + + responseExecutor.execute { + synchronized(listeners) { + listeners.forEach( + Consumer { appCardListener: AppCardListener -> + appCardListener.onComponentReceived( + appCardComponentContainer + ) + } + ) + } + } + } + } catch (exception: Exception) { + val identifierToReport = + synchronized(idBrokerMap) { + val freshIdentifier = authorityToIdentifierMap[authority] + val identifierForError = freshIdentifier ?: identifier + + if ( + freshIdentifier != null && + exception is + ContentProviderBroker.ContentProviderBrokerException && + exception.cause is DeadObjectException && + idBrokerMap[freshIdentifier] === brokerWrapper + ) { + handleInvalidBroker(freshIdentifier) + } + identifierForError + } + + Log.e(TAG, exception.message, exception) + + responseExecutor.execute { + synchronized(listeners) { + listeners.forEach( + Consumer { appCardListener: AppCardListener -> + appCardListener.onPackageCommunicationError( + identifierToReport, + exception, + ) + } + ) + } + } + } + } } } + /** Internal interface to abstract out executors from tests */ + internal interface ExecutorProvider { + fun getExecutorService(ipcThreadPoolSize: Int): ListeningExecutorService + + fun getScheduledExecutorService(): ScheduledExecutorService + } + /** Internal interface to abstract out timers from tests */ internal interface AppCardTimerProvider { fun getNewTimer( listener: UpdateReadyListener, updateRateMs: Int, fastUpdateRateMs: Int, + scheduledExecutorService: ScheduledExecutorService, ): AppCardTimer } @@ -1051,6 +1104,7 @@ companion object { private const val TAG = "AppCardHost" private const val DATA_SCHEME_PACKAGE = "package" + private const val DEFAULT_IPC_THREAD_COUNT = 8 @Throws(AppCardHostException::class) private fun getHostAppCardsFromTransport(
diff --git a/car-app-card-host-lib/app-card-host/src/main/java/com/android/car/appcard/host/AppCardTimer.kt b/car-app-card-host-lib/app-card-host/src/main/java/com/android/car/appcard/host/AppCardTimer.kt index 55150a7..fc3f104 100644 --- a/car-app-card-host-lib/app-card-host/src/main/java/com/android/car/appcard/host/AppCardTimer.kt +++ b/car-app-card-host-lib/app-card-host/src/main/java/com/android/car/appcard/host/AppCardTimer.kt
@@ -17,28 +17,30 @@ package com.android.car.appcard.host import com.android.car.appcard.ImageAppCard -import java.util.Timer -import java.util.TimerTask import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentMap +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference import java.util.function.Consumer internal class AppCardTimer( private val listener: UpdateReadyListener, private val updateRateMs: Int, private val fastUpdateRateMs: Int, - private val timerFactory: TimerFactory = - object : TimerFactory { - override fun getTimer() = Timer() - }, + private val scheduledExecutorService: ScheduledExecutorService, ) { private val componentUpdateStatusMap: ConcurrentMap<String, Boolean> + private val componentUpdateFutureMap: ConcurrentMap<String, ScheduledFuture<*>> + private val lock = Any() private var identifier: ApplicationIdentifier? = null private var appCardId: String? = null - private var refreshTimer = timerFactory.getTimer() + private var refreshFuture: ScheduledFuture<*>? = null init { componentUpdateStatusMap = ConcurrentHashMap() + componentUpdateFutureMap = ConcurrentHashMap() } fun updateAppCard(appCardContainer: AppCardContainer) { @@ -48,9 +50,8 @@ } private fun handleImageAppCardUpdate(imageAppCard: ImageAppCard, id: ApplicationIdentifier) { - synchronized(lock = this) { - refreshTimer.cancel() - refreshTimer = timerFactory.getTimer() + synchronized(lock) { + cancelAllFutures() componentUpdateStatusMap.clear() @@ -59,35 +60,41 @@ imageAppCard.progressBar?.let { componentUpdateStatusMap[it.componentId] = false - refreshTimer.schedule( - object : TimerTask() { - override fun run() { - componentUpdateStatusMap[it.componentId] = true - } - }, - fastUpdateRateMs.toLong(), - ) + val componentId = it.componentId + val futureRef = AtomicReference<ScheduledFuture<*>>() + val future = + scheduledExecutorService.schedule( + { + synchronized(lock) { + if (componentUpdateFutureMap[componentId] === futureRef.get()) { + componentUpdateStatusMap[componentId] = true + componentUpdateFutureMap.remove(componentId) + } + } + }, + fastUpdateRateMs.toLong(), + TimeUnit.MILLISECONDS, + ) + futureRef.set(future) + componentUpdateFutureMap[componentId] = future } - refreshTimer.schedule( - object : TimerTask() { - override fun run() { - listener.appCardIsReadyForUpdate(identifier, appCardId) - } - }, - updateRateMs.toLong(), - updateRateMs.toLong(), - ) + refreshFuture = + scheduledExecutorService.scheduleAtFixedRate( + { listener.appCardIsReadyForUpdate(identifier, appCardId) }, + updateRateMs.toLong(), + updateRateMs.toLong(), + TimeUnit.MILLISECONDS, + ) } } fun resetAppCardTimerAndRequestUpdate() { - synchronized(lock = this) { + synchronized(lock) { appCardId ?: return identifier ?: return - refreshTimer.cancel() - refreshTimer = timerFactory.getTimer() + cancelAllFutures() listener.appCardIsReadyForUpdate(identifier, appCardId) @@ -95,67 +102,85 @@ componentUpdateStatusMap.keys.forEach( Consumer { componentId: String -> - refreshTimer.schedule( - object : TimerTask() { - override fun run() { - componentUpdateStatusMap[componentId] = true - } - }, - fastUpdateRateMs.toLong(), - ) + val futureRef = AtomicReference<ScheduledFuture<*>>() + val future = + scheduledExecutorService.schedule( + { + synchronized(lock) { + if (componentUpdateFutureMap[componentId] === futureRef.get()) { + componentUpdateStatusMap[componentId] = true + componentUpdateFutureMap.remove(componentId) + } + } + }, + fastUpdateRateMs.toLong(), + TimeUnit.MILLISECONDS, + ) + futureRef.set(future) + componentUpdateFutureMap[componentId] = future } ) - refreshTimer.schedule( - object : TimerTask() { - override fun run() { - listener.appCardIsReadyForUpdate(identifier, appCardId) - } - }, - updateRateMs.toLong(), - updateRateMs.toLong(), - ) + refreshFuture = + scheduledExecutorService.scheduleAtFixedRate( + { listener.appCardIsReadyForUpdate(identifier, appCardId) }, + updateRateMs.toLong(), + updateRateMs.toLong(), + TimeUnit.MILLISECONDS, + ) } } fun isComponentReadyForUpdate(componentId: String): Boolean { - synchronized(lock = this) { + synchronized(lock) { val defaultValue = false return componentUpdateStatusMap.getOrDefault(componentId, defaultValue) } } fun destroy() { - synchronized(lock = this) { - refreshTimer.cancel() - refreshTimer = timerFactory.getTimer() - + synchronized(lock) { + cancelAllFutures() componentUpdateStatusMap.clear() } } fun componentUpdate(componentId: String) { - synchronized(lock = this) { - if (!componentUpdateStatusMap.containsKey(componentId)) return + synchronized(lock) { + if (componentUpdateStatusMap[componentId] != true) { + return + } componentUpdateStatusMap[componentId] = false + componentUpdateFutureMap.remove(componentId)?.cancel(false) - refreshTimer.schedule( - object : TimerTask() { - override fun run() { - componentUpdateStatusMap[componentId] = true - } - }, - fastUpdateRateMs.toLong(), - ) + val futureRef = AtomicReference<ScheduledFuture<*>>() + val future = + scheduledExecutorService.schedule( + { + synchronized(lock) { + if (componentUpdateFutureMap[componentId] === futureRef.get()) { + componentUpdateStatusMap[componentId] = true + componentUpdateFutureMap.remove(componentId) + } + } + }, + fastUpdateRateMs.toLong(), + TimeUnit.MILLISECONDS, + ) + futureRef.set(future) + componentUpdateFutureMap[componentId] = future } } + private fun cancelAllFutures() { + refreshFuture?.cancel(false) + refreshFuture = null + componentUpdateFutureMap.values.forEach { it.cancel(false) } + componentUpdateFutureMap.clear() + } + internal interface UpdateReadyListener { fun appCardIsReadyForUpdate(identifier: ApplicationIdentifier?, appCardId: String?) } - - internal interface TimerFactory { - fun getTimer(): Timer - } }
diff --git a/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/AppCardContentProvider.kt b/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/AppCardContentProvider.kt index 87a4afe..cf53a5d 100644 --- a/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/AppCardContentProvider.kt +++ b/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/AppCardContentProvider.kt
@@ -292,40 +292,45 @@ private fun getMethod(uri: Uri) = uri.pathSegments[0] final override fun call(method: String, arg: String?, extras: Bundle?): Bundle? { - synchronized(lock) { - var id: String? = null - var componentId: String? = null - var interactionId: String? = null - var appCardContext: AppCardContext? = null + try { + synchronized(lock) { + var id: String? = null + var componentId: String? = null + var interactionId: String? = null + var appCardContext: AppCardContext? = null - extras?.let { - val defaultValue = null - id = it.getString(BUNDLE_KEY_APP_CARD_ID, defaultValue) - componentId = it.getString(BUNDLE_KEY_APP_CARD_COMPONENT_ID, defaultValue) - interactionId = it.getString(BUNDLE_KEY_APP_CARD_INTERACTION_ID, defaultValue) - val appCardContextBundle = it.getBundle(BUNDLE_KEY_APP_CARD_CONTEXT) - appCardContext = fromBundle(appCardContextBundle) + extras?.let { + val defaultValue = null + id = it.getString(BUNDLE_KEY_APP_CARD_ID, defaultValue) + componentId = it.getString(BUNDLE_KEY_APP_CARD_COMPONENT_ID, defaultValue) + interactionId = it.getString(BUNDLE_KEY_APP_CARD_INTERACTION_ID, defaultValue) + val appCardContextBundle = it.getBundle(BUNDLE_KEY_APP_CARD_CONTEXT) + appCardContext = fromBundle(appCardContextBundle) + } + + var bundle: Bundle? = null + when (method) { + AppCardMessageConstants.MSG_APP_CARD_COMPONENT_UPDATE -> + bundle = handleAppCardComponentUpdate(id, componentId) + + AppCardMessageConstants.MSG_APP_CARD_REMOVED -> removeAppCard(id) + + AppCardMessageConstants.MSG_APP_CARD_INTERACTION -> + handleInteraction(id, componentId, interactionId) + + AppCardMessageConstants.MSG_APP_CARD_CONTEXT_UPDATE -> + handleAppCardContextUpdate(id, appCardContext) + + AppCardMessageConstants.MSG_CLOSE_PROVIDER -> handleClose() + + else -> Log.e(TAG, "Unrecognized method: $method") + } + + return bundle } - - var bundle: Bundle? = null - when (method) { - AppCardMessageConstants.MSG_APP_CARD_COMPONENT_UPDATE -> - bundle = handleAppCardComponentUpdate(id, componentId) - - AppCardMessageConstants.MSG_APP_CARD_REMOVED -> removeAppCard(id) - - AppCardMessageConstants.MSG_APP_CARD_INTERACTION -> - handleInteraction(id, componentId, interactionId) - - AppCardMessageConstants.MSG_APP_CARD_CONTEXT_UPDATE -> - handleAppCardContextUpdate(id, appCardContext) - - AppCardMessageConstants.MSG_CLOSE_PROVIDER -> handleClose() - - else -> Log.e(TAG, "Unrecognized method: $method") - } - - return bundle + } catch (e: Exception) { + Log.e(TAG, "Exception occurred during call ($method)", e) + return null } } @@ -470,17 +475,16 @@ } latestAppCard[appCard.id] = appCard - activeAppCardCountMap[id] = - (activeAppCardCountMap[id]?.let { it + 1 }) - ?: run { - if (dispatcher.getDesiredState() != Lifecycle.Event.ON_RESUME) { - if (dispatcher.getDesiredState() != Lifecycle.Event.ON_START) - dispatcher.queueOnStart() - - dispatcher.queueOnResume() - } - 1 // run's return value + activeAppCardCountMap.compute(id) { _, count -> + val newCount = (count ?: 0) + 1 + if (newCount == 1 && dispatcher.getDesiredState() != Lifecycle.Event.ON_RESUME) { + if (dispatcher.getDesiredState() != Lifecycle.Event.ON_START) { + dispatcher.queueOnStart() } + dispatcher.queueOnResume() + } + newCount + } appCardIdComponentMap[id] = getComponentMapFromAppCard(appCard) return appCard @@ -493,29 +497,42 @@ return } - val defaultValue = 0 - var count = activeAppCardCountMap.getOrDefault(id, defaultValue) - - when (count) { - 0 -> Log.e(TAG, "App card remove requested for an inactive app card") - 1 -> { - activeAppCardCountMap.remove(id) - appCardIdComponentMap.remove(id) - latestAppCard.remove(id) - onAppCardRemoved(id) - - if ( - activeAppCardCountMap.isEmpty() && - dispatcher.getDesiredState() != Lifecycle.Event.ON_STOP - ) { - if (dispatcher.getDesiredState() != Lifecycle.Event.ON_PAUSE) - dispatcher.queueOnPause() - - dispatcher.queueOnStop() + var removed = false + var wasInactive = false + activeAppCardCountMap.compute(id) { _, count -> + when (count) { + null, + 0 -> { + wasInactive = true + null } + 1 -> { + removed = true + null + } + else -> count - 1 } + } - else -> activeAppCardCountMap[id] = --count + if (wasInactive) { + Log.e(TAG, "App card remove requested for an inactive app card") + return + } + + if (removed) { + appCardIdComponentMap.remove(id) + latestAppCard.remove(id) + onAppCardRemoved(id) + + if ( + activeAppCardCountMap.isEmpty() && + dispatcher.getDesiredState() != Lifecycle.Event.ON_STOP + ) { + if (dispatcher.getDesiredState() != Lifecycle.Event.ON_PAUSE) { + dispatcher.queueOnPause() + } + dispatcher.queueOnStop() + } } }
diff --git a/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/component/Button.kt b/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/component/Button.kt index 8e87038..ee34ed0 100644 --- a/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/component/Button.kt +++ b/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/component/Button.kt
@@ -123,12 +123,11 @@ } override fun hashCode(): Int { - var result = text?.hashCode() ?: 0 + var result = super.hashCode() + result = 31 * result + (text?.hashCode() ?: 0) result = 31 * result + (image?.hashCode() ?: 0) result = 31 * result + buttonType.hashCode() result = 31 * result + (onClickListener?.hashCode() ?: 0) - result = 31 * result + (text?.hashCode() ?: 0) - result = 31 * result + (image?.hashCode() ?: 0) result = 31 * result + (intent?.hashCode() ?: 0) return result }
diff --git a/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/component/Image.kt b/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/component/Image.kt index 85f5c18..9866c67 100644 --- a/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/component/Image.kt +++ b/car-app-card-lib/app-card/src/main/java/com/android/car/appcard/component/Image.kt
@@ -21,41 +21,53 @@ import com.android.car.appcard.internal.proto.Image.ImageMessage import com.google.protobuf.ByteString import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer import java.security.MessageDigest +import java.util.Arrays /** * Image is a component that contains an image and information on how the image should be displayed. */ class Image private constructor(builder: Builder) : Component(builder) { /** @return [ContentScale] */ + @Volatile var contentScale: ContentScale private set /** @return [ColorFilter] */ + @Volatile var colorFilter: ColorFilter private set /** @return [android.graphics.Bitmap] */ + @Volatile var imageData: Bitmap? private set - private var imageHashString: String? = null + @Volatile private var imageHash: ByteArray? = null + @Volatile private var capturedGenerationId: Int = -1 + @Volatile private var compressedData: ByteArray? = null + @Volatile private var cachedHashCode: Int? = null + private val lock = Any() init { imageData = builder.imageData contentScale = builder.contentScale colorFilter = builder.colorFilter - updateImageHash() + updateImageHashAndCache() } /** @return protobuf message */ - fun toMessage(): ImageMessage = - ImageMessage.newBuilder() - .setComponentId(componentId) - .setImage(imageData?.let { ByteString.copyFrom(toByteArray(it)) }) - .setContentScale(toContentScale(contentScale)) - .setColorFilter(toColorFilter(colorFilter)) - .build() + fun toMessage(): ImageMessage { + synchronized(lock) { + return ImageMessage.newBuilder() + .setComponentId(componentId) + .setImage(getCompressedData()?.let { ByteString.copyFrom(it) }) + .setContentScale(toContentScale(contentScale)) + .setColorFilter(toColorFilter(colorFilter)) + .build() + } + } /** @return protobuf byte array */ override fun toByteArray(): ByteArray = toMessage().toByteArray() @@ -63,43 +75,116 @@ override fun updateComponent(component: Component): Boolean { if (component !is Image || componentId != component.componentId) return false - component.imageData?.let { - imageData = component.imageData - updateImageHash() - } - - contentScale = component.contentScale - colorFilter = component.colorFilter + withSafeMultiLock(component) { performUpdate(component) } return true } + private fun performUpdate(other: Image) { + contentScale = other.contentScale + colorFilter = other.colorFilter + + if (other.imageData != null) { + imageData = other.imageData + imageHash = other.imageHash + capturedGenerationId = other.capturedGenerationId + compressedData = other.compressedData + cachedHashCode = other.cachedHashCode + } else { + cachedHashCode = null + } + } + + /** + * Note: This method compares the properties of the [Image] component, including a snapshot of + * the [Bitmap] content taken at construction time. If the underlying [Bitmap] is mutated in + * place after construction, this instance will still be considered equal to other [Image] + * instances created from the same [Bitmap] state. To ensure UI updates after a [Bitmap] + * mutation, a new [Image] instance should be created or the existing one should be updated via + * [updateComponent]. + */ override fun equals(other: Any?): Boolean { if (other === this) return true - if (other !is Image) return false + if (!super.equals(other)) return false - val imageEquals = - imageHashString?.let { it == other.imageHashString } ?: (other.imageHashString == null) - return imageEquals && - super.equals(other) && - contentScale == other.contentScale && - colorFilter == other.colorFilter + return withSafeMultiLock(other) { equalsInternal(other) } + } + + private fun equalsInternal(other: Image): Boolean { + if (contentScale != other.contentScale || colorFilter != other.colorFilter) { + return false + } + + if (imageData === other.imageData && capturedGenerationId == other.capturedGenerationId) { + return true + } + + return Arrays.equals(imageHash, other.imageHash) + } + + private inline fun <T> withSafeMultiLock(other: Image, block: () -> T): T { + val h1 = System.identityHashCode(this.lock) + val h2 = System.identityHashCode(other.lock) + + return when { + h1 < h2 -> synchronized(this.lock) { synchronized(other.lock) { block() } } + h1 > h2 -> synchronized(other.lock) { synchronized(this.lock) { block() } } + else -> + synchronized(TIE_LOCK) { + synchronized(this.lock) { synchronized(other.lock) { block() } } + } + } } override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + contentScale.hashCode() - result = 31 * result + colorFilter.hashCode() - result = 31 * result + (imageData?.hashCode() ?: 0) - return result + var cached = cachedHashCode + if (cached != null) return cached + + synchronized(lock) { + cached = cachedHashCode + if (cached != null) return cached + + val currentContentScale = contentScale + val currentColorFilter = colorFilter + val currentImageHash = imageHash + + var result = super.hashCode() + result = 31 * result + currentContentScale.hashCode() + result = 31 * result + currentColorFilter.hashCode() + result = 31 * result + Arrays.hashCode(currentImageHash) + cached = result + cachedHashCode = cached + return cached + } } - private fun updateImageHash() { - val md = MessageDigest.getInstance(HASH_ALGORITHM) - val imageByteArray = imageData?.let { toByteArray(it) } - val hashBytes = imageByteArray?.let { md.digest(it) } - imageHashString = hashBytes?.joinToString { HEX_FORMAT.format(it) } + private fun updateImageHashAndCache() { + synchronized(lock) { + val bitmap = imageData ?: return + capturedGenerationId = bitmap.generationId + val md = MessageDigest.getInstance(HASH_ALGORITHM) + val byteBuffer = ByteBuffer.allocate(bitmap.byteCount) + bitmap.copyPixelsToBuffer(byteBuffer) + imageHash = md.digest(byteBuffer.array()) + compressedData = null + cachedHashCode = null + } + } + + private fun getCompressedData(): ByteArray? { + var result = compressedData + if (result == null) { + synchronized(lock) { + result = compressedData + if (result == null) { + val currentImageData = imageData + result = toByteArray(currentImageData) + compressedData = result + } + } + } + return result } /** @@ -201,8 +286,8 @@ private const val TAG = "Image" private const val BITMAP_QUALITY = 100 private const val BITMAP_OFFSET = 0 - private const val HASH_ALGORITHM = "SHA-512" - private const val HEX_FORMAT = "%02x" + private const val HASH_ALGORITHM = "MD5" + private val TIE_LOCK = Any() /** @return an instance of [Builder] */ @JvmStatic fun newBuilder(componentId: String) = Builder(componentId)
diff --git a/car-apps-common/res/color/app_bar_background_color.xml b/car-apps-common/res/color/app_bar_background_color.xml new file mode 100644 index 0000000..6d6744f --- /dev/null +++ b/car-apps-common/res/color/app_bar_background_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorBackground" android:alpha="0.88" /> +</selector>
diff --git a/car-apps-common/res/color/background_image_scrim_30pc_bg_color.xml b/car-apps-common/res/color/background_image_scrim_30pc_bg_color.xml new file mode 100644 index 0000000..6729456 --- /dev/null +++ b/car-apps-common/res/color/background_image_scrim_30pc_bg_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorBackground" android:alpha="0.30" /> +</selector>
diff --git a/car-apps-common/res/color/background_image_scrim_50pc_bg_color.xml b/car-apps-common/res/color/background_image_scrim_50pc_bg_color.xml new file mode 100644 index 0000000..4fe6b31 --- /dev/null +++ b/car-apps-common/res/color/background_image_scrim_50pc_bg_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorBackground" android:alpha="0.50" /> +</selector>
diff --git a/car-apps-common/res/color/background_image_scrim_70pc_bg_color.xml b/car-apps-common/res/color/background_image_scrim_70pc_bg_color.xml new file mode 100644 index 0000000..6a0c0cb --- /dev/null +++ b/car-apps-common/res/color/background_image_scrim_70pc_bg_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorBackground" android:alpha="0.70" /> +</selector>
diff --git a/car-apps-common/res/color/background_image_scrim_80pc_bg_color.xml b/car-apps-common/res/color/background_image_scrim_80pc_bg_color.xml new file mode 100644 index 0000000..f54b13d --- /dev/null +++ b/car-apps-common/res/color/background_image_scrim_80pc_bg_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorBackground" android:alpha="0.80" /> +</selector>
diff --git a/car-apps-common/res/color/background_image_scrim_96pc_bg_color.xml b/car-apps-common/res/color/background_image_scrim_96pc_bg_color.xml new file mode 100644 index 0000000..79cead4 --- /dev/null +++ b/car-apps-common/res/color/background_image_scrim_96pc_bg_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorBackground" android:alpha="0.96" /> +</selector>
diff --git a/car-apps-common/res/color/car_card_ripple_background.xml b/car-apps-common/res/color/car_card_ripple_background.xml new file mode 100644 index 0000000..5d1ba12 --- /dev/null +++ b/car-apps-common/res/color/car_card_ripple_background.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorForeground" android:alpha="0.09" /> +</selector>
diff --git a/car-apps-common/res/color/car_tab_selected_color.xml b/car-apps-common/res/color/car_tab_selected_color.xml new file mode 100644 index 0000000..c4c8780 --- /dev/null +++ b/car-apps-common/res/color/car_tab_selected_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorForeground" /> +</selector>
diff --git a/car-apps-common/res/color/car_tab_unselected_color.xml b/car-apps-common/res/color/car_tab_unselected_color.xml new file mode 100644 index 0000000..b0c6e1f --- /dev/null +++ b/car-apps-common/res/color/car_tab_unselected_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorForeground" android:alpha="0.50" /> +</selector>
diff --git a/car-apps-common/res/color/control_bar_button_background_color.xml b/car-apps-common/res/color/control_bar_button_background_color.xml new file mode 100644 index 0000000..52f29a9 --- /dev/null +++ b/car-apps-common/res/color/control_bar_button_background_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorForeground" android:alpha="0.40" /> +</selector>
diff --git a/car-apps-common/res/color/primary_app_icon_color.xml b/car-apps-common/res/color/primary_app_icon_color.xml new file mode 100644 index 0000000..c4c8780 --- /dev/null +++ b/car-apps-common/res/color/primary_app_icon_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorForeground" /> +</selector>
diff --git a/car-apps-common/res/color/scrim_overlay_color.xml b/car-apps-common/res/color/scrim_overlay_color.xml new file mode 100644 index 0000000..923d57e --- /dev/null +++ b/car-apps-common/res/color/scrim_overlay_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorBackground" android:alpha="0.78" /> +</selector>
diff --git a/car-apps-common/res/color/secondary_app_icon_color.xml b/car-apps-common/res/color/secondary_app_icon_color.xml new file mode 100644 index 0000000..9dfa255 --- /dev/null +++ b/car-apps-common/res/color/secondary_app_icon_color.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2026 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?android:attr/colorForeground" android:alpha="0.72" /> +</selector>
diff --git a/car-apps-common/res/color/selectable_button_tint_selector.xml b/car-apps-common/res/color/selectable_button_tint_selector.xml index c0c387d..fd20c19 100644 --- a/car-apps-common/res/color/selectable_button_tint_selector.xml +++ b/car-apps-common/res/color/selectable_button_tint_selector.xml
@@ -15,6 +15,6 @@ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_selected="true" android:color="#000000"/> - <item android:state_selected="false" android:color="@color/primary_app_icon_color"/> + <item android:state_selected="true" android:color="?android:attr/colorBackground"/> + <item android:state_selected="false" android:color="?android:attr/colorForeground"/> </selector>
diff --git a/car-apps-common/res/color/uxr_button_image_color_selector.xml b/car-apps-common/res/color/uxr_button_image_color_selector.xml index 6f8e9ba..8ce2612 100644 --- a/car-apps-common/res/color/uxr_button_image_color_selector.xml +++ b/car-apps-common/res/color/uxr_button_image_color_selector.xml
@@ -17,6 +17,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> - <item app:state_ux_restricted="true" android:color="@color/uxr_button_image_disabled_color"/> - <item app:state_ux_restricted="false" android:color="@color/uxr_button_image_color"/> + <item app:state_ux_restricted="true" android:color="?android:attr/colorForeground" android:alpha="0.50"/> + <item app:state_ux_restricted="false" android:color="?android:attr/colorForeground"/> </selector>
diff --git a/car-apps-common/res/drawable-ldrtl/background_image_scrim.xml b/car-apps-common/res/drawable-ldrtl/background_image_scrim.xml index 9035a65..fc0c882 100644 --- a/car-apps-common/res/drawable-ldrtl/background_image_scrim.xml +++ b/car-apps-common/res/drawable-ldrtl/background_image_scrim.xml
@@ -20,9 +20,9 @@ <shape> <gradient android:angle="270" - android:startColor="#F5000000" + android:startColor="@color/background_image_scrim_96pc_bg_color" android:centerColor="@android:color/transparent" - android:endColor="#F5000000"/> + android:endColor="@color/background_image_scrim_96pc_bg_color"/> </shape> </item> <item> @@ -31,8 +31,8 @@ android:angle="180" android:centerX="0.52" android:startColor="@android:color/transparent" - android:centerColor="@android:color/black" - android:endColor="@android:color/black" /> + android:centerColor="?android:attr/colorBackground" + android:endColor="?android:attr/colorBackground" /> </shape> </item> <item> @@ -41,13 +41,13 @@ android:angle="180" android:centerX="0.62" android:startColor="@android:color/transparent" - android:centerColor="#B3000000" - android:endColor="#B3000000" /> + android:centerColor="@color/background_image_scrim_70pc_bg_color" + android:endColor="@color/background_image_scrim_70pc_bg_color" /> </shape> </item> <item> <shape> - <solid android:color="@color/background_image_30p_black"/> + <solid android:color="@color/background_image_scrim_30pc_bg_color"/> </shape> </item> </layer-list>
diff --git a/car-apps-common/res/drawable-port/background_image_scrim.xml b/car-apps-common/res/drawable-port/background_image_scrim.xml index 3e3437f..34b6f14 100644 --- a/car-apps-common/res/drawable-port/background_image_scrim.xml +++ b/car-apps-common/res/drawable-port/background_image_scrim.xml
@@ -21,8 +21,8 @@ <gradient android:angle="90" android:centerY="0.3" - android:startColor="@android:color/black" - android:centerColor="@android:color/black" + android:startColor="?android:attr/colorBackground" + android:centerColor="?android:attr/colorBackground" android:endColor="@android:color/transparent"/> </shape> </item> @@ -31,17 +31,18 @@ <gradient android:angle="90" android:centerY="0.48" - android:startColor="#80000000" - android:centerColor="#80000000" + android:startColor="#FFFF0000" + android:centerColor="#FFFF0000" android:endColor="@android:color/transparent"/> </shape> + <!-- @color/background_image_scrim_50pc_bg_color--> </item> <item> <shape> <gradient android:angle="270" android:centerY="0.64" - android:startColor="#CC000000" + android:startColor="@color/background_image_scrim_80pc_bg_color" android:centerColor="@android:color/transparent"/> </shape> </item>
diff --git a/car-apps-common/res/drawable/background_image_scrim.xml b/car-apps-common/res/drawable/background_image_scrim.xml index 89a8092..db55043 100644 --- a/car-apps-common/res/drawable/background_image_scrim.xml +++ b/car-apps-common/res/drawable/background_image_scrim.xml
@@ -20,9 +20,9 @@ <shape> <gradient android:angle="90" - android:startColor="#F5000000" + android:startColor="@color/background_image_scrim_96pc_bg_color" android:centerColor="@android:color/transparent" - android:endColor="#F5000000"/> + android:endColor="@color/background_image_scrim_96pc_bg_color"/> </shape> </item> <item> @@ -31,8 +31,8 @@ android:angle="0" android:centerX="0.48" android:startColor="@android:color/transparent" - android:centerColor="@android:color/black" - android:endColor="@android:color/black" /> + android:centerColor="?android:attr/colorBackground" + android:endColor="?android:attr/colorBackground" /> </shape> </item> <item> @@ -41,13 +41,14 @@ android:angle="0" android:centerX="0.38" android:startColor="@android:color/transparent" - android:centerColor="#B3000000" - android:endColor="#B3000000" /> + android:centerColor="@color/background_image_scrim_70pc_bg_color" + android:endColor="@color/background_image_scrim_70pc_bg_color" /> </shape> + <!-- --> </item> <item> <shape> - <solid android:color="@color/background_image_30p_black"/> + <solid android:color="@color/background_image_scrim_30pc_bg_color"/> </shape> </item> </layer-list>
diff --git a/car-apps-common/res/layout-port/background_image.xml b/car-apps-common/res/layout-port/background_image.xml index 7e4ee8e..dffbcd2 100644 --- a/car-apps-common/res/layout-port/background_image.xml +++ b/car-apps-common/res/layout-port/background_image.xml
@@ -41,7 +41,7 @@ android:id="@+id/background_image_darkening_scrim" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background_image_30p_black" + android:background="@color/background_image_scrim_30pc_bg_color" android:visibility="gone" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
diff --git a/car-apps-common/res/values-de/strings.xml b/car-apps-common/res/values-de/strings.xml index 025a447..631b09b 100644 --- a/car-apps-common/res/values-de/strings.xml +++ b/car-apps-common/res/values-de/strings.xml
@@ -16,7 +16,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="control_bar_expand_collapse_button" msgid="3420351169078117938">"Schaltfläche zum Maximieren/Minimieren"</string> + <string name="control_bar_expand_collapse_button" msgid="3420351169078117938">"Button zum Maximieren/Minimieren"</string> <string name="car_drawer_open" msgid="2676372472514742324">"Leiste öffnen"</string> <string name="car_drawer_close" msgid="5329374630462464855">"Leiste schließen"</string> <string name="restricted_while_driving" msgid="2278031053760704437">"Funktion während der Fahrt nicht verfügbar."</string>
diff --git a/car-apps-common/res/values-night/colors.xml b/car-apps-common/res/values-night/colors.xml deleted file mode 100644 index d00832e..0000000 --- a/car-apps-common/res/values-night/colors.xml +++ /dev/null
@@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 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. - --> - -<resources> - <color name="car_tab_selected_color">@color/car_tab_selected_color_dark</color> - <color name="car_tab_unselected_color">@color/car_tab_unselected_color_dark</color> - <color name="scrim_overlay_color">#D6000000</color> - - <color name="primary_app_icon_color">#E0FFFFFF</color> - <color name="secondary_app_icon_color">#99FFFFFF</color> - - <color name="app_bar_background_color">#EB000000</color> -</resources>
diff --git a/car-apps-common/res/values/colors.xml b/car-apps-common/res/values/colors.xml index ddac704..22e323a 100644 --- a/car-apps-common/res/values/colors.xml +++ b/car-apps-common/res/values/colors.xml
@@ -38,27 +38,9 @@ <color name="improper_image_refs_tint_color">#C8FF0000</color> <color name="control_bar_background_color">@android:color/transparent</color> - <color name="scrim_overlay_color">#C7000000</color> - <color name="app_bar_background_color">#E0000000</color> - - <color name="car_tab_selected_color">@color/car_tab_selected_color_light</color> - <color name="car_tab_selected_color_dark">#E2FFFFFF</color> - <color name="car_tab_selected_color_light">#FFFFFFFF</color> - <color name="car_tab_unselected_color">@color/car_tab_unselected_color_light</color> - <color name="car_tab_unselected_color_dark">#80FFFFFF</color> - <color name="car_tab_unselected_color_light">#90FFFFFF</color> - - <color name="primary_app_icon_color">#FFFFFFFF</color> - <color name="secondary_app_icon_color">#B8FFFFFF</color> - <color name="car_card_ripple_background">#17000000</color> <color name="background_image_30p_black">#4D000000</color> - <color name="uxr_button_image_color">@color/primary_app_icon_color</color> - <color name="uxr_button_image_disabled_color">#80FFFFFF</color> - - <color name="control_bar_button_background_color">#66ffffff</color> - <color name="hero_button_background_color">@color/car_grey_868</color> <color name="hero_button_text_color">@color/primary_text_color</color> @@ -67,7 +49,6 @@ <color name="car_seekbar_background_color">#404659</color> <color name="car_seekbar_icon_color">#002B76</color> - <!-- Color palette for cars. Those values are NOT part of the car-ui-lib "resource API", they are just constants used in @@ -99,5 +80,5 @@ <color name="car_red_500a">#ffd50000</color> <!-- End color palette --> - </resources> +
diff --git a/car-apps-common/res/values/overlayable.xml b/car-apps-common/res/values/overlayable.xml index 3e5c656..2a47347 100644 --- a/car-apps-common/res/values/overlayable.xml +++ b/car-apps-common/res/values/overlayable.xml
@@ -16,6 +16,12 @@ <item type="bool" name="use_media_source_color_for_minimized_progress_bar"/> <item type="color" name="action_button_icon_tint" /> + <item type="color" name="background_image_scrim_30pc_bg_color" /> + <item type="color" name="background_image_scrim_50pc_bg_color" /> + <item type="color" name="background_image_scrim_70pc_bg_color" /> + <item type="color" name="background_image_scrim_80pc_bg_color" /> + <item type="color" name="background_image_scrim_96pc_bg_color" /> + <item type="color" name="control_bar_button_background_color" /> <item type="dimen" name="control_bar_button_size" /> <item type="dimen" name="control_bar_button_padding" />
diff --git a/car-apps-common/src/com/android/car/apps/common/ControlBar.java b/car-apps-common/src/com/android/car/apps/common/ControlBar.java index e167cd5..4940727 100644 --- a/car-apps-common/src/com/android/car/apps/common/ControlBar.java +++ b/car-apps-common/src/com/android/car/apps/common/ControlBar.java
@@ -102,6 +102,9 @@ // Weight for the spacers used between buttons private static final float SPACERS_WEIGHT = 1f; + // Delay before closing the control bar when it becomes empty while expanded. + private static final long CLOSE_IF_EMPTY_DELAY_MS = 100; + private final OnGlobalFocusChangeListener mFocusChangeListener = (oldFocus, newFocus) -> { // Collapse the control bar when it is expanded and loses focus. @@ -112,7 +115,6 @@ mHasFocus = hasFocus; }; - public ControlBar(Context context) { super(context); init(context, null, 0, 0); @@ -344,14 +346,22 @@ // mRowsContainer's children are in reverse order (last row is at index 0) // When changing source (b/381304903), when the number of extra rows is less than before, // all rows except the main row should be hidden. When the number of rows is greater than - // before, only the main row should be set visible + // before, the main row should be set visible, and the extra rows should be set to visible + // if mIsExpanded is true. if (numExtraRowsInUseBefore > mNumExtraRowsInUse) { - for (int i = 0; i < mRowsContainer.getChildCount() - 1; i++) { + for (int i = mNumExtraRowsInUse; i < mRowsContainer.getChildCount() - 1; i++) { mRowsContainer.getChildAt(i).setVisibility(View.GONE); } } else if (numExtraRowsInUseBefore < mNumExtraRowsInUse) { mRowsContainer.getChildAt(mRowsContainer.getChildCount() - 1) .setVisibility(View.VISIBLE); + for (int i = numExtraRowsInUseBefore; i < mNumExtraRowsInUse; i++) { + mRowsContainer.getChildAt(i).setVisibility(mIsExpanded ? View.VISIBLE : View.GONE); + } + } + + if (mIsExpanded && mNumExtraRowsInUse == 0) { + close(); } }
diff --git a/car-media-common/res/drawable/ic_placeholder_tintable.xml b/car-media-common/res/drawable/ic_placeholder_tintable.xml index cac71dd..1faefff 100644 --- a/car-media-common/res/drawable/ic_placeholder_tintable.xml +++ b/car-media-common/res/drawable/ic_placeholder_tintable.xml
@@ -19,7 +19,7 @@ android:width="48dp" android:viewportHeight="103" android:viewportWidth="103"> - <path android:fillColor="#FFF" android:fillType="evenOdd" + <path android:fillColor="?android:attr/colorForeground" android:fillType="evenOdd" android:pathData="M52,34.5L52,53.842C50.918,53.218 49.672,52.833 48.333,52.833C44.282,52.833 41,56.115 41,60.167C41,64.218 44.282,67.5 48.333,67.5C52.385,67.5 55.667,64.218 55.667,60.167L55.667,41.833L63,41.833L63,34.5L52,34.5Z" - android:strokeColor="#FFF" android:strokeWidth="1"/> + android:strokeColor="?android:attr/colorForeground" android:strokeWidth="1"/> </vector>
diff --git a/car-media-common/src/com/android/car/media/common/CustomPlaybackAction.java b/car-media-common/src/com/android/car/media/common/CustomPlaybackAction.java index 85c776c..ca469a4 100644 --- a/car-media-common/src/com/android/car/media/common/CustomPlaybackAction.java +++ b/car-media-common/src/com/android/car/media/common/CustomPlaybackAction.java
@@ -26,9 +26,6 @@ import com.android.car.media.common.playback.PlaybackViewModel.PlaybackStateWrapper; -import java.util.Objects; - - /** * Abstract representation of a custom playback action. A custom playback action represents a * visual element that can be used to trigger playback actions not included in the standard @@ -52,10 +49,14 @@ /** * Creates a custom action */ + @Nullable public static CustomPlaybackAction create(@NonNull Context context, @NonNull String action, @Nullable Bundle extras, @NonNull IconCompat iconCompat) { - return new CustomPlaybackAction(Objects.requireNonNull(iconCompat.loadDrawable(context)), - action, extras, iconCompat); + Drawable drawable = iconCompat.loadDrawable(context); + if (drawable == null) { + return null; + } + return new CustomPlaybackAction(drawable, action, extras, iconCompat); } private CustomPlaybackAction(
diff --git a/car-media-common/src/com/android/car/media/common/MediaButtonController.java b/car-media-common/src/com/android/car/media/common/MediaButtonController.java index 4abc3f5..3484baf 100644 --- a/car-media-common/src/com/android/car/media/common/MediaButtonController.java +++ b/car-media-common/src/com/android/car/media/common/MediaButtonController.java
@@ -44,6 +44,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -266,6 +267,7 @@ imageButtons.addAll(state.getCustomActions() .stream() .map(rawAction -> rawAction.fetchDrawable(mContext)) + .filter(Objects::nonNull) .map(action -> getOrCreateIconButton(action)) .collect(Collectors.toList())); }
diff --git a/car-media-common/src/com/android/car/media/common/MediaItemMetadata.java b/car-media-common/src/com/android/car/media/common/MediaItemMetadata.java index 075e602..b59279c 100644 --- a/car-media-common/src/com/android/car/media/common/MediaItemMetadata.java +++ b/car-media-common/src/com/android/car/media/common/MediaItemMetadata.java
@@ -445,6 +445,14 @@ } } + /** + * Clears the static cache of placeholder drawables. This should be called + * when the application's theme or configuration changes. + */ + public static void clearPlaceholderCache() { + sPlaceHolders.clear(); + } + private static List<Drawable> getPlaceHolders(PlaceholderType type, Context context) { List<Drawable> placeHolders = sPlaceHolders.get(type); if (placeHolders == null) { @@ -457,7 +465,11 @@ placeHolders = new ArrayList<>(placeholderImages.length()); for (int i = 0; i < placeholderImages.length(); i++) { - placeHolders.add(placeholderImages.getDrawable(i)); + int resId = placeholderImages.getResourceId(i, 0); + if (resId != 0) { + // Use the context to get proper theming. + placeHolders.add(context.getDrawable(resId)); + } } placeholderImages.recycle(); sPlaceHolders.put(type, placeHolders);
diff --git a/car-media-common/src/com/android/car/media/common/MediaLinkHandler.java b/car-media-common/src/com/android/car/media/common/MediaLinkHandler.java index 1de725a..25743c6 100644 --- a/car-media-common/src/com/android/car/media/common/MediaLinkHandler.java +++ b/car-media-common/src/com/android/car/media/common/MediaLinkHandler.java
@@ -61,6 +61,7 @@ if (TextUtils.isEmpty(mLinkedMediaId) || (mControllerDelegate == null) || (mMediaItemsRepository == null)) { mDisplayView.setOnClickListener(null); + mDisplayView.setClickable(false); // setOnClickListener makes the view clickable... } else { mDisplayView.setOnClickListener(view -> mMediaItemsRepository.getItem(mLinkedMediaId, new MediaBrowserCompat.ItemCallback() {
diff --git a/car-media-common/src/com/android/car/media/common/source/MediaSessionHelper.java b/car-media-common/src/com/android/car/media/common/source/MediaSessionHelper.java index 69d995f..3e5208a 100644 --- a/car-media-common/src/com/android/car/media/common/source/MediaSessionHelper.java +++ b/car-media-common/src/com/android/car/media/common/source/MediaSessionHelper.java
@@ -423,7 +423,7 @@ return; } mActiveOrPausedMediaSources - .setValue(mInputFactory.getMediaSources(activeMediaControllers)); + .setValue(mInputFactory.getMediaSources(activeMediaControllers)); } } @@ -435,34 +435,73 @@ parseMediaControllers(filteredControllers, activeMediaControllers, activeOrPausedMediaControllers); - MediaSource savedMediaSource = null; // Setup initial activeMediaController if (activeMediaControllers.isEmpty()) { // Get the last saved media source - String savedMediaSourceName = getLastActiveMediaSource(); - if (TextUtils.isEmpty(savedMediaSourceName)) { - // Don't change default values - return; + String savedMediaSourceName = mSharedPrefs.getString(LAST_ACTIVE_MEDIA_SOURCE, ""); + if (!TextUtils.isEmpty(savedMediaSourceName)) { + setSavedMediaSource(savedMediaSourceName, activeOrPausedMediaControllers); + } else { + getCarMediaServiceSessionAsync(activeOrPausedMediaControllers); } - savedMediaSource = createSavedMediaSource(savedMediaSourceName, - activeOrPausedMediaControllers); - savedMediaSource = maybeReplacePrimaryMediaSource(savedMediaSource); - mPrimaryMediaSource.setValue(savedMediaSource); } else { updatePrimaryMediaSource(activeMediaControllers); } // Setup initial activeOrPausedMediaSources + if (!activeOrPausedMediaControllers.isEmpty()) { + updateActiveOrPausedMediaSources(activeOrPausedMediaControllers); + } + } + + private void setSavedMediaSource(String savedMediaSourceName, + List<MediaController> activeOrPausedMediaControllers) { + MediaSource savedMediaSource = createSavedMediaSource(savedMediaSourceName, + activeOrPausedMediaControllers); + savedMediaSource = maybeReplacePrimaryMediaSource(savedMediaSource); + mPrimaryMediaSource.setValue(savedMediaSource); + if (activeOrPausedMediaControllers.isEmpty()) { if (savedMediaSource != null) { mActiveOrPausedMediaSources.setValue(Collections.singletonList(savedMediaSource)); } - } else { - updateActiveOrPausedMediaSources(activeOrPausedMediaControllers); } } + private void getCarMediaServiceSessionAsync( + List<MediaController> activeOrPausedMediaControllers) { + Context context = mContext.get(); + if (context == null) { + return; + } + + Car.createCar(context, null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, (car, ready) -> { + if (!ready) { + return; + } + try { + CarMediaManager carMediaManager = + (CarMediaManager) car.getCarManager(Car.CAR_MEDIA_SERVICE); + if (carMediaManager != null) { + ComponentName componentName = carMediaManager.getMediaSource( + MEDIA_SOURCE_MODE_PLAYBACK); + if (componentName != null) { + setSavedMediaSource(componentName.flattenToString(), + activeOrPausedMediaControllers); + } + } + } catch (SecurityException e) { + Log.e(TAG, "Unable to read CarMediaManager media source. Requires " + + "android.permission.MEDIA_CONTENT_CONTROL " + e); + } finally { + if (car != null) { + car.disconnect(); + } + } + }); + } + private MediaSource createSavedMediaSource(String savedMediaSourceName, List<MediaController> activeOrPausedControllers) { String packageName = savedMediaSourceName; @@ -538,31 +577,7 @@ } } - private String getLastActiveMediaSource() { - String mediaSource = mSharedPrefs.getString(LAST_ACTIVE_MEDIA_SOURCE, ""); - return TextUtils.isEmpty(mediaSource) - ? getCarMediaServiceSession() : mediaSource; - } - - private String getCarMediaServiceSession() { - Car car = Car.createCar(mContext.get()); - ComponentName componentName = null; - if (car != null) { - CarMediaManager carMediaManager = - (CarMediaManager) car.getCarManager(Car.CAR_MEDIA_SERVICE); - try { - componentName = carMediaManager.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK); - } catch (SecurityException e) { - Log.e(TAG, "Unable to read CarMediaManager media source. Requires " - + "android.permission.MEDIA_CONTENT_CONTROL " + e); - } - car.disconnect(); - } - - // ComponentName may be null b/355078140 - return componentName == null ? "" : componentName.flattenToString(); - } /* Copy of PlaybackState.isActive() which is only available for minsdk >=S */ private boolean isActive(int playbackState) {
diff --git a/car-media-common/src/com/android/car/media/common/source/MediaSource.java b/car-media-common/src/com/android/car/media/common/source/MediaSource.java index cb4461b..398f9a1 100644 --- a/car-media-common/src/com/android/car/media/common/source/MediaSource.java +++ b/car-media-common/src/com/android/car/media/common/source/MediaSource.java
@@ -328,12 +328,16 @@ Log.e(TAG, "getServiceFromExtras null controller"); return null; } - Bundle extras = controller.getExtras(); - if (extras == null || extras.getString(Car.CAR_EXTRA_BROWSE_SERVICE_FOR_SESSION) == null) { + + final Bundle extras = controller.getExtras(); + if (extras == null) { return null; } - String serviceNameString = - controller.getExtras().getString(Car.CAR_EXTRA_BROWSE_SERVICE_FOR_SESSION); + + final String serviceNameString = extras.getString(Car.CAR_EXTRA_BROWSE_SERVICE_FOR_SESSION); + if (serviceNameString == null) { + return null; + } return new ComponentName(controller.getPackageName(), serviceNameString); } @@ -395,6 +399,15 @@ } /** + * @return the {@link ComponentName} of the ignored browse service, if it was created with + * ignoreBrowser=true. + */ + @Nullable + public ComponentName getIgnoredBrowseServiceComponentName() { + return mIgnoredBrowseService; + } + + /** * @return a {@link Drawable} as the media source's icon. */ @NonNull
diff --git a/car-media-common/tests/unittests/src/com/android/car/media/common/playback/PlaybackViewModelTest.java b/car-media-common/tests/unittests/src/com/android/car/media/common/playback/PlaybackViewModelTest.java index 5f7b664..5619c94 100644 --- a/car-media-common/tests/unittests/src/com/android/car/media/common/playback/PlaybackViewModelTest.java +++ b/car-media-common/tests/unittests/src/com/android/car/media/common/playback/PlaybackViewModelTest.java
@@ -40,6 +40,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.android.car.media.common.CustomPlaybackAction; import com.android.car.media.common.MediaItemMetadata; import com.android.car.media.common.source.MediaBrowserConnector.BrowsingState; import com.android.car.media.common.source.MediaBrowserConnector.ConnectionStatus; @@ -298,6 +299,17 @@ deliverValuesToCallbacks(newCallbackCaptor, newMetadata, newPlaybackState); } + @Test + public void testRawCustomPlaybackAction_fetchDrawable_invalidResource_returnsNull() { + int nonDrawableResourceId = android.R.string.ok; + PlaybackViewModel.RawCustomPlaybackAction rawAction = + new PlaybackViewModel.RawCustomPlaybackAction( + nonDrawableResourceId, null, "action", null); + + CustomPlaybackAction action = rawAction.fetchDrawable(mContext); + assertThat(action).isNull(); + } + private void deliverValuesToCallbacks( ArgumentCaptor<MediaControllerCompat.Callback> callbackCaptor, MediaMetadataCompat metadata,
diff --git a/car-ui-lib/aaos-studio/AndroidManifest.xml b/car-ui-lib/aaos-studio/AndroidManifest.xml index 23a6244..e9b3ad2 100644 --- a/car-ui-lib/aaos-studio/AndroidManifest.xml +++ b/car-ui-lib/aaos-studio/AndroidManifest.xml
@@ -32,6 +32,10 @@ <uses-library android:name="com.android.oem.tokens" android:required="false"/> + <provider android:name=".TokenProvider" + android:authorities="com.android.aaos.studio.provider" + android:exported="true" /> + <activity android:name=".TokenActivity" android:exported="true">
diff --git a/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenActivity.java b/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenActivity.java index c88c2da..049b7f6 100644 --- a/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenActivity.java +++ b/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenActivity.java
@@ -23,9 +23,10 @@ import android.graphics.Color; import android.graphics.drawable.GradientDrawable; import android.os.Bundle; -import android.util.Pair; import android.util.TypedValue; import android.view.View; +import android.widget.EditText; +import android.widget.RadioButton; import android.widget.SeekBar; import android.widget.Switch; import android.widget.Toast; @@ -39,12 +40,21 @@ import com.android.car.ui.toolbar.ToolbarController; import com.google.ux.material.libmonet.blend.Blend; +import com.google.ux.material.libmonet.dynamiccolor.DynamicScheme; import com.google.ux.material.libmonet.hct.Hct; +import com.google.ux.material.libmonet.palettes.TonalPalette; +import com.google.ux.material.libmonet.scheme.SchemeContent; +import com.google.ux.material.libmonet.scheme.SchemeExpressive; +import com.google.ux.material.libmonet.scheme.SchemeNeutral; +import com.google.ux.material.libmonet.scheme.SchemeTonalSpot; import com.google.ux.material.libmonet.scheme.SchemeVibrant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; + /** * Activity that shows displays the values of OEM design tokens. @@ -58,10 +68,14 @@ private OverlayManager mOverlayManager; private View mColorPreview; - private SchemeVibrant mScheme; + private DynamicScheme mScheme; private boolean mIsLightMode; private boolean mSquareCorners; private SeekBar mSeekBar; + private EditText mHexInput; + private String mSchemeType = "Vibrant"; + private float mSaturation = 1.0f; + private float mValue = 1.0f; @Override protected void onCreate(Bundle savedInstanceState) { @@ -111,30 +125,106 @@ Switch cornerSwitch = requireViewById(R.id.corner_switch); mColorPreview = requireViewById(R.id.color_preview); mSeekBar = requireViewById(R.id.color_seekbar); + mHexInput = requireViewById(R.id.hex_input); + mHexInput.setOnEditorActionListener((v, actionId, event) -> { + String text = v.getText().toString(); + if (text.matches("^#?([0-9a-fA-F]{6})$")) { + int color = Color.parseColor(text.startsWith("#") ? text : "#" + text); + float[] hsv = new float[3]; + Color.colorToHSV(color, hsv); + mSeekBar.setProgress((int) hsv[0]); + mSaturation = hsv[1]; + mValue = hsv[2]; + updateColorPreview(); + } else { + Toast.makeText(this, "Invalid hex color", Toast.LENGTH_SHORT).show(); + mSeekBar.setProgress(0); + mSaturation = 1.0f; + mValue = 1.0f; + updateColorPreview(); + } + return false; + }); mIsLightMode = isLightMode(Token.getColor(this, R.attr.oemColorSurface), Token.getColor(this, R.attr.oemColorOnSurface)); - lightSwitch.setChecked(mIsLightMode); - lightSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> mIsLightMode = isChecked); + lightSwitch.setChecked(!mIsLightMode); + lightSwitch.setOnCheckedChangeListener( + (buttonView, isChecked) -> mIsLightMode = !isChecked); mSquareCorners = Token.getCornerRadius(this, R.attr.oemShapeCornerFull) == 0; cornerSwitch.setChecked(mSquareCorners); cornerSwitch.setOnCheckedChangeListener( (buttonView, isChecked) -> mSquareCorners = isChecked); + List<RadioButton> radioButtons = new ArrayList<>(); + RadioButton rbTonalSpot = requireViewById(R.id.scheme_tonal_spot); + RadioButton rbVibrant = requireViewById(R.id.scheme_vibrant); + RadioButton rbExpressive = requireViewById(R.id.scheme_expressive); + RadioButton rbNeutral = requireViewById(R.id.scheme_neutral); + RadioButton rbContent = requireViewById(R.id.scheme_content); + + radioButtons.add(rbTonalSpot); + radioButtons.add(rbVibrant); + radioButtons.add(rbExpressive); + radioButtons.add(rbNeutral); + radioButtons.add(rbContent); + + for (RadioButton rb : radioButtons) { + rb.setOnClickListener(v -> { + for (RadioButton other : radioButtons) { + other.setChecked(other == v); + } + if (v.getId() == R.id.scheme_tonal_spot) { + mSchemeType = "Tonal Spot"; + } else if (v.getId() == R.id.scheme_vibrant) { + mSchemeType = "Vibrant"; + } else if (v.getId() == R.id.scheme_expressive) { + mSchemeType = "Expressive"; + } else if (v.getId() == R.id.scheme_neutral) { + mSchemeType = "Neutral"; + } else if (v.getId() == R.id.scheme_content) { + mSchemeType = "Content"; + } + }); + } + List<MenuItem> menuItems = new ArrayList<>(); menuItems.add(MenuItem.builder(this) - .setTitle("Enable RRO") + .setTitle("Apply") .setOnClickListener(i -> { int seedColor = getSeedColor(); Hct seed = Hct.fromInt(seedColor); - mScheme = new SchemeVibrant(seed, !mIsLightMode, 0.0); - updateOverlay(); - updateFrameworkOverlay(); + boolean isDark = !mIsLightMode; + switch (mSchemeType) { + case "Tonal Spot": mScheme = new SchemeTonalSpot(seed, isDark, 0.0); break; + case "Expressive": mScheme = new SchemeExpressive(seed, isDark, 0.0); break; + case "Neutral": mScheme = new SchemeNeutral(seed, isDark, 0.0); break; + case "Content": mScheme = new SchemeContent(seed, isDark, 0.0); break; + default: mScheme = new SchemeVibrant(seed, isDark, 0.0); break; + } + + FabricatedOverlay appOverlay = createAppOverlay(); + FabricatedOverlay frameworkOverlay = createFrameworkOverlay(); + + OverlayManagerTransaction.Builder transaction = + new OverlayManagerTransaction.Builder() + .unregisterFabricatedOverlay( + new OverlayIdentifier(OWNING_PACKAGE, OVERLAY_NAME)) + .unregisterFabricatedOverlay( + new OverlayIdentifier(OWNING_PACKAGE, ANDROID_OVERLAY_NAME)) + .registerFabricatedOverlay(appOverlay) + .setEnabled(appOverlay.getIdentifier(), true) + .setEnabled(appOverlay.getIdentifier(), true, 0) + .registerFabricatedOverlay(frameworkOverlay) + .setEnabled(frameworkOverlay.getIdentifier(), true) + .setEnabled(frameworkOverlay.getIdentifier(), true, 0); + + mOverlayManager.commit(transaction.build()); }) .build()); menuItems.add(MenuItem.builder(this) - .setTitle("Disable RRO") + .setTitle("Reset") .setOnClickListener(i -> { disableOverlay(); disableFrameworkOverlay(); @@ -173,14 +263,16 @@ private int getSeedColor() { float hue = (float) mSeekBar.getProgress(); - // Set saturation and value to max 1.0f - float[] hsv = {hue, 1.0f, 1.0f}; + float[] hsv = {hue, mSaturation, mValue}; return Color.HSVToColor(hsv); } private void updateColorPreview() { int color = getSeedColor(); ((GradientDrawable) mColorPreview.getBackground()).setColor(color); + if (mHexInput != null) { + mHexInput.setText(String.format("#%06X", (0xFFFFFF & color))); + } } private void disableOverlay() { @@ -199,11 +291,10 @@ mOverlayManager.commit(transaction.build()); } - private void updateOverlay() { - disableOverlay(); + private FabricatedOverlay createAppOverlay() { - FabricatedOverlay overlay = new FabricatedOverlay.Builder(OWNING_PACKAGE, OVERLAY_NAME, - TARGET_PACKAGE) + FabricatedOverlay.Builder builder = new FabricatedOverlay.Builder( + OWNING_PACKAGE, OVERLAY_NAME, TARGET_PACKAGE) .setResourceValue("com.android.oem.tokens:string/theme_overlay", TypedValue.TYPE_STRING, "oem.brand.model.android.rro:style/OemThemeOverlay", null) @@ -242,10 +333,7 @@ TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getErrorContainer(), null) .setResourceValue("com.android.oem.tokens:color/color_on_error_container", TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnErrorContainer(), null) - .setResourceValue("com.android.oem.tokens:color/color_background", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getBackground(), null) - .setResourceValue("com.android.oem.tokens:color/color_on_background", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnBackground(), null) + .setResourceValue("com.android.oem.tokens:color/color_surface", TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurface(), null) .setResourceValue("com.android.oem.tokens:color/color_on_surface", @@ -280,53 +368,94 @@ TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainerHighest(), null) .setResourceValue("com.android.oem.tokens:color/color_blue", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_blue_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorBlue), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_on_blue", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_on_blue_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorOnBlue), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_blue_container", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_blue_container_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorBlueContainer), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_on_blue_container", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_on_blue_container_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorOnBlueContainer), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_green", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_green_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorGreen), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_on_green", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_on_green_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorOnGreen), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_green_container", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_green_container_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorGreenContainer), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_on_green_container", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_on_green_container_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorOnGreenContainer), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_yellow", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_yellow_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorYellow), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_on_yellow", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_on_yellow_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorOnYellow), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_yellow_container", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_yellow_container_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorYellowContainer), mScheme.getPrimary()), null) .setResourceValue("com.android.oem.tokens:color/color_on_yellow_container", TypedValue.TYPE_INT_COLOR_ARGB8, - Blend.harmonize(getColor(R.color.oem_color_on_yellow_container_dark), + Blend.harmonize(Token.getColor(this, R.attr.oemColorOnYellowContainer), mScheme.getPrimary()), null) - .build(); + .setResourceValue("com.android.oem.tokens:color/color_red", + TypedValue.TYPE_INT_COLOR_ARGB8, + Blend.harmonize(Token.getColor(this, R.attr.oemColorRed), + mScheme.getPrimary()), null) + .setResourceValue("com.android.oem.tokens:color/color_on_red", + TypedValue.TYPE_INT_COLOR_ARGB8, + Blend.harmonize(Token.getColor(this, R.attr.oemColorOnRed), + mScheme.getPrimary()), null) + .setResourceValue("com.android.oem.tokens:color/color_red_container", + TypedValue.TYPE_INT_COLOR_ARGB8, + Blend.harmonize(Token.getColor(this, R.attr.oemColorRedContainer), + mScheme.getPrimary()), null) + .setResourceValue("com.android.oem.tokens:color/color_on_red_container", + TypedValue.TYPE_INT_COLOR_ARGB8, + Blend.harmonize(Token.getColor(this, R.attr.oemColorOnRedContainer), + mScheme.getPrimary()), null); + + // Palette tones + int[] tones = {100, 99, 95, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0}; + String[] palettes = { + "primary", "secondary", "tertiary", "error", "neutral", "neutral_variant"}; + + for (String palette : palettes) { + TonalPalette tonalPalette; + switch (palette) { + case "primary": tonalPalette = mScheme.primaryPalette; break; + case "secondary": tonalPalette = mScheme.secondaryPalette; break; + case "tertiary": tonalPalette = mScheme.tertiaryPalette; break; + case "error": tonalPalette = mScheme.errorPalette; break; + case "neutral": tonalPalette = mScheme.neutralPalette; break; + case "neutral_variant": tonalPalette = mScheme.neutralVariantPalette; break; + default: continue; + } + + for (int tone : tones) { + String resName = "color_" + palette + "_palette_" + tone; + builder.setResourceValue("com.android.oem.tokens:color/" + resName, + TypedValue.TYPE_INT_COLOR_ARGB8, tonalPalette.tone(tone), null); + } + } + + FabricatedOverlay overlay = builder.build(); if (mSquareCorners) { overlay.setResourceValue("com.android.oem.tokens:dimen/corner_none", @@ -345,16 +474,10 @@ 0f, TypedValue.COMPLEX_UNIT_DIP, null); } - OverlayManagerTransaction.Builder transaction = - new OverlayManagerTransaction.Builder() - .registerFabricatedOverlay(overlay) - .setEnabled(overlay.getIdentifier(), true) - .setEnabled(overlay.getIdentifier(), true, 0); - - mOverlayManager.commit(transaction.build()); + return overlay; } - private void updateFrameworkOverlay() { + private FabricatedOverlay createFrameworkOverlay() { FabricatedOverlay overlay = new FabricatedOverlay.Builder(OWNING_PACKAGE, ANDROID_OVERLAY_NAME, "android") .build(); @@ -372,275 +495,238 @@ overlay.setResourceValue("android:bool/config_useRoundIcon", TypedValue.TYPE_INT_BOOLEAN, isRoundCorner, null); - overlay.setResourceValue("android:color/system_primary_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getPrimary(), null); - overlay.setResourceValue("android:color/system_primary_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getPrimary(), null); - overlay.setResourceValue("android:color/system_on_primary_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnPrimary(), null); - overlay.setResourceValue("android:color/system_on_primary_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnPrimary(), null); - overlay.setResourceValue("android:color/system_primary_container_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getPrimaryContainer(), null); - overlay.setResourceValue("android:color/system_primary_container_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getPrimaryContainer(), null); - overlay.setResourceValue("android:color/system_on_primary_container_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnPrimaryContainer(), null); - overlay.setResourceValue("android:color/system_on_primary_container_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnPrimaryContainer(), null); - overlay.setResourceValue("android:color/system_secondary_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSecondary(), null); - overlay.setResourceValue("android:color/system_secondary_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSecondary(), null); - overlay.setResourceValue("android:color/system_on_secondary_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnSecondary(), null); - overlay.setResourceValue("android:color/system_on_secondary_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnSecondary(), null); - overlay.setResourceValue("android:color/system_secondary_container_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSecondaryContainer(), null); - overlay.setResourceValue("android:color/system_secondary_container_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSecondaryContainer(), null); - overlay.setResourceValue("android:color/system_on_secondary_container_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnSecondaryContainer(), null); - overlay.setResourceValue("android:color/system_on_secondary_container_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnSecondaryContainer(), null); - overlay.setResourceValue("android:color/system_tertiary_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getTertiary(), null); - overlay.setResourceValue("android:color/system_tertiary_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getTertiary(), null); - overlay.setResourceValue("android:color/system_on_tertiary_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnTertiary(), null); - overlay.setResourceValue("android:color/system_on_tertiary_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnTertiary(), null); - overlay.setResourceValue("android:color/system_tertiary_container_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getTertiaryContainer(), null); - overlay.setResourceValue("android:color/system_tertiary_container_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getTertiaryContainer(), null); - overlay.setResourceValue("android:color/system_on_tertiary_container_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnTertiaryContainer(), null); - overlay.setResourceValue("android:color/system_on_tertiary_container_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnTertiaryContainer(), null); - overlay.setResourceValue("android:color/system_error_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getError(), null); - overlay.setResourceValue("android:color/system_error_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getError(), null); - overlay.setResourceValue("android:color/system_on_error_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnError(), null); - overlay.setResourceValue("android:color/system_on_error_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnError(), null); - overlay.setResourceValue("android:color/system_error_container_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getErrorContainer(), null); - overlay.setResourceValue("android:color/system_error_container_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getErrorContainer(), null); - overlay.setResourceValue("android:color/system_on_error_container_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnErrorContainer(), null); - overlay.setResourceValue("android:color/system_on_error_container_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnErrorContainer(), null); - overlay.setResourceValue("android:color/system_surface_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurface(), null); - overlay.setResourceValue("android:color/system_surface_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurface(), null); - overlay.setResourceValue("android:color/system_on_surface_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnSurface(), null); - overlay.setResourceValue("android:color/system_on_surface_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnSurface(), null); - overlay.setResourceValue("android:color/system_surface_variant_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceVariant(), null); - overlay.setResourceValue("android:color/system_surface_variant_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceVariant(), null); - overlay.setResourceValue("android:color/system_on_surface_variant_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnSurfaceVariant(), null); - overlay.setResourceValue("android:color/system_on_surface_variant_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOnSurfaceVariant(), null); - overlay.setResourceValue("android:color/system_inverse_surface_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getInverseSurface(), null); - overlay.setResourceValue("android:color/system_inverse_surface_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getInverseSurface(), null); - overlay.setResourceValue("android:color/system_inverse_on_surface_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getInverseOnSurface(), null); - overlay.setResourceValue("android:color/system_inverse_on_surface_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getInverseOnSurface(), null); - overlay.setResourceValue("android:color/system_outline_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOutline(), null); - overlay.setResourceValue("android:color/system_outline_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOutline(), null); - overlay.setResourceValue("android:color/system_outline_variant_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOutlineVariant(), null); - overlay.setResourceValue("android:color/system_outline_variant_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getOutlineVariant(), null); - overlay.setResourceValue("android:color/system_scrim_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getScrim(), null); - overlay.setResourceValue("android:color/system_scrim_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getScrim(), null); - overlay.setResourceValue("android:color/system_surface_dim_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceDim(), null); - overlay.setResourceValue("android:color/system_surface_dim_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceDim(), null); - overlay.setResourceValue("android:color/system_surface_bright_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceBright(), null); - overlay.setResourceValue("android:color/system_surface_bright_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceBright(), null); - overlay.setResourceValue("android:color/system_surface_container_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainer(), null); - overlay.setResourceValue("android:color/system_surface_container_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainer(), null); - overlay.setResourceValue("android:color/system_surface_container_low_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainerLow(), null); - overlay.setResourceValue("android:color/system_surface_container_low_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainerLow(), null); - overlay.setResourceValue("android:color/system_surface_container_lowest_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainerLowest(), null); - overlay.setResourceValue("android:color/system_surface_container_lowest_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainerLowest(), null); - overlay.setResourceValue("android:color/system_surface_container_high_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainerHigh(), null); - overlay.setResourceValue("android:color/system_surface_container_high_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainerHigh(), null); - overlay.setResourceValue("android:color/system_surface_container_highest_dark", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainerHighest(), null); - overlay.setResourceValue("android:color/system_surface_container_highest_light", - TypedValue.TYPE_INT_COLOR_ARGB8, mScheme.getSurfaceContainerHighest(), null); + Map<String, Integer> semanticColors = new HashMap<>(); + semanticColors.put("primary", mScheme.getPrimary()); + semanticColors.put("on_primary", mScheme.getOnPrimary()); + semanticColors.put("primary_container", mScheme.getPrimaryContainer()); + semanticColors.put("on_primary_container", mScheme.getOnPrimaryContainer()); + semanticColors.put("secondary", mScheme.getSecondary()); + semanticColors.put("on_secondary", mScheme.getOnSecondary()); + semanticColors.put("secondary_container", mScheme.getSecondaryContainer()); + semanticColors.put("on_secondary_container", mScheme.getOnSecondaryContainer()); + semanticColors.put("tertiary", mScheme.getTertiary()); + semanticColors.put("on_tertiary", mScheme.getOnTertiary()); + semanticColors.put("tertiary_container", mScheme.getTertiaryContainer()); + semanticColors.put("on_tertiary_container", mScheme.getOnTertiaryContainer()); + semanticColors.put("error", mScheme.getError()); + semanticColors.put("on_error", mScheme.getOnError()); + semanticColors.put("error_container", mScheme.getErrorContainer()); + semanticColors.put("on_error_container", mScheme.getOnErrorContainer()); + semanticColors.put("surface", mScheme.getSurface()); + semanticColors.put("on_surface", mScheme.getOnSurface()); + semanticColors.put("surface_variant", mScheme.getSurfaceVariant()); + semanticColors.put("on_surface_variant", mScheme.getOnSurfaceVariant()); + semanticColors.put("inverse_surface", mScheme.getInverseSurface()); + semanticColors.put("inverse_on_surface", mScheme.getInverseOnSurface()); + semanticColors.put("outline", mScheme.getOutline()); + semanticColors.put("outline_variant", mScheme.getOutlineVariant()); + semanticColors.put("scrim", mScheme.getScrim()); + semanticColors.put("surface_dim", mScheme.getSurfaceDim()); + semanticColors.put("surface_bright", mScheme.getSurfaceBright()); + semanticColors.put("surface_container", mScheme.getSurfaceContainer()); + semanticColors.put("surface_container_low", mScheme.getSurfaceContainerLow()); + semanticColors.put("surface_container_lowest", mScheme.getSurfaceContainerLowest()); + semanticColors.put("surface_container_high", mScheme.getSurfaceContainerHigh()); + semanticColors.put("surface_container_highest", mScheme.getSurfaceContainerHighest()); - OverlayManagerTransaction.Builder transaction = - new OverlayManagerTransaction.Builder() - .registerFabricatedOverlay(overlay) - .setEnabled(overlay.getIdentifier(), true) - .setEnabled(overlay.getIdentifier(), true, 0); + for (Map.Entry<String, Integer> entry : semanticColors.entrySet()) { + String name = entry.getKey(); + int color = entry.getValue(); + overlay.setResourceValue("android:color/system_" + name + "_dark", + TypedValue.TYPE_INT_COLOR_ARGB8, color, null); + overlay.setResourceValue("android:color/system_" + name + "_light", + TypedValue.TYPE_INT_COLOR_ARGB8, color, null); + } - mOverlayManager.commit(transaction.build()); + // Palette tones + int[] tones = {0, 10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}; + String[] palettes = {"accent1", "accent2", "accent3", "neutral1", "neutral2", "error"}; + + for (String palette : palettes) { + TonalPalette tonalPalette; + switch (palette) { + case "accent1": tonalPalette = mScheme.primaryPalette; break; + case "accent2": tonalPalette = mScheme.secondaryPalette; break; + case "accent3": tonalPalette = mScheme.tertiaryPalette; break; + case "neutral1": tonalPalette = mScheme.neutralPalette; break; + case "neutral2": tonalPalette = mScheme.neutralVariantPalette; break; + case "error": tonalPalette = mScheme.errorPalette; break; + default: continue; + } + + for (int tone : tones) { + int toneValue = 100 - tone / 10; + overlay.setResourceValue("android:color/system_" + palette + "_" + tone, + TypedValue.TYPE_INT_COLOR_ARGB8, tonalPalette.tone(toneValue), null); + } + } + + return overlay; } - private List<Pair<String, Integer>> createColorList() { - List<Pair<String, Integer>> list = new ArrayList<>(); + private List<TokenDemoAdapter.TokenItem> createColorList() { + List<TokenDemoAdapter.TokenItem> list = new ArrayList<>(); - list.add(new Pair<>("colorPrimary", R.attr.oemColorPrimary)); - list.add(new Pair<>("colorOnPrimary", R.attr.oemColorOnPrimary)); - list.add(new Pair<>("colorPrimaryContainer", - R.attr.oemColorPrimaryContainer)); - list.add(new Pair<>("colorPrimaryOnContainer", - R.attr.oemColorOnPrimaryContainer)); + list.add(new TokenDemoAdapter.TokenItem("colorPrimary", R.attr.oemColorPrimary, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnPrimary", R.attr.oemColorOnPrimary, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorPrimaryContainer", + R.attr.oemColorPrimaryContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorPrimaryOnContainer", + R.attr.oemColorOnPrimaryContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); - list.add(new Pair<>("colorSecondary", R.attr.oemColorSecondary)); - list.add(new Pair<>("colorOnSecondary", R.attr.oemColorOnSecondary)); - list.add(new Pair<>("colorSecondaryContainer", - R.attr.oemColorSecondaryContainer)); - list.add(new Pair<>("colorSecondaryOnContainer", - R.attr.oemColorOnSecondaryContainer)); + list.add(new TokenDemoAdapter.TokenItem("colorSecondary", R.attr.oemColorSecondary, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnSecondary", R.attr.oemColorOnSecondary, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorSecondaryContainer", + R.attr.oemColorSecondaryContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorSecondaryOnContainer", + R.attr.oemColorOnSecondaryContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); - list.add(new Pair<>("colorTertiary", R.attr.oemColorTertiary)); - list.add(new Pair<>("colorOnTertiary", R.attr.oemColorOnTertiary)); - list.add(new Pair<>("colorTertiaryContainer", - R.attr.oemColorTertiaryContainer)); - list.add(new Pair<>("colorTertiaryOnContainer", - R.attr.oemColorOnTertiaryContainer)); + list.add(new TokenDemoAdapter.TokenItem("colorTertiary", R.attr.oemColorTertiary, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnTertiary", R.attr.oemColorOnTertiary, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorTertiaryContainer", + R.attr.oemColorTertiaryContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorTertiaryOnContainer", + R.attr.oemColorOnTertiaryContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); - list.add(new Pair<>("colorError", R.attr.oemColorError)); - list.add(new Pair<>("colorOnError", R.attr.oemColorOnError)); - list.add(new Pair<>("colorErrorContainer", R.attr.oemColorErrorContainer)); - list.add(new Pair<>("colorErrorOnContainer", - R.attr.oemColorOnErrorContainer)); + list.add(new TokenDemoAdapter.TokenItem("colorError", R.attr.oemColorError, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnError", R.attr.oemColorOnError, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorErrorContainer", + R.attr.oemColorErrorContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorErrorOnContainer", + R.attr.oemColorOnErrorContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); - list.add(new Pair<>("colorSurface", R.attr.oemColorSurface)); - list.add(new Pair<>("colorOnSurface", R.attr.oemColorOnSurface)); - list.add(new Pair<>("colorSurfaceVariant", R.attr.oemColorSurfaceVariant)); - list.add(new Pair<>("colorOnSurfaceVariant", - R.attr.oemColorOnSurfaceVariant)); + list.add(new TokenDemoAdapter.TokenItem("colorSurface", R.attr.oemColorSurface, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnSurface", R.attr.oemColorOnSurface, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorSurfaceVariant", + R.attr.oemColorSurfaceVariant, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnSurfaceVariant", + R.attr.oemColorOnSurfaceVariant, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); - list.add(new Pair<>("colorOutline", R.attr.oemColorOutline)); + list.add(new TokenDemoAdapter.TokenItem("colorOutline", R.attr.oemColorOutline, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); - list.add(new Pair<>("colorSurfaceDim", R.attr.oemColorSurfaceDim)); - list.add(new Pair<>("colorSurfaceBright", R.attr.oemColorSurfaceBright)); - list.add(new Pair<>("colorSurfaceContainer", - R.attr.oemColorSurfaceContainer)); - list.add(new Pair<>("colorSurfaceContainerLow", R.attr.oemColorSurfaceContainerLow)); - list.add(new Pair<>("colorSurfaceContainerLowest", R.attr.oemColorSurfaceContainerLowest)); - list.add(new Pair<>("colorSurfaceContainerHigh", - R.attr.oemColorSurfaceContainerHigh)); - list.add(new Pair<>("colorSurfaceContainerHighest", - R.attr.oemColorSurfaceContainerHighest)); - list.add(new Pair<>("colorShadow", - R.attr.oemColorShadow)); + list.add(new TokenDemoAdapter.TokenItem("colorSurfaceDim", R.attr.oemColorSurfaceDim, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorSurfaceBright", R.attr.oemColorSurfaceBright, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorSurfaceContainer", + R.attr.oemColorSurfaceContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorSurfaceContainerLow", + R.attr.oemColorSurfaceContainerLow, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorSurfaceContainerLowest", + R.attr.oemColorSurfaceContainerLowest, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorSurfaceContainerHigh", + R.attr.oemColorSurfaceContainerHigh, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorSurfaceContainerHighest", + R.attr.oemColorSurfaceContainerHighest, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorShadow", + R.attr.oemColorShadow, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); - list.add(new Pair<>("colorBlue", R.attr.oemColorBlue)); - list.add(new Pair<>("colorOnBlue", - R.attr.oemColorOnBlue)); - list.add(new Pair<>("colorBlueContainer", R.attr.oemColorBlueContainer)); - list.add(new Pair<>("colorOnBlueContainer", - R.attr.oemColorOnBlueContainer)); + list.add(new TokenDemoAdapter.TokenItem("colorBlue", R.attr.oemColorBlue, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnBlue", + R.attr.oemColorOnBlue, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorBlueContainer", R.attr.oemColorBlueContainer, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnBlueContainer", + R.attr.oemColorOnBlueContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); - list.add(new Pair<>("colorGreen", R.attr.oemColorGreen)); - list.add(new Pair<>("colorOnGreen", - R.attr.oemColorOnGreenContainer)); - list.add(new Pair<>("colorGreenContainer", R.attr.oemColorGreenContainer)); - list.add(new Pair<>("colorOnGreenContainer", - R.attr.oemColorOnGreenContainer)); + list.add(new TokenDemoAdapter.TokenItem("colorGreen", R.attr.oemColorGreen, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnGreen", + R.attr.oemColorOnGreen, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorGreenContainer", + R.attr.oemColorGreenContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnGreenContainer", + R.attr.oemColorOnGreenContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); - list.add(new Pair<>("colorYellow", R.attr.oemColorYellow)); - list.add(new Pair<>("colorOnYellow", - R.attr.oemColorOnYellow)); - list.add(new Pair<>("colorYellowContainer", R.attr.oemColorYellowContainer)); - list.add(new Pair<>("colorOnYellowContainer", - R.attr.oemColorOnYellowContainer)); + list.add(new TokenDemoAdapter.TokenItem("colorYellow", R.attr.oemColorYellow, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnYellow", + R.attr.oemColorOnYellow, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorYellowContainer", + R.attr.oemColorYellowContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnYellowContainer", + R.attr.oemColorOnYellowContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + + list.add(new TokenDemoAdapter.TokenItem("colorRed", R.attr.oemColorRed, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnRed", R.attr.oemColorOnRed, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorRedContainer", R.attr.oemColorRedContainer, + TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); + list.add(new TokenDemoAdapter.TokenItem("colorOnRedContainer", + R.attr.oemColorOnRedContainer, TokenDemoAdapter.VIEW_TYPE_LIST_COLOR)); return list; } - private List<Pair<String, Integer>> createTextList() { - List<Pair<String, Integer>> list = new ArrayList<>(); - list.add(new Pair<>("textAppearanceDisplayLarge", - R.attr.oemTextAppearanceDisplayLarge)); - list.add(new Pair<>("textAppearanceDisplayMedium", - R.attr.oemTextAppearanceDisplayMedium)); - list.add(new Pair<>("textAppearanceDisplaySmall", - R.attr.oemTextAppearanceDisplaySmall)); + private List<TokenDemoAdapter.TokenItem> createTextList() { + List<TokenDemoAdapter.TokenItem> list = new ArrayList<>(); + list.add(new TokenDemoAdapter.TokenItem("Display Large", + R.attr.oemTextAppearanceDisplayLarge, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Display Medium", + R.attr.oemTextAppearanceDisplayMedium, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Display Small", + R.attr.oemTextAppearanceDisplaySmall, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); - list.add(new Pair<>("textAppearanceHeadlineLarge", - R.attr.oemTextAppearanceHeadlineLarge)); - list.add(new Pair<>("textAppearanceHeadlineMedium", - R.attr.oemTextAppearanceHeadlineMedium)); - list.add(new Pair<>("textAppearanceHeadlineSmall", - R.attr.oemTextAppearanceHeadlineSmall)); + list.add(new TokenDemoAdapter.TokenItem("Headline Large", + R.attr.oemTextAppearanceHeadlineLarge, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Headline Medium", + R.attr.oemTextAppearanceHeadlineMedium, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Headline Small", + R.attr.oemTextAppearanceHeadlineSmall, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); - list.add(new Pair<>("textAppearanceTitleLarge", - R.attr.oemTextAppearanceTitleLarge)); - list.add(new Pair<>("textAppearanceTitleMedium", - R.attr.oemTextAppearanceTitleMedium)); - list.add(new Pair<>("textAppearanceTitleSmall", - R.attr.oemTextAppearanceTitleSmall)); + list.add(new TokenDemoAdapter.TokenItem("Title Large", + R.attr.oemTextAppearanceTitleLarge, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Title Medium", + R.attr.oemTextAppearanceTitleMedium, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Title Small", + R.attr.oemTextAppearanceTitleSmall, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); - list.add(new Pair<>("textAppearanceLabelLarge", - R.attr.oemTextAppearanceLabelLarge)); - list.add(new Pair<>("textAppearanceLabelMedium", - R.attr.oemTextAppearanceLabelMedium)); - list.add(new Pair<>("textAppearanceLabelSmall", - R.attr.oemTextAppearanceLabelSmall)); + list.add(new TokenDemoAdapter.TokenItem("Label Large", + R.attr.oemTextAppearanceLabelLarge, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Label Medium", + R.attr.oemTextAppearanceLabelMedium, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Label Small", + R.attr.oemTextAppearanceLabelSmall, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); - list.add(new Pair<>("textAppearanceBodyLarge", - R.attr.oemTextAppearanceBodyLarge)); - list.add(new Pair<>("textAppearanceBodyMedium", - R.attr.oemTextAppearanceBodyMedium)); - list.add(new Pair<>("textAppearanceBodySmall", - R.attr.oemTextAppearanceBodySmall)); + list.add(new TokenDemoAdapter.TokenItem("Body Large", + R.attr.oemTextAppearanceBodyLarge, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Body Medium", + R.attr.oemTextAppearanceBodyMedium, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); + list.add(new TokenDemoAdapter.TokenItem("Body Small", + R.attr.oemTextAppearanceBodySmall, TokenDemoAdapter.VIEW_TYPE_LIST_TEXT)); return list; } - private List<Pair<String, Integer>> createCornerRadiusList() { - List<Pair<String, Integer>> list = new ArrayList<>(); - list.add(new Pair<>("shapeCornerNone", - R.attr.oemShapeCornerNone)); - list.add(new Pair<>("shapeCornerExtraSmall", - R.attr.oemShapeCornerExtraSmall)); - list.add(new Pair<>("shapeCornerSmall", - R.attr.oemShapeCornerSmall)); - list.add(new Pair<>("shapeCornerMedium", - R.attr.oemShapeCornerMedium)); - list.add(new Pair<>("shapeCornerLarge", - R.attr.oemShapeCornerLarge)); - list.add(new Pair<>("shapeCornerExtraLarge", - R.attr.oemShapeCornerExtraLarge)); - list.add(new Pair<>("shapeCornerFull", - R.attr.oemShapeCornerFull)); + private List<TokenDemoAdapter.TokenItem> createCornerRadiusList() { + List<TokenDemoAdapter.TokenItem> list = new ArrayList<>(); + list.add(new TokenDemoAdapter.TokenItem("Corner None", + R.attr.oemShapeCornerNone, TokenDemoAdapter.VIEW_TYPE_LIST_SHAPE)); + list.add(new TokenDemoAdapter.TokenItem("Corner Extra Small", + R.attr.oemShapeCornerExtraSmall, TokenDemoAdapter.VIEW_TYPE_LIST_SHAPE)); + list.add(new TokenDemoAdapter.TokenItem("Corner Small", + R.attr.oemShapeCornerSmall, TokenDemoAdapter.VIEW_TYPE_LIST_SHAPE)); + list.add(new TokenDemoAdapter.TokenItem("Corner Medium", + R.attr.oemShapeCornerMedium, TokenDemoAdapter.VIEW_TYPE_LIST_SHAPE)); + list.add(new TokenDemoAdapter.TokenItem("Corner Large", + R.attr.oemShapeCornerLarge, TokenDemoAdapter.VIEW_TYPE_LIST_SHAPE)); + list.add(new TokenDemoAdapter.TokenItem("Corner Extra Large", + R.attr.oemShapeCornerExtraLarge, TokenDemoAdapter.VIEW_TYPE_LIST_SHAPE)); + list.add(new TokenDemoAdapter.TokenItem("Corner Full", + R.attr.oemShapeCornerFull, TokenDemoAdapter.VIEW_TYPE_LIST_SHAPE)); return list; } @@ -653,6 +739,10 @@ if (r <= 0.001f) { return "M 0 0 H 100 V 100 H 0 Z"; // Simple rectangle } + // Handle the fully rounded (circle) case + if (r >= 49.99f) { + return "M 50 0 A 50 50 0 1 1 50 100 A 50 50 0 1 1 50 0 Z"; + } return String.format( Locale.US, "M %f 0 L %f 0 A %f %f 0 0 1 100 %f L 100 %f A %f %f 0 0 1 %f 100 L %f 100 A %f "
diff --git a/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenDemoAdapter.java b/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenDemoAdapter.java index 0d6f3be..6878958 100644 --- a/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenDemoAdapter.java +++ b/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenDemoAdapter.java
@@ -18,7 +18,6 @@ import android.content.Context; import android.graphics.drawable.GradientDrawable; -import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -41,9 +40,23 @@ static final int VIEW_TYPE_LIST_TEXT = 2; static final int VIEW_TYPE_LIST_SHAPE = 3; - private final List<Pair<String, Integer>> mItems; + public static class TokenItem { + public final String name; + public final int attrId; + public final int type; - public TokenDemoAdapter(List<Pair<String, Integer>> items) { + public TokenItem(String name, int attrId, int type) { + this.name = name; + this.attrId = attrId; + this.type = type; + } + } + + private final List<TokenItem> mItems; + // Cached value to avoid repeated slow reads of oemShapeCornerFull token + private Float mFullCornerRadius = null; + + public TokenDemoAdapter(List<TokenItem> items) { mItems = items; } @@ -51,36 +64,60 @@ @Override public TokenDemoItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - View view = inflater.inflate(R.layout.car_ui_recycler_view_list_item, parent, false); + View view; + switch (viewType) { + case VIEW_TYPE_LIST_COLOR: + view = inflater.inflate(R.layout.token_list_item_color, parent, false); + break; + case VIEW_TYPE_LIST_TEXT: + view = inflater.inflate(R.layout.token_list_item_text, parent, false); + break; + case VIEW_TYPE_LIST_SHAPE: + view = inflater.inflate(R.layout.token_list_item_shape, parent, false); + break; + default: + throw new IllegalArgumentException("Invalid view type: " + viewType); + } return new TokenDemoItemViewHolder(view); } @Override public void onBindViewHolder(@NonNull TokenDemoItemViewHolder holder, int position) { - Pair<String, Integer> tokenInfo = mItems.get(position); + TokenItem item = mItems.get(position); Context oemContext = Token.createOemStyledContext(holder.itemView.getContext()); switch (holder.getItemViewType()) { case VIEW_TYPE_LIST_COLOR: - holder.mText.setText(tokenInfo.first); - int color = Token.getColor(oemContext, tokenInfo.second); - holder.mText.setTextColor(color); + holder.mText.setText(item.name); + int color = Token.getColor(oemContext, item.attrId); + GradientDrawable circle = new GradientDrawable(); + circle.setShape(GradientDrawable.RECTANGLE); + if (mFullCornerRadius == null) { + mFullCornerRadius = Token.getCornerRadius(oemContext, + R.attr.oemShapeCornerFull); + } + circle.setCornerRadius(mFullCornerRadius); + circle.setColor(color); + holder.mColorIndicator.setBackground(circle); + holder.mText.setTextColor(Token.getColor(oemContext, R.attr.oemColorOnSurface)); break; case VIEW_TYPE_LIST_TEXT: - holder.mText.setText(tokenInfo.first); + holder.mText.setText(item.name); int textAppearanceId = Token.getTextAppearance(oemContext, - tokenInfo.second); + item.attrId); holder.mText.setTextAppearance(textAppearanceId); break; case VIEW_TYPE_LIST_SHAPE: float cornerRadius = Token.getCornerRadius(oemContext, - tokenInfo.second); - GradientDrawable background = new GradientDrawable(); - background.setColor( - holder.itemView.getResources().getColor(android.R.color.holo_blue_dark)); - background.setCornerRadius(cornerRadius); - holder.mText.setText(tokenInfo.first); - holder.mText.setBackground(background); + item.attrId); + GradientDrawable shape = new GradientDrawable(); + shape.setShape(GradientDrawable.RECTANGLE); + shape.setColor(Token.getColor(oemContext, R.attr.oemColorPrimary)); + shape.setCornerRadius(cornerRadius); + holder.mColorIndicator.setBackground(shape); + + holder.mText.setText(item.name); + break; default: throw new IllegalStateException("Unexpected value: " + holder.getItemViewType()); @@ -89,19 +126,10 @@ @Override public int getItemViewType(int position) { - Pair<String, Integer> tokenInfo = mItems.get(position); - if (tokenInfo.first.contains("color")) { - return VIEW_TYPE_LIST_COLOR; - } else if (tokenInfo.first.contains("textAppearance")) { - return VIEW_TYPE_LIST_TEXT; - } else if (tokenInfo.first.contains("shapeCorner")) { - return VIEW_TYPE_LIST_SHAPE; - } - - throw new IllegalStateException("Unknown view type."); - + return mItems.get(position).type; } + @Override public int getItemCount() { return mItems.size(); @@ -109,10 +137,12 @@ public static class TokenDemoItemViewHolder extends RecyclerView.ViewHolder { TextView mText; + View mColorIndicator; TokenDemoItemViewHolder(@NonNull View itemView) { super(itemView); mText = itemView.findViewById(R.id.textTitle); + mColorIndicator = itemView.findViewById(R.id.color_indicator); } } }
diff --git a/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenProvider.java b/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenProvider.java new file mode 100644 index 0000000..9932b6d --- /dev/null +++ b/car-ui-lib/aaos-studio/src/main/java/com/android/aaos/studio/TokenProvider.java
@@ -0,0 +1,74 @@ +/* + * Copyright (C) 2026 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.aaos.studio; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.car.oem.tokens.Token; + +import java.util.Objects; + +public class TokenProvider extends ContentProvider { + @Override + public boolean onCreate() { + return true; + } + + @Override + @Nullable + public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) { + if ("dumpTokens".equals(method)) { + Bundle bundle = new Bundle(); + bundle.putString("tokens", Token.dump(Objects.requireNonNull(getContext()))); + return bundle; + } + return null; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +}
diff --git a/car-ui-lib/aaos-studio/src/main/res/layout/car_ui_recycler_view_list_item.xml b/car-ui-lib/aaos-studio/src/main/res/layout/car_ui_recycler_view_list_item.xml deleted file mode 100644 index 597ebcc..0000000 --- a/car-ui-lib/aaos-studio/src/main/res/layout/car_ui_recycler_view_list_item.xml +++ /dev/null
@@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright 2019 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. - --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:layout_marginBottom="10dp" - android:id="@+id/textTitle" - android:text="TESTING"/> - -</LinearLayout>
diff --git a/car-ui-lib/aaos-studio/src/main/res/layout/token_activity.xml b/car-ui-lib/aaos-studio/src/main/res/layout/token_activity.xml index 14004b0..dd8df0c 100644 --- a/car-ui-lib/aaos-studio/src/main/res/layout/token_activity.xml +++ b/car-ui-lib/aaos-studio/src/main/res/layout/token_activity.xml
@@ -31,59 +31,146 @@ <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:padding="16dp"> <TextView - android:id="@+id/primary_label" - android:textSize="24sp" - android:layout_marginBottom="50dp" + android:id="@+id/seed_color_label" + android:textAppearance="?attr/oemTextAppearanceHeadlineSmall" + android:textStyle="bold" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Seed color"/> + android:text="Seed Color"/> <SeekBar android:id="@+id/color_seekbar" - android:layout_width="320dp" + android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginTop="8dp" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/primary_label" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/seed_color_label" android:progressDrawable="@drawable/hue_gradient" android:thumbTint="?android:attr/textColorPrimary" android:splitTrack="false" - android:max="320" + android:max="360" android:progress="0" /> + <TextView + android:id="@+id/hex_code_label" + android:textAppearance="?attr/oemTextAppearanceBodyLarge" + android:layout_marginTop="16dp" + app:layout_constraintTop_toBottomOf="@id/color_seekbar" + app:layout_constraintStart_toStartOf="parent" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Hex Code"/> + + <LinearLayout + android:id="@+id/hex_container" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:orientation="horizontal" + android:gravity="center_vertical" + app:layout_constraintTop_toBottomOf="@id/hex_code_label" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + <View + android:id="@+id/color_preview" + android:layout_width="56dp" + android:layout_height="56dp" + android:background="@drawable/round_bg"/> + + <EditText + android:id="@+id/hex_input" + android:layout_width="0dp" + android:layout_height="76dp" + android:layout_weight="1" + android:layout_marginStart="8dp" + android:fontFamily="monospace" + android:text="#B5CFFF" + android:textAllCaps="true" + android:textAppearance="?attr/oemTextAppearanceBodyLarge" + android:gravity="center_vertical" + android:inputType="textCapCharacters"/> + </LinearLayout> + + <GridLayout + android:id="@+id/scheme_grid" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:columnCount="2" + app:layout_constraintTop_toBottomOf="@id/hex_container" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + <RadioButton + android:id="@+id/scheme_tonal_spot" + android:layout_width="0dp" + android:layout_columnWeight="1" + android:minHeight="76dp" + android:textAppearance="?attr/oemTextAppearanceBodyLarge" + android:text="Tonal Spot"/> + <RadioButton + android:id="@+id/scheme_vibrant" + android:layout_width="0dp" + android:layout_columnWeight="1" + android:minHeight="76dp" + android:textAppearance="?attr/oemTextAppearanceBodyLarge" + android:text="Vibrant" + android:checked="true"/> + <RadioButton + android:id="@+id/scheme_expressive" + android:layout_width="0dp" + android:layout_columnWeight="1" + android:minHeight="76dp" + android:textAppearance="?attr/oemTextAppearanceBodyLarge" + android:text="Expressive"/> + <RadioButton + android:id="@+id/scheme_neutral" + android:layout_width="0dp" + android:layout_columnWeight="1" + android:minHeight="76dp" + android:textAppearance="?attr/oemTextAppearanceBodyLarge" + android:text="Neutral"/> + <RadioButton + android:id="@+id/scheme_content" + android:layout_width="0dp" + android:layout_columnWeight="1" + android:minHeight="76dp" + android:textAppearance="?attr/oemTextAppearanceBodyLarge" + android:text="Content"/> + </GridLayout> + <Switch android:id="@+id/light_switch" - app:layout_constraintTop_toBottomOf="@id/color_seekbar" + android:layout_marginTop="16dp" + app:layout_constraintTop_toBottomOf="@id/scheme_grid" app:layout_constraintStart_toStartOf="parent" - android:text="Light Mode" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" /> + app:layout_constraintEnd_toEndOf="parent" + android:text="Dark Mode" + android:textAppearance="?attr/oemTextAppearanceTitleLarge" + android:minHeight="76dp" + android:layout_width="0dp" + android:layout_height="wrap_content" /> <Switch android:id="@+id/corner_switch" + android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@id/light_switch" app:layout_constraintStart_toStartOf="parent" - android:text="Squared Corners" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" /> - - <View - android:id="@+id/color_preview" - app:layout_constraintTop_toTopOf="@id/primary_label" - app:layout_constraintBottom_toBottomOf="@id/light_switch" app:layout_constraintEnd_toEndOf="parent" - android:layout_width="100dp" - android:layout_height="100dp" - android:background="@drawable/round_bg"/> + android:text="Squared Corners" + android:textAppearance="?attr/oemTextAppearanceTitleLarge" + android:minHeight="76dp" + android:layout_width="0dp" + android:layout_height="wrap_content" /> </androidx.constraintlayout.widget.ConstraintLayout> - - </ScrollView> <androidx.constraintlayout.widget.Guideline
diff --git a/car-ui-lib/aaos-studio/src/main/res/layout/token_list_item_color.xml b/car-ui-lib/aaos-studio/src/main/res/layout/token_list_item_color.xml new file mode 100644 index 0000000..bcdd786 --- /dev/null +++ b/car-ui-lib/aaos-studio/src/main/res/layout/token_list_item_color.xml
@@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2025 The Android Open Source Project --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical"> + + <View + android:id="@+id/color_indicator" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginStart="16dp" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:id="@+id/textTitle"/> + +</LinearLayout>
diff --git a/car-ui-lib/aaos-studio/src/main/res/layout/token_list_item_shape.xml b/car-ui-lib/aaos-studio/src/main/res/layout/token_list_item_shape.xml new file mode 100644 index 0000000..1b9197e --- /dev/null +++ b/car-ui-lib/aaos-studio/src/main/res/layout/token_list_item_shape.xml
@@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2025 The Android Open Source Project --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical"> + + <View + android:id="@+id/color_indicator" + android:layout_width="104dp" + android:layout_height="48dp" + android:layout_marginStart="16dp" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:id="@+id/textTitle"/> + +</LinearLayout>
diff --git a/car-ui-lib/aaos-studio/src/main/res/layout/token_list_item_text.xml b/car-ui-lib/aaos-studio/src/main/res/layout/token_list_item_text.xml new file mode 100644 index 0000000..40ad034 --- /dev/null +++ b/car-ui-lib/aaos-studio/src/main/res/layout/token_list_item_text.xml
@@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2025 The Android Open Source Project --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:id="@+id/textTitle" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp"/> + +</LinearLayout>
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/pluginsupport/PluginFactorySingleton.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/pluginsupport/PluginFactorySingleton.java index 4c13a60..6b702cc 100644 --- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/pluginsupport/PluginFactorySingleton.java +++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/pluginsupport/PluginFactorySingleton.java
@@ -255,6 +255,14 @@ Log.e(TAG, "Something went wrong when invoking getPluginFactory: ", e); sInstance = new PluginFactoryStub(); return; + } catch (IncompatibleClassChangeError e) { + Log.e(TAG, "Something went wrong when invoking getPluginFactory: ", e); + sInstance = new PluginFactoryStub(); + return; + } catch (Throwable e) { + Log.e(TAG, "Something went wrong when invoking getPluginFactory: ", e); + sInstance = new PluginFactoryStub(); + return; } Log.i(TAG, "Loaded plugin " + pluginPackageName
diff --git a/car-ui-lib/car-ui-lib/src/main/res-private/values/priv-release_id.xml b/car-ui-lib/car-ui-lib/src/main/res-private/values/priv-release_id.xml index d6a7826..3144e0c 100644 --- a/car-ui-lib/car-ui-lib/src/main/res-private/values/priv-release_id.xml +++ b/car-ui-lib/car-ui-lib/src/main/res-private/values/priv-release_id.xml
@@ -17,5 +17,5 @@ <!-- This value should NOT be overlaid or changed by OEMs. It represents the approximate date when the code was pushed to AOSP. --> - <string name="com_android_car_ui_ub_release_id" translatable="false">2026-04-24</string> + <string name="com_android_car_ui_ub_release_id" translatable="false">2026-05-20</string> </resources>
diff --git a/car-ui-lib/oem-tokens/OWNERS b/car-ui-lib/oem-tokens/OWNERS new file mode 100644 index 0000000..43a3ffa --- /dev/null +++ b/car-ui-lib/oem-tokens/OWNERS
@@ -0,0 +1,9 @@ +# People who can approve changes for submission. +rlagos@google.com +alwa@google.com +nirajdesai@google.com +rampara@google.com + +# For urgent reviews only +stenning@google.com +
diff --git a/car-ui-lib/oem-tokens/lib/src/main/java/com/android/car/oem/tokens/Token.java b/car-ui-lib/oem-tokens/lib/src/main/java/com/android/car/oem/tokens/Token.java index 31f4ebe..498b923 100644 --- a/car-ui-lib/oem-tokens/lib/src/main/java/com/android/car/oem/tokens/Token.java +++ b/car-ui-lib/oem-tokens/lib/src/main/java/com/android/car/oem/tokens/Token.java
@@ -40,6 +40,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.TreeMap; /** * Public interface for general CarUi static functions. @@ -254,7 +255,12 @@ * Return a mapping of current values for OEM Design tokens. */ public static String dump(@NonNull Context context) { - return createTokenMap(context).toString(); + TreeMap<String, String> map = new TreeMap<>(createTokenMap(context)); + StringBuilder sb = new StringBuilder(); + for (java.util.Map.Entry<String, String> entry : map.entrySet()) { + sb.append(entry.getKey()).append("=").append(entry.getValue()).append("\n"); + } + return sb.toString(); } /**
diff --git a/car-ui-lib/oem-tokens/lib/src/main/res/values/colors.xml b/car-ui-lib/oem-tokens/lib/src/main/res/values/colors.xml index 68d251c..93bd831 100644 --- a/car-ui-lib/oem-tokens/lib/src/main/res/values/colors.xml +++ b/car-ui-lib/oem-tokens/lib/src/main/res/values/colors.xml
@@ -15,87 +15,87 @@ --> <resources> <color name="oem_default_color_primary_palette_100">#FFFFFF</color> - <color name="oem_default_color_primary_palette_99">#fbfcff</color> - <color name="oem_default_color_primary_palette_95">#e4f3ff</color> - <color name="oem_default_color_primary_palette_90">#c5e7ff</color> - <color name="oem_default_color_primary_palette_80">#91cef5</color> - <color name="oem_default_color_primary_palette_70">#76b2d8</color> - <color name="oem_default_color_primary_palette_60">#5a98bc</color> - <color name="oem_default_color_primary_palette_50">#3e7ea1</color> - <color name="oem_default_color_primary_palette_40">#1f6587</color> - <color name="oem_default_color_primary_palette_30">#004c6b</color> - <color name="oem_default_color_primary_palette_20">#00344b</color> - <color name="oem_default_color_primary_palette_10">#001e2d</color> + <color name="oem_default_color_primary_palette_99">#FAFBFF</color> + <color name="oem_default_color_primary_palette_95">#ECF3FE</color> + <color name="oem_default_color_primary_palette_90">#D3E3FD</color> + <color name="oem_default_color_primary_palette_80">#A8C7FA</color> + <color name="oem_default_color_primary_palette_70">#7CACF8</color> + <color name="oem_default_color_primary_palette_60">#4C8DF6</color> + <color name="oem_default_color_primary_palette_50">#1B6EF3</color> + <color name="oem_default_color_primary_palette_40">#0B57D0</color> + <color name="oem_default_color_primary_palette_30">#0842A0</color> + <color name="oem_default_color_primary_palette_20">#062E6F</color> + <color name="oem_default_color_primary_palette_10">#041E49</color> <color name="oem_default_color_primary_palette_0">#000000</color> <color name="oem_default_color_secondary_palette_100">#FFFFFF</color> - <color name="oem_default_color_secondary_palette_99">#fbfcff</color> - <color name="oem_default_color_secondary_palette_95">#EDF0FF</color> - <color name="oem_default_color_secondary_palette_90">#d2e5f4</color> - <color name="oem_default_color_secondary_palette_80">#b6c9d8</color> - <color name="oem_default_color_secondary_palette_70">#9baebc</color> - <color name="oem_default_color_secondary_palette_60">#8093a1</color> - <color name="oem_default_color_secondary_palette_50">#677987</color> - <color name="oem_default_color_secondary_palette_40">#4f616d</color> - <color name="oem_default_color_secondary_palette_30">#374955</color> - <color name="oem_default_color_secondary_palette_20">#20333e</color> - <color name="oem_default_color_secondary_palette_10">#0a1e28</color> + <color name="oem_default_color_secondary_palette_99">#F7FCFF</color> + <color name="oem_default_color_secondary_palette_95">#DFF3FF</color> + <color name="oem_default_color_secondary_palette_90">#C2E7FF</color> + <color name="oem_default_color_secondary_palette_80">#7FCFFF</color> + <color name="oem_default_color_secondary_palette_70">#5AB3F0</color> + <color name="oem_default_color_secondary_palette_60">#3998D3</color> + <color name="oem_default_color_secondary_palette_50">#047DB7</color> + <color name="oem_default_color_secondary_palette_40">#00639B</color> + <color name="oem_default_color_secondary_palette_30">#004A77</color> + <color name="oem_default_color_secondary_palette_20">#003355</color> + <color name="oem_default_color_secondary_palette_10">#001D35</color> <color name="oem_default_color_secondary_palette_0">#000000</color> <color name="oem_default_color_tertiary_palette_100">#FFFFFF</color> - <color name="oem_default_color_tertiary_palette_99">#fbfcff</color> - <color name="oem_default_color_tertiary_palette_95">#f5eeff</color> - <color name="oem_default_color_tertiary_palette_90">#e7deff</color> - <color name="oem_default_color_tertiary_palette_80">#cbc1e9</color> - <color name="oem_default_color_tertiary_palette_70">#b0a6cd</color> - <color name="oem_default_color_tertiary_palette_60">#958bb1</color> - <color name="oem_default_color_tertiary_palette_50">#7b7296</color> - <color name="oem_default_color_tertiary_palette_40">#62597c</color> - <color name="oem_default_color_tertiary_palette_30">#4a4263</color> - <color name="oem_default_color_tertiary_palette_20">#332c4c</color> - <color name="oem_default_color_tertiary_palette_10">#1e1735</color> + <color name="oem_default_color_tertiary_palette_99">#F2FFEE</color> + <color name="oem_default_color_tertiary_palette_95">#E7F8ED</color> + <color name="oem_default_color_tertiary_palette_90">#C4EED0</color> + <color name="oem_default_color_tertiary_palette_80">#6DD58C</color> + <color name="oem_default_color_tertiary_palette_70">#37BE5F</color> + <color name="oem_default_color_tertiary_palette_60">#1EA446</color> + <color name="oem_default_color_tertiary_palette_50">#198639</color> + <color name="oem_default_color_tertiary_palette_40">#146C2E</color> + <color name="oem_default_color_tertiary_palette_30">#0F5223</color> + <color name="oem_default_color_tertiary_palette_20">#0A3818</color> + <color name="oem_default_color_tertiary_palette_10">#072711</color> <color name="oem_default_color_tertiary_palette_0">#000000</color> <color name="oem_default_color_error_palette_100">#FFFFFF</color> <color name="oem_default_color_error_palette_99">#FFFBFF</color> - <color name="oem_default_color_error_palette_95">#ffedea</color> - <color name="oem_default_color_error_palette_90">#ffdad5</color> - <color name="oem_default_color_error_palette_80">#ffb4aa</color> - <color name="oem_default_color_error_palette_70">#ff897c</color> - <color name="oem_default_color_error_palette_60">#ff5448</color> - <color name="oem_default_color_error_palette_50">#de372f</color> - <color name="oem_default_color_error_palette_40">#ba1b19</color> - <color name="oem_default_color_error_palette_30">#930008</color> - <color name="oem_default_color_error_palette_20">#690004</color> - <color name="oem_default_color_error_palette_10">#410001</color> + <color name="oem_default_color_error_palette_95">#FFEDEA</color> + <color name="oem_default_color_error_palette_90">#FFDAD6</color> + <color name="oem_default_color_error_palette_80">#FFB4AB</color> + <color name="oem_default_color_error_palette_70">#FF897D</color> + <color name="oem_default_color_error_palette_60">#FF5449</color> + <color name="oem_default_color_error_palette_50">#DE3730</color> + <color name="oem_default_color_error_palette_40">#B3261E</color> + <color name="oem_default_color_error_palette_30">#8C1D18</color> + <color name="oem_default_color_error_palette_20">#601410</color> + <color name="oem_default_color_error_palette_10">#410E0B</color> <color name="oem_default_color_error_palette_0">#000000</color> <color name="oem_default_color_neutral_palette_100">#FFFFFF</color> - <color name="oem_default_color_neutral_palette_99">#fbfcff</color> - <color name="oem_default_color_neutral_palette_95">#eef1f6</color> - <color name="oem_default_color_neutral_palette_90">#dfe3e7</color> - <color name="oem_default_color_neutral_palette_80">#c3c7cb</color> - <color name="oem_default_color_neutral_palette_70">#a8acb0</color> - <color name="oem_default_color_neutral_palette_60">#8d9195</color> - <color name="oem_default_color_neutral_palette_50">#73787c</color> - <color name="oem_default_color_neutral_palette_40">#5a5f63</color> - <color name="oem_default_color_neutral_palette_30">#43474b</color> - <color name="oem_default_color_neutral_palette_20">#2c3134</color> - <color name="oem_default_color_neutral_palette_10">#181c1f</color> + <color name="oem_default_color_neutral_palette_99">#FEFBFF</color> + <color name="oem_default_color_neutral_palette_95">#F1F0F7</color> + <color name="oem_default_color_neutral_palette_90">#E3E1E9</color> + <color name="oem_default_color_neutral_palette_80">#C7C6CD</color> + <color name="oem_default_color_neutral_palette_70">#ABAAB1</color> + <color name="oem_default_color_neutral_palette_60">#909097</color> + <color name="oem_default_color_neutral_palette_50">#77767D</color> + <color name="oem_default_color_neutral_palette_40">#5E5E64</color> + <color name="oem_default_color_neutral_palette_30">#46464C</color> + <color name="oem_default_color_neutral_palette_20">#2F3036</color> + <color name="oem_default_color_neutral_palette_10">#1A1B21</color> <color name="oem_default_color_neutral_palette_0">#000000</color> <color name="oem_default_color_neutral_variant_palette_100">#FFFFFF</color> - <color name="oem_default_color_neutral_variant_palette_99">#fbfcff</color> - <color name="oem_default_color_neutral_variant_palette_95">#ebf1f8</color> - <color name="oem_default_color_neutral_variant_palette_90">#dde3ea</color> - <color name="oem_default_color_neutral_variant_palette_80">#c1c7ce</color> - <color name="oem_default_color_neutral_variant_palette_70">#a5acb2</color> - <color name="oem_default_color_neutral_variant_palette_60">#8b9297</color> - <color name="oem_default_color_neutral_variant_palette_50">#71787e</color> - <color name="oem_default_color_neutral_variant_palette_40">#585f65</color> - <color name="oem_default_color_neutral_variant_palette_30">#41484d</color> - <color name="oem_default_color_neutral_variant_palette_20">#2b3136</color> - <color name="oem_default_color_neutral_variant_palette_10">#161c21</color> + <color name="oem_default_color_neutral_variant_palette_99">#FEFBFF</color> + <color name="oem_default_color_neutral_variant_palette_95">#F0F0FA</color> + <color name="oem_default_color_neutral_variant_palette_90">#E2E1EC</color> + <color name="oem_default_color_neutral_variant_palette_80">#C6C6D0</color> + <color name="oem_default_color_neutral_variant_palette_70">#AAAAB4</color> + <color name="oem_default_color_neutral_variant_palette_60">#90909A</color> + <color name="oem_default_color_neutral_variant_palette_50">#767680</color> + <color name="oem_default_color_neutral_variant_palette_40">#5D5E67</color> + <color name="oem_default_color_neutral_variant_palette_30">#45464F</color> + <color name="oem_default_color_neutral_variant_palette_20">#2F3038</color> + <color name="oem_default_color_neutral_variant_palette_10">#1A1B23</color> <color name="oem_default_color_neutral_variant_palette_0">#000000</color> <color name="oem_color_surface_dim">#D7DADF</color> @@ -105,6 +105,7 @@ <color name="oem_color_surface_container_lowest">@color/oem_default_color_neutral_palette_100</color> <color name="oem_color_surface_container_high">#E5E8ED</color> <color name="oem_color_surface_container_highest">@color/oem_default_color_neutral_palette_90</color> + <color name="oem_color_blue">#0057CC</color> <color name="oem_color_on_blue">#FFFFFF</color> <color name="oem_color_blue_container">#D9E2FF</color>
diff --git a/car-ui-lib/oem-tokens/lib/src/main/res/values/styles.xml b/car-ui-lib/oem-tokens/lib/src/main/res/values/styles.xml index ae2e456..7a50e81 100644 --- a/car-ui-lib/oem-tokens/lib/src/main/res/values/styles.xml +++ b/car-ui-lib/oem-tokens/lib/src/main/res/values/styles.xml
@@ -155,13 +155,13 @@ <item name="oemColorScrim">?attr/oemColorNeutralPalette10</item> <item name="oemColorOutlineVariant">?attr/oemColorNeutralVariantPalette90</item> <item name="oemColorSurfaceTint">?attr/oemColorPrimaryPalette10</item> - <item name="oemColorSurfaceDim">@color/oem_color_surface_dim</item> - <item name="oemColorSurfaceBright">@color/oem_color_surface_bright</item> - <item name="oemColorSurfaceContainer">@color/oem_color_surface_container</item> - <item name="oemColorSurfaceContainerLow">@color/oem_color_surface_container_low</item> - <item name="oemColorSurfaceContainerLowest">@color/oem_color_surface_container_lowest</item> - <item name="oemColorSurfaceContainerHigh">@color/oem_color_surface_container_high</item> - <item name="oemColorSurfaceContainerHighest">@color/oem_color_surface_container_highest</item> + <item name="oemColorSurfaceDim">?attr/oemColorNeutralPalette90</item> + <item name="oemColorSurfaceBright">?attr/oemColorNeutralPalette99</item> + <item name="oemColorSurfaceContainer">?attr/oemColorNeutralPalette95</item> + <item name="oemColorSurfaceContainerLow">?attr/oemColorNeutralPalette99</item> + <item name="oemColorSurfaceContainerLowest">?attr/oemColorNeutralPalette100</item> + <item name="oemColorSurfaceContainerHigh">?attr/oemColorNeutralPalette90</item> + <item name="oemColorSurfaceContainerHighest">?attr/oemColorNeutralPalette80</item> <item name="oemColorBlue">@color/oem_color_blue</item> <item name="oemColorOnBlue">@color/oem_color_on_blue</item> <item name="oemColorBlueContainer">@color/oem_color_blue_container</item> @@ -169,7 +169,7 @@ <item name="oemColorRed">@color/oem_color_red</item> <item name="oemColorOnRed">@color/oem_color_on_red</item> <item name="oemColorRedContainer">@color/oem_color_red_container</item> - <item name="oemColorOnRedContainer">@color/oem_color_on_blue_container</item> + <item name="oemColorOnRedContainer">@color/oem_color_on_red_container</item> <item name="oemColorGreen">@color/oem_color_green</item> <item name="oemColorOnGreen">@color/oem_color_on_green</item> <item name="oemColorGreenContainer">@color/oem_color_green_container</item> @@ -205,16 +205,16 @@ <item name="oemColorOnSurfaceInverse">?attr/oemColorNeutralVariantPalette10</item> <item name="oemColorPrimaryInverse">?attr/oemColorPrimaryPalette40</item> <item name="oemColorOutline">?attr/oemColorNeutralVariantPalette60</item> - <item name="oemColorScrim">?attr/oemColorNeutralPalette80</item> + <item name="oemColorScrim">?attr/oemColorNeutralPalette0</item> <item name="oemColorOutlineVariant">?attr/oemColorNeutralVariantPalette30</item> <item name="oemColorSurfaceTint">?attr/oemColorPrimaryPalette80</item> - <item name="oemColorSurfaceDim">#0F1417</item> - <item name="oemColorSurfaceBright">#353A3D</item> - <item name="oemColorSurfaceContainer">#1C2024</item> + <item name="oemColorSurfaceDim">?attr/oemColorNeutralPalette10</item> + <item name="oemColorSurfaceBright">?attr/oemColorNeutralPalette30</item> + <item name="oemColorSurfaceContainer">?attr/oemColorNeutralPalette10</item> <item name="oemColorSurfaceContainerLow">?attr/oemColorNeutralPalette10</item> - <item name="oemColorSurfaceContainerLowest">#0A0F12</item> - <item name="oemColorSurfaceContainerHigh">#262B2E</item> - <item name="oemColorSurfaceContainerHighest">#313539</item> + <item name="oemColorSurfaceContainerLowest">?attr/oemColorNeutralPalette0</item> + <item name="oemColorSurfaceContainerHigh">?attr/oemColorNeutralPalette20</item> + <item name="oemColorSurfaceContainerHighest">?attr/oemColorNeutralPalette30</item> <item name="oemColorBlue">@color/oem_color_blue_dark</item> <item name="oemColorOnBlue">@color/oem_color_on_blue_dark</item> <item name="oemColorBlueContainer">@color/oem_color_blue_container_dark</item> @@ -392,7 +392,6 @@ <item name="oemShapeCornerLarge">?com.android.oem.tokens:attr/shapeCornerLarge</item> <item name="oemShapeCornerExtraLarge">?com.android.oem.tokens:attr/shapeCornerExtraLarge</item> <item name="oemShapeCornerFull">?com.android.oem.tokens:attr/shapeCornerFull</item> - </style> <style name="OemTokenSharedLibraryOverlay.Base"> @@ -507,40 +506,40 @@ </style> <style name="OemTokenSharedLibraryOverlay.Base.Light"> - <item name="com.android.oem.tokens:colorPrimary">?attr/com.android.oem.tokens:colorPrimaryPalette40</item> - <item name="com.android.oem.tokens:colorOnPrimary">?attr/com.android.oem.tokens:colorPrimaryPalette100</item> - <item name="com.android.oem.tokens:colorPrimaryContainer">?attr/com.android.oem.tokens:colorPrimaryPalette90</item> - <item name="com.android.oem.tokens:colorOnPrimaryContainer">?attr/com.android.oem.tokens:colorPrimaryPalette10</item> - <item name="com.android.oem.tokens:colorSecondary">?attr/com.android.oem.tokens:colorSecondaryPalette40</item> - <item name="com.android.oem.tokens:colorOnSecondary">?attr/com.android.oem.tokens:colorSecondaryPalette100</item> - <item name="com.android.oem.tokens:colorSecondaryContainer">?attr/com.android.oem.tokens:colorSecondaryPalette90</item> - <item name="com.android.oem.tokens:colorOnSecondaryContainer">?attr/com.android.oem.tokens:colorSecondaryPalette10</item> - <item name="com.android.oem.tokens:colorTertiary">?attr/com.android.oem.tokens:colorTertiaryPalette40</item> - <item name="com.android.oem.tokens:colorOnTertiary">?attr/com.android.oem.tokens:colorTertiaryPalette100</item> - <item name="com.android.oem.tokens:colorTertiaryContainer">?attr/com.android.oem.tokens:colorTertiaryPalette90</item> - <item name="com.android.oem.tokens:colorOnTertiaryContainer">?attr/com.android.oem.tokens:colorTertiaryPalette10</item> - <item name="com.android.oem.tokens:colorError">?attr/com.android.oem.tokens:colorErrorPalette40</item> - <item name="com.android.oem.tokens:colorOnError">?attr/com.android.oem.tokens:colorErrorPalette100</item> - <item name="com.android.oem.tokens:colorErrorContainer">?attr/com.android.oem.tokens:colorErrorPalette90</item> - <item name="com.android.oem.tokens:colorOnErrorContainer">?attr/com.android.oem.tokens:colorErrorPalette10</item> - <item name="com.android.oem.tokens:colorSurface">?attr/com.android.oem.tokens:colorNeutralPalette99</item> - <item name="com.android.oem.tokens:colorOnSurface">?attr/com.android.oem.tokens:colorNeutralPalette10</item> - <item name="com.android.oem.tokens:colorSurfaceVariant">?attr/com.android.oem.tokens:colorNeutralVariantPalette90</item> - <item name="com.android.oem.tokens:colorOnSurfaceVariant">?attr/com.android.oem.tokens:colorNeutralVariantPalette30</item> - <item name="com.android.oem.tokens:colorSurfaceInverse">?attr/com.android.oem.tokens:colorNeutralVariantPalette10</item> - <item name="com.android.oem.tokens:colorOnSurfaceInverse">?attr/com.android.oem.tokens:colorNeutralVariantPalette99</item> - <item name="com.android.oem.tokens:colorPrimaryInverse">?attr/com.android.oem.tokens:colorPrimaryPalette80</item> - <item name="com.android.oem.tokens:colorOutline">?attr/com.android.oem.tokens:colorNeutralVariantPalette50</item> - <item name="com.android.oem.tokens:colorScrim">?attr/com.android.oem.tokens:colorNeutralPalette10</item> - <item name="com.android.oem.tokens:colorOutlineVariant">?attr/com.android.oem.tokens:colorNeutralVariantPalette90</item> - <item name="com.android.oem.tokens:colorSurfaceTint">?attr/com.android.oem.tokens:colorPrimaryPalette10</item> - <item name="com.android.oem.tokens:colorSurfaceDim">@color/oem_color_surface_dim</item> - <item name="com.android.oem.tokens:colorSurfaceBright">@color/oem_color_surface_bright</item> - <item name="com.android.oem.tokens:colorSurfaceContainer">@color/oem_color_surface_container</item> - <item name="com.android.oem.tokens:colorSurfaceContainerLow">@color/oem_color_surface_container_low</item> - <item name="com.android.oem.tokens:colorSurfaceContainerLowest">@color/oem_color_surface_container_lowest</item> - <item name="com.android.oem.tokens:colorSurfaceContainerHigh">@color/oem_color_surface_container_high</item> - <item name="com.android.oem.tokens:colorSurfaceContainerHighest">@color/oem_color_surface_container_highest</item> + <item name="com.android.oem.tokens:colorPrimary">?com.android.oem.tokens:attr/colorPrimaryPalette40</item> + <item name="com.android.oem.tokens:colorOnPrimary">?com.android.oem.tokens:attr/colorPrimaryPalette100</item> + <item name="com.android.oem.tokens:colorPrimaryContainer">?com.android.oem.tokens:attr/colorPrimaryPalette90</item> + <item name="com.android.oem.tokens:colorOnPrimaryContainer">?com.android.oem.tokens:attr/colorPrimaryPalette10</item> + <item name="com.android.oem.tokens:colorSecondary">?com.android.oem.tokens:attr/colorSecondaryPalette40</item> + <item name="com.android.oem.tokens:colorOnSecondary">?com.android.oem.tokens:attr/colorSecondaryPalette100</item> + <item name="com.android.oem.tokens:colorSecondaryContainer">?com.android.oem.tokens:attr/colorSecondaryPalette90</item> + <item name="com.android.oem.tokens:colorOnSecondaryContainer">?com.android.oem.tokens:attr/colorSecondaryPalette10</item> + <item name="com.android.oem.tokens:colorTertiary">?com.android.oem.tokens:attr/colorTertiaryPalette40</item> + <item name="com.android.oem.tokens:colorOnTertiary">?com.android.oem.tokens:attr/colorTertiaryPalette100</item> + <item name="com.android.oem.tokens:colorTertiaryContainer">?com.android.oem.tokens:attr/colorTertiaryPalette90</item> + <item name="com.android.oem.tokens:colorOnTertiaryContainer">?com.android.oem.tokens:attr/colorTertiaryPalette10</item> + <item name="com.android.oem.tokens:colorError">?com.android.oem.tokens:attr/colorErrorPalette40</item> + <item name="com.android.oem.tokens:colorOnError">?com.android.oem.tokens:attr/colorErrorPalette100</item> + <item name="com.android.oem.tokens:colorErrorContainer">?com.android.oem.tokens:attr/colorErrorPalette90</item> + <item name="com.android.oem.tokens:colorOnErrorContainer">?com.android.oem.tokens:attr/colorErrorPalette10</item> + <item name="com.android.oem.tokens:colorSurface">?com.android.oem.tokens:attr/colorNeutralPalette99</item> + <item name="com.android.oem.tokens:colorOnSurface">?com.android.oem.tokens:attr/colorNeutralPalette10</item> + <item name="com.android.oem.tokens:colorSurfaceVariant">?com.android.oem.tokens:attr/colorNeutralVariantPalette90</item> + <item name="com.android.oem.tokens:colorOnSurfaceVariant">?com.android.oem.tokens:attr/colorNeutralVariantPalette30</item> + <item name="com.android.oem.tokens:colorSurfaceInverse">?com.android.oem.tokens:attr/colorNeutralVariantPalette10</item> + <item name="com.android.oem.tokens:colorOnSurfaceInverse">?com.android.oem.tokens:attr/colorNeutralVariantPalette99</item> + <item name="com.android.oem.tokens:colorPrimaryInverse">?com.android.oem.tokens:attr/colorPrimaryPalette80</item> + <item name="com.android.oem.tokens:colorOutline">?com.android.oem.tokens:attr/colorNeutralVariantPalette50</item> + <item name="com.android.oem.tokens:colorScrim">?com.android.oem.tokens:attr/colorNeutralPalette10</item> + <item name="com.android.oem.tokens:colorOutlineVariant">?com.android.oem.tokens:attr/colorNeutralVariantPalette90</item> + <item name="com.android.oem.tokens:colorSurfaceTint">?com.android.oem.tokens:attr/colorPrimaryPalette10</item> + <item name="com.android.oem.tokens:colorSurfaceDim">?com.android.oem.tokens:attr/colorNeutralPalette90</item> + <item name="com.android.oem.tokens:colorSurfaceBright">?com.android.oem.tokens:attr/colorNeutralPalette99</item> + <item name="com.android.oem.tokens:colorSurfaceContainer">?com.android.oem.tokens:attr/colorNeutralPalette95</item> + <item name="com.android.oem.tokens:colorSurfaceContainerLow">?com.android.oem.tokens:attr/colorNeutralPalette99</item> + <item name="com.android.oem.tokens:colorSurfaceContainerLowest">?com.android.oem.tokens:attr/colorNeutralPalette100</item> + <item name="com.android.oem.tokens:colorSurfaceContainerHigh">?com.android.oem.tokens:attr/colorNeutralPalette90</item> + <item name="com.android.oem.tokens:colorSurfaceContainerHighest">?com.android.oem.tokens:attr/colorNeutralPalette80</item> <item name="com.android.oem.tokens:colorBlue">@color/oem_color_blue</item> <item name="com.android.oem.tokens:colorOnBlue">@color/oem_color_on_blue</item> <item name="com.android.oem.tokens:colorBlueContainer">@color/oem_color_blue_container</item> @@ -560,40 +559,40 @@ </style> <style name="OemTokenSharedLibraryOverlay.Base.Dark"> - <item name="com.android.oem.tokens:colorPrimary">?attr/com.android.oem.tokens:colorPrimaryPalette80</item> - <item name="com.android.oem.tokens:colorOnPrimary">?attr/com.android.oem.tokens:colorPrimaryPalette20</item> - <item name="com.android.oem.tokens:colorPrimaryContainer">?attr/com.android.oem.tokens:colorPrimaryPalette30</item> - <item name="com.android.oem.tokens:colorOnPrimaryContainer">?attr/com.android.oem.tokens:colorPrimaryPalette90</item> - <item name="com.android.oem.tokens:colorSecondary">?attr/com.android.oem.tokens:colorSecondaryPalette80</item> - <item name="com.android.oem.tokens:colorOnSecondary">?attr/com.android.oem.tokens:colorSecondaryPalette20</item> - <item name="com.android.oem.tokens:colorSecondaryContainer">?attr/com.android.oem.tokens:colorSecondaryPalette30</item> - <item name="com.android.oem.tokens:colorOnSecondaryContainer">?attr/com.android.oem.tokens:colorSecondaryPalette90</item> - <item name="com.android.oem.tokens:colorTertiary">?attr/com.android.oem.tokens:colorTertiaryPalette80</item> - <item name="com.android.oem.tokens:colorOnTertiary">?attr/com.android.oem.tokens:colorTertiaryPalette20</item> - <item name="com.android.oem.tokens:colorTertiaryContainer">?attr/com.android.oem.tokens:colorTertiaryPalette30</item> - <item name="com.android.oem.tokens:colorOnTertiaryContainer">?attr/com.android.oem.tokens:colorTertiaryPalette90</item> - <item name="com.android.oem.tokens:colorError">?attr/com.android.oem.tokens:colorErrorPalette80</item> - <item name="com.android.oem.tokens:colorOnError">?attr/com.android.oem.tokens:colorErrorPalette20</item> - <item name="com.android.oem.tokens:colorErrorContainer">?attr/com.android.oem.tokens:colorErrorPalette30</item> - <item name="com.android.oem.tokens:colorOnErrorContainer">?attr/com.android.oem.tokens:colorErrorPalette90</item> - <item name="com.android.oem.tokens:colorSurface">?attr/com.android.oem.tokens:colorNeutralPalette10</item> - <item name="com.android.oem.tokens:colorOnSurface">?attr/com.android.oem.tokens:colorNeutralPalette80</item> - <item name="com.android.oem.tokens:colorSurfaceVariant">?attr/com.android.oem.tokens:colorNeutralVariantPalette30</item> - <item name="com.android.oem.tokens:colorOnSurfaceVariant">?attr/com.android.oem.tokens:colorNeutralVariantPalette80</item> - <item name="com.android.oem.tokens:colorSurfaceInverse">?attr/com.android.oem.tokens:colorNeutralVariantPalette80</item> - <item name="com.android.oem.tokens:colorOnSurfaceInverse">?attr/com.android.oem.tokens:colorNeutralVariantPalette10</item> - <item name="com.android.oem.tokens:colorPrimaryInverse">?attr/com.android.oem.tokens:colorPrimaryPalette40</item> - <item name="com.android.oem.tokens:colorOutline">?attr/com.android.oem.tokens:colorNeutralVariantPalette60</item> - <item name="com.android.oem.tokens:colorScrim">?attr/com.android.oem.tokens:colorNeutralPalette80</item> - <item name="com.android.oem.tokens:colorOutlineVariant">?attr/com.android.oem.tokens:colorNeutralVariantPalette30</item> - <item name="com.android.oem.tokens:colorSurfaceTint">?attr/com.android.oem.tokens:colorPrimaryPalette80</item> - <item name="com.android.oem.tokens:colorSurfaceDim">#0F1417</item> - <item name="com.android.oem.tokens:colorSurfaceBright">#353A3D</item> - <item name="com.android.oem.tokens:colorSurfaceContainer">#1C2024</item> - <item name="com.android.oem.tokens:colorSurfaceContainerLow">?attr/com.android.oem.tokens:colorNeutralPalette10</item> - <item name="com.android.oem.tokens:colorSurfaceContainerLowest">#0A0F12</item> - <item name="com.android.oem.tokens:colorSurfaceContainerHigh">#262B2E</item> - <item name="com.android.oem.tokens:colorSurfaceContainerHighest">#313539</item> + <item name="com.android.oem.tokens:colorPrimary">?com.android.oem.tokens:attr/colorPrimaryPalette80</item> + <item name="com.android.oem.tokens:colorOnPrimary">?com.android.oem.tokens:attr/colorPrimaryPalette20</item> + <item name="com.android.oem.tokens:colorPrimaryContainer">?com.android.oem.tokens:attr/colorPrimaryPalette30</item> + <item name="com.android.oem.tokens:colorOnPrimaryContainer">?com.android.oem.tokens:attr/colorPrimaryPalette90</item> + <item name="com.android.oem.tokens:colorSecondary">?com.android.oem.tokens:attr/colorSecondaryPalette80</item> + <item name="com.android.oem.tokens:colorOnSecondary">?com.android.oem.tokens:attr/colorSecondaryPalette20</item> + <item name="com.android.oem.tokens:colorSecondaryContainer">?com.android.oem.tokens:attr/colorSecondaryPalette30</item> + <item name="com.android.oem.tokens:colorOnSecondaryContainer">?com.android.oem.tokens:attr/colorSecondaryPalette90</item> + <item name="com.android.oem.tokens:colorTertiary">?com.android.oem.tokens:attr/colorTertiaryPalette80</item> + <item name="com.android.oem.tokens:colorOnTertiary">?com.android.oem.tokens:attr/colorTertiaryPalette20</item> + <item name="com.android.oem.tokens:colorTertiaryContainer">?com.android.oem.tokens:attr/colorTertiaryPalette30</item> + <item name="com.android.oem.tokens:colorOnTertiaryContainer">?com.android.oem.tokens:attr/colorTertiaryPalette90</item> + <item name="com.android.oem.tokens:colorError">?com.android.oem.tokens:attr/colorErrorPalette80</item> + <item name="com.android.oem.tokens:colorOnError">?com.android.oem.tokens:attr/colorErrorPalette20</item> + <item name="com.android.oem.tokens:colorErrorContainer">?com.android.oem.tokens:attr/colorErrorPalette30</item> + <item name="com.android.oem.tokens:colorOnErrorContainer">?com.android.oem.tokens:attr/colorErrorPalette90</item> + <item name="com.android.oem.tokens:colorSurface">?com.android.oem.tokens:attr/colorNeutralPalette10</item> + <item name="com.android.oem.tokens:colorOnSurface">?com.android.oem.tokens:attr/colorNeutralPalette80</item> + <item name="com.android.oem.tokens:colorSurfaceVariant">?com.android.oem.tokens:attr/colorNeutralVariantPalette30</item> + <item name="com.android.oem.tokens:colorOnSurfaceVariant">?com.android.oem.tokens:attr/colorNeutralVariantPalette80</item> + <item name="com.android.oem.tokens:colorSurfaceInverse">?com.android.oem.tokens:attr/colorNeutralVariantPalette80</item> + <item name="com.android.oem.tokens:colorOnSurfaceInverse">?com.android.oem.tokens:attr/colorNeutralVariantPalette10</item> + <item name="com.android.oem.tokens:colorPrimaryInverse">?com.android.oem.tokens:attr/colorPrimaryPalette40</item> + <item name="com.android.oem.tokens:colorOutline">?com.android.oem.tokens:attr/colorNeutralVariantPalette60</item> + <item name="com.android.oem.tokens:colorScrim">?com.android.oem.tokens:attr/colorNeutralPalette0</item> + <item name="com.android.oem.tokens:colorOutlineVariant">?com.android.oem.tokens:attr/colorNeutralVariantPalette30</item> + <item name="com.android.oem.tokens:colorSurfaceTint">?com.android.oem.tokens:attr/colorPrimaryPalette80</item> + <item name="com.android.oem.tokens:colorSurfaceDim">?com.android.oem.tokens:attr/colorNeutralPalette10</item> + <item name="com.android.oem.tokens:colorSurfaceBright">?com.android.oem.tokens:attr/colorNeutralPalette30</item> + <item name="com.android.oem.tokens:colorSurfaceContainer">?com.android.oem.tokens:attr/colorNeutralPalette10</item> + <item name="com.android.oem.tokens:colorSurfaceContainerLow">?com.android.oem.tokens:attr/colorNeutralPalette10</item> + <item name="com.android.oem.tokens:colorSurfaceContainerLowest">?com.android.oem.tokens:attr/colorNeutralPalette0</item> + <item name="com.android.oem.tokens:colorSurfaceContainerHigh">?com.android.oem.tokens:attr/colorNeutralPalette20</item> + <item name="com.android.oem.tokens:colorSurfaceContainerHighest">?com.android.oem.tokens:attr/colorNeutralPalette30</item> <item name="com.android.oem.tokens:colorBlue">@color/oem_color_blue_dark</item> <item name="com.android.oem.tokens:colorOnBlue">@color/oem_color_on_blue_dark</item> <item name="com.android.oem.tokens:colorBlueContainer">@color/oem_color_blue_container_dark</item> @@ -613,10 +612,10 @@ </style> <style name="BaseTextAppearance"> - <item name="android:textColor">?oemColorOnSurface</item> - <item name="android:textColorHighlight">?android:textColorHighlight</item> - <item name="android:textColorHint">?android:textColorHint</item> - <item name="android:textColorLink">?android:textColorLink</item> + <item name="android:textColor">?attr/oemColorOnSurface</item> + <item name="android:textColorHighlight">?android:attr/textColorHighlight</item> + <item name="android:textColorHint">?android:attr/textColorHint</item> + <item name="android:textColorLink">?android:attr/textColorLink</item> <item name="android:textSize">16sp</item> <item name="android:textStyle">normal</item> </style>
diff --git a/car-ui-lib/oem-tokens/shared-lib/src/main/res/values/styles.xml b/car-ui-lib/oem-tokens/shared-lib/src/main/res/values/styles.xml index fc9e162..58bbade 100644 --- a/car-ui-lib/oem-tokens/shared-lib/src/main/res/values/styles.xml +++ b/car-ui-lib/oem-tokens/shared-lib/src/main/res/values/styles.xml
@@ -67,11 +67,90 @@ <color name="color_yellow_container">@android:color/holo_orange_dark</color> <color name="color_on_yellow_container">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_100">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_99">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_95">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_90">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_80">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_70">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_60">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_50">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_40">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_30">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_20">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_10">@android:color/holo_orange_dark</color> + <color name="color_primary_palette_0">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_100">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_99">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_95">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_90">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_80">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_70">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_60">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_50">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_40">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_30">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_20">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_10">@android:color/holo_orange_dark</color> + <color name="color_secondary_palette_0">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_100">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_99">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_95">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_90">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_80">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_70">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_60">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_50">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_40">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_30">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_20">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_10">@android:color/holo_orange_dark</color> + <color name="color_tertiary_palette_0">@android:color/holo_orange_dark</color> + <color name="color_error_palette_100">@android:color/holo_orange_dark</color> + <color name="color_error_palette_99">@android:color/holo_orange_dark</color> + <color name="color_error_palette_95">@android:color/holo_orange_dark</color> + <color name="color_error_palette_90">@android:color/holo_orange_dark</color> + <color name="color_error_palette_80">@android:color/holo_orange_dark</color> + <color name="color_error_palette_70">@android:color/holo_orange_dark</color> + <color name="color_error_palette_60">@android:color/holo_orange_dark</color> + <color name="color_error_palette_50">@android:color/holo_orange_dark</color> + <color name="color_error_palette_40">@android:color/holo_orange_dark</color> + <color name="color_error_palette_30">@android:color/holo_orange_dark</color> + <color name="color_error_palette_20">@android:color/holo_orange_dark</color> + <color name="color_error_palette_10">@android:color/holo_orange_dark</color> + <color name="color_error_palette_0">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_100">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_99">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_95">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_90">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_80">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_70">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_60">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_50">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_40">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_30">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_20">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_10">@android:color/holo_orange_dark</color> + <color name="color_neutral_palette_0">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_100">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_99">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_95">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_90">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_80">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_70">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_60">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_50">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_40">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_30">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_20">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_10">@android:color/holo_orange_dark</color> + <color name="color_neutral_variant_palette_0">@android:color/holo_orange_dark</color> + <dimen name="corner_none">0dp</dimen> <dimen name="corner_extra_small">8dp</dimen> <dimen name="corner_small">16dp</dimen> <dimen name="corner_medium">24dp</dimen> - <dimen name="corner_large">48dp</dimen> + <dimen name="corner_large">32dp</dimen> <dimen name="corner_extra_large">48dp</dimen> <dimen name="corner_full">1000dp</dimen> @@ -127,6 +206,85 @@ <item name="com.android.oem.tokens:colorYellowContainer">@color/color_yellow_container</item> <item name="com.android.oem.tokens:colorOnYellowContainer">@color/color_on_yellow_container</item> + <item name="com.android.oem.tokens:colorPrimaryPalette100">@color/color_primary_palette_100</item> + <item name="com.android.oem.tokens:colorPrimaryPalette99">@color/color_primary_palette_99</item> + <item name="com.android.oem.tokens:colorPrimaryPalette95">@color/color_primary_palette_95</item> + <item name="com.android.oem.tokens:colorPrimaryPalette90">@color/color_primary_palette_90</item> + <item name="com.android.oem.tokens:colorPrimaryPalette80">@color/color_primary_palette_80</item> + <item name="com.android.oem.tokens:colorPrimaryPalette70">@color/color_primary_palette_70</item> + <item name="com.android.oem.tokens:colorPrimaryPalette60">@color/color_primary_palette_60</item> + <item name="com.android.oem.tokens:colorPrimaryPalette50">@color/color_primary_palette_50</item> + <item name="com.android.oem.tokens:colorPrimaryPalette40">@color/color_primary_palette_40</item> + <item name="com.android.oem.tokens:colorPrimaryPalette30">@color/color_primary_palette_30</item> + <item name="com.android.oem.tokens:colorPrimaryPalette20">@color/color_primary_palette_20</item> + <item name="com.android.oem.tokens:colorPrimaryPalette10">@color/color_primary_palette_10</item> + <item name="com.android.oem.tokens:colorPrimaryPalette0">@color/color_primary_palette_0</item> + <item name="com.android.oem.tokens:colorSecondaryPalette100">@color/color_secondary_palette_100</item> + <item name="com.android.oem.tokens:colorSecondaryPalette99">@color/color_secondary_palette_99</item> + <item name="com.android.oem.tokens:colorSecondaryPalette95">@color/color_secondary_palette_95</item> + <item name="com.android.oem.tokens:colorSecondaryPalette90">@color/color_secondary_palette_90</item> + <item name="com.android.oem.tokens:colorSecondaryPalette80">@color/color_secondary_palette_80</item> + <item name="com.android.oem.tokens:colorSecondaryPalette70">@color/color_secondary_palette_70</item> + <item name="com.android.oem.tokens:colorSecondaryPalette60">@color/color_secondary_palette_60</item> + <item name="com.android.oem.tokens:colorSecondaryPalette50">@color/color_secondary_palette_50</item> + <item name="com.android.oem.tokens:colorSecondaryPalette40">@color/color_secondary_palette_40</item> + <item name="com.android.oem.tokens:colorSecondaryPalette30">@color/color_secondary_palette_30</item> + <item name="com.android.oem.tokens:colorSecondaryPalette20">@color/color_secondary_palette_20</item> + <item name="com.android.oem.tokens:colorSecondaryPalette10">@color/color_secondary_palette_10</item> + <item name="com.android.oem.tokens:colorSecondaryPalette0">@color/color_secondary_palette_0</item> + <item name="com.android.oem.tokens:colorTertiaryPalette100">@color/color_tertiary_palette_100</item> + <item name="com.android.oem.tokens:colorTertiaryPalette99">@color/color_tertiary_palette_99</item> + <item name="com.android.oem.tokens:colorTertiaryPalette95">@color/color_tertiary_palette_95</item> + <item name="com.android.oem.tokens:colorTertiaryPalette90">@color/color_tertiary_palette_90</item> + <item name="com.android.oem.tokens:colorTertiaryPalette80">@color/color_tertiary_palette_80</item> + <item name="com.android.oem.tokens:colorTertiaryPalette70">@color/color_tertiary_palette_70</item> + <item name="com.android.oem.tokens:colorTertiaryPalette60">@color/color_tertiary_palette_60</item> + <item name="com.android.oem.tokens:colorTertiaryPalette50">@color/color_tertiary_palette_50</item> + <item name="com.android.oem.tokens:colorTertiaryPalette40">@color/color_tertiary_palette_40</item> + <item name="com.android.oem.tokens:colorTertiaryPalette30">@color/color_tertiary_palette_30</item> + <item name="com.android.oem.tokens:colorTertiaryPalette20">@color/color_tertiary_palette_20</item> + <item name="com.android.oem.tokens:colorTertiaryPalette10">@color/color_tertiary_palette_10</item> + <item name="com.android.oem.tokens:colorTertiaryPalette0">@color/color_tertiary_palette_0</item> + <item name="com.android.oem.tokens:colorErrorPalette100">@color/color_error_palette_100</item> + <item name="com.android.oem.tokens:colorErrorPalette99">@color/color_error_palette_99</item> + <item name="com.android.oem.tokens:colorErrorPalette95">@color/color_error_palette_95</item> + <item name="com.android.oem.tokens:colorErrorPalette90">@color/color_error_palette_90</item> + <item name="com.android.oem.tokens:colorErrorPalette80">@color/color_error_palette_80</item> + <item name="com.android.oem.tokens:colorErrorPalette70">@color/color_error_palette_70</item> + <item name="com.android.oem.tokens:colorErrorPalette60">@color/color_error_palette_60</item> + <item name="com.android.oem.tokens:colorErrorPalette50">@color/color_error_palette_50</item> + <item name="com.android.oem.tokens:colorErrorPalette40">@color/color_error_palette_40</item> + <item name="com.android.oem.tokens:colorErrorPalette30">@color/color_error_palette_30</item> + <item name="com.android.oem.tokens:colorErrorPalette20">@color/color_error_palette_20</item> + <item name="com.android.oem.tokens:colorErrorPalette10">@color/color_error_palette_10</item> + <item name="com.android.oem.tokens:colorErrorPalette0">@color/color_error_palette_0</item> + <item name="com.android.oem.tokens:colorNeutralPalette100">@color/color_neutral_palette_100</item> + <item name="com.android.oem.tokens:colorNeutralPalette99">@color/color_neutral_palette_99</item> + <item name="com.android.oem.tokens:colorNeutralPalette95">@color/color_neutral_palette_95</item> + <item name="com.android.oem.tokens:colorNeutralPalette90">@color/color_neutral_palette_90</item> + <item name="com.android.oem.tokens:colorNeutralPalette80">@color/color_neutral_palette_80</item> + <item name="com.android.oem.tokens:colorNeutralPalette70">@color/color_neutral_palette_70</item> + <item name="com.android.oem.tokens:colorNeutralPalette60">@color/color_neutral_palette_60</item> + <item name="com.android.oem.tokens:colorNeutralPalette50">@color/color_neutral_palette_50</item> + <item name="com.android.oem.tokens:colorNeutralPalette40">@color/color_neutral_palette_40</item> + <item name="com.android.oem.tokens:colorNeutralPalette30">@color/color_neutral_palette_30</item> + <item name="com.android.oem.tokens:colorNeutralPalette20">@color/color_neutral_palette_20</item> + <item name="com.android.oem.tokens:colorNeutralPalette10">@color/color_neutral_palette_10</item> + <item name="com.android.oem.tokens:colorNeutralPalette0">@color/color_neutral_palette_0</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette100">@color/color_neutral_variant_palette_100</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette99">@color/color_neutral_variant_palette_99</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette95">@color/color_neutral_variant_palette_95</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette90">@color/color_neutral_variant_palette_90</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette80">@color/color_neutral_variant_palette_80</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette70">@color/color_neutral_variant_palette_70</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette60">@color/color_neutral_variant_palette_60</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette50">@color/color_neutral_variant_palette_50</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette40">@color/color_neutral_variant_palette_40</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette30">@color/color_neutral_variant_palette_30</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette20">@color/color_neutral_variant_palette_20</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette10">@color/color_neutral_variant_palette_10</item> + <item name="com.android.oem.tokens:colorNeutralVariantPalette0">@color/color_neutral_variant_palette_0</item> + <item name="com.android.oem.tokens:shapeCornerNone">@dimen/corner_small</item> <item name="com.android.oem.tokens:shapeCornerExtraSmall">@dimen/corner_extra_small</item> <item name="com.android.oem.tokens:shapeCornerSmall">@dimen/corner_small</item>