Skip to content

Commit

Permalink
Merge branch 'main' into fix-blink-favorite-icon
Browse files Browse the repository at this point in the history
  • Loading branch information
takahirom authored Sep 4, 2024
2 parents d55402c + a184a96 commit 4efc2b0
Show file tree
Hide file tree
Showing 24 changed files with 675 additions and 117 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ internal class IosOrientationSensorManager(
private val motionManager = CMMotionManager()

override fun start() {
if (!motionManager.deviceMotionActive) {
if (!motionManager.deviceMotionAvailable) {
return
}
NSOperationQueue.currentQueue()?.let {
NSOperationQueue.mainQueue().let { queue ->
motionManager.startDeviceMotionUpdatesToQueue(
it,
queue,
) { motion, _ ->
if (motion == null) {
return@startDeviceMotionUpdatesToQueue
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package io.github.droidkaigi.confsched.testing.robot

import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorManager
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.github.takahirom.roborazzi.provideRoborazziContext
import com.github.takahirom.roborazzi.roboOutputName
Expand Down Expand Up @@ -38,7 +43,13 @@ import io.github.droidkaigi.confsched.testing.rules.RobotTestRule
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.test.TestDispatcher
import org.robolectric.RuntimeEnvironment
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Implementation
import org.robolectric.annotation.Implements
import org.robolectric.shadows.SensorEventBuilder
import org.robolectric.shadows.ShadowLooper
import org.robolectric.shadows.ShadowSensor
import org.robolectric.shadows.ShadowSensorManager
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds

Expand Down Expand Up @@ -171,6 +182,144 @@ class DefaultDeviceSetupRobot @Inject constructor() : DeviceSetupRobot {
}
}

interface SensorRobot {
fun setupMockSensors(sensorTypes: List<Int>)
fun cleanUpSensors()
fun tiltPitch(pitch: Float = 10f)
fun tiltRoll(roll: Float = 10f)
fun tiltAzimuth(azimuth: Float = 10f)
fun tiltAllAxes(pitch: Float = 10f, roll: Float = 10f, azimuth: Float = 10f)
}

class DefaultSensorRobot @Inject constructor() : SensorRobot {
private val sensorManager: SensorManager =
getApplicationContext<Context>().getSystemService(Context.SENSOR_SERVICE) as SensorManager
private val shadowSensorManager = shadowOf(sensorManager)

private lateinit var mockAccelerometerSensor: Sensor
private lateinit var mockMagneticFieldSensor: Sensor

override fun setupMockSensors(sensorTypes: List<Int>) {
sensorTypes.forEach { sensorType ->
val sensor = ShadowSensor.newInstance(sensorType)
shadowSensorManager.addSensor(sensor)
when (sensorType) {
Sensor.TYPE_ACCELEROMETER -> mockAccelerometerSensor = sensor
Sensor.TYPE_MAGNETIC_FIELD -> mockMagneticFieldSensor = sensor
else -> throw IllegalArgumentException("Unsupported sensor type: $sensorType")
}
}
}

override fun cleanUpSensors() {
CustomShadowSensorManager.setCustomRotationMatrix(floatArrayOf())
CustomShadowSensorManager.setCustomOrientationAngles(floatArrayOf())
}

override fun tiltPitch(pitch: Float) {
sendTiltEvent(mockAccelerometerSensor, pitch = pitch)
sendTiltEvent(mockMagneticFieldSensor, pitch = pitch)
}

override fun tiltRoll(roll: Float) {
sendTiltEvent(mockAccelerometerSensor, roll = roll)
sendTiltEvent(mockMagneticFieldSensor, roll = roll)
}

override fun tiltAzimuth(azimuth: Float) {
sendTiltEvent(mockAccelerometerSensor, azimuth = azimuth)
sendTiltEvent(mockMagneticFieldSensor, azimuth = azimuth)
}

override fun tiltAllAxes(pitch: Float, roll: Float, azimuth: Float) {
sendTiltEvent(mockAccelerometerSensor, pitch, roll, azimuth)
sendTiltEvent(mockMagneticFieldSensor, pitch, roll, azimuth)
}

private fun sendTiltEvent(
sensor: Sensor?,
pitch: Float = 0f,
roll: Float = 0f,
azimuth: Float = 0f,
) {
if (sensor != null) {
val event = createTiltEvent(sensor, pitch, roll, azimuth)
CustomShadowSensorManager.setCustomRotationMatrix(
FloatArray(9).apply {
SensorManager.getRotationMatrix(
this,
null,
floatArrayOf(pitch, roll, azimuth),
floatArrayOf(0f, 0f, 0f),
)
},
)
CustomShadowSensorManager.setCustomOrientationAngles(floatArrayOf(azimuth, pitch, roll))
shadowSensorManager.sendSensorEventToListeners(event)
}
}

private fun createTiltEvent(
sensor: Sensor,
pitch: Float,
roll: Float,
azimuth: Float,
): SensorEvent {
return SensorEventBuilder.newBuilder()
.setSensor(sensor)
.setTimestamp(System.currentTimeMillis())
.setValues(floatArrayOf(pitch, roll, azimuth))
.build()
}

@Implements(SensorManager::class)
class CustomShadowSensorManager : ShadowSensorManager() {

@Suppress("UNUSED_PARAMETER")
companion object {
private var customRotationMatrix: FloatArray? = null
private var customOrientationAngles: FloatArray? = null

fun setCustomRotationMatrix(rotationMatrix: FloatArray) {
customRotationMatrix = rotationMatrix
}

@Implementation
@JvmStatic
fun getRotationMatrix(
r: FloatArray?,
i: FloatArray?,
gravity: FloatArray?,
geomagnetic: FloatArray?,
): Boolean {
customRotationMatrix?.let {
if (r != null && it.size == r.size) {
System.arraycopy(it, 0, r, 0, it.size)
}
return true
}
return false
}

fun setCustomOrientationAngles(orientationAngles: FloatArray) {
customOrientationAngles = orientationAngles
}

@Implementation
@JvmStatic
fun getOrientation(r: FloatArray?, values: FloatArray?): FloatArray {
customOrientationAngles?.let {
if (values != null && it.size == values.size) {
System.arraycopy(it, 0, values, 0, it.size)
}
return it
}
return r!!
}
}
}
}

interface TimetableServerRobot {
enum class ServerStatus {
Operational,
Expand Down Expand Up @@ -340,6 +489,7 @@ class DefaultSettingsDataStoreRobot @Inject constructor(
),
)
}

UseSystemDefaultFont -> {
settingsDataStore.save(
Settings.Exists(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.droidkaigi.confsched.testing.robot

import android.graphics.RenderNode
import android.hardware.Sensor
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
Expand All @@ -25,12 +26,16 @@ import io.github.droidkaigi.confsched.profilecard.component.ProfileCardFlipCardF
import io.github.droidkaigi.confsched.profilecard.component.ProfileCardFlipCardTestTag
import org.robolectric.util.ReflectionHelpers
import javax.inject.Inject
import kotlin.math.PI

class ProfileCardScreenRobot @Inject constructor(
screenRobot: DefaultScreenRobot,
profileCardRepositoryRobot: DefaultProfileCardDataStoreRobot,
sensorRobot: DefaultSensorRobot,
) : ScreenRobot by screenRobot,
ProfileCardDataStoreRobot by profileCardRepositoryRobot {
ProfileCardDataStoreRobot by profileCardRepositoryRobot,
SensorRobot by sensorRobot {

fun setupScreenContent() {
robotTestRule.setContent {
KaigiTheme {
Expand All @@ -50,6 +55,15 @@ class ProfileCardScreenRobot @Inject constructor(
waitUntilIdle()
}

fun setupMockSensor() {
setupMockSensors(
listOf(
Sensor.TYPE_ACCELEROMETER,
Sensor.TYPE_MAGNETIC_FIELD,
),
)
}

fun inputNickName(
nickName: String,
) {
Expand Down Expand Up @@ -100,6 +114,70 @@ class ProfileCardScreenRobot @Inject constructor(
waitUntilIdle()
}

fun tiltToHorizontal() {
tiltAllAxes(
pitch = degreeToRadian(0f),
roll = degreeToRadian(0f),
)
waitUntilIdle()
}

fun tiltToMidRange() {
tiltAllAxes(
pitch = degreeToRadian(45f),
roll = degreeToRadian(45f),
)
waitUntilIdle()
}

fun tiltToUpperBound() {
tiltAllAxes(
pitch = degreeToRadian(75f),
roll = degreeToRadian(75f),
)
waitUntilIdle()
}

fun tiltPitchOutOfBounds() {
tiltAllAxes(
pitch = degreeToRadian(-80f),
roll = degreeToRadian(0f),
)
waitUntilIdle()
}

fun tiltRollOutOfBounds() {
tiltAllAxes(
pitch = degreeToRadian(0f),
roll = degreeToRadian(80f),
)
waitUntilIdle()
}

fun tiltBothAxesOutOfBounds() {
tiltAllAxes(
pitch = degreeToRadian(-80f),
roll = degreeToRadian(80f),
)
waitUntilIdle()
}

fun tiltToPitchRollBoundary() {
tiltAllAxes(
pitch = degreeToRadian(-75f),
roll = degreeToRadian(75f),
)
waitUntilIdle()
}

fun tiltToPitchRollBoundaryOpposite() {
tiltAllAxes(
pitch = degreeToRadian(75f),
roll = degreeToRadian(-75f),
)
waitUntilIdle()
}

fun checkCreateButtonDisabled() {
composeTestRule
.onNode(hasTestTag(ProfileCardCreateButtonTestTag))
Expand Down Expand Up @@ -175,4 +253,12 @@ class ProfileCardScreenRobot @Inject constructor(
.onNode(hasTestTag(ProfileCardFlipCardBackTestTag))
.assertIsDisplayed()
}

fun cleanUp() {
cleanUpSensors()
}

private fun degreeToRadian(degree: Float): Float {
return (degree * PI / 180f).toFloat()
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit 4efc2b0

Please sign in to comment.