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
|
// Compose
|
||||||
implementation("androidx.compose.ui:ui:1.4.3")
|
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-graphics:1.4.3")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview:1.4.3")
|
implementation("androidx.compose.ui:ui-tooling-preview:1.4.3")
|
||||||
implementation("androidx.compose.material:material: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_NO
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
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.border
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
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.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
|
@ -18,9 +28,15 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.buildAnnotatedString
|
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.font.FontWeight
|
||||||
import androidx.compose.ui.text.withStyle
|
import androidx.compose.ui.text.withStyle
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.rplexicon.LocalActivity
|
import com.pixelized.rplexicon.LocalActivity
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
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.rootOption
|
||||||
|
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexicon
|
||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
import com.pixelized.rplexicon.ui.theme.colors.GoogleColorPalette
|
import com.pixelized.rplexicon.ui.theme.colors.GoogleColorPalette
|
||||||
|
import com.pixelized.rplexicon.utilitary.sensor.Gyroscope
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlin.math.E
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AuthenticationScreen(
|
fun AuthenticationScreen(
|
||||||
|
|
@ -85,6 +107,9 @@ private fun AuthenticationScreenContent(
|
||||||
onGoogleSignIn: () -> Unit,
|
onGoogleSignIn: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val typography = MaterialTheme.typography
|
val typography = MaterialTheme.typography
|
||||||
|
|
||||||
|
PartyBackground()
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.Bottom),
|
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
|
@Composable
|
||||||
private fun rememeberGoogleStringResource(): AnnotatedString {
|
private fun rememeberGoogleStringResource(): AnnotatedString {
|
||||||
val default = LocalTextStyle.current.toSpanStyle()
|
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.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.AnimatedContent
|
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.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
|
@ -118,8 +122,9 @@ fun LexiconScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class,
|
@OptIn(
|
||||||
ExperimentalFoundationApi::class
|
ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class,
|
||||||
|
ExperimentalFoundationApi::class, ExperimentalAnimationApi::class
|
||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
private fun LexiconScreenContent(
|
private fun LexiconScreenContent(
|
||||||
|
|
@ -141,7 +146,7 @@ private fun LexiconScreenContent(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
// floatingActionButton = {
|
||||||
// FloatingActionButton(
|
// FloatingActionButton(
|
||||||
// modifier = Modifier.padding(start = 32.dp),
|
// modifier = Modifier.padding(start = 32.dp),
|
||||||
// expended = isFabExpended.value,
|
// expended = isFabExpended.value,
|
||||||
|
|
@ -162,50 +167,63 @@ private fun LexiconScreenContent(
|
||||||
// )
|
// )
|
||||||
// },
|
// },
|
||||||
// )
|
// )
|
||||||
}
|
// }
|
||||||
) { padding ->
|
) { padding ->
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.padding(paddingValues = padding),
|
modifier = Modifier.padding(paddingValues = padding),
|
||||||
contentAlignment = Alignment.TopCenter,
|
contentAlignment = Alignment.TopCenter,
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
AnimatedContent(
|
||||||
modifier = Modifier
|
targetState = items.value.isEmpty(),
|
||||||
.fillMaxSize()
|
transitionSpec = { fadeIn() with fadeOut() },
|
||||||
.pullRefresh(state = refreshState),
|
label = "AnimatedLexicon"
|
||||||
state = lazyColumnState,
|
) { empty ->
|
||||||
contentPadding = PaddingValues(
|
when (empty) {
|
||||||
top = 8.dp,
|
true -> LazyColumn(
|
||||||
bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
|
modifier = Modifier
|
||||||
),
|
.fillMaxSize()
|
||||||
) {
|
.pullRefresh(state = refreshState),
|
||||||
if (items.value.isEmpty()) {
|
state = lazyColumnState,
|
||||||
items(
|
contentPadding = PaddingValues(
|
||||||
count = 6,
|
top = 8.dp,
|
||||||
key = { it },
|
bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
|
||||||
contentType = { "Lexicon" },
|
),
|
||||||
) {
|
) {
|
||||||
LexiconItem(
|
items(count = 6) {
|
||||||
modifier = Modifier
|
LexiconItem(
|
||||||
.animateItemPlacement()
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.animateItemPlacement()
|
||||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
.fillMaxWidth()
|
||||||
item = LexiconItemUio.Brulkhai,
|
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||||
)
|
item = LexiconItemUio.Brulkhai,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
items(
|
else -> LazyColumn(
|
||||||
items = items.value,
|
modifier = Modifier
|
||||||
key = { it.id },
|
.fillMaxSize()
|
||||||
contentType = { "Lexicon" },
|
.pullRefresh(state = refreshState),
|
||||||
|
state = lazyColumnState,
|
||||||
|
contentPadding = PaddingValues(
|
||||||
|
top = 8.dp,
|
||||||
|
bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
LexiconItem(
|
items(
|
||||||
modifier = Modifier
|
items = items.value,
|
||||||
.animateItemPlacement()
|
key = { it.id },
|
||||||
.clickable { onItem(it) }
|
contentType = { "Lexicon" },
|
||||||
.fillMaxWidth()
|
) {
|
||||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
LexiconItem(
|
||||||
item = it,
|
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