Skip to content

Commit 6172601

Browse files
committed
Merge remote-tracking branch 'origin/issue/17214-final-design-touces' into issue/17214-final-design-touces
2 parents 979e289 + 177df3c commit 6172601

1 file changed

Lines changed: 46 additions & 31 deletions

File tree

WordPress/src/jetpack/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedViewModel.kt

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,73 +11,88 @@ import androidx.lifecycle.ViewModel
1111
import dagger.hilt.android.lifecycle.HiltViewModel
1212
import dagger.hilt.android.qualifiers.ApplicationContext
1313
import javax.inject.Inject
14-
import kotlin.math.exp
15-
import kotlin.math.ln
14+
import kotlin.math.PI
1615

17-
// This factor is used to convert the raw values emitted from device sensor to an appropriate scale for the consuming
18-
// composables.
19-
private const val ACCELERATION_FACTOR = -0.01425f
16+
/**
17+
* This factor is used to convert the raw values emitted from device sensor to an appropriate scale for the consuming
18+
* composables. The range for pitch values is -π/2 to π/2, representing an upright pose and an upside-down pose,
19+
* respectively. E.g. Setting this factor to 0.2 / (π/2) will scale this output range to -0.2 to 0.2. The resulting
20+
* product is interpreted in units proportional to the repeating child composable's height per second, i.e. at a
21+
* velocity of 0.1, it will take 10 seconds for the looping animation to repeat. When applied, the product can also be
22+
* thought of as a frequency value in Hz.
23+
*/
24+
private const val VELOCITY_FACTOR = (0.2 / (PI / 2)).toFloat()
2025

21-
// The maximum velocity (in either direction)
22-
private const val MAXIMUM_VELOCITY = 0.1f
23-
24-
// The velocity decay factor (i.e. 1/7th velocity after 1 second)
25-
private val VELOCITY_DECAY = -ln(7f)
26-
27-
// An additional acceleration applied to make the text scroll when the device is flat on a table
28-
private const val DRIFT = -0.05f
26+
/**
27+
* This is the default pitch provided for devices lacking support for the sensors used. This is consumed by the model
28+
* as a value in radians, but is specified here as -30 degrees for convenience. This represents a device pose that is
29+
* slightly upright from flat, approximating a typical usage pattern, and will ensure that the text is animated at
30+
* an appropriate velocity when sensors are unavailable.
31+
*/
32+
private const val DEFAULT_PITCH = (-30 * PI / 180).toFloat()
2933

3034
@HiltViewModel
3135
class LoginPrologueRevampedViewModel @Inject constructor(
3236
@ApplicationContext appContext: Context,
3337
) : ViewModel() {
34-
private var acceleration = -0.3f // Default value when sensor is off
35-
private var velocity = 0f
38+
private val accelerometerData = FloatArray(3)
39+
private val magnetometerData = FloatArray(3)
40+
private val rotationMatrix = FloatArray(9)
41+
private val orientationAngles = floatArrayOf(0f, DEFAULT_PITCH, 0f)
3642
private var position = 0f
3743

3844
/**
3945
* This function updates the physics model for the interactive animation by applying the elapsed time (in seconds)
4046
* to update the velocity and position.
4147
*
42-
* * Velocity is constrained so that it does not fall below -MAXIMUM_VELOCITY and does not exceed MAXIMUM_VELOCITY.
48+
* * Velocity is calculated as proportional to the pitch angle
4349
* * Position is constrained so that it always falls between 0 and 1, and represents the relative vertical offset
4450
* in terms of the height of the repeated child composable.
4551
*
4652
* @param elapsed the elapsed time (in seconds) since the last frame
4753
*/
4854
fun updateForFrame(elapsed: Float) {
49-
// Update the velocity, (decayed and clamped to the maximum)
50-
velocity = (velocity * exp(elapsed * VELOCITY_DECAY) + elapsed * acceleration)
51-
.coerceIn(-MAXIMUM_VELOCITY, MAXIMUM_VELOCITY)
52-
// Update the position, modulo 1 (ensuring a value greater or equal to 0, and less than 1)
53-
position = ((position + elapsed * velocity) % 1 + 1) % 1
55+
orientationAngles.let { (_, pitch) ->
56+
val velocity = pitch * VELOCITY_FACTOR
57+
58+
// Update the position, modulo 1 (ensuring a value greater or equal to 0, and less than 1)
59+
position = ((position + elapsed * velocity) % 1 + 1) % 1
5460

55-
_positionData.postValue(position)
61+
_positionData.postValue(position)
62+
}
5663
}
5764

58-
/** This LiveData responds to accelerometer data from the y-axis of the device and emits updated position data. */
65+
/**
66+
* This LiveData responds to orientation data to calculate the pitch of the device. This is then used to update the
67+
* velocity and position for each frame.
68+
*/
5969
private val _positionData = object : MutableLiveData<Float>(), SensorEventListener {
6070
private val sensorManager
6171
get() = appContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager
6272

6373
override fun onActive() {
6474
super.onActive()
65-
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
66-
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME)
75+
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also {
76+
sensorManager.registerListener( this, it, SensorManager.SENSOR_DELAY_GAME)
77+
}
78+
sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also {
79+
sensorManager.registerListener( this, it, SensorManager.SENSOR_DELAY_GAME)
80+
}
6781
}
6882

6983
override fun onInactive() {
7084
super.onInactive()
7185
sensorManager.unregisterListener(this)
7286
}
7387

74-
override fun onSensorChanged(event: SensorEvent?) {
75-
event?.values?.let { (_, yAxisAcceleration, _) ->
76-
acceleration = yAxisAcceleration * ACCELERATION_FACTOR +
77-
(if (yAxisAcceleration >= -0.2f) DRIFT else -DRIFT) // drift shouldn't change scroll direction.
78-
79-
postValue(position)
88+
override fun onSensorChanged(event: SensorEvent) {
89+
when (event.sensor.type) {
90+
Sensor.TYPE_ACCELEROMETER -> event.values.copyInto(accelerometerData)
91+
Sensor.TYPE_MAGNETIC_FIELD -> event.values.copyInto(magnetometerData)
8092
}
93+
// Update the orientation angles when sensor data is updated
94+
SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerData, magnetometerData)
95+
SensorManager.getOrientation(rotationMatrix, orientationAngles)
8196
}
8297

8398
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit

0 commit comments

Comments
 (0)