Taula de continguts

Sensors de moviment d'Android

eixos-accelerometre.jpg

Referències:

, , , , , , , , , ,


Sensors

Quan parlem de «sensors» en Android solem referir-nos als de moviment: l'acceleròmetre i el giroscopi (brúixola 3D).

També son sensors (però no de moviment) la càmera, el micro, la pantalla tàctil i el GPS.

Si mirem la documentació oficial podrem veure que la llibreria Android disposa dels següents deteccions:

Els dos darrers son les dades «en cru» tal i com arriben dels sensors. Els altres son mecanismes de la llibreria per facilitar-nos la programació.

Exemple amb dades de l'acceleròmetre

Versió Kotlin

En aquesta versió apareix la clàssica herència de AppCompatActivity però alhora s'aplica la interface SensorEventListener, fet que ens obligarà a implementar els mètodes onSensorChanged i onAccuracyChanged:

MainActivity.kt
class MainActivity : AppCompatActivity(), SensorEventListener {
 
    private lateinit var sensorManager: SensorManager
    private lateinit var accelerometre: Sensor
    private lateinit var binding: ActivityMainBinding
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
 
        // Utilitzem bindings per facilitar accés als elements gràfics
        // recorda activar-los a build.gradle.kts afegint:
        // viewBinding { enable = true }
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
 
        // escoltar variacions dels sensors
        sensorManager = getSystemService(
                        Context.SENSOR_SERVICE) as SensorManager
        accelerometre = sensorManager.getDefaultSensor(
                        Sensor.TYPE_ACCELEROMETER) as Sensor
        sensorManager.registerListener(this, accelerometre,
                        SensorManager.SENSOR_DELAY_NORMAL)
    }
 
    override fun onSensorChanged(event: SensorEvent) {
        // emprem les dades del sensor
        val x = event.values[0]
        val y = event.values[1]
        val z = event.values[2]
        // 1g = 9,8 m/s² , què és un valor força alt.
        // Al fer *10 ens acostem als 100, que és el valor màxim per defecte de la ProgressBar
        binding.xProgressBar.progress = abs(x*10.0).toInt()
        binding.yProgressBar.progress = abs(y*10.0).toInt()
        binding.zProgressBar.progress = abs(z*10.0).toInt()
    }
 
    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
        // TODO("Not yet implemented")
    }
}

Versió Java

Versió amb objecte SensorEventListener que encapsula les callbacks onSensorChanged i onAccuracyChanged:

MainActivity.java
public class MainActivity extends AppCompatActivity {
 
    private SensorManager sensorManager;
    private Sensor sensor;
    SensorEventListener sensorListener;
 
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        sensorListener = new SensorEventListener() {
            @Override
            public void onSensorChanged(SensorEvent sensorEvent) {
                // Valors de l'acceleròmetre en m/s^2
                float xAcc = sensorEvent.values[0];
                float yAcc = sensorEvent.values[1];
                float zAcc = sensorEvent.values[2];
 
                // Processament o visualització de dades...
            }
 
            @Override
            public void onAccuracyChanged(Sensor sensor, int i) {
                // Es pot ignorar aquesta CB de moment
            }
        };
 
        // Seleccionem el tipus de sensor (veure doc oficial)
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);        
        sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
 
        // registrem el Listener per capturar els events del sensor
        if( sensor!=null ) {
            sensorManager.registerListener(sensorListener,sensor,
                    SensorManager.SENSOR_DELAY_NORMAL);
        }
    }
}


Exercici

Implementa una app que visualitzi els valors de l'acceleròmetre seguint l'exemple anterior on emprem el Sensor.TYPE_ACCELEROMETER. Tens unes captures de pantalla més a baix de com pots fer la app.

Llença l'app al teu dispositiu mòbil.

Com has de col·locar el mòbil per aconseguir aquests valors (aproximats)?:

tapapp1.jpg tapapp2.jpg tapapp3.jpg

Substitueix els TextView amb número per una ProgressBar, i mostra-ho en valor absolut (si no, els valors negatius no es visualitzaran).

Compara amb el resultat de Sensor.TYPE_LINEAR_ACCELERATION. Quina diferència veus?

Double «thud»

Normalment parlem de tap o double tap per a interaccions de «toc» de la pantalla. Aquest exercici vol detectar «tocs» però no de pantalla, sinó «tocs» a qualsevol part del mòbil. Per això en direm thud enlloc de tap (consultant un sinònim de "tap").

Implementarem un detector de «double thud». No ha de reaccionar amb 1 sol «thud» ni tampoc si ens desplacem ràpid o fem una sacsejada del mòbil.

Una primera aproximació podria ser implementar un comptador de thuds en cadascun dels eixos XYZ. Hauràs d'establir un llindar d'acceleració a partir del qual consideres que ha succeït un «thud». Defineix el llindar en una constant de l'aplicació (fàcilment localitzable i ajustable).

Assegura que al canviar la orientació (portrait/landscape) no es reinicien els comptadors a zero (un canvi d'orientació reconstrueix l'objecte Activity de nou). El mètode més directe és implementar onSaveInstanceState de la Activity Lifecycle.