Update AuthenticationScreen
This commit is contained in:
parent
fb31de8130
commit
70c25b0fc5
10 changed files with 477 additions and 40 deletions
|
|
@ -84,6 +84,7 @@ dependencies {
|
|||
|
||||
// Compose
|
||||
implementation("androidx.compose.ui:ui:1.4.3")
|
||||
implementation("androidx.compose.ui:ui-util:1.4.3")
|
||||
implementation("androidx.compose.ui:ui-graphics:1.4.3")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview:1.4.3")
|
||||
implementation("androidx.compose.material:material:1.4.3")
|
||||
|
|
|
|||
|
|
@ -2,12 +2,22 @@ package com.pixelized.rplexicon.ui.screens.authentication
|
|||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
|
|
@ -18,9 +28,15 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
|
|
@ -28,16 +44,22 @@ import androidx.compose.ui.text.font.FontStyle
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.rplexicon.LocalActivity
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexicon
|
||||
import com.pixelized.rplexicon.ui.navigation.rootOption
|
||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexicon
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import com.pixelized.rplexicon.ui.theme.colors.GoogleColorPalette
|
||||
import com.pixelized.rplexicon.utilitary.sensor.Gyroscope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlin.math.E
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
|
||||
@Composable
|
||||
fun AuthenticationScreen(
|
||||
|
|
@ -85,6 +107,9 @@ private fun AuthenticationScreenContent(
|
|||
onGoogleSignIn: () -> Unit,
|
||||
) {
|
||||
val typography = MaterialTheme.typography
|
||||
|
||||
PartyBackground()
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.Bottom),
|
||||
|
|
@ -111,6 +136,148 @@ private fun AuthenticationScreenContent(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PartyBackground(
|
||||
modifier: Modifier = Modifier,
|
||||
spacerWidth: Dp = 1.dp,
|
||||
images: List<Int> = rememberPortrait(),
|
||||
) {
|
||||
Gyroscope {
|
||||
val balance = remember {
|
||||
derivedStateOf {
|
||||
max(-1f, min(1f, this.gyroscope.value.roll))
|
||||
}
|
||||
}
|
||||
val colorFilter = remember {
|
||||
ColorFilter.colorMatrix(
|
||||
ColorMatrix().also { it.setToSaturation(0f) }
|
||||
)
|
||||
}
|
||||
Row(modifier = modifier) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(weight = animatedWeight(progress = balance, divergence = 0.95f, position = 1f)),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
colorFilter = colorFilter,
|
||||
painter = painterResource(id = images[0]),
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(width = spacerWidth)
|
||||
.fillMaxHeight()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(weight = animatedWeight(progress = balance, divergence = 0.93f, position = .5f)),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
colorFilter = colorFilter,
|
||||
painter = painterResource(id = images[1]),
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(width = spacerWidth)
|
||||
.fillMaxHeight()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(weight = animatedWeight(progress = balance, divergence = 0.91f, position = 0f)),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
colorFilter = colorFilter,
|
||||
painter = painterResource(id = images[2]),
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(width = spacerWidth)
|
||||
.fillMaxHeight()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(weight = animatedWeight(progress = balance, divergence = 0.93f, position = -.5f)),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
colorFilter = colorFilter,
|
||||
painter = painterResource(id = images[3]),
|
||||
contentDescription = null,
|
||||
)
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(width = spacerWidth)
|
||||
.fillMaxHeight()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(weight = animatedWeight(progress = balance, divergence = 0.95f, position = -1f)),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
colorFilter = colorFilter,
|
||||
painter = painterResource(id = images[4]),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(brush = rememberBackgroundGradient())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberPortrait(): List<Int> = remember {
|
||||
listOf(
|
||||
R.drawable.im_tigrane,
|
||||
R.drawable.im_unathana,
|
||||
R.drawable.im_brulkhai,
|
||||
R.drawable.im_nelia,
|
||||
R.drawable.im_leandre,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun animatedWeight(
|
||||
progress: State<Float>,
|
||||
amplitude: Float = E.toFloat(),
|
||||
divergence: Float = 0.95f,
|
||||
position: Float,
|
||||
step: Float = .16f,
|
||||
): Float {
|
||||
val animatedWeight = animateFloatAsState(
|
||||
targetValue = divergence * amplitude.pow(-(position - progress.value).pow(2f) / step) + (1f - divergence),
|
||||
label = "AnimatedBackgroundWeight",
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioNoBouncy,
|
||||
stiffness = Spring.StiffnessLow,
|
||||
)
|
||||
)
|
||||
return animatedWeight.value
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberBackgroundGradient(): Brush {
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
return remember {
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
colorScheme.surface.copy(alpha = 0.25f),
|
||||
colorScheme.surface.copy(alpha = 0.5f),
|
||||
colorScheme.surface.copy(alpha = 0.75f),
|
||||
colorScheme.surface.copy(alpha = 1.0f),
|
||||
colorScheme.surface.copy(alpha = 1.0f),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememeberGoogleStringResource(): AnnotatedString {
|
||||
val default = LocalTextStyle.current.toSpanStyle()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
|||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.with
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -118,8 +122,9 @@ fun LexiconScreen(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class,
|
||||
ExperimentalFoundationApi::class
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class,
|
||||
ExperimentalFoundationApi::class, ExperimentalAnimationApi::class
|
||||
)
|
||||
@Composable
|
||||
private fun LexiconScreenContent(
|
||||
|
|
@ -141,7 +146,7 @@ private fun LexiconScreenContent(
|
|||
},
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
// floatingActionButton = {
|
||||
// FloatingActionButton(
|
||||
// modifier = Modifier.padding(start = 32.dp),
|
||||
// expended = isFabExpended.value,
|
||||
|
|
@ -162,50 +167,63 @@ private fun LexiconScreenContent(
|
|||
// )
|
||||
// },
|
||||
// )
|
||||
}
|
||||
// }
|
||||
) { padding ->
|
||||
Box(
|
||||
modifier = Modifier.padding(paddingValues = padding),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.pullRefresh(state = refreshState),
|
||||
state = lazyColumnState,
|
||||
contentPadding = PaddingValues(
|
||||
top = 8.dp,
|
||||
bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
|
||||
),
|
||||
) {
|
||||
if (items.value.isEmpty()) {
|
||||
items(
|
||||
count = 6,
|
||||
key = { it },
|
||||
contentType = { "Lexicon" },
|
||||
AnimatedContent(
|
||||
targetState = items.value.isEmpty(),
|
||||
transitionSpec = { fadeIn() with fadeOut() },
|
||||
label = "AnimatedLexicon"
|
||||
) { empty ->
|
||||
when (empty) {
|
||||
true -> LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.pullRefresh(state = refreshState),
|
||||
state = lazyColumnState,
|
||||
contentPadding = PaddingValues(
|
||||
top = 8.dp,
|
||||
bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
|
||||
),
|
||||
) {
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
item = LexiconItemUio.Brulkhai,
|
||||
)
|
||||
items(count = 6) {
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
item = LexiconItemUio.Brulkhai,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items(
|
||||
items = items.value,
|
||||
key = { it.id },
|
||||
contentType = { "Lexicon" },
|
||||
|
||||
else -> LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.pullRefresh(state = refreshState),
|
||||
state = lazyColumnState,
|
||||
contentPadding = PaddingValues(
|
||||
top = 8.dp,
|
||||
bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
|
||||
),
|
||||
) {
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.clickable { onItem(it) }
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
item = it,
|
||||
)
|
||||
items(
|
||||
items = items.value,
|
||||
key = { it.id },
|
||||
contentType = { "Lexicon" },
|
||||
) {
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.clickable { onItem(it) }
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
item = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,187 @@
|
|||
package com.pixelized.rplexicon.utilitary.sensor
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
@Composable
|
||||
fun Gyroscope(
|
||||
content: @Composable GyroscopeScope.() -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val gyroscope = remember {
|
||||
mutableStateOf(GyroscopeData.Zero)
|
||||
}
|
||||
|
||||
val scope = remember {
|
||||
GyroscopeScopeImpl(
|
||||
gyroscope = gyroscope,
|
||||
initialAngle = GyroscopeScopeImpl.INITIAL_ANGLE_X,
|
||||
)
|
||||
}
|
||||
|
||||
if (LocalInspectionMode.current.not()) {
|
||||
val onSensorChanged = remember {
|
||||
object : SensorEventListener {
|
||||
override fun onSensorChanged(event: SensorEvent?) {
|
||||
if (event?.sensor?.type == Sensor.TYPE_GRAVITY) {
|
||||
scope.gravity = event.values
|
||||
}
|
||||
if (event?.sensor?.type == Sensor.TYPE_MAGNETIC_FIELD) {
|
||||
scope.geomagnetic = event.values
|
||||
}
|
||||
val orientation = getGyroscopeData(
|
||||
gravity = scope.gravity,
|
||||
geomagnetic = scope.geomagnetic,
|
||||
deviceRotationMatrix = scope.deviceRotationMatrix,
|
||||
rotationMatrix = scope.localRotationMatrix,
|
||||
localRotationMatrix = scope.localOrientationMatrix,
|
||||
orientationMatrix = scope.orientationMatrix,
|
||||
)
|
||||
if (orientation != null) {
|
||||
gyroscope.value = orientation
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(key1 = "SensorServices") {
|
||||
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
val gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
|
||||
val magnetometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
|
||||
|
||||
sensorManager.registerListener(
|
||||
onSensorChanged,
|
||||
gravitySensor,
|
||||
SensorManager.SENSOR_DELAY_UI,
|
||||
)
|
||||
sensorManager.registerListener(
|
||||
onSensorChanged,
|
||||
magnetometerSensor,
|
||||
SensorManager.SENSOR_DELAY_UI,
|
||||
)
|
||||
|
||||
onDispose {
|
||||
sensorManager.unregisterListener(onSensorChanged)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scope.content()
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// region: GyroscopeScope
|
||||
|
||||
@Immutable
|
||||
interface GyroscopeScope {
|
||||
val gyroscope: State<GyroscopeData>
|
||||
}
|
||||
|
||||
@Immutable
|
||||
class GyroscopeScopeImpl(
|
||||
override val gyroscope: State<GyroscopeData>,
|
||||
initialAngle: Float,
|
||||
) : GyroscopeScope {
|
||||
var gravity: FloatArray? = null
|
||||
var geomagnetic: FloatArray? = null
|
||||
val deviceRotationMatrix: FloatArray = FloatArray(9)
|
||||
val orientationMatrix: FloatArray = FloatArray(3)
|
||||
val localRotationMatrix: FloatArray = getRotationMatrix(angle = initialAngle)
|
||||
val localOrientationMatrix: FloatArray = FloatArray(9)
|
||||
|
||||
// https://fr.wikipedia.org/wiki/Matrice_de_rotation
|
||||
private fun getRotationMatrix(angle: Float): FloatArray {
|
||||
return floatArrayOf(1f, 0f, 0f, 0f, cos(angle), -sin(angle), 0f, sin(angle), cos(angle))
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val INITIAL_ANGLE_X: Float = -Math.PI.toFloat() / 6f
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
//////////////////////////////////////
|
||||
// region: Helper method.
|
||||
|
||||
/**
|
||||
* Helper method to build a [GyroscopeData] instance containing the computation Pitch and Roll.
|
||||
*
|
||||
* @param gravity a FloatArray? of 3 elements representing the gravity acceleration.
|
||||
* @param geomagnetic a FloatArray? of 3 elements representing the magnetosphere acceleration.
|
||||
* @param deviceRotationMatrix a FloatArray of 9 elements representing the device rotation.
|
||||
* @param rotationMatrix a FloatArray of 9 elements representing the wanted initial rotation.
|
||||
* @param localRotationMatrix a FloatArray of 9 elements representing the device rotation with the initial angle set.
|
||||
* @param orientationMatrix a FloatArray of 3 elements representing the Azimuth, Pitch and Roll.
|
||||
*/
|
||||
private fun getGyroscopeData(
|
||||
gravity: FloatArray?,
|
||||
geomagnetic: FloatArray?,
|
||||
deviceRotationMatrix: FloatArray,
|
||||
rotationMatrix: FloatArray,
|
||||
localRotationMatrix: FloatArray,
|
||||
orientationMatrix: FloatArray,
|
||||
): GyroscopeData? {
|
||||
if (gravity != null && geomagnetic != null) {
|
||||
// populate the rotation matrix.
|
||||
if (SensorManager.getRotationMatrix(deviceRotationMatrix, null, gravity, geomagnetic)) {
|
||||
// rotate the orientation matrix
|
||||
rotate(
|
||||
a = deviceRotationMatrix,
|
||||
b = rotationMatrix,
|
||||
out = localRotationMatrix,
|
||||
)
|
||||
// populate the orientation matrix.
|
||||
SensorManager.getOrientation(localRotationMatrix, orientationMatrix)
|
||||
// return the orientation.
|
||||
return GyroscopeData(
|
||||
pitch = orientationMatrix[1],
|
||||
roll = orientationMatrix[2],
|
||||
)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is use to introduce an initial angle for the gyroscope by multiplying
|
||||
* [a] by [b] and store the result into [out].
|
||||
*
|
||||
* @param a a FloatArray of length 9
|
||||
* @param b a FloatArray of length 9
|
||||
* @param out a FloatArray of length 9
|
||||
*/
|
||||
private fun rotate(
|
||||
a: FloatArray,
|
||||
b: FloatArray,
|
||||
out: FloatArray,
|
||||
) {
|
||||
if (a.size == 9 && b.size == 9 && out.size == 9) {
|
||||
out[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6]
|
||||
out[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7]
|
||||
out[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8]
|
||||
out[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6]
|
||||
out[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7]
|
||||
out[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8]
|
||||
out[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6]
|
||||
out[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7]
|
||||
out[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.pixelized.rplexicon.utilitary.sensor
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.util.packFloats
|
||||
import androidx.compose.ui.util.unpackFloat1
|
||||
import androidx.compose.ui.util.unpackFloat2
|
||||
|
||||
@Stable
|
||||
fun GyroscopeData(pitch: Float, roll: Float): GyroscopeData =
|
||||
GyroscopeData(packFloats(pitch, roll))
|
||||
|
||||
@Immutable
|
||||
@JvmInline
|
||||
value class GyroscopeData internal constructor(@PublishedApi internal val packedValue: Long) {
|
||||
@Stable
|
||||
val pitch: Float
|
||||
get() = unpackFloat1(packedValue)
|
||||
|
||||
@Stable
|
||||
val roll: Float
|
||||
get() = unpackFloat2(packedValue)
|
||||
|
||||
@Stable
|
||||
operator fun component1(): Float = pitch
|
||||
|
||||
@Stable
|
||||
operator fun component2(): Float = roll
|
||||
|
||||
@Suppress("unused")
|
||||
fun copy(pitch: Float = this.pitch, roll: Float = this.roll): GyroscopeData =
|
||||
GyroscopeData(pitch = pitch, roll = roll)
|
||||
|
||||
@Stable
|
||||
operator fun minus(other: GyroscopeData): GyroscopeData =
|
||||
GyroscopeData(pitch = pitch - other.pitch, roll = roll - other.roll)
|
||||
|
||||
@Stable
|
||||
operator fun plus(other: GyroscopeData): GyroscopeData =
|
||||
GyroscopeData(pitch = pitch + other.pitch, roll = roll + other.roll)
|
||||
|
||||
@Stable
|
||||
operator fun unaryMinus(): GyroscopeData =
|
||||
GyroscopeData(pitch = -pitch, roll = -roll)
|
||||
|
||||
@Stable
|
||||
operator fun times(operand: Float): GyroscopeData =
|
||||
GyroscopeData(pitch = pitch * operand, roll = roll * operand)
|
||||
|
||||
@Stable
|
||||
operator fun div(operand: Float): GyroscopeData =
|
||||
GyroscopeData(pitch = pitch / operand, roll = roll / operand)
|
||||
|
||||
@Stable
|
||||
operator fun rem(operand: Int): GyroscopeData =
|
||||
GyroscopeData(pitch = pitch % operand, roll = roll % operand)
|
||||
|
||||
@Stable
|
||||
override fun toString(): String = "(pitch:$pitch, roll:$roll)"
|
||||
|
||||
companion object {
|
||||
val Zero = GyroscopeData(0f, 0f)
|
||||
}
|
||||
}
|
||||
BIN
app/src/main/res/drawable/im_brulkhai.webp
Normal file
BIN
app/src/main/res/drawable/im_brulkhai.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
app/src/main/res/drawable/im_leandre.webp
Normal file
BIN
app/src/main/res/drawable/im_leandre.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
BIN
app/src/main/res/drawable/im_nelia.webp
Normal file
BIN
app/src/main/res/drawable/im_nelia.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
BIN
app/src/main/res/drawable/im_tigrane.webp
Normal file
BIN
app/src/main/res/drawable/im_tigrane.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 147 KiB |
BIN
app/src/main/res/drawable/im_unathana.webp
Normal file
BIN
app/src/main/res/drawable/im_unathana.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
Loading…
Add table
Add a link
Reference in a new issue