Snap for 12354992 from de190fc22c4bc8107ea9f289a3a7b248945103b6 to 24Q4-release
Change-Id: Idc680b0ac5edb06f4a92e5f64e55ae03c109961a
diff --git a/msdllib/src/com/google/android/msdl/data/repository/MSDLRepository.kt b/msdllib/src/com/google/android/msdl/data/repository/MSDLRepository.kt
index 5aed913..b891661 100644
--- a/msdllib/src/com/google/android/msdl/data/repository/MSDLRepository.kt
+++ b/msdllib/src/com/google/android/msdl/data/repository/MSDLRepository.kt
@@ -15,23 +15,24 @@
*/
package com.google.android.msdl.data.repository
+import androidx.annotation.VisibleForTesting
import com.google.android.msdl.data.model.HapticToken
import com.google.android.msdl.data.model.SoundToken
/**
- * A repository of data for [ReferenceToken].
+ * A repository of data for [HapticToken] and [SoundToken].
*
* The principle behind this repository is to hold the data for all tokens as a cache in memory.
* This is only suitable if the number of tokens and the data stored is manageable. The purpose of
* this design choice is to provide fast and easy access to the data when required to be played by
* UI interactions.
*/
-interface MSDLRepository {
+sealed interface MSDLRepository {
/**
- * Get the [MSDLData] that corresponds to the given haptic reference token. This function needs
- * to be fast since it will be called repeatedly to deliver feedback. If necessary, a caching
- * strategy should be applied.
+ * Get the [MSDLHapticData] that corresponds to the given haptic reference token. This function
+ * needs to be fast since it will be called repeatedly to deliver feedback. If necessary, a
+ * caching strategy should be applied.
*
* @param[hapticToken] The [HapticToken] that points to the data.
* @return the data that corresponds to the token at the time this function is called.
@@ -39,14 +40,19 @@
fun getHapticData(hapticToken: HapticToken): MSDLHapticData?
/**
- * Get the [MSDLData] that corresponds to the given sound reference token. This function needs
- * to be fast since it will be called repeatedly to deliver feedback. If necessary, a caching
- * strategy should be applied.
+ * Get the [MSDLSoundData] that corresponds to the given sound reference token. This function
+ * needs to be fast since it will be called repeatedly to deliver feedback. If necessary, a
+ * caching strategy should be applied.
*
* @param[soundToken] The [SoundToken] that points to the data.
* @return the data that corresponds to the token at the time this function is called.
*/
fun getAudioData(soundToken: SoundToken): MSDLSoundData?
+
+ companion object {
+
+ @VisibleForTesting fun createRepository(): MSDLRepository = MSDLRepositoryImpl()
+ }
}
/** Representation of data contained in a [MSDLRepository] */
diff --git a/msdllib/src/com/google/android/msdl/data/repository/MSDLRepositoryImpl.kt b/msdllib/src/com/google/android/msdl/data/repository/MSDLRepositoryImpl.kt
index e409e02..5074a99 100644
--- a/msdllib/src/com/google/android/msdl/data/repository/MSDLRepositoryImpl.kt
+++ b/msdllib/src/com/google/android/msdl/data/repository/MSDLRepositoryImpl.kt
@@ -23,7 +23,7 @@
import com.google.android.msdl.data.model.SoundToken
/** A [MSDLRepository] that holds haptic compositions as haptic data. */
-class MSDLRepositoryImpl : MSDLRepository {
+internal class MSDLRepositoryImpl : MSDLRepository {
override fun getAudioData(soundToken: SoundToken): MSDLSoundData? {
// TODO(b/345248875) Implement a caching strategy in accordance to the audio file strategy
@@ -63,13 +63,13 @@
VibrationEffect.Composition.PRIMITIVE_SPIN,
scale = 1f,
delayMillis = SPIN_DELAY.toInt(),
- )
+ ),
),
VibrationEffect.createWaveform(
SPIN_WAVEFORM_TIMINGS,
SPIN_WAVEFORM_AMPLITUDES,
-1,
- )
+ ),
)
},
HapticToken.NEGATIVE_CONFIRMATION_MEDIUM_EMPHASIS to
@@ -79,24 +79,24 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 0
+ delayMillis = 0,
),
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 114
+ delayMillis = 114,
),
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 114
- )
+ delayMillis = 114,
+ ),
),
VibrationEffect.createWaveform(
longArrayOf(10, 10, 10, 114, 10, 10, 10, 114, 10, 10, 10),
intArrayOf(10, 255, 20, 0, 10, 255, 20, 0, 10, 255, 20),
- -1
- )
+ -1,
+ ),
)
},
HapticToken.POSITIVE_CONFIRMATION_HIGH_EMPHASIS to
@@ -106,19 +106,19 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 0
+ delayMillis = 0,
),
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 114
- )
+ delayMillis = 114,
+ ),
),
VibrationEffect.createWaveform(
longArrayOf(10, 10, 10, 114, 10, 10, 10),
intArrayOf(10, 255, 20, 0, 10, 255, 20),
- -1
- )
+ -1,
+ ),
)
},
HapticToken.POSITIVE_CONFIRMATION_MEDIUM_EMPHASIS to
@@ -128,19 +128,19 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 0
+ delayMillis = 0,
),
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 52
- )
+ delayMillis = 52,
+ ),
),
VibrationEffect.createWaveform(
longArrayOf(10, 10, 10, 52, 10, 10, 10),
intArrayOf(10, 255, 20, 0, 10, 255, 20),
- -1
- )
+ -1,
+ ),
)
},
HapticToken.POSITIVE_CONFIRMATION_LOW_EMPHASIS to
@@ -150,19 +150,19 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_TICK,
scale = 1f,
- delayMillis = 0
+ delayMillis = 0,
),
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 52
- )
+ delayMillis = 52,
+ ),
),
VibrationEffect.createWaveform(
longArrayOf(5, 52, 10, 10, 10),
intArrayOf(100, 0, 10, 255, 20),
- -1
- )
+ -1,
+ ),
)
},
HapticToken.NEUTRAL_CONFIRMATION_HIGH_EMPHASIS to
@@ -172,14 +172,14 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_THUD,
scale = 1f,
- delayMillis = 0
+ delayMillis = 0,
)
),
VibrationEffect.createWaveform(
longArrayOf(50, 100, 100, 50),
intArrayOf(5, 50, 20, 10),
- -1
- )
+ -1,
+ ),
)
},
HapticToken.NEUTRAL_CONFIRMATION_MEDIUM_EMPHASIS to
@@ -192,7 +192,7 @@
delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
)
},
HapticToken.LONG_PRESS to
@@ -202,10 +202,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 0
+ delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
)
},
HapticToken.SWIPE_THRESHOLD_INDICATOR to
@@ -215,10 +215,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 0.7f,
- delayMillis = 0
+ delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
)
},
HapticToken.TAP_HIGH_EMPHASIS to
@@ -228,10 +228,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 0.7f,
- delayMillis = 0
+ delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
)
},
HapticToken.TAP_MEDIUM_EMPHASIS to
@@ -241,10 +241,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 0.5f,
- delayMillis = 0
- ),
+ delayMillis = 0,
+ )
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
)
},
HapticToken.DRAG_THRESHOLD_INDICATOR to
@@ -254,10 +254,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_TICK,
scale = 1f,
- delayMillis = 0
+ delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK),
)
},
HapticToken.DRAG_INDICATOR to
@@ -267,10 +267,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_TICK,
scale = 0.5f,
- delayMillis = 0
+ delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK),
)
},
HapticToken.TAP_LOW_EMPHASIS to
@@ -280,10 +280,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 0.3f,
- delayMillis = 0
+ delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
)
},
HapticToken.KEYPRESS_STANDARD to
@@ -293,10 +293,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_TICK,
scale = 0.7f,
- delayMillis = 0
- ),
+ delayMillis = 0,
+ )
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK),
)
},
HapticToken.KEYPRESS_SPACEBAR to
@@ -306,10 +306,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 0.7f,
- delayMillis = 0
+ delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
)
},
HapticToken.KEYPRESS_RETURN to
@@ -319,10 +319,10 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 0.7f,
- delayMillis = 0
+ delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
)
},
HapticToken.KEYPRESS_DELETE to
@@ -332,12 +332,12 @@
HapticCompositionPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK,
scale = 1f,
- delayMillis = 0
+ delayMillis = 0,
)
),
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
)
- }
+ },
)
}
}
diff --git a/msdllib/src/com/google/android/msdl/domain/MSDLPlayer.kt b/msdllib/src/com/google/android/msdl/domain/MSDLPlayer.kt
index 6fcd75c..c869037 100644
--- a/msdllib/src/com/google/android/msdl/domain/MSDLPlayer.kt
+++ b/msdllib/src/com/google/android/msdl/domain/MSDLPlayer.kt
@@ -16,11 +16,11 @@
package com.google.android.msdl.domain
-import android.content.Context
-import android.os.VibratorManager
+import android.os.Vibrator
import com.google.android.msdl.data.model.FeedbackLevel
import com.google.android.msdl.data.model.HapticComposition
import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.data.repository.MSDLRepository
import com.google.android.msdl.data.repository.MSDLRepositoryImpl
import com.google.android.msdl.domain.MSDLPlayerImpl.Companion.REQUIRED_PRIMITIVES
import java.util.concurrent.Executor
@@ -59,34 +59,43 @@
/**
* Create a new [MSDLPlayer].
*
- * @param[context] The [Context] this player will get its services from.
+ * @param[vibrator] The [Vibrator] this player will use for haptic playback.
* @param[executor] An [Executor] to schedule haptic playback.
+ * @param[useHapticFeedbackForToken] A map that determines if a haptic fallback effect
+ * should be used to play haptics for a given [MSDLToken]. If null, the map will be
+ * created using the support information from the given vibrator.
*/
fun createPlayer(
- context: Context,
+ vibrator: Vibrator,
executor: Executor = Executors.newSingleThreadExecutor(),
+ useHapticFeedbackForToken: Map<MSDLToken, Boolean>? = null,
): MSDLPlayer {
- // Gather vibration dependencies
- val vibratorManager =
- context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
- val vibrator = vibratorManager.defaultVibrator
// Create repository
val repository = MSDLRepositoryImpl()
- // Determine the support for haptic primitives to know if fallbacks will be used
+ // Determine the support for haptic primitives to know if fallbacks will be used.
+ // This can be provided by the client. If omitted, it will be determined from the
+ // supported primitives of the given vibrator.
+ val shouldUseFallbackForToken =
+ useHapticFeedbackForToken ?: createHapticFallbackDecisionMap(vibrator, repository)
+
+ return MSDLPlayerImpl(repository, vibrator, executor, shouldUseFallbackForToken)
+ }
+
+ private fun createHapticFallbackDecisionMap(
+ vibrator: Vibrator,
+ repository: MSDLRepository,
+ ): Map<MSDLToken, Boolean> {
val supportedPrimitives =
REQUIRED_PRIMITIVES.associateWith { vibrator.arePrimitivesSupported(it).first() }
- val useHapticFallbackForToken =
- MSDLToken.entries.associateWith { token ->
- // For each token, determine if the haptic data from the repository should use
- // the fallback effect
- val hapticComposition =
- repository.getHapticData(token.hapticToken)?.get() as? HapticComposition
- hapticComposition?.shouldPlayFallback(supportedPrimitives)
- }
-
- return MSDLPlayerImpl(repository, vibrator, executor, useHapticFallbackForToken)
+ return MSDLToken.entries.associateWith { token ->
+ // For each token, determine if the haptic data from the repository
+ // should use the fallback effect.
+ val hapticComposition =
+ repository.getHapticData(token.hapticToken)?.get() as? HapticComposition
+ hapticComposition?.shouldPlayFallback(supportedPrimitives) ?: false
+ }
}
}
}
diff --git a/msdllib/src/com/google/android/msdl/domain/MSDLPlayerImpl.kt b/msdllib/src/com/google/android/msdl/domain/MSDLPlayerImpl.kt
index 9e78cea..97ca638 100644
--- a/msdllib/src/com/google/android/msdl/domain/MSDLPlayerImpl.kt
+++ b/msdllib/src/com/google/android/msdl/domain/MSDLPlayerImpl.kt
@@ -36,7 +36,7 @@
* @param[useHapticFallbackForToken] A map that determines if the haptic fallback effect should be
* used for a given token.
*/
-class MSDLPlayerImpl(
+internal class MSDLPlayerImpl(
private val repository: MSDLRepository,
private val vibrator: Vibrator,
private val executor: Executor,
@@ -55,10 +55,7 @@
playData(token, properties)
}
- private fun playData(
- token: MSDLToken,
- properties: InteractionProperties?,
- ) {
+ private fun playData(token: MSDLToken, properties: InteractionProperties?) {
// Gather the data from the repositories
val hapticData = repository.getHapticData(token.hapticToken)
val soundData = repository.getAudioData(token.soundToken)
diff --git a/msdllib/tests/src/com/google/android/msdl/data/repository/MSDLRepositoryImplTest.kt b/msdllib/tests/src/com/google/android/msdl/data/repository/MSDLRepositoryImplTest.kt
index 698a67a..576e749 100644
--- a/msdllib/tests/src/com/google/android/msdl/data/repository/MSDLRepositoryImplTest.kt
+++ b/msdllib/tests/src/com/google/android/msdl/data/repository/MSDLRepositoryImplTest.kt
@@ -19,7 +19,6 @@
import com.google.android.msdl.data.model.HapticComposition
import com.google.android.msdl.data.model.HapticToken
import com.google.common.truth.Truth.assertThat
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -27,12 +26,7 @@
@RunWith(JUnit4::class)
class MSDLRepositoryImplTest {
- private lateinit var repository: MSDLRepositoryImpl
-
- @Before
- fun setup() {
- repository = MSDLRepositoryImpl()
- }
+ private val repository = MSDLRepository.createRepository()
@Test
fun getHapticData_forAllHapticTokens_returnsCompositions() {
diff --git a/msdllib/tests/src/com/google/android/msdl/domain/MSDLPlayerImplTest.kt b/msdllib/tests/src/com/google/android/msdl/domain/MSDLPlayerImplTest.kt
index 1b10a90..1b929ab 100644
--- a/msdllib/tests/src/com/google/android/msdl/domain/MSDLPlayerImplTest.kt
+++ b/msdllib/tests/src/com/google/android/msdl/domain/MSDLPlayerImplTest.kt
@@ -21,7 +21,7 @@
import com.google.android.msdl.data.model.FeedbackLevel
import com.google.android.msdl.data.model.HapticComposition
import com.google.android.msdl.data.model.MSDLToken
-import com.google.android.msdl.data.repository.MSDLRepositoryImpl
+import com.google.android.msdl.data.repository.MSDLRepository
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
import org.junit.Before
@@ -34,13 +34,12 @@
@Parameterized.Parameter lateinit var token: MSDLToken
- private val repository = MSDLRepositoryImpl()
+ private val repository = MSDLRepository.createRepository()
private val vibrator = FakeVibrator()
private val executor = Executor { it.run() }
private val useHapticFallbackForToken = MSDLToken.entries.associateWith { false }.toMutableMap()
- private var msdlPlayer =
- MSDLPlayerImpl(repository, vibrator, executor, useHapticFallbackForToken)
+ private var msdlPlayer = MSDLPlayer.createPlayer(vibrator, executor, useHapticFallbackForToken)
@Before
fun setup() {
diff --git a/tracinglib/core/src/FlowTracing.kt b/tracinglib/core/src/FlowTracing.kt
index 69b5965..ce2b645 100644
--- a/tracinglib/core/src/FlowTracing.kt
+++ b/tracinglib/core/src/FlowTracing.kt
@@ -40,7 +40,7 @@
flowName: String,
logcat: Boolean = false,
traceEmissionCount: Boolean = false,
- crossinline valueToString: (T) -> String = { it.toString() }
+ crossinline valueToString: (T) -> String = { it.toString() },
): Flow<T> {
val stateLogger = TraceStateLogger(flowName, logcat = logcat)
val baseFlow = if (traceEmissionCount) traceEmissionCount(flowName) else this
@@ -51,7 +51,7 @@
fun <T : Number> Flow<T>.traceAsCounter(
counterName: String,
traceEmissionCount: Boolean = false,
- valueToInt: (T) -> Int = { it.toInt() }
+ valueToInt: (T) -> Int = { it.toInt() },
): Flow<T> {
val baseFlow = if (traceEmissionCount) traceEmissionCount(counterName) else this
return baseFlow.onEach {
@@ -80,7 +80,7 @@
*/
fun <T> Flow<T>.traceEmissionCount(
flowName: () -> String,
- uniqueSuffix: Boolean = false
+ uniqueSuffix: Boolean = false,
): Flow<T> {
val trackName by lazy {
"${flowName()}#emissionCount" + if (uniqueSuffix) "\$${counter.addAndGet(1)}" else ""
@@ -121,7 +121,7 @@
*/
fun <T> tracedConflatedCallbackFlow(
name: String,
- @BuilderInference block: suspend ProducerScope<T>.() -> Unit
+ @BuilderInference block: suspend ProducerScope<T>.() -> Unit,
): Flow<T> {
return callbackFlow {
traceAsync(DEFAULT_ASYNC_TRACK_NAME, { "$name#CallbackFlowBlock" }) {
diff --git a/tracinglib/core/src/TraceUtils.kt b/tracinglib/core/src/TraceUtils.kt
index 14b29eb..8ae5482 100644
--- a/tracinglib/core/src/TraceUtils.kt
+++ b/tracinglib/core/src/TraceUtils.kt
@@ -132,7 +132,7 @@
@JvmStatic
inline fun traceRunnable(
crossinline tag: () -> String,
- crossinline block: () -> Unit
+ crossinline block: () -> Unit,
): Runnable {
return Runnable { traceSection(tag) { block() } }
}
diff --git a/tracinglib/core/src/coroutines/CoroutineTracing.kt b/tracinglib/core/src/coroutines/CoroutineTracing.kt
index 1cb4687..2138fad 100644
--- a/tracinglib/core/src/coroutines/CoroutineTracing.kt
+++ b/tracinglib/core/src/coroutines/CoroutineTracing.kt
@@ -40,7 +40,7 @@
@OptIn(ExperimentalContracts::class)
suspend inline fun <R> coroutineScope(
traceName: String,
- crossinline block: suspend CoroutineScope.() -> R
+ crossinline block: suspend CoroutineScope.() -> R,
): R {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return traceCoroutine(traceName) {
@@ -59,7 +59,7 @@
crossinline spanName: () -> String,
context: CoroutineContext = EmptyCoroutineContext,
// TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size
- crossinline block: suspend CoroutineScope.() -> Unit
+ crossinline block: suspend CoroutineScope.() -> Unit,
): Job = launch(context) { traceCoroutine(spanName) { block() } }
/**
@@ -71,7 +71,7 @@
spanName: String,
context: CoroutineContext = EmptyCoroutineContext,
// TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size
- crossinline block: suspend CoroutineScope.() -> Unit
+ crossinline block: suspend CoroutineScope.() -> Unit,
): Job = launch(context) { traceCoroutine(spanName) { block() } }
/**
@@ -83,7 +83,7 @@
crossinline spanName: () -> String,
context: CoroutineContext = EmptyCoroutineContext,
// TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size
- crossinline block: suspend CoroutineScope.() -> T
+ crossinline block: suspend CoroutineScope.() -> T,
): Deferred<T> = async(context) { traceCoroutine(spanName) { block() } }
/**
@@ -95,7 +95,7 @@
spanName: String,
context: CoroutineContext = EmptyCoroutineContext,
// TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size
- crossinline block: suspend CoroutineScope.() -> T
+ crossinline block: suspend CoroutineScope.() -> T,
): Deferred<T> = async(context) { traceCoroutine(spanName) { block() } }
/**
@@ -106,7 +106,7 @@
inline fun <T> runBlocking(
crossinline spanName: () -> String,
context: CoroutineContext,
- crossinline block: suspend () -> T
+ crossinline block: suspend () -> T,
): T = runBlocking(context) { traceCoroutine(spanName) { block() } }
/**
@@ -117,7 +117,7 @@
inline fun <T> runBlocking(
spanName: String,
context: CoroutineContext,
- crossinline block: suspend CoroutineScope.() -> T
+ crossinline block: suspend CoroutineScope.() -> T,
): T = runBlocking(context) { traceCoroutine(spanName) { block() } }
/**
@@ -128,7 +128,7 @@
suspend inline fun <T> withContext(
spanName: String,
context: CoroutineContext,
- crossinline block: suspend CoroutineScope.() -> T
+ crossinline block: suspend CoroutineScope.() -> T,
): T = withContext(context) { traceCoroutine(spanName) { block() } }
/**
@@ -139,7 +139,7 @@
suspend inline fun <T> withContext(
crossinline spanName: () -> String,
context: CoroutineContext,
- crossinline block: suspend CoroutineScope.() -> T
+ crossinline block: suspend CoroutineScope.() -> T,
): T = withContext(context) { traceCoroutine(spanName) { block() } }
/**
diff --git a/tracinglib/core/src/coroutines/TraceData.kt b/tracinglib/core/src/coroutines/TraceData.kt
index 97687fe..675a24e 100644
--- a/tracinglib/core/src/coroutines/TraceData.kt
+++ b/tracinglib/core/src/coroutines/TraceData.kt
@@ -42,9 +42,7 @@
* @see traceCoroutine
*/
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-class TraceData(
- val slices: ArrayDeque<TraceSection> = ArrayDeque(),
-) : Cloneable {
+class TraceData(val slices: ArrayDeque<TraceSection> = ArrayDeque()) : Cloneable {
/**
* ThreadLocal counter for how many open trace sections there are. This is needed because it is
diff --git a/tracinglib/core/src/coroutines/flow/FlowExt.kt b/tracinglib/core/src/coroutines/flow/FlowExt.kt
index ec3a55a..6ace77e 100644
--- a/tracinglib/core/src/coroutines/flow/FlowExt.kt
+++ b/tracinglib/core/src/coroutines/flow/FlowExt.kt
@@ -54,9 +54,7 @@
}
@OptIn(ExperimentalTypeInference::class)
-suspend inline fun <T> Flow<T>.collectTraced(
- @BuilderInference block: FlowCollector<T>,
-) {
+suspend inline fun <T> Flow<T>.collectTraced(@BuilderInference block: FlowCollector<T>) {
collect(walkStackForClassName(), block)
}
diff --git a/tracinglib/demo/src/com/android/app/tracing/demo/MainActivity.kt b/tracinglib/demo/src/com/android/app/tracing/demo/MainActivity.kt
index 45689e6..d4a3080 100644
--- a/tracinglib/demo/src/com/android/app/tracing/demo/MainActivity.kt
+++ b/tracinglib/demo/src/com/android/app/tracing/demo/MainActivity.kt
@@ -54,7 +54,7 @@
context.getString(
R.string.run_experiment_button_text,
className,
- demo.getDescription()
+ demo.getDescription(),
)
setOnClickListener {
val experimentName = "$className #${launchCounter++}"
@@ -67,7 +67,7 @@
Trace.TRACE_TAG_APP,
TRACK_NAME,
"Running $experimentName",
- it.hashCode()
+ it.hashCode(),
)
}
}
diff --git a/tracinglib/demo/src/com/android/app/tracing/demo/experiments/NestedLaunchesWithParentSpan.kt b/tracinglib/demo/src/com/android/app/tracing/demo/experiments/NestedLaunchesWithParentSpan.kt
index 5c0632f..bfaed55 100644
--- a/tracinglib/demo/src/com/android/app/tracing/demo/experiments/NestedLaunchesWithParentSpan.kt
+++ b/tracinglib/demo/src/com/android/app/tracing/demo/experiments/NestedLaunchesWithParentSpan.kt
@@ -30,7 +30,7 @@
@Inject
constructor(
@FixedThread1 private var fixedThreadContext1: CoroutineContext,
- @FixedThread2 private var fixedThreadContext2: CoroutineContext
+ @FixedThread2 private var fixedThreadContext2: CoroutineContext,
) : Experiment {
override fun getDescription(): String =
"Nested launches in which only the parent uses a trace name"
diff --git a/tracinglib/demo/src/com/android/app/tracing/demo/experiments/NestedLaunchesWithoutName.kt b/tracinglib/demo/src/com/android/app/tracing/demo/experiments/NestedLaunchesWithoutName.kt
index 0fa7333..bcd4fa5 100644
--- a/tracinglib/demo/src/com/android/app/tracing/demo/experiments/NestedLaunchesWithoutName.kt
+++ b/tracinglib/demo/src/com/android/app/tracing/demo/experiments/NestedLaunchesWithoutName.kt
@@ -29,7 +29,7 @@
@Inject
constructor(
@FixedThread1 private var fixedThreadContext1: CoroutineContext,
- @FixedThread2 private var fixedThreadContext2: CoroutineContext
+ @FixedThread2 private var fixedThreadContext2: CoroutineContext,
) : Experiment {
override fun getDescription(): String =
"Nested launches in which only the leaf uses a trace name"
diff --git a/tracinglib/robolectric/src/CoroutineTracingFlagsTest.kt b/tracinglib/robolectric/src/CoroutineTracingFlagsTest.kt
index 7e3298a..1b1cc81 100644
--- a/tracinglib/robolectric/src/CoroutineTracingFlagsTest.kt
+++ b/tracinglib/robolectric/src/CoroutineTracingFlagsTest.kt
@@ -80,7 +80,7 @@
assertTrue(
"Lazy string should have been called when Compile.IS_DEBUG=true, " +
"even when Trace.isEnabled()=false",
- lazyStringCalled
+ lazyStringCalled,
)
val traceData = traceThreadLocal.get()
assertNotNull(traceData)
diff --git a/tracinglib/robolectric/src/CoroutineTracingTest.kt b/tracinglib/robolectric/src/CoroutineTracingTest.kt
index 3fa968e..f337e52 100644
--- a/tracinglib/robolectric/src/CoroutineTracingTest.kt
+++ b/tracinglib/robolectric/src/CoroutineTracingTest.kt
@@ -117,26 +117,26 @@
expect(
"span-for-coroutineScope-1",
"span-for-coroutineScope-2",
- "span-for-launch-3"
+ "span-for-launch-3",
)
delay(1)
expect(
"span-for-coroutineScope-1",
"span-for-coroutineScope-2",
- "span-for-launch-3"
+ "span-for-launch-3",
)
}
launch("span-for-launch-4") {
expect(
"span-for-coroutineScope-1",
"span-for-coroutineScope-2",
- "span-for-launch-4"
+ "span-for-launch-4",
)
delay(1)
expect(
"span-for-coroutineScope-1",
"span-for-coroutineScope-2",
- "span-for-launch-4"
+ "span-for-launch-4",
)
}
}
@@ -227,13 +227,7 @@
"stuff"
}
- val threadContexts =
- listOf(
- thread1,
- thread2,
- thread3,
- thread4,
- )
+ val threadContexts = listOf(thread1, thread2, thread3, thread4)
val finishedLaunches = Channel<Int>()
@@ -264,7 +258,7 @@
private fun CoroutineScope.testTraceSectionsMultiThreaded(
thread1Context: CoroutineContext,
- thread2Context: CoroutineContext
+ thread2Context: CoroutineContext,
) {
val fetchData1: suspend () -> String = {
expect("span-for-launch-1")
@@ -277,18 +271,12 @@
}
val fetchData2: suspend () -> String = {
- expect(
- "span-for-launch-1",
- "span-for-launch-2",
- )
+ expect("span-for-launch-1", "span-for-launch-2")
delay(1L)
traceCoroutine("span-for-fetchData-2") {
expect("span-for-launch-1", "span-for-launch-2", "span-for-fetchData-2")
}
- expect(
- "span-for-launch-1",
- "span-for-launch-2",
- )
+ expect("span-for-launch-1", "span-for-launch-2")
"stuff-2"
}
@@ -316,7 +304,7 @@
// Thread-#1 and Thread-#2 inherit TraceContextElement from the test's CoroutineContext.
testTraceSectionsMultiThreaded(
thread1Context = EmptyCoroutineContext,
- thread2Context = EmptyCoroutineContext
+ thread2Context = EmptyCoroutineContext,
)
}
@@ -326,7 +314,7 @@
// does not need a TraceContextElement because it does not do any tracing.
testTraceSectionsMultiThreaded(
thread1Context = TraceContextElement(),
- thread2Context = EmptyCoroutineContext
+ thread2Context = EmptyCoroutineContext,
)
}
@@ -337,7 +325,7 @@
// trace context because it does not do any tracing.
testTraceSectionsMultiThreaded(
thread1Context = TraceContextElement(),
- thread2Context = TraceContextElement()
+ thread2Context = TraceContextElement(),
)
}
@@ -347,7 +335,7 @@
// trace results.
testTraceSectionsMultiThreaded(
thread1Context = TraceContextElement(),
- thread2Context = TraceContextElement()
+ thread2Context = TraceContextElement(),
)
}
@@ -365,16 +353,12 @@
}
val fetchData2: suspend () -> String = {
- expect(
- "span-for-launch-2",
- )
+ expect("span-for-launch-2")
channel.receive()
traceCoroutine("span-for-fetchData-2") {
expect("span-for-launch-2", "span-for-fetchData-2")
}
- expect(
- "span-for-launch-2",
- )
+ expect("span-for-launch-2")
"stuff-2"
}
diff --git a/tracinglib/robolectric/src/FlowTracingTest.kt b/tracinglib/robolectric/src/FlowTracingTest.kt
index 9099451..3492c27 100644
--- a/tracinglib/robolectric/src/FlowTracingTest.kt
+++ b/tracinglib/robolectric/src/FlowTracingTest.kt
@@ -73,7 +73,7 @@
expect(
"launch-for-collect",
"com.android.app.tracing.coroutines.FlowTracingTest\$stateFlowCollection$1\$collectJob$1$2:collect",
- "com.android.app.tracing.coroutines.FlowTracingTest\$stateFlowCollection$1\$collectJob$1$2:emit"
+ "com.android.app.tracing.coroutines.FlowTracingTest\$stateFlowCollection$1\$collectJob$1$2:emit",
)
incrementCounter()
}
@@ -114,7 +114,7 @@
"state-flow:collect",
"flowOn(the-name):collect",
"flowOn(the-name):emit",
- "state-flow:emit"
+ "state-flow:emit",
)
}
}
@@ -140,7 +140,7 @@
"my-flow:emit",
"multiply-by-3:emit",
"mod-2:emit",
- "my-collect-call:emit"
+ "my-collect-call:emit",
)
}
}
diff --git a/tracinglib/robolectric/src/TestBase.kt b/tracinglib/robolectric/src/TestBase.kt
index ecb1c43..39657e9 100644
--- a/tracinglib/robolectric/src/TestBase.kt
+++ b/tracinglib/robolectric/src/TestBase.kt
@@ -46,7 +46,7 @@
fun setup() {
assumeTrue(
"Coroutine tracing tests are only applicable on debuggable builds",
- Compile.IS_DEBUG
+ Compile.IS_DEBUG,
)
TraceData.strictModeForTesting = true
FakeTraceState.isTracingEnabled = true
@@ -109,7 +109,7 @@
private fun assertTraceSectionsEquals(
expectedOpenTraceSections: Array<out String>,
- actualOpenSections: Array<String>
+ actualOpenSections: Array<String>,
) {
assertArrayEquals(
"""
@@ -118,7 +118,7 @@
"""
.trimIndent(),
expectedOpenTraceSections,
- actualOpenSections
+ actualOpenSections,
)
}
diff --git a/tracinglib/robolectric/src/util/ExampleClass.kt b/tracinglib/robolectric/src/util/ExampleClass.kt
index 9f12b93..e753ae2 100644
--- a/tracinglib/robolectric/src/util/ExampleClass.kt
+++ b/tracinglib/robolectric/src/util/ExampleClass.kt
@@ -27,7 +27,7 @@
testBase.expect(
"launch-for-collect",
"com.android.app.tracing.coroutines.FlowTracingTest\$stateFlowCollection$1\$collectJob$1$3:collect",
- "com.android.app.tracing.coroutines.FlowTracingTest\$stateFlowCollection$1\$collectJob$1$3:emit"
+ "com.android.app.tracing.coroutines.FlowTracingTest\$stateFlowCollection$1\$collectJob$1$3:emit",
)
incrementCounter()
}
diff --git a/tracinglib/robolectric/src/util/FakeTraceState.kt b/tracinglib/robolectric/src/util/FakeTraceState.kt
index 850e606..476ee8d 100644
--- a/tracinglib/robolectric/src/util/FakeTraceState.kt
+++ b/tracinglib/robolectric/src/util/FakeTraceState.kt
@@ -41,7 +41,7 @@
assertFalse(
"Attempting to close trace section on thread=$threadId, " +
"but there are no open sections",
- allThreadStates[threadId].isNullOrEmpty()
+ allThreadStates[threadId].isNullOrEmpty(),
)
// TODO: Replace with .removeLast() once available
allThreadStates[threadId]!!.removeAt(allThreadStates[threadId]!!.lastIndex)
diff --git a/tracinglib/robolectric/src/util/ShadowTrace.kt b/tracinglib/robolectric/src/util/ShadowTrace.kt
index 6137252..11f8ecd 100644
--- a/tracinglib/robolectric/src/util/ShadowTrace.kt
+++ b/tracinglib/robolectric/src/util/ShadowTrace.kt
@@ -63,7 +63,7 @@
traceTag: Long,
trackName: String,
methodName: String,
- cookie: Int
+ cookie: Int,
) {
debugLog(
"asyncTraceForTrackBegin: track=$trackName name=$methodName cookie=${cookie.toHexString()}"
diff --git a/viewcapturelib/AndroidManifest.xml b/viewcapturelib/AndroidManifest.xml
index 1da8129..d86f1c5 100644
--- a/viewcapturelib/AndroidManifest.xml
+++ b/viewcapturelib/AndroidManifest.xml
@@ -15,9 +15,4 @@
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.app.viewcapture">
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
- tools:ignore="ProtectedPermissions" />
-</manifest>
+<manifest package="com.android.app.viewcapture"/>
\ No newline at end of file
diff --git a/viewcapturelib/tests/AndroidManifest.xml b/viewcapturelib/tests/AndroidManifest.xml
index 8d31c0e..f32f93c 100644
--- a/viewcapturelib/tests/AndroidManifest.xml
+++ b/viewcapturelib/tests/AndroidManifest.xml
@@ -15,7 +15,12 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.android.app.viewcapture.test">
+
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
+ tools:ignore="ProtectedPermissions" />
+
<application
android:debuggable="true"
android:theme="@android:style/Theme.NoTitleBar">
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/WeatherEffect.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/WeatherEffect.kt
index 0517fa3..0761d3c 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/WeatherEffect.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/WeatherEffect.kt
@@ -16,6 +16,7 @@
package com.google.android.wallpaper.weathereffects.graphics
+import android.graphics.Bitmap
import android.graphics.Canvas
import android.util.SizeF
import androidx.annotation.FloatRange
@@ -59,4 +60,16 @@
* @param intensity [0, 1] the intensity of the weather effect.
*/
fun setIntensity(@FloatRange(from = 0.0, to = 1.0) intensity: Float)
+
+ /**
+ * Reuse current shader but change background, foreground
+ *
+ * @param foreground A bitmap containing the foreground of the image
+ * @param background A bitmap containing the background of the image
+ */
+ fun setBitmaps(foreground: Bitmap, background: Bitmap)
+
+ companion object {
+ val DEFAULT_INTENSITY = 1f
+ }
}
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/fog/FogEffect.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/fog/FogEffect.kt
index 0f7f5d1..73d870c 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/fog/FogEffect.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/fog/FogEffect.kt
@@ -16,24 +16,28 @@
package com.google.android.wallpaper.weathereffects.graphics.fog
+import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Shader
import android.util.SizeF
import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect
+import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect.Companion.DEFAULT_INTENSITY
import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils
import com.google.android.wallpaper.weathereffects.graphics.utils.ImageCrop
import com.google.android.wallpaper.weathereffects.graphics.utils.TimeUtils
-import java.util.concurrent.TimeUnit
import kotlin.math.sin
import kotlin.random.Random
/** Defines and generates the fog weather effect animation. */
class FogEffect(
private val fogConfig: FogEffectConfig,
+ private var foreground: Bitmap,
+ private var background: Bitmap,
+ private var intensity: Float = DEFAULT_INTENSITY,
/** The initial size of the surface where the effect will be shown. */
- surfaceSize: SizeF
+ private var surfaceSize: SizeF
) : WeatherEffect {
private val fogPaint = Paint().also { it.shader = fogConfig.colorGradingShader }
@@ -44,12 +48,13 @@
adjustCropping(surfaceSize)
prepareColorGrading()
updateFogGridSize(surfaceSize)
- setIntensity(fogConfig.intensity)
+ setIntensity(intensity)
}
override fun resize(newSurfaceSize: SizeF) {
adjustCropping(newSurfaceSize)
updateFogGridSize(newSurfaceSize)
+ surfaceSize = newSurfaceSize
}
override fun update(deltaMillis: Long, frameTimeNanos: Long) {
@@ -98,13 +103,27 @@
)
}
+ override fun setBitmaps(foreground: Bitmap, background: Bitmap) {
+ this.foreground = foreground
+ this.background = background
+ fogConfig.shader.setInputBuffer(
+ "background",
+ BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ )
+ fogConfig.shader.setInputBuffer(
+ "foreground",
+ BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ )
+ adjustCropping(surfaceSize)
+ }
+
private fun adjustCropping(surfaceSize: SizeF) {
val imageCropFgd =
ImageCrop.centerCoverCrop(
surfaceSize.width,
surfaceSize.height,
- fogConfig.foreground.width.toFloat(),
- fogConfig.foreground.height.toFloat()
+ foreground.width.toFloat(),
+ foreground.height.toFloat()
)
fogConfig.shader.setFloatUniform(
"uvOffsetFgd",
@@ -120,8 +139,8 @@
ImageCrop.centerCoverCrop(
surfaceSize.width,
surfaceSize.height,
- fogConfig.background.width.toFloat(),
- fogConfig.background.height.toFloat()
+ background.width.toFloat(),
+ background.height.toFloat()
)
fogConfig.shader.setFloatUniform(
"uvOffsetBgd",
@@ -143,12 +162,12 @@
private fun updateTextureUniforms() {
fogConfig.shader.setInputBuffer(
"foreground",
- BitmapShader(fogConfig.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
fogConfig.shader.setInputBuffer(
"background",
- BitmapShader(fogConfig.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
fogConfig.shader.setInputBuffer(
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/fog/FogEffectConfig.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/fog/FogEffectConfig.kt
index 35f3073..4ac9b06 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/fog/FogEffectConfig.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/fog/FogEffectConfig.kt
@@ -40,14 +40,8 @@
* tileable, and at least 16-bit per channel for render quality.
*/
val fogTexture: Bitmap,
- /** A bitmap containing the foreground of the image. */
- val foreground: Bitmap,
- /** A bitmap containing the background of the image. */
- val background: Bitmap,
/** Pixel density of the display. Used for dithering. */
val pixelDensity: Float,
- /** The amount of the fog. This contributes to the color grading as well. */
- @FloatRange(from = 0.0, to = 1.0) val intensity: Float,
/** The intensity of the color grading. 0: no color grading, 1: color grading in full effect. */
@FloatRange(from = 0.0, to = 1.0) val colorGradingIntensity: Float,
) {
@@ -55,30 +49,22 @@
* Constructor for [FogEffectConfig].
*
* @param assets the application [AssetManager].
- * @param foreground a bitmap containing the foreground of the image.
- * @param background a bitmap containing the background of the image.
* @param pixelDensity pixel density of the display.
- * @param intensity initial intensity that affects the amount of fog and color grading. Expected
- * range is [0, 1]. You can always change the intensity dynamically. Defaults to 1.
*/
constructor(
assets: AssetManager,
- foreground: Bitmap,
- background: Bitmap,
pixelDensity: Float,
- intensity: Float = DEFAULT_INTENSITY,
) : this(
shader = GraphicsUtils.loadShader(assets, SHADER_PATH),
colorGradingShader = GraphicsUtils.loadShader(assets, COLOR_GRADING_SHADER_PATH),
lut = GraphicsUtils.loadTexture(assets, LOOKUP_TABLE_TEXTURE_PATH),
- cloudsTexture = GraphicsUtils.loadTexture(assets, CLOUDS_TEXTURE_PATH)
+ cloudsTexture =
+ GraphicsUtils.loadTexture(assets, CLOUDS_TEXTURE_PATH)
?: throw RuntimeException("Clouds texture is missing."),
- fogTexture = GraphicsUtils.loadTexture(assets, FOG_TEXTURE_PATH)
+ fogTexture =
+ GraphicsUtils.loadTexture(assets, FOG_TEXTURE_PATH)
?: throw RuntimeException("Fog texture is missing."),
- foreground,
- background,
pixelDensity,
- intensity,
COLOR_GRADING_INTENSITY
)
@@ -88,7 +74,6 @@
private const val LOOKUP_TABLE_TEXTURE_PATH = "textures/lut_rain_and_fog.png"
private const val CLOUDS_TEXTURE_PATH = "textures/clouds.png"
private const val FOG_TEXTURE_PATH = "textures/fog.png"
- private const val DEFAULT_INTENSITY = 1f
private const val COLOR_GRADING_INTENSITY = 0.7f
}
}
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/none/NoEffect.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/none/NoEffect.kt
index 592b627..d1aedcd 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/none/NoEffect.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/none/NoEffect.kt
@@ -23,7 +23,7 @@
import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils
/** Simply draws foreground and background images with no weather effect. */
-class NoEffect(val foreground: Bitmap, val background: Bitmap, private var surfaceSize: SizeF) :
+class NoEffect(var foreground: Bitmap, var background: Bitmap, private var surfaceSize: SizeF) :
WeatherEffect {
override fun resize(newSurfaceSize: SizeF) {
surfaceSize = newSurfaceSize
@@ -40,7 +40,6 @@
),
null
)
-
canvas.drawBitmap(
foreground,
MatrixUtils.centerCropMatrix(
@@ -56,4 +55,9 @@
override fun release() {}
override fun setIntensity(intensity: Float) {}
+
+ override fun setBitmaps(foreground: Bitmap, background: Bitmap) {
+ this.foreground = foreground
+ this.background = background
+ }
}
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/rain/RainEffect.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/rain/RainEffect.kt
index f66e0b6..c554b5e 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/rain/RainEffect.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/rain/RainEffect.kt
@@ -16,6 +16,7 @@
package com.google.android.wallpaper.weathereffects.graphics.rain
+import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Color
@@ -25,20 +26,23 @@
import android.util.SizeF
import com.google.android.wallpaper.weathereffects.graphics.FrameBuffer
import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect
+import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect.Companion.DEFAULT_INTENSITY
import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils
import com.google.android.wallpaper.weathereffects.graphics.utils.ImageCrop
import com.google.android.wallpaper.weathereffects.graphics.utils.SolidColorShader
import com.google.android.wallpaper.weathereffects.graphics.utils.TimeUtils
import java.util.concurrent.Executor
-import java.util.concurrent.TimeUnit
import kotlin.random.Random
/** Defines and generates the rain weather effect animation. */
class RainEffect(
/** The config of the rain effect. */
private val rainConfig: RainEffectConfig,
+ private var foreground: Bitmap,
+ private var background: Bitmap,
+ private var intensity: Float = DEFAULT_INTENSITY,
/** The initial size of the surface where the effect will be shown. */
- surfaceSize: SizeF,
+ private var surfaceSize: SizeF,
private val mainExecutor: Executor
) : WeatherEffect {
@@ -46,8 +50,8 @@
// Set blur effect to reduce the outline noise. No need to set blur effect every time we
// re-generate the outline buffer.
- private val outlineBuffer =
- FrameBuffer(rainConfig.background.width, rainConfig.background.height).apply {
+ private var outlineBuffer =
+ FrameBuffer(background.width, background.height).apply {
setRenderEffect(RenderEffect.createBlurEffect(2f, 2f, Shader.TileMode.CLAMP))
}
private val outlineBufferPaint = Paint().also { it.shader = rainConfig.outlineShader }
@@ -59,12 +63,13 @@
adjustCropping(surfaceSize)
prepareColorGrading()
updateRainGridSize(surfaceSize)
- setIntensity(rainConfig.intensity)
+ setIntensity(intensity)
}
override fun resize(newSurfaceSize: SizeF) {
adjustCropping(newSurfaceSize)
updateRainGridSize(newSurfaceSize)
+ surfaceSize = newSurfaceSize
}
override fun update(deltaMillis: Long, frameTimeNanos: Long) {
@@ -104,13 +109,27 @@
createOutlineBuffer()
}
+ override fun setBitmaps(foreground: Bitmap, background: Bitmap) {
+ this.foreground = foreground
+ this.background = background
+ outlineBuffer =
+ FrameBuffer(background.width, background.height).apply {
+ setRenderEffect(RenderEffect.createBlurEffect(2f, 2f, Shader.TileMode.CLAMP))
+ }
+ adjustCropping(surfaceSize)
+ updateTextureUniforms()
+
+ // Need to recreate the outline buffer as the outlineBuffer has changed due to background
+ createOutlineBuffer()
+ }
+
private fun adjustCropping(surfaceSize: SizeF) {
val imageCropFgd =
ImageCrop.centerCoverCrop(
surfaceSize.width,
surfaceSize.height,
- rainConfig.foreground.width.toFloat(),
- rainConfig.foreground.height.toFloat()
+ foreground.width.toFloat(),
+ foreground.height.toFloat()
)
rainConfig.rainShowerShader.setFloatUniform(
"uvOffsetFgd",
@@ -127,8 +146,8 @@
ImageCrop.centerCoverCrop(
surfaceSize.width,
surfaceSize.height,
- rainConfig.background.width.toFloat(),
- rainConfig.background.height.toFloat()
+ background.width.toFloat(),
+ background.height.toFloat()
)
rainConfig.rainShowerShader.setFloatUniform(
"uvOffsetBgd",
@@ -159,13 +178,13 @@
private fun updateTextureUniforms() {
val foregroundBuffer =
- BitmapShader(rainConfig.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
rainConfig.rainShowerShader.setInputBuffer("foreground", foregroundBuffer)
rainConfig.outlineShader.setInputBuffer("texture", foregroundBuffer)
rainConfig.rainShowerShader.setInputBuffer(
"background",
- BitmapShader(rainConfig.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
}
@@ -198,7 +217,8 @@
}
private fun updateRainGridSize(surfaceSize: SizeF) {
- val widthScreenScale = GraphicsUtils.computeDefaultGridSize(surfaceSize, rainConfig.pixelDensity)
+ val widthScreenScale =
+ GraphicsUtils.computeDefaultGridSize(surfaceSize, rainConfig.pixelDensity)
rainConfig.rainShowerShader.setFloatUniform("gridScale", widthScreenScale)
rainConfig.glassRainShader.setFloatUniform("gridScale", widthScreenScale)
}
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/rain/RainEffectConfig.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/rain/RainEffectConfig.kt
index 77c93de..1567db3 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/rain/RainEffectConfig.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/rain/RainEffectConfig.kt
@@ -34,43 +34,27 @@
val outlineShader: RuntimeShader,
/** The main lut (color grading) for the effect. */
val lut: Bitmap?,
- /** A bitmap containing the foreground of the image. */
- val foreground: Bitmap,
- /** A bitmap containing the background of the image. */
- val background: Bitmap,
/** Pixel density of the display. Used for dithering. */
val pixelDensity: Float,
- /** The amount of the rain. This contributes to the color grading as well. */
- @FloatRange(from = 0.0, to = 1.0) val intensity: Float,
/** The intensity of the color grading. 0: no color grading, 1: color grading in full effect. */
@FloatRange(from = 0.0, to = 1.0) val colorGradingIntensity: Float,
) {
/**
* Constructor for [RainEffectConfig].
*
- * @param assets asset manager.
- * @param foreground a bitmap containing the foreground of the image.
- * @param background a bitmap containing the background of the image.
+ * @param assets asset manager,
* @param pixelDensity pixel density of the display.
- * @param intensity initial intensity that affects the amount of rain and color grading.
- * Expected range is [0, 1]. You can always change the intensity dynamically. Defaults to 1.
*/
constructor(
assets: AssetManager,
- foreground: Bitmap,
- background: Bitmap,
pixelDensity: Float,
- intensity: Float = DEFAULT_INTENSITY,
) : this(
rainShowerShader = GraphicsUtils.loadShader(assets, RAIN_SHOWER_LAYER_SHADER_PATH),
glassRainShader = GraphicsUtils.loadShader(assets, GLASS_RAIN_LAYER_SHADER_PATH),
colorGradingShader = GraphicsUtils.loadShader(assets, COLOR_GRADING_SHADER_PATH),
outlineShader = GraphicsUtils.loadShader(assets, OUTLINE_SHADER_PATH),
lut = GraphicsUtils.loadTexture(assets, LOOKUP_TABLE_TEXTURE_PATH),
- foreground,
- background,
pixelDensity,
- intensity,
COLOR_GRADING_INTENSITY
)
@@ -80,7 +64,6 @@
private const val COLOR_GRADING_SHADER_PATH = "shaders/color_grading_lut.agsl"
private const val OUTLINE_SHADER_PATH = "shaders/outline.agsl"
private const val LOOKUP_TABLE_TEXTURE_PATH = "textures/lut_rain_and_fog.png"
- private const val DEFAULT_INTENSITY = 1f
private const val COLOR_GRADING_INTENSITY = 0.7f
}
}
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffect.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffect.kt
index ad353e4..cec6cc2 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffect.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffect.kt
@@ -16,6 +16,7 @@
package com.google.android.wallpaper.weathereffects.graphics.snow
+import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Paint
@@ -24,6 +25,7 @@
import android.util.SizeF
import com.google.android.wallpaper.weathereffects.graphics.FrameBuffer
import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect
+import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect.Companion.DEFAULT_INTENSITY
import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils
import com.google.android.wallpaper.weathereffects.graphics.utils.ImageCrop
import com.google.android.wallpaper.weathereffects.graphics.utils.MathUtils
@@ -35,8 +37,11 @@
class SnowEffect(
/** The config of the snow effect. */
private val snowConfig: SnowEffectConfig,
+ private var foreground: Bitmap,
+ private var background: Bitmap,
+ private var intensity: Float = DEFAULT_INTENSITY,
/** The initial size of the surface where the effect will be shown. */
- surfaceSize: SizeF,
+ private var surfaceSize: SizeF,
/** App main executor. */
private val mainExecutor: Executor
) : WeatherEffect {
@@ -45,7 +50,7 @@
private val snowPaint = Paint().also { it.shader = snowConfig.colorGradingShader }
private var elapsedTime: Float = 0f
- private val frameBuffer = FrameBuffer(snowConfig.background.width, snowConfig.background.height)
+ private var frameBuffer = FrameBuffer(background.width, background.height)
private val frameBufferPaint = Paint().also { it.shader = snowConfig.accumulatedSnowShader }
init {
@@ -54,7 +59,7 @@
adjustCropping(surfaceSize)
prepareColorGrading()
updateSnowGridSize(surfaceSize)
- setIntensity(snowConfig.intensity)
+ setIntensity(intensity)
// Generate accumulated snow at the end after we updated all the uniforms.
generateAccumulatedSnow()
@@ -63,6 +68,7 @@
override fun resize(newSurfaceSize: SizeF) {
adjustCropping(newSurfaceSize)
updateSnowGridSize(newSurfaceSize)
+ surfaceSize = newSurfaceSize
}
override fun update(deltaMillis: Long, frameTimeNanos: Long) {
@@ -105,13 +111,31 @@
generateAccumulatedSnow()
}
+ override fun setBitmaps(foreground: Bitmap, background: Bitmap) {
+ this.foreground = foreground
+ this.background = background
+ frameBuffer = FrameBuffer(background.width, background.height)
+ snowConfig.shader.setInputBuffer(
+ "background",
+ BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ )
+ snowConfig.shader.setInputBuffer(
+ "foreground",
+ BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ )
+ adjustCropping(surfaceSize)
+ // generateAccumulatedSnow needs foreground for accumulatedSnowShader, and needs frameBuffer
+ // which is also changed with background
+ generateAccumulatedSnow()
+ }
+
private fun adjustCropping(surfaceSize: SizeF) {
val imageCropFgd =
ImageCrop.centerCoverCrop(
surfaceSize.width,
surfaceSize.height,
- snowConfig.foreground.width.toFloat(),
- snowConfig.foreground.height.toFloat()
+ foreground.width.toFloat(),
+ foreground.height.toFloat()
)
snowConfig.shader.setFloatUniform(
"uvOffsetFgd",
@@ -127,8 +151,8 @@
ImageCrop.centerCoverCrop(
surfaceSize.width,
surfaceSize.height,
- snowConfig.background.width.toFloat(),
- snowConfig.background.height.toFloat()
+ background.width.toFloat(),
+ background.height.toFloat()
)
snowConfig.shader.setFloatUniform(
"uvOffsetBgd",
@@ -150,12 +174,12 @@
private fun updateTextureUniforms() {
snowConfig.shader.setInputBuffer(
"foreground",
- BitmapShader(snowConfig.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
snowConfig.shader.setInputBuffer(
"background",
- BitmapShader(snowConfig.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
snowConfig.shader.setInputBuffer(
@@ -182,7 +206,7 @@
)
snowConfig.accumulatedSnowShader.setInputBuffer(
"foreground",
- BitmapShader(snowConfig.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
renderingCanvas.drawPaint(frameBufferPaint)
frameBuffer.endDrawing()
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffectConfig.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffectConfig.kt
index 0d28c68..76b4892 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffectConfig.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffectConfig.kt
@@ -37,14 +37,8 @@
val noiseTexture: Bitmap,
/** The main lut (color grading) for the effect. */
val lut: Bitmap?,
- /** A bitmap containing the foreground of the image. */
- val foreground: Bitmap,
- /** A bitmap containing the background of the image. */
- val background: Bitmap,
/** Pixel density of the display. Used for dithering. */
val pixelDensity: Float,
- /** The amount of the snow flakes. This contributes to the color grading as well. */
- @FloatRange(from = 0.0, to = 1.0) val intensity: Float,
/** The intensity of the color grading. 0: no color grading, 1: color grading in full effect. */
@FloatRange(from = 0.0, to = 1.0) val colorGradingIntensity: Float,
/** Max thickness for the accumulated snow. */
@@ -53,30 +47,22 @@
/**
* Constructor for [SnowEffectConfig].
*
- * @param context the application context.
- * @param foreground a bitmap containing the foreground of the image.
- * @param background a bitmap containing the background of the image.
- * @param pixelDensity pixel density of the display.
- * @param intensity initial intensity that affects the amount of snow flakes and color grading.
- * Expected range is [0, 1]. You can always change the intensity dynamically. Defaults to 1.
+ * @param assets asset manager,
+ * @param pixelDensity pixel density of the display. Expected range is [0, 1]. You can always
+ * change the intensity dynamically. Defaults to 1.
*/
constructor(
assets: AssetManager,
- foreground: Bitmap,
- background: Bitmap,
pixelDensity: Float,
- intensity: Float = DEFAULT_INTENSITY,
) : this(
shader = GraphicsUtils.loadShader(assets, SHADER_PATH),
accumulatedSnowShader = GraphicsUtils.loadShader(assets, ACCUMULATED_SNOW_SHADER_PATH),
colorGradingShader = GraphicsUtils.loadShader(assets, COLOR_GRADING_SHADER_PATH),
- noiseTexture = GraphicsUtils.loadTexture(assets, NOISE_TEXTURE_PATH)
- ?: throw RuntimeException("Noise texture is missing."),
+ noiseTexture =
+ GraphicsUtils.loadTexture(assets, NOISE_TEXTURE_PATH)
+ ?: throw RuntimeException("Noise texture is missing."),
lut = GraphicsUtils.loadTexture(assets, LOOKUP_TABLE_TEXTURE_PATH),
- foreground,
- background,
pixelDensity,
- intensity,
COLOR_GRADING_INTENSITY,
MAX_SNOW_THICKNESS
)
@@ -87,7 +73,6 @@
private const val COLOR_GRADING_SHADER_PATH = "shaders/color_grading_lut.agsl"
private const val NOISE_TEXTURE_PATH = "textures/clouds.png"
private const val LOOKUP_TABLE_TEXTURE_PATH = "textures/lut_rain_and_fog.png"
- private const val DEFAULT_INTENSITY = 1f
private const val COLOR_GRADING_INTENSITY = 0.7f
private const val MAX_SNOW_THICKNESS = 10f
}
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/sun/SunEffect.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/sun/SunEffect.kt
index f403724..29ffe5d 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/sun/SunEffect.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/sun/SunEffect.kt
@@ -16,6 +16,7 @@
package com.google.android.wallpaper.weathereffects.graphics.sun
+import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Paint
@@ -31,8 +32,11 @@
class SunEffect(
/** The config of the sunny effect. */
private val sunConfig: SunEffectConfig,
+ private var foreground: Bitmap,
+ private var background: Bitmap,
+ private var intensity: Float = WeatherEffect.DEFAULT_INTENSITY,
/** The initial size of the surface where the effect will be shown. */
- surfaceSize: SizeF
+ var surfaceSize: SizeF
) : WeatherEffect {
private val sunnyPaint = Paint().also { it.shader = sunConfig.colorGradingShader }
@@ -42,10 +46,13 @@
updateTextureUniforms()
adjustCropping(surfaceSize)
prepareColorGrading()
- setIntensity(sunConfig.intensity)
+ setIntensity(intensity)
}
- override fun resize(newSurfaceSize: SizeF) = adjustCropping(newSurfaceSize)
+ override fun resize(newSurfaceSize: SizeF) {
+ adjustCropping(newSurfaceSize)
+ surfaceSize = newSurfaceSize
+ }
override fun update(deltaMillis: Long, frameTimeNanos: Long) {
elapsedTime += TimeUnit.MILLISECONDS.toSeconds(deltaMillis)
@@ -73,13 +80,28 @@
)
}
- private fun adjustCropping(surfaceSize: SizeF) {
- val imageCropFgd = ImageCrop.centerCoverCrop(
- surfaceSize.width,
- surfaceSize.height,
- sunConfig.foreground.width.toFloat(),
- sunConfig.foreground.height.toFloat()
+ override fun setBitmaps(foreground: Bitmap, background: Bitmap) {
+ this.foreground = foreground
+ this.background = background
+ sunConfig.shader.setInputBuffer(
+ "background",
+ BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
+ sunConfig.shader.setInputBuffer(
+ "foreground",
+ BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ )
+ adjustCropping(surfaceSize)
+ }
+
+ private fun adjustCropping(surfaceSize: SizeF) {
+ val imageCropFgd =
+ ImageCrop.centerCoverCrop(
+ surfaceSize.width,
+ surfaceSize.height,
+ foreground.width.toFloat(),
+ foreground.height.toFloat()
+ )
sunConfig.shader.setFloatUniform(
"uvOffsetFgd",
imageCropFgd.leftOffset,
@@ -90,12 +112,13 @@
imageCropFgd.horizontalScale,
imageCropFgd.verticalScale
)
- val imageCropBgd = ImageCrop.centerCoverCrop(
- surfaceSize.width,
- surfaceSize.height,
- sunConfig.background.width.toFloat(),
- sunConfig.background.height.toFloat()
- )
+ val imageCropBgd =
+ ImageCrop.centerCoverCrop(
+ surfaceSize.width,
+ surfaceSize.height,
+ background.width.toFloat(),
+ background.height.toFloat()
+ )
sunConfig.shader.setFloatUniform(
"uvOffsetBgd",
imageCropBgd.leftOffset,
@@ -116,12 +139,12 @@
private fun updateTextureUniforms() {
sunConfig.shader.setInputBuffer(
"foreground",
- BitmapShader(sunConfig.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
sunConfig.shader.setInputBuffer(
"background",
- BitmapShader(sunConfig.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
+ BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
}
@@ -133,9 +156,6 @@
BitmapShader(it, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
)
}
- sunConfig.colorGradingShader.setFloatUniform(
- "intensity",
- sunConfig.colorGradingIntensity
- )
+ sunConfig.colorGradingShader.setFloatUniform("intensity", sunConfig.colorGradingIntensity)
}
}
diff --git a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/sun/SunEffectConfig.kt b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/sun/SunEffectConfig.kt
index 1307d7f..05f6b80 100644
--- a/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/sun/SunEffectConfig.kt
+++ b/weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/sun/SunEffectConfig.kt
@@ -16,10 +16,10 @@
package com.google.android.wallpaper.weathereffects.graphics.sun
-import androidx.annotation.FloatRange
import android.content.res.AssetManager
import android.graphics.Bitmap
import android.graphics.RuntimeShader
+import androidx.annotation.FloatRange
import com.google.android.wallpaper.weathereffects.graphics.fog.FogEffectConfig
import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils
@@ -31,14 +31,8 @@
val colorGradingShader: RuntimeShader,
/** The main lut (color grading) for the effect. */
val lut: Bitmap?,
- /** A bitmap containing the foreground of the image. */
- val foreground: Bitmap,
- /** A bitmap containing the background of the image. */
- val background: Bitmap,
/** Pixel density of the display. Used for dithering. */
val pixelDensity: Float,
- /** The amount of the fog. This contributes to the color grading as well. */
- @FloatRange(from = 0.0, to = 1.0) val intensity: Float,
/** The intensity of the color grading. 0: no color grading, 1: color grading in full effect. */
@FloatRange(from = 0.0, to = 1.0) val colorGradingIntensity: Float,
) {
@@ -46,33 +40,23 @@
* Constructor for [FogEffectConfig].
*
* @param assets the application [AssetManager].
- * @param foreground a bitmap containing the foreground of the image.
- * @param background a bitmap containing the background of the image.
* @param pixelDensity pixel density of the display.
- * @param intensity initial intensity that affects the amount of fog and color grading. Expected
- * range is [0, 1]. You can always change the intensity dynamically. Defaults to 1.
*/
constructor(
assets: AssetManager,
- foreground: Bitmap,
- background: Bitmap,
pixelDensity: Float,
- intensity: Float = DEFAULT_INTENSITY,
) : this(
shader = GraphicsUtils.loadShader(assets, SHADER_PATH),
colorGradingShader = GraphicsUtils.loadShader(assets, COLOR_GRADING_SHADER_PATH),
lut = GraphicsUtils.loadTexture(assets, LOOKUP_TABLE_TEXTURE_PATH),
- foreground,
- background,
pixelDensity,
- intensity,
COLOR_GRADING_INTENSITY
)
+
companion object {
private const val SHADER_PATH = "shaders/sun_effect.agsl"
private const val COLOR_GRADING_SHADER_PATH = "shaders/color_grading_lut.agsl"
private const val LOOKUP_TABLE_TEXTURE_PATH = "textures/lut_rain_and_fog.png"
- private const val DEFAULT_INTENSITY = 1f
private const val COLOR_GRADING_INTENSITY = 0.7f
}
}
diff --git a/weathereffects/src/com/google/android/wallpaper/weathereffects/WeatherEngine.kt b/weathereffects/src/com/google/android/wallpaper/weathereffects/WeatherEngine.kt
index 1686509..ce6ccf7 100644
--- a/weathereffects/src/com/google/android/wallpaper/weathereffects/WeatherEngine.kt
+++ b/weathereffects/src/com/google/android/wallpaper/weathereffects/WeatherEngine.kt
@@ -44,11 +44,11 @@
import com.google.android.wallpaper.weathereffects.provider.WallpaperInfoContract
import com.google.android.wallpaper.weathereffects.sensor.UserPresenceController
import com.google.android.wallpaper.weathereffects.shared.model.WallpaperImageModel
+import kotlin.math.max
+import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-import kotlin.math.max
-import kotlin.math.roundToInt
class WeatherEngine(
defaultHolder: SurfaceHolder,
@@ -56,8 +56,10 @@
private val interactor: WeatherEffectsInteractor,
private val context: Context,
private val isDebugActivity: Boolean = false,
- hardwareAccelerated: Boolean = true
-) : CanvasWallpaperEngine(defaultHolder, hardwareAccelerated), LiveWallpaperKeyguardEventListener,
+ hardwareAccelerated: Boolean = true,
+) :
+ CanvasWallpaperEngine(defaultHolder, hardwareAccelerated),
+ LiveWallpaperKeyguardEventListener,
LiveWallpaperEventListener {
private var lockStartTime: Long = 0
@@ -87,9 +89,7 @@
init {
/* Load assets. */
if (interactor.wallpaperImageModel.value == null) {
- applicationScope.launch {
- interactor.loadWallpaper()
- }
+ applicationScope.launch { interactor.loadWallpaper() }
}
}
@@ -115,14 +115,15 @@
}
override fun onResume() {
- collectWallpaperImageJob = applicationScope.launch {
- interactor.wallpaperImageModel.collect { asset ->
- if (asset == null || asset == currentAssets) return@collect
- currentAssets = asset
- createWeatherEffect(asset.foreground, asset.background, asset.weatherEffect)
- updateWallpaperColors(asset.background)
+ collectWallpaperImageJob =
+ applicationScope.launch {
+ interactor.wallpaperImageModel.collect { asset ->
+ if (asset == null || asset == currentAssets) return@collect
+ currentAssets = asset
+ createWeatherEffect(asset.foreground, asset.background, asset.weatherEffect)
+ updateWallpaperColors(asset.background)
+ }
}
- }
if (activeEffect != null) {
if (shouldTriggerUpdate()) startUpdateLoop()
}
@@ -187,40 +188,63 @@
private fun createWeatherEffect(
foreground: Bitmap,
background: Bitmap,
- weatherEffect: WallpaperInfoContract.WeatherEffect? = null
+ weatherEffect: WallpaperInfoContract.WeatherEffect? = null,
) {
activeEffect?.release()
activeEffect = null
when (weatherEffect) {
WallpaperInfoContract.WeatherEffect.RAIN -> {
- val rainConfig = RainEffectConfig(
- context.assets, foreground, background, context.resources.displayMetrics.density
- )
- activeEffect = RainEffect(rainConfig, screenSize.toSizeF(), context.mainExecutor)
+ val rainConfig =
+ RainEffectConfig(context.assets, context.resources.displayMetrics.density)
+ activeEffect =
+ RainEffect(
+ rainConfig,
+ foreground,
+ background,
+ effectIntensity,
+ screenSize.toSizeF(),
+ context.mainExecutor,
+ )
}
-
WallpaperInfoContract.WeatherEffect.FOG -> {
- val fogConfig = FogEffectConfig(
- context.assets, foreground, background, context.resources.displayMetrics.density
- )
- activeEffect = FogEffect(fogConfig, screenSize.toSizeF())
- }
+ val fogConfig =
+ FogEffectConfig(context.assets, context.resources.displayMetrics.density)
+ activeEffect =
+ FogEffect(
+ fogConfig,
+ foreground,
+ background,
+ effectIntensity,
+ screenSize.toSizeF()
+ )
+ }
WallpaperInfoContract.WeatherEffect.SNOW -> {
- val snowConfig = SnowEffectConfig(
- context.assets, foreground, background, context.resources.displayMetrics.density
- )
- activeEffect = SnowEffect(snowConfig, screenSize.toSizeF(), context.mainExecutor)
+ val snowConfig =
+ SnowEffectConfig(context.assets, context.resources.displayMetrics.density)
+ activeEffect =
+ SnowEffect(
+ snowConfig,
+ foreground,
+ background,
+ effectIntensity,
+ screenSize.toSizeF(),
+ context.mainExecutor,
+ )
}
-
WallpaperInfoContract.WeatherEffect.SUN -> {
- val snowConfig = SunEffectConfig(
- context.assets, foreground, background, context.resources.displayMetrics.density
- )
- activeEffect = SunEffect(snowConfig, screenSize.toSizeF())
+ val snowConfig =
+ SunEffectConfig(context.assets, context.resources.displayMetrics.density)
+ activeEffect =
+ SunEffect(
+ snowConfig,
+ foreground,
+ background,
+ effectIntensity,
+ screenSize.toSizeF()
+ )
}
-
else -> {
activeEffect = NoEffect(foreground, background, screenSize.toSizeF())
}
@@ -239,11 +263,9 @@
private fun onUserPresenceChange(
newUserPresence: UserPresenceController.UserPresence,
- oldUserPresence: UserPresenceController.UserPresence
+ oldUserPresence: UserPresenceController.UserPresence,
) {
- playIntensityFadeOutAnimation(
- getAnimationType(newUserPresence, oldUserPresence)
- )
+ playIntensityFadeOutAnimation(getAnimationType(newUserPresence, oldUserPresence))
}
private fun updateCurrentIntensity(intensity: Float = effectIntensity) {
@@ -261,7 +283,6 @@
lockStartTime = SystemClock.elapsedRealtime()
animateWeatherIntensityOut(AUTO_FADE_DELAY_FROM_AWAY_MILLIS)
}
-
AnimationType.UNLOCK -> {
// If already running, don't stop it.
if (unlockAnimator?.isRunning == true) {
@@ -289,7 +310,6 @@
updateCurrentIntensity()
animateWeatherIntensityOut(delayTime, AUTO_FADE_SHORT_DURATION_MILLIS)
}
-
AnimationType.NONE -> {
// No-op.
}
@@ -300,22 +320,23 @@
private fun animateWeatherIntensityOut(
delayMillis: Long,
- durationMillis: Long = AUTO_FADE_DURATION_MILLIS
+ durationMillis: Long = AUTO_FADE_DURATION_MILLIS,
) {
- unlockAnimator = ValueAnimator.ofFloat(effectIntensity, 0f).apply {
- duration = durationMillis
- startDelay = delayMillis
- addUpdateListener { updatedAnimation ->
- effectIntensity = updatedAnimation.animatedValue as Float
- updateCurrentIntensity()
+ unlockAnimator =
+ ValueAnimator.ofFloat(effectIntensity, 0f).apply {
+ duration = durationMillis
+ startDelay = delayMillis
+ addUpdateListener { updatedAnimation ->
+ effectIntensity = updatedAnimation.animatedValue as Float
+ updateCurrentIntensity()
+ }
+ start()
}
- start()
- }
}
private fun getAnimationType(
newPresence: UserPresenceController.UserPresence,
- oldPresence: UserPresenceController.UserPresence
+ oldPresence: UserPresenceController.UserPresence,
): AnimationType {
if (shouldSkipIntensityOutAnimation()) {
return AnimationType.NONE
@@ -324,18 +345,16 @@
UserPresenceController.UserPresence.AWAY -> {
if (
newPresence == UserPresenceController.UserPresence.LOCKED ||
- newPresence == UserPresenceController.UserPresence.ACTIVE
+ newPresence == UserPresenceController.UserPresence.ACTIVE
) {
return AnimationType.WAKE
}
}
-
UserPresenceController.UserPresence.LOCKED -> {
if (newPresence == UserPresenceController.UserPresence.ACTIVE) {
return AnimationType.UNLOCK
}
}
-
else -> {
// No-op.
}
@@ -345,14 +364,15 @@
}
private fun updateWallpaperColors(background: Bitmap) {
- backgroundColor = WallpaperColors.fromBitmap(
- Bitmap.createScaledBitmap(
- background,
- 256,
- (background.width / background.height.toFloat() * 256).roundToInt(),
- /* filter = */ true
+ backgroundColor =
+ WallpaperColors.fromBitmap(
+ Bitmap.createScaledBitmap(
+ background,
+ 256,
+ (background.width / background.height.toFloat() * 256).roundToInt(),
+ /* filter = */ true
+ )
)
- )
}
/**