Entradas y sensores

El SDK de Android te brinda acceso a los diversos sensores y entradas disponibles con Glass EE2. En esta página, se proporciona una descripción general de las funciones disponibles, los detalles de implementación y sugerencias útiles.

Gestos táctiles

Puedes usar el SDK de Android para habilitar el acceso a los datos sin procesar del panel táctil de Glass. Esto se logra a través de un detector de gestos que detecta automáticamente gestos comunes en Glass, como tocar, deslizar y desplazar.

También puedes usar este detector de gestos en tus apps para tener en cuenta los toques, los deslizamientos hacia adelante, los deslizamientos hacia atrás y los deslizamientos hacia abajo. Esto es similar a los dispositivos Glass anteriores.

Es mejor usar estos gestos de las siguientes maneras:

  • Presiona: Confirma o ingresa.
  • Deslizar hacia adelante y deslizar hacia atrás: Navega por las tarjetas y las pantallas.
  • Deslizar hacia abajo: Volver o salir

Para obtener detalles sobre la implementación, lee el detector de gestos de muestra.

Cómo detectar gestos con el detector de gestos de Android

El GestureDetector de Android te permite detectar gestos simples y complejos, como los que usan varios dedos o el desplazamiento.

Cómo detectar gestos a nivel de la actividad

Detecta gestos a nivel de la actividad solo cuando no importa qué parte de la IU tiene el enfoque. Por ejemplo, si quieres mostrar un menú cuando un usuario presiona el panel táctil, independientemente de la vista que tenga el enfoque, controla el evento MotionEvent dentro de la actividad.

A continuación, se muestra un ejemplo de detección de gestos a nivel de la actividad que usa GestureDetector y, luego, implementa GestureDetector.OnGestureListener para procesar los gestos reconocidos, que luego se controlan y traducen a lo siguiente:

  • TAP
  • SWIPE_FORWARD
  • SWIPE_BACKWARD
  • SWIPE_UP
  • SWIPE_DOWN

Kotlin

class GlassGestureDetector(context: Context, private val onGestureListener: OnGestureListener) :
    GestureDetector.OnGestureListener {

    private val gestureDetector = GestureDetector(context, this)

    enum class Gesture {
        TAP,
        SWIPE_FORWARD,
        SWIPE_BACKWARD,
        SWIPE_UP,
        SWIPE_DOWN
    }

    interface OnGestureListener {
        fun onGesture(gesture: Gesture): Boolean
    }

    fun onTouchEvent(motionEvent: MotionEvent): Boolean {
        return gestureDetector.onTouchEvent(motionEvent)
    }

    override fun onDown(e: MotionEvent): Boolean {
        return false
    }

    override fun onShowPress(e: MotionEvent) {}

    override fun onSingleTapUp(e: MotionEvent): Boolean {
        return onGestureListener.onGesture(Gesture.TAP)
    }

    override fun onScroll(
        e1: MotionEvent,
        e2: MotionEvent,
        distanceX: Float,
        distanceY: Float
    ): Boolean {
        return false
    }

    override fun onLongPress(e: MotionEvent) {}

    /**
     * Swipe detection depends on the:
     * - movement tan value,
     * - movement distance,
     * - movement velocity.
     *
     * To prevent unintentional SWIPE_DOWN and SWIPE_UP gestures, they are detected if movement
     * angle is only between 60 and 120 degrees.
     * Any other detected swipes, will be considered as SWIPE_FORWARD and SWIPE_BACKWARD, depends
     * on deltaX value sign.
     *
     * ______________________________________________________________
     * |                     \        UP         /                    |
     * |                       \               /                      |
     * |                         60         120                       |
     * |                           \       /                          |
     * |                             \   /                            |
     * |  BACKWARD  <-------  0  ------------  180  ------>  FORWARD  |
     * |                             /   \                            |
     * |                           /       \                          |
     * |                         60         120                       |
     * |                       /               \                      |
     * |                     /       DOWN        \                    |
     * --------------------------------------------------------------
     */
    override fun onFling(
        e1: MotionEvent,
        e2: MotionEvent,
        velocityX: Float,
        velocityY: Float
    ): Boolean {
        val deltaX = e2.x - e1.x
        val deltaY = e2.y - e1.y
        val tan =
            if (deltaX != 0f) abs(deltaY / deltaX).toDouble() else java.lang.Double.MAX_VALUE

        return if (tan > TAN_60_DEGREES) {
            if (abs(deltaY) < SWIPE_DISTANCE_THRESHOLD_PX || Math.abs(velocityY) < SWIPE_VELOCITY_THRESHOLD_PX) {
                false
            } else if (deltaY < 0) {
                onGestureListener.onGesture(Gesture.SWIPE_UP)
            } else {
                onGestureListener.onGesture(Gesture.SWIPE_DOWN)
            }
        } else {
            if (Math.abs(deltaX) < SWIPE_DISTANCE_THRESHOLD_PX || Math.abs(velocityX) < SWIPE_VELOCITY_THRESHOLD_PX) {
                false
            } else if (deltaX < 0) {
                onGestureListener.onGesture(Gesture.SWIPE_FORWARD)
            } else {
                onGestureListener.onGesture(Gesture.SWIPE_BACKWARD)
            }
        }
    }

    companion object {

        private const val SWIPE_DISTANCE_THRESHOLD_PX = 100
        private const val SWIPE_VELOCITY_THRESHOLD_PX = 100
        private val TAN_60_DEGREES = tan(Math.toRadians(60.0))
    }
}

Java

  public class GlassGestureDetector implements GestureDetector.OnGestureListener {

   enum Gesture {
     TAP,
     SWIPE_FORWARD,
     SWIPE_BACKWARD,
     SWIPE_UP,
     SWIPE_DOWN,
   }

   interface OnGestureListener {
     boolean onGesture(Gesture gesture);
   }

   private static final int SWIPE_DISTANCE_THRESHOLD_PX = 100;
   private static final int SWIPE_VELOCITY_THRESHOLD_PX = 100;
   private static final double TAN_60_DEGREES = Math.tan(Math.toRadians(60));

   private GestureDetector gestureDetector;
   private OnGestureListener onGestureListener;

   public GlassGestureDetector(Context context, OnGestureListener onGestureListener) {
     gestureDetector = new GestureDetector(context, this);
     this.onGestureListener = onGestureListener;
   }

   public boolean onTouchEvent(MotionEvent motionEvent) {
     return gestureDetector.onTouchEvent(motionEvent);
   }

   @Override
   public boolean onDown(MotionEvent e) {
     return false;
   }

   @Override
   public void onShowPress(MotionEvent e) {
   }

   @Override
   public boolean onSingleTapUp(MotionEvent e) {
     return onGestureListener.onGesture(Gesture.TAP);
   }

   @Override
   public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
     return false;
   }

   @Override
   public void onLongPress(MotionEvent e) {
   }

   /**
    * Swipe detection depends on the:
    * - movement tan value,
    * - movement distance,
    * - movement velocity.
    *
    * To prevent unintentional SWIPE_DOWN and SWIPE_UP gestures, they are detected if movement
    * angle is only between 60 and 120 degrees.
    * Any other detected swipes, will be considered as SWIPE_FORWARD and SWIPE_BACKWARD, depends
    * on deltaX value sign.
    *
    *           ______________________________________________________________
    *          |                     \        UP         /                    |
    *          |                       \               /                      |
    *          |                         60         120                       |
    *          |                           \       /                          |
    *          |                             \   /                            |
    *          |  BACKWARD  <-------  0  ------------  180  ------>  FORWARD  |
    *          |                             /   \                            |
    *          |                           /       \                          |
    *          |                         60         120                       |
    *          |                       /               \                      |
    *          |                     /       DOWN        \                    |
    *           --------------------------------------------------------------
    */
   @Override
   public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
     final float deltaX = e2.getX() - e1.getX();
     final float deltaY = e2.getY() - e1.getY();
     final double tan = deltaX != 0 ? Math.abs(deltaY/deltaX) : Double.MAX_VALUE;

     if (tan > TAN_60_DEGREES) {
       if (Math.abs(deltaY) < SWIPE_DISTANCE_THRESHOLD_PX || Math.abs(velocityY) < SWIPE_VELOCITY_THRESHOLD_PX) {
         return false;
       } else if (deltaY < 0) {
         return onGestureListener.onGesture(Gesture.SWIPE_UP);
       } else {
         return onGestureListener.onGesture(Gesture.SWIPE_DOWN);
       }
     } else {
       if (Math.abs(deltaX) < SWIPE_DISTANCE_THRESHOLD_PX || Math.abs(velocityX) < SWIPE_VELOCITY_THRESHOLD_PX) {
         return false;
       } else if (deltaX < 0) {
         return onGestureListener.onGesture(Gesture.SWIPE_FORWARD);
       } else {
         return onGestureListener.onGesture(Gesture.SWIPE_BACKWARD);
       }
     }
   }
  }

Ejemplo de uso

Para usar la detección de gestos a nivel de la actividad, debes completar las siguientes tareas:

  1. Agrega la siguiente declaración al archivo de manifiesto, dentro de la declaración de la aplicación. Esto permite que tu app reciba el MotionEvent en la actividad:
    <application>
    <!-- Copy below declaration into your manifest file -->
    <meta-data
      android:name="com.google.android.glass.TouchEnabledApplication"
      android:value="true" />
    </application>
  2. Anula el método dispatchTouchEvent(motionEvent) de la actividad para pasar los eventos de movimiento al método onTouchEvent(motionEvent) del detector de gestos.
  3. Implementa GlassGestureDetector.OnGestureListener en tu actividad.

A continuación, se muestra un ejemplo de un detector de gestos a nivel de la actividad:

Kotlin

class MainAcvitiy : AppCompatActivity(), GlassGestureDetector.OnGestureListener {

    private lateinit var glassGestureDetector: GlassGestureDetector

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        glassGestureDetector = GlassGestureDetector(this, this)
    }

    override fun onGesture(gesture: GlassGestureDetector.Gesture): Boolean {
        when (gesture) {
            TAP ->
                // Response for TAP gesture
                return true
            SWIPE_FORWARD ->
                // Response for SWIPE_FORWARD gesture
                return true
            SWIPE_BACKWARD ->
                // Response for SWIPE_BACKWARD gesture
                return true
            else -> return false
        }
    }

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        return if (glassGestureDetector.onTouchEvent(ev)) {
            true
        } else super.dispatchTouchEvent(ev)
    }
}

Java

  public class MainActivity extends AppCompatActivity implements OnGestureListener {

   private GlassGestureDetector glassGestureDetector;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);

     glassGestureDetector = new GlassGestureDetector(this, this);
   }

   @Override
   public boolean onGesture(Gesture gesture) {
     switch (gesture) {
       case TAP:
         // Response for TAP gesture
         return true;
       case SWIPE_FORWARD:
         // Response for SWIPE_FORWARD gesture
         return true;
       case SWIPE_BACKWARD:
         // Response for SWIPE_BACKWARD gesture
         return true;
       default:
         return false;
     }
   }

   @Override
   public boolean dispatchTouchEvent(MotionEvent ev) {
     if (glassGestureDetector.onTouchEvent(ev)) {
       return true;
     }
     return super.dispatchTouchEvent(ev);
   }
  }

Entrada de audio

Glass Enterprise Edition 2 es un dispositivo estándar basado en AOSP que admite fuentes de audio básicas.

Las siguientes fuentes de audio tienen implementado un procesamiento de señales avanzado:

Reconocimiento de voz

Glass Enterprise Edition 2 admite una implementación nativa para el reconocimiento de voz. Esta función solo está disponible en inglés.

Imagen de reconocimiento de voz de Glass.

La IU de reconocimiento de voz espera a que los usuarios hablen y, luego, devuelve el texto transcrito después de que terminan. Para comenzar la actividad, sigue estos pasos:

  1. Llama a startActivityForResult() con el intent ACTION_RECOGNIZE_SPEECH. Se admiten los siguientes parámetros adicionales del intent cuando inicias la actividad:
  2. Anula la devolución de llamada onActivityResult() para recibir el texto transcrito del extra del intent EXTRA_RESULTS, como se muestra en el siguiente ejemplo de código. Se llama a esta devolución de llamada cuando el usuario termina de hablar.

Kotlin

private const val SPEECH_REQUEST = 109

private fun displaySpeechRecognizer() {
    val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
    startActivityForResult(intent, SPEECH_REQUEST)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == SPEECH_REQUEST && resultCode == RESULT_OK) {
        val results: List<String>? =
            data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
        val spokenText = results?.get(0)
        // Do something with spokenText.
    }
    super.onActivityResult(requestCode, resultCode, data)
}

Java

private static final int SPEECH_REQUEST = 109;

private void displaySpeechRecognizer() {
    Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
    startActivityForResult(intent, SPEECH_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode,
        Intent data) {
    if (requestCode == SPEECH_REQUEST && resultCode == RESULT_OK) {
        List<String> results = data.getStringArrayListExtra(
                RecognizerIntent.EXTRA_RESULTS);
        String spokenText = results.get(0);
        // Do something with spokenText.
    }
    super.onActivityResult(requestCode, resultCode, data);
}

Para obtener detalles sobre la implementación, consulta la app de ejemplo de reconocimiento de voz.

Ajuste de sesgo para palabras clave

El reconocimiento de voz en Glass puede estar sesgado para una lista de palabras clave. El sesgo aumenta la precisión del reconocimiento de palabras clave. Para habilitar el ajuste de sesgo para las palabras clave, usa lo siguiente:

Kotlin

val keywords = arrayOf("Example", "Biasing", "Keywords")

val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra("recognition-phrases", keywords)

startActivityForResult(intent, SPEECH_REQUEST)

Java

final String[] keywords = {"Example", "Biasing", "Keywords"};

Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra("recognition-phrases", keywords);

startActivityForResult(intent, SPEECH_REQUEST);

Comandos por voz

Los comandos por voz permiten a los usuarios realizar acciones desde las actividades. Compilas comandos por voz con las APIs de menú estándar de Android, pero los usuarios pueden invocar los elementos del menú con comandos por voz en lugar de con el tacto.

Para habilitar los comandos por voz en una actividad en particular, sigue estos pasos:

  1. Llama a getWindow().requestFeature(FEATURE_VOICE_COMMANDS) en la actividad deseada para habilitar los comandos por voz. Con esta función habilitada, el ícono del micrófono aparece en la esquina inferior izquierda de la pantalla cada vez que esta actividad recibe el enfoque.
  2. Solicita el permiso RECORD_AUDIO en tu app.
  3. Anula onCreatePanelMenu() y aumenta un recurso de menú.
  4. Anula onContextItemSelected() para controlar los comandos por voz detectados.

Kotlin

class VoiceCommandsActivity : AppCompatActivity() {

    companion object {
        const val FEATURE_VOICE_COMMANDS = 14
        const val REQUEST_PERMISSION_CODE = 200
        val PERMISSIONS = arrayOf(Manifest.permission.RECORD_AUDIO)
        val TAG = VoiceCommandsActivity::class.java.simpleName
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.requestFeature(FEATURE_VOICE_COMMANDS)
        setContentView(R.layout.activity_voice_commands)

        // Requesting permissions to enable voice commands menu
        ActivityCompat.requestPermissions(
            this,
            PERMISSIONS,
            REQUEST_PERMISSION_CODE
        )
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        if (requestCode == REQUEST_PERMISSION_CODE) {
            for (result in grantResults) {
                if (result != PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Permission denied. Voice commands menu is disabled.")
                }
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        }
    }

    override fun onCreatePanelMenu(featureId: Int, menu: Menu): Boolean {
        menuInflater.inflate(R.menu.voice_commands_menu, menu)
        return true
    }

    override fun onContextItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            // Handle selected menu item
            R.id.edit -> {
                // Handle edit action
                true
            }
            else -> super.onContextItemSelected(item)
        }
    }
}

Java

public class VoiceCommandsActivity extends AppCompatActivity {

  private static final String TAG = VoiceCommandsActivity.class.getSimpleName();
  private static final int FEATURE_VOICE_COMMANDS = 14;
  private static final int REQUEST_PERMISSION_CODE = 200;
  private static final String[] PERMISSIONS = new String[]{Manifest.permission.RECORD_AUDIO};

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().requestFeature(FEATURE_VOICE_COMMANDS);
    setContentView(R.layout.activity_voice_commands);

    // Requesting permissions to enable voice commands menu
    ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_PERMISSION_CODE);
  }

  @Override
  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_PERMISSION_CODE) {
      for (int result : grantResults) {
        if (result != PackageManager.PERMISSION_GRANTED) {
          Log.d(TAG, "Permission denied. Voice commands menu is disabled.");
        }
      }
    } else {
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
  }

  @Override
  public boolean onCreatePanelMenu(int featureId, @NonNull Menu menu) {
    getMenuInflater().inflate(R.menu.voice_commands_menu, menu);
    return true;
  }

  @Override
  public boolean onContextItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
      // Handle selected menu item
      case R.id.edit:
        // Handle edit action
        return true;
      default:
        return super.onContextItemSelected(item);
    }
  }
}

A continuación, se muestra un ejemplo del recurso de menú que usa la actividad anterior:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/delete"
        android:icon="@drawable/ic_delete"
        android:title="@string/delete"/>
    <item
        android:id="@+id/edit"
        android:icon="@drawable/ic_edit"
        android:title="@string/edit"/>
    <item
        android:id="@+id/find"
        android:icon="@drawable/ic_search"
        android:title="@string/find"/>
</menu>

Consulta la app de ejemplo para tomar notas para ver un ejemplo completo.

Se está volviendo a cargar la lista de comandos por voz

Puedes volver a cargar la lista de comandos por voz de forma dinámica. Para ello, reemplaza el recurso menu en el método onCreatePanelMenu() o modifica el objeto de menú en el método onPreparePanel(). Para aplicar los cambios, llama al método invalidateOptionsMenu().

Kotlin

private val options = mutableListOf<String>()

fun onPreparePanel(featureId: Int, view: View?, menu: Menu): Boolean {
  if (featureId != FEATURE_VOICE_COMMANDS) {
    return super.onCreatePanelMenu(featureId, menu)
  }
  for (optionTitle in options) {
    menu.add(optionTitle)
  }
  return true
}

/**
 * Method showing example implementation of voice command list modification
 *
 * If you call [Activity.invalidateOptionsMenu] method, voice command  list will be
 * reloaded (onCreatePanelMenu and onPreparePanel methods will be called).
 */
private fun modifyVoiceCommandList() {
  options.add("Delete")
  options.add("Play")
  options.add("Pause")
  invalidateOptionsMenu()
}

Java

private final List<String> options = new ArrayList<>();

@Override
public boolean onPreparePanel(int featureId, View view, Menu menu) {
  if (featureId != FEATURE_VOICE_COMMANDS) {
    return super.onCreatePanelMenu(featureId, menu);
  }
  for (String optionTitle : options) {
    menu.add(optionTitle);
  }
  return true;
}

/**
 * Method showing example implementation of voice command list modification
 *
 * If you call {@link Activity#invalidateOptionsMenu()} method, voice command  list will be
 * reloaded (onCreatePanelMenu and onPreparePanel methods will be called).
 */
private void modifyVoiceCommandList() {
  options.add("Delete");
  options.add("Play");
  options.add("Pause");
  invalidateOptionsMenu();
}

Solución de AppCompatActivity

Para volver a cargar una lista de comandos por voz en una actividad que extiende AppCompatActivity, usa el método sendBroadcast() con la acción de intent reload-voice-commands:

Kotlin

sendBroadcast(Intent("reload-voice-commands"))

Java

sendBroadcast(new Intent("reload-voice-commands"));

Cómo habilitar y deshabilitar comandos por voz en el tiempo de ejecución

Puedes habilitar e inhabilitar los comandos por voz durante el tiempo de ejecución. Para ello, devuelve un valor adecuado del método onCreatePanelMenu() de la siguiente manera:

  • Establece el valor en true para habilitar.
  • Establece el valor en false para inhabilitar la opción.

Modo de depuración

Para habilitar el modo de depuración de los comandos por voz, llama a getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS) en la actividad deseada. El modo de depuración activa las siguientes funciones:

  • Logcat contiene el registro con la frase reconocida para la depuración.
  • La superposición de la IU se muestra cuando se detecta un comando no reconocido, como se muestra a continuación:
  • Imagen de un comando no reconocido por el reconocimiento de voz de Glass.

Kotlin

class VoiceCommandsActivity : AppCompatActivity() {

    companion object {
        const val FEATURE_VOICE_COMMANDS = 14
        const val FEATURE_DEBUG_VOICE_COMMANDS = 15
        const val REQUEST_PERMISSION_CODE = 200
        val PERMISSIONS = arrayOf(Manifest.permission.RECORD_AUDIO)
        val TAG = VoiceCommandsActivity::class.java.simpleName
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.requestFeature(FEATURE_VOICE_COMMANDS)
        window.requestFeature(FEATURE_DEBUG_VOICE_COMMANDS)
        setContentView(R.layout.activity_voice_commands)

        // Requesting permissions to enable voice commands menu
        ActivityCompat.requestPermissions(
            this,
            PERMISSIONS,
            REQUEST_PERMISSION_CODE
        )
    }
    .
    .
    .
}

Java

public class VoiceCommandsActivity extends AppCompatActivity {

  private static final String TAG = VoiceCommandsActivity.class.getSimpleName();
  private static final int FEATURE_VOICE_COMMANDS = 14;
  private static final int FEATURE_DEBUG_VOICE_COMMANDS = 15;
  private static final int REQUEST_PERMISSION_CODE = 200;
  private static final String[] PERMISSIONS = new String[]{Manifest.permission.RECORD_AUDIO};

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().requestFeature(FEATURE_VOICE_COMMANDS);
    getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS);
    setContentView(R.layout.activity_voice_commands);

    // Requesting permissions to enable voice commands menu
    ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_PERMISSION_CODE);
  }
  .
  .
  .
}

Para obtener detalles de la implementación, lee la app de recarga de comandos por voz de muestra.

Texto a voz (TTS)

La función Text-to-Speech convierte texto digital en voz sintetizada. Para obtener más información, consulta Android Developers Documentation TextToSpeech.

Glass EE2 tiene instalado el motor de texto a voz de Google. Se establece como el motor de TTS predeterminado y funciona sin conexión.

Los siguientes parámetros de configuración regional se incluyen en el motor de texto a voz de Google:

Bengalí
Chino mandarín
Checo
Danés
Alemán
Griego
Inglés
Español
Estonio
Finlandés
Francés
Gujarati
Hindi
Húngaro
Indonesio
Italiano
Japonés
Javanés
Austronesio
Austroasiático
Kannada
Coreano
Malayalam
Noruego
Neerlandés

Portugués
Ruso
Eslovaco
Sundanés
Sueco
Tamil
Telugu
Tailandés
Turco
Ucraniano
Vietnamita

Cámara

Los Glass Enterprise Edition 2 están equipados con una cámara de enfoque fijo de 8 megapíxeles que tiene una apertura de f/2.4, una relación de aspecto del sensor de 4:3 y un campo visual diagonal de 83° (71° x 57° en orientación horizontal). Te recomendamos que uses la API estándar de CameraX o Camera2.

Para obtener detalles sobre la implementación, lee la app de cámara de ejemplo.

Botón de cámara

El botón de la cámara es el botón físico que se encuentra en la bisagra del dispositivo Glass Enterprise Edition 2. Se puede controlar como una acción del teclado estándar y se puede identificar con el código de tecla KeyEvent#KEYCODE_CAMERA.

A partir de la actualización del SO OPM1.200313.001, la aplicación de Launcher envía intents con las siguientes acciones:

Sensores

Los desarrolladores tienen a su disposición una variedad de sensores para desarrollar aplicaciones en Glass EE2.

Glass admite los siguientes sensores estándar de Android:

Los siguientes sensores de Android no son compatibles con Glass:

En la siguiente ilustración, se muestra el sistema de coordenadas del sensor de Glass. Es relativa a la pantalla de los Glass. Para obtener más información, consulta Sistema de coordenadas del sensor.

Aquí se muestra el sistema de coordenadas del sensor de Glass en relación con la pantalla de Glass.

El acelerómetro, el giroscopio y el magnetómetro se encuentran en el soporte óptico del dispositivo Glass, que los usuarios rotan para alinear el dispositivo con su vista. No puedes medir el ángulo del módulo de la cámara directamente, así que ten en cuenta esto cuando uses los ángulos de estos sensores para aplicaciones, como el rumbo de una brújula.

Para conservar la duración de la batería, solo escucha los sensores cuando los necesites. Ten en cuenta las necesidades y el ciclo de vida de la app cuando decidas cuándo comenzar a detectar los sensores y cuándo detenerlos.

Las devoluciones de llamada de eventos de sensores se ejecutan en el subproceso de IU, por lo que los eventos se procesan y se muestran lo más rápido posible. Si el procesamiento tarda demasiado, envía los eventos del sensor a una cola y usa un subproceso en segundo plano para controlarlos.

50 Hz suele ser una tasa de muestreo suficiente para hacer un seguimiento del movimiento de la cabeza.

Para obtener más información sobre cómo usar sensores, consulta la guía para desarrolladores de Android.

Servicios de ubicación

El dispositivo Glass Enterprise Edition 2 no está equipado con un módulo GPS y no proporciona la ubicación del usuario. Sin embargo, sí tiene implementados los servicios de ubicación, que son necesarios para mostrar una lista de redes Wi-Fi y dispositivos Bluetooth cercanos.

Si tu aplicación tiene privilegios de propietario del dispositivo, puedes usarla para cambiar de forma programática el valor del parámetro de configuración seguro correspondiente:

Kotlin

val devicePolicyManager = context
    .getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
if (devicePolicyManager.isDeviceOwnerApp(context.getPackageName())) {
    val componentName = ComponentName(context, MyDeviceAdmin::class.java)
    devicePolicyManager.setSecureSetting(
        componentName,
        Settings.Secure.LOCATION_MODE,
        Settings.Secure.LOCATION_MODE_SENSORS_ONLY.toString()
    )
}

Java

final DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context
    .getSystemService(Context.DEVICE_POLICY_SERVICE);
if (devicePolicyManager.isDeviceOwnerApp(context.getPackageName())) {
  final ComponentName componentName = new ComponentName(context, MyDeviceAdmin.class);
  devicePolicyManager.setSecureSetting(componentName, Settings.Secure.LOCATION_MODE,
      String.valueOf(Settings.Secure.LOCATION_MODE_SENSORS_ONLY));
}

Si usas una solución de MDM de terceros, esta debe poder cambiar estos parámetros de configuración por ti.