Dane wejściowe i czujniki

Pakiet Android SDK zapewnia dostęp do różnych urządzeń wejściowych i czujników, które są dostępne w Google Glass EE2. Na tej stronie znajdziesz przegląd dostępnych funkcji, szczegółów implementacji i przydatne wskazówki.

Kliknięcia

Za pomocą pakietu SDK na Androida możesz zapewnić dostęp do nieprzetworzonych danych na touchpadzie Glass. Można to osiągnąć za pomocą wzorca do wykrywania gestów, który automatycznie wykrywa typowe gesty w Google Glass, takie jak klikanie, wyrzucanie czy przewijanie.

W swoich aplikacjach możesz też używać tego wzorca do wykrywania gestów, aby np. klikać, przesuwać palcem do przodu lub do tyłu, a także przesuwać palcem w dół. Podobne do poprzednich urządzeń Glass.

Najlepiej używać tych gestów:

  • Klikanie: potwierdź lub wpisz.
  • Przesuń do przodu lub do tyłu: przeglądaj karty i ekrany.
  • Przesuń w dół: Wstecz lub Wyjdź.

Szczegółowe informacje o implementacji znajdziesz w tym artykule.

Wykrywanie gestów za pomocą wzorca do wykrywania gestów w Androidzie

Android GestureDetector umożliwia wykrywanie prostych i złożonych gestów, np. gestów wymagających użycia wielu palców lub przewijania.

Wykrywanie gestów na poziomie aktywności

Wykrywaj gesty na poziomie aktywności tylko wtedy, gdy nie jest ważny. Jeśli na przykład chcesz otworzyć menu, gdy użytkownik kliknie touchpada, niezależnie od tego, jaki widok jest aktywny, potraktuj element MotionEvent wewnątrz aktywności.

Oto przykład wykrywania gestów na poziomie aktywności z wykorzystaniem GestureDetector i implementuje właściwość GestureDetector.OnGestureListener w celu przetwarzania rozpoznanych gestów, które są następnie obsługiwane i tłumaczone na:

  • 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);
       }
     }
   }
  }

Przykład użycia

Aby korzystać z funkcji wykrywania gestów na poziomie aktywności, musisz wykonać te zadania:

  1. Dodaj do pliku manifestu tej deklaracji w deklaracji aplikacji. Dzięki temu aplikacja otrzyma MotionEvent w ramach aktywności:
    <application>
    <!-- Copy below declaration into your manifest file -->
    <meta-data
      android:name="com.google.android.glass.TouchEnabledApplication"
      android:value="true" />
    </application>
    
  2. Zastąp metodę dispatchTouchEvent(motionEvent) aktywności, aby przekazać zdarzenia ruchu do wzorca do wykrywania gestów onTouchEvent(motionEvent).
  3. Zaimplementuj GlassGestureDetector.OnGestureListener w swojej aktywności.

Oto przykład wzorca do wykrywania gestów na poziomie aktywności:

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);
   }
  }

Wejście audio

Glass Enterprise Edition 2 to standardowe urządzenie oparte na AOSP, które obsługuje podstawowe źródła dźwięku.

Te źródła dźwięku mają zaimplementowane zaawansowane przetwarzanie sygnałów:

Rozpoznawanie głosu

Glass Enterprise Edition 2 obsługuje natywną implementację rozpoznawania mowy. Ta funkcja jest dostępna tylko w języku angielskim.

Obraz rozpoznawania głosu przez Google Glass.

Interfejs rozpoznawania mowy czeka, aż użytkownik się wypowiedzieć, i po zakończeniu transkrypcji zwraca tekst. Aby rozpocząć aktywność, wykonaj te czynności:

  1. Wywołaj startActivityForResult() z intencją ACTION_RECOGNIZE_SPEECH. Po rozpoczęciu aktywności obsługiwane są te dodatkowe intencje:
  2. Zastąp wywołanie zwrotne onActivityResult(), aby otrzymać transkrypcję tekstu z intencji EXTRA_RESULTS, jak pokazano w poniższym przykładzie kodu. To wywołanie zwrotne jest wywoływane, gdy użytkownik skończy mówić.

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);
}

Szczegółowe informacje o implementacji znajdziesz w przykładowej aplikacji rozpoznawania głosu.

Uwzględnianie słów kluczowych

Rozpoznawanie mowy w Google Glass może powodować odchylenie listy słów kluczowych. Uwzględnianie słów kluczowych zwiększa dokładność rozpoznawania słów kluczowych. Aby włączyć promowanie wyników ze względu na słowa kluczowe:

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);

Polecenia głosowe

Polecenia głosowe umożliwiają użytkownikom wykonywanie działań. Polecenia głosowe tworzysz za pomocą standardowych interfejsów API menu Androida, ale użytkownicy mogą wywoływać pozycje menu za pomocą poleceń głosowych zamiast dotyku.

Aby włączyć polecenia głosowe dla określonej aktywności, wykonaj te czynności:

  1. Aby włączyć polecenia głosowe, wywołaj getWindow().requestFeature(FEATURE_VOICE_COMMANDS) w wybranej aktywności. Gdy ta funkcja jest włączona, w lewym dolnym rogu ekranu pojawia się ikona mikrofonu, gdy aktywność jest skupiona.
  2. Poproś o uprawnienia RECORD_AUDIO w Twojej aplikacji.
  3. Zastąp plik onCreatePanelMenu() i zapełnij zasób menu.
  4. Zastąp onContextItemSelected(), aby obsługiwać wykryte polecenia głosowe.

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);
    }
  }
}

Oto przykład zasobu menu używanego przez poprzednią aktywność:

<?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>

Pełny przykład znajdziesz w aplikacji do robienia notatek.

Ponowne ładowanie listy poleceń głosowych

Listę poleceń głosowych możesz dynamicznie wczytywać ponownie. Aby to zrobić, zastąp zasób menu w metodzie onCreatePanelMenu() lub zmodyfikuj obiekt menu w metodzie onPreparePanel(). Aby zastosować zmiany, wywołaj metodę 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();
}

Rozwiązanie AppCompatActivity

Aby ponownie wczytać listę poleceń głosowych w aktywności, która obejmuje AppCompatActivity, użyj metody sendBroadcast() z działaniem reload-voice-commands:

Kotlin

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

Java

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

Włączanie i wyłączanie poleceń głosowych w czasie działania

Polecenia głosowe możesz włączyć lub wyłączyć w czasie działania. W tym celu zwróć odpowiednią wartość z metody onCreatePanelMenu():

  • Aby ją włączyć, ustaw wartość true.
  • Aby wyłączyć, ustaw wartość false.

Tryb debugowania

Aby włączyć tryb debugowania poleceń głosowych, wywołaj getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS) w wybranej aktywności. Tryb debugowania aktywuje te funkcje:

  • Logcat zawiera dziennik z rozpoznanym wyrażeniem na potrzeby debugowania.
  • Nakładka interfejsu jest wyświetlana, gdy zostanie wykryte nierozpoznane polecenie, jak pokazano:
  • Nierozpoznane zdjęcie polecenia głosowego dotyczące szkła.

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);
  }
  .
  .
  .
}

Szczegółowe informacje znajdziesz w przykładowej aplikacji do ponownego ładowania poleceń głosowych.

Zamiana tekstu na mowę

Zamiana tekstu na mowę przekształca tekst cyfrowy na dane wyjściowe syntezatora. Więcej informacji znajdziesz w dokumentacji TextToSpeech.

Glass EE2 ma zainstalowany mechanizm Zamiana tekstu na mowę Google. Jako domyślny mechanizm zamiany tekstu na mowę działa tryb offline.

Te języki są dołączone do mechanizmu zamiany tekstu na mowę Google:

bengalski
Chiński mandaryński
Czeski
Duński
Grecki
Angielski
Hiszpański
Estoński
Fiński
Gudżarati
hindi
Węgierski
indonezyjski
włoski
japoński
jawajski
austronejski
australijski
koreański
malajalam
norweski
niderlandzki
polski
portugalski
rosyjski
słowacki
sundajski
szwedzki
tamilski
tajski
turecki
ukraiński
wietnamski

Aparat

Glass Enterprise Edition 2 jest wyposażony w aparat o stałej ostrości (8 megapikseli) z przysłoną F/2,4, czujnikiem 4:3 i polem widzenia 83° (71° x 57°). Zalecamy używanie standardowego interfejsu API CameraX lub Kamera2.

Szczegółowe informacje znajdziesz w przykładowej aplikacji aparatu.

Przycisk aparatu

Przycisk aparatu to fizyczny przycisk na zawiasie urządzenia Glass Enterprise Edition 2. Można ją obsługiwać tak samo jak w przypadku standardowego działania klawiatury, rozpoznając kod klucza KeyEvent#KEYCODE_CAMERA.

Od aktualizacji systemu operacyjnego OPM1.200313.001 zamiar jest wysyłany z aplikacji uruchamiającej za pomocą tych działań:

Czujniki

Programiści mogą korzystać z różnych czujników tworzonych w aplikacji Glass EE2.

Szklane czujniki Androida są obsługiwane przez te standardowe czujniki:

Te czujniki Androida nie są obsługiwane przez Glass:

Układ współrzędnych czujnika Glass pokazuje ta ilustracja. w stosunku do wyświetlacza Glass. Więcej informacji znajdziesz w artykule Układ współrzędnych czujnika.

Widoczny jest tu układ współrzędnych czujnika Glass w stosunku do wyświetlacza Glass.

Akcelerometr, żyroskop i magnetometr znajdują się na źródle optycznym urządzenia Glass, które użytkownik obraca, aby dopasować do siebie widok. Nie można bezpośrednio zmierzyć kąta bloku optycznego, dlatego należy o tym pamiętać podczas korzystania z tych czujników w przypadku aplikacji, np. nagłówka.

Aby wydłużyć żywotność baterii, słuchaj czujników tylko wtedy, gdy są Ci potrzebne. Decyzję o tym, kiedy włączyć lub wyłączyć czujniki, weź pod uwagę potrzeby i cykl życia aplikacji.

Wywołania zwrotne zdarzeń z czujnika są wykonywane w wątku użytkownika, więc przetwarzaj zdarzenia i zwracaj je jak najszybciej. Jeśli przetwarzanie trwa zbyt długo, przekaż zdarzenia czujnika do kolejki i zarządzaj nimi za pomocą wątku w tle.

Częstotliwość próbkowania 50 Hz jest często wystarczająca do próbkowania ruchu głowy.

Więcej informacji o używaniu czujników znajdziesz w przewodniku dla programistów Androida.

Usługi lokalizacyjne

Urządzenie Glass Enterprise Edition 2 nie zawiera modułu GPS, a informacje o lokalizacji użytkownika nie są dostępne. Wdrożono jednak usługi lokalizacyjne, które są niezbędne do wyświetlania listy pobliskich sieci Wi-Fi i urządzeń Bluetooth.

Jeśli Twoja aplikacja ma uprawnienia właściciela urządzenia, możesz za jej pomocą automatycznie zmienić wartość odpowiedniego zabezpieczenia:

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));
}

Jeśli korzystasz z rozwiązania MDM innej firmy, musi ono być w stanie zmienić te ustawienia.