Práctica 3. Android. Tutorial appFotoVoz

Transcripción

Práctica 3. Android. Tutorial appFotoVoz
Práctica 3. Android.
Tutorial appFotoVoz
José Antonio Larrubia García
José Miguel Navarro Moreno
Índice:
1.- Introducción.
2.- Descripción de la solución y problemas encontrados.
3.- Manual de uso.
4.- Referencias.
1.- Introducción.
La aplicación consiste en una brújula, a la que se le indica mediante voz un punto cardinal al
que tiene que apuntar.
2.- Descripción de la solución y problemas encontrados.
Partiendo de un proyecto “Blank Activity” de Android Studio, lo primero que hacemos es
usar los sensores Acelerometro y Magnetic Field para que señale al norte:
Para ello la clase debe implementar la clase SensorEventListener y definimos las variables
necesarias:
public class MainActivity extends AppCompatActivity implements
SensorEventListener {
private ImageView imgBrujula;
private ImageView flecha;
private TextView txtAngle;
private TextView textoRec;
// guarda el angulo (grado) actual del compass
private float currentDegree = 0f;
// El sensor manager del dispositivo
private SensorManager mSensorManager;
// Los dos sensores que son necesarios
private Sensor accelerometer;
private Sensor magnetometer;
// Los angulos del movimiento respecto al norte
float degree;
// Guarda el valor del azimut
float azimut;
// Guarda los valores que cambián con las variaciones del sensor
TYPE_ACCELEROMETER
float[] mGravity;
// Guarda los valores que cambián con las variaciones del sensor
TYPE_MAGNETIC_FIELD
Ahora inicializamos los sensores en el método onCreate():
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//imagen que estara orientada hacia el norte
imgBrujula = (ImageView) findViewById(R.id.imageBrujula);
txtAngle= (TextView) findViewById(R.id.textView);
// Se inicializa los sensores del dispositivo
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
accelerometer =mSensorManager.getDefaultSensor
(Sensor.TYPE_ACCELEROMETER);
magnetometer = mSensorManager.getDefaultSensor
(Sensor.TYPE_MAGNETIC_FIELD);
mGravity = null;
mGeomagnetic = null;
En los métodos onPause() y onResumen() se paran los sensores y se reanudad de nuevo para
no estén activos cuando la aplicación este en segundo plano:
protected void onResume() {
super.onResume();
// Se registra un listener para los sensores del accelerometer y el
magnetometer
mSensorManager.registerListener (this, accelerometer,
SensorManager.SENSOR_DELAY_UI);
mSensorManager.registerListener(this, magnetometer,
SensorManager.SENSOR_DELAY_UI);
}
protected void onPause() {
super.onPause();
// Se detiene el listener para no malgastar la bateria
mSensorManager.unregisterListener(this);
}
En el método “onSensorChanged” se obtiene la orientación del dispositivo, se crea la
animación de rotación y se aplica al imageView correspondiente que puede contener una
imagen con una flecha o una brújula cuya dirección sea hacia arriba:
public void onSensorChanged(SensorEvent event) {
//Referencia: http://agamboadev.esy.es/como-crear-un-brujula-en-android/
// Se comprueba que tipo de sensor está activo en cada momento
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
mGravity = event.values;
break;
case Sensor.TYPE_MAGNETIC_FIELD:
mGeomagnetic = event.values;
break;
}
//Si los sensores estan activos, se obtiene el valor de la
orientación del dispositivo en grados.
if ((mGravity != null) && (mGeomagnetic != null)) {
float RotationMatrix[] = new float[16];
boolean success= SensorManager.getRotationMatrix(
RotationMatrix,
null,
mGravity,
mGeomagnetic);
if (success) {
float orientation[] = new float[3];
SensorManager.getOrientation(RotationMatrix,
orientation);
azimut = orientation[0] * (180 / (float) Math.PI);
}
}
degree = azimut;
//se muestra el angulo del norte respecto a la dirección del
dispositivo.
txtAngle.setText("Ángulo: " + Float.toString(degree) + " grados");
// se crea la animacion de la rottacion
(se revierte el giro en grados, negativo)
RotateAnimation ra = new RotateAnimation(
currentDegree,
degree,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF,
0.5f);
// el tiempo durante el cual la animación se llevará a cabo
ra.setDuration(1000);
// establecer la animación después del final de la estado de reserva
ra.setFillAfter(true);
// Inicio de la animacion
imgBrujula.startAnimation(ra);
currentDegree = -degree;
}
Con esto ya tenemos el una brújula que apunta hacia el norte, ahora solo nos queda usarel
detector de voz para indicarle una dirección:
Primero añadimos las variables necesarias:
//------------------------------------------//Referencia:
https://github.com/zoraidacallejas/sandra/tree/master/Apps/ASRWithIntent
//Default values for the language model and maximum number of recognition
results
// They are shown in the GUI when the app starts, and they are used when
the user selection is not valid
private final static int DEFAULT_NUMBER_RESULTS = 10;
private final static String DEFAULT_LANG_MODEL =
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
private int numberRecoResults = DEFAULT_NUMBER_RESULTS;
private String languageModel = DEFAULT_LANG_MODEL;
private static int ASR_CODE = 123;
//----------------------------------------------------------------------//String en el que se va a almacenar la dirección obtenida por la
captación de voz
private String direccion=null;
private int margen_error=30;
//Para el ángulo de la dirección que se le indica.
float angulo_flecha;
float angulo_ant=0f;
Después, en el método onCreate() se añade lo siguiente para el botón que iniciara la
captación de voz:
//botón que inicia el detector de voz cuando lo pulsas
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//----------------------------------------------------------//Referencia:
https://github.com/zoraidacallejas/sandra/tree/master/Apps/ASRWithIntent
//Speech recognition does not currently work on simulated
//devices, it the user is attempting to run the app in a
//simulated device they will get a Toast
if("generic".equals(Build.BRAND.toLowerCase())){
Toast toast = Toast.makeText(getApplicationContext(),
"ASR is not supported on virtual devices",
Toast.LENGTH_SHORT);
toast.show();
Log.d(LOGTAG, "ASR attempt on virtual device");
}
else{
numberRecoResults = 10;
//Read speech recognition parameters from GUI
languageModel = DEFAULT_LANG_MODEL;
Intent intent =
new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
// Specify language model
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
languageModel);
// Specify how many results to receive
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS,
numberRecoResults);
// Start listening
startActivityForResult(intent, ASR_CODE);
}
//----------------------------------------------------------}
});
//imagen que indica la dirección captada por voz
flecha=(ImageView) findViewById(R.id.imageFlecha);
Ahora se crea el método onActivityResult() en el que se obtendrán los resultados de la
captación de voz:
/**
* Se recoge los datos del evento de captación de voz
*/
@SuppressLint("InlinedApi")
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
//Referencia:
https://github.com/zoraidacallejas/sandra/tree/master/Apps/ASRWithIntent
if (requestCode == ASR_CODE) {
if (resultCode == RESULT_OK) {
if(data!=null) {
//Retrieves the N-best list and the confidences from the
//ASR result
ArrayList<String> nBestList =
data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
//Para obtener la direccion y el margen de error
obtenerDireccion(nBestList);
textoRec= (TextView) findViewById(R.id.textRec);
textoRec.setText("Dirección: "+direccion + " " +
margen_error);
}
}
}
else {
//Reports error in recognition error in log
Log.e(LOGTAG, "Recognition was not successful");
}
}
Solo falta el método obtenerDireccion() usado en el método onActivityResult():
/**
* Método para obtener la dirección y el margen de error de los resultados
* de la captación de voz.
* @param a Lista con les resultados del reconocedor de voz
*/
private void obtenerDireccion(ArrayList<String> a){
//Lista con los posibles puntos cardinales
ArrayList<String> puntos_car=new ArrayList<String>();
puntos_car.add("norte");
puntos_car.add("sur");
puntos_car.add("este");
puntos_car.add("oeste");
boolean coincide=false;
direccion=null;
// se comprueba de la lista de string de la detección de voz
for(int i=0;i<puntos_car.size() && !coincide;i++){
for(int j=0;j<a.size() && !coincide;j++){
if(a.get(j).length() >= puntos_car.get(i).length()) {
//Si coincide con un punto cardinal se guarda en dirección
if (puntos_car.get(i).equals((a.get(j).substring(
0, puntos_car.get(i).length())).toLowerCase())) {
coincide = true;
direccion = puntos_car.get(i);
}
}
}
}
//Ahora hay que obtener el margen de error
String numero;
boolean es_num=false;
//se obtiene el margen de error
if(direccion!=null) {
//limpiamos el string obtenido por el detector de voz para que
//solo contenga el número eliminado todas las letras
Pattern pat=Pattern.compile("[a-zA-Záéíóú]*(\\s)*[a-zA-Záéíóú]*");
Matcher mat;
for (int i = 0; i < a.size() && !es_num; i++) {
mat= pat.matcher(a.get(i));
//quitamos las letras del string
numero=mat.replaceAll("");
//si en el string se obtiene un número ese será el margen
//de error.
if(numero.length()>0){
margen_error=Integer.parseInt(numero);
es_num=true;
}
}
}
}
Ya reconocemos por voz un punto cardinal seguido de un número que sera el margen de
error. Lo que vamos a hacer ahora es mostrar la dirección con una flecha, para ello
añadimos en el método onSensorChanged una animación para la imagen correspondiente y
hacer que cambie de color si el dispositivo apunta hacia esa dirección:
//Si se le ha pasado una dirección por voz
//Se calcula el ángulo adecuado en función del ángulo en el que se
//encuentra el norte.
if(direccion!=null){
if(direccion.equals("sur")){
if(degree>0) angulo_flecha=degree-180;
else angulo_flecha=degree+180;
}else if(direccion.equals("norte")){
angulo_flecha=degree;
}else if(direccion.equals("este")){
if(degree-90<-180) angulo_flecha=degree-90+360;
else angulo_flecha=degree-90;
}else if(direccion.equals("oeste")){
if(degree+90>180) angulo_flecha=degree+90-360;
else angulo_flecha=degree+90;
}
//Se crea la animación
RotateAnimation ra2 = new RotateAnimation(
angulo_ant,
angulo_flecha,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF,
0.5f);
// el tiempo durante el cual la animación se llevará a cabo
ra2.setDuration(1000);
// establecer la animación después del final del estado de reserva
ra2.setFillAfter(true);
// Inicio de la animacion
flecha.startAnimation(ra2);
angulo_ant=-angulo_flecha;
//Si el dispositivo apunta hacia la dirección especificada se cambia
//el color de la flecha
if(angulo_flecha<margen_error && angulo_flecha>-margen_error){
flecha.setImageResource(R.drawable.flechag);
}else flecha.setImageResource(R.drawable.flechar);
}
Pues con esto ya tenemos la aplicación appFotoVoz, el código completo se puede encontrar
en: https://github.com/JMNM/appFotoVoz
3.- Manual de uso.
El uso de la aplicación es muy sencillo, una vez iniciada solo hay que pulsar el botón de la
esquina inferior derecha he indicarle un punto cardinal seguido de un número, entonces la
flecha roja señalara la dirección indicada, y esta flecha se pondrá verde cuando el
dispositivo apunte hacia esa dirección.
4.- Referencias.
[1] http://agamboadev.esy.es/como-crear-un-brujula-en-android/
[2] https://github.com/zoraidacallejas/sandra/tree/master/Apps/ASRWithIntent

Documentos relacionados