Đầu vào và cảm biến

Android SDK cho phép bạn truy cập vào nhiều đầu vào và cảm biến có sẵn trên Glass EE2. Trang này cung cấp cho bạn thông tin tổng quan về các tính năng hiện có, thông tin chi tiết về việc triển khai và các mẹo hữu ích.

Cử chỉ chạm

Bạn có thể sử dụng Android SDK để cho phép truy cập vào dữ liệu thô từ bàn di chuột của Glass. Việc này được thực hiện thông qua một trình phát hiện cử chỉ tự động phát hiện các cử chỉ phổ biến trên Glass, chẳng hạn như nhấn, hất và cuộn.

Bạn cũng có thể sử dụng trình phát hiện cử chỉ này trong các ứng dụng của mình để tính đến thao tác nhấn, vuốt về phía trước, vuốt ngược lại và vuốt xuống. Điều này tương tự như các thiết bị Glass trước đây.

Bạn nên dùng các cử chỉ này theo những cách sau:

  • Nhấn: Xác nhận hoặc nhập.
  • Vuốt về phía trước, vuốt về phía sau: Di chuyển qua các thẻ và màn hình.
  • Vuốt xuống: Quay lại hoặc thoát.

Để biết thông tin chi tiết về cách triển khai, hãy đọc trình phát hiện cử chỉ mẫu.

Phát hiện cử chỉ bằng trình phát hiện cử chỉ trên Android

GestureDetector của Android cho phép bạn phát hiện các cử chỉ đơn giản và phức tạp, chẳng hạn như những cử chỉ sử dụng nhiều ngón tay hoặc cử chỉ cuộn.

Phát hiện cử chỉ ở cấp hoạt động

Chỉ phát hiện cử chỉ ở cấp độ hoạt động khi không quan trọng phần nào trong giao diện người dùng của bạn có tiêu điểm. Ví dụ: nếu bạn muốn hiển thị một trình đơn khi người dùng nhấn vào bàn di chuột, bất kể khung hiển thị nào đang được lấy tiêu điểm, hãy xử lý MotionEvent bên trong hoạt động.

Sau đây là ví dụ về tính năng phát hiện cử chỉ ở cấp hoạt động sử dụng GestureDetector và triển khai GestureDetector.OnGestureListener để xử lý các cử chỉ đã nhận dạng, sau đó được xử lý và chuyển đổi thành các cử chỉ sau:

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

Ví dụ về cách sử dụng

Để sử dụng tính năng phát hiện cử chỉ ở cấp hoạt động, bạn cần hoàn tất các tác vụ sau:

  1. Thêm nội dung khai báo sau vào tệp kê khai, bên trong nội dung khai báo ứng dụng. Thao tác này cho phép ứng dụng của bạn nhận MotionEvent trong hoạt động:
    <application>
    <!-- Copy below declaration into your manifest file -->
    <meta-data
      android:name="com.google.android.glass.TouchEnabledApplication"
      android:value="true" />
    </application>
  2. Ghi đè phương thức dispatchTouchEvent(motionEvent) của hoạt động để truyền các sự kiện chuyển động đến phương thức onTouchEvent(motionEvent) của trình phát hiện cử chỉ.
  3. Triển khai GlassGestureDetector.OnGestureListener trong hoạt động của bạn.

Sau đây là ví dụ về một trình phát hiện cử chỉ ở cấp hoạt động:

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

Đầu vào âm thanh

Glass Enterprise Edition 2 là một thiết bị tiêu chuẩn dựa trên AOSP, hỗ trợ các nguồn âm thanh cơ bản.

Các nguồn âm thanh sau đây đã triển khai quy trình xử lý tín hiệu nâng cao:

Nhận dạng giọng nói

Glass Enterprise Edition 2 hỗ trợ việc triển khai gốc cho tính năng nhận dạng lời nói. Tính năng này chỉ hỗ trợ tiếng Anh.

Hình ảnh về tính năng nhận dạng giọng nói trên Glass.

Giao diện người dùng nhận dạng giọng nói sẽ chờ người dùng nói rồi trả về văn bản được chép lời sau khi họ nói xong. Để bắt đầu hoạt động, hãy làm theo các bước sau:

  1. Gọi startActivityForResult() bằng ý định ACTION_RECOGNIZE_SPEECH. Các thuộc tính bổ sung sau đây của ý định được hỗ trợ khi bạn bắt đầu hoạt động:
  2. Ghi đè lệnh gọi lại onActivityResult() để nhận văn bản được chép lời từ phần bổ sung ý định EXTRA_RESULTS như minh hoạ trong đoạn mã mẫu sau. Lệnh gọi lại này được gọi khi người dùng nói xong.

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

Để biết thông tin chi tiết về cách triển khai, hãy đọc ứng dụng nhận dạng giọng nói mẫu.

Thiên vị cho từ khoá

Tính năng nhận dạng giọng nói trên Glass có thể thiên về một danh sách từ khoá. Việc thiên vị sẽ làm tăng độ chính xác của tính năng nhận dạng từ khoá. Để bật tính năng thiên vị cho từ khoá, hãy sử dụng nội dung sau:

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

Lệnh thoại

Lệnh thoại cho phép người dùng thực hiện các thao tác từ các hoạt động. Bạn tạo các lệnh thoại bằng API trình đơn Android tiêu chuẩn, nhưng người dùng có thể gọi các mục trong trình đơn bằng lệnh thoại thay vì thao tác chạm.

Để bật lệnh thoại cho một hoạt động cụ thể, hãy làm theo các bước sau:

  1. Gọi getWindow().requestFeature(FEATURE_VOICE_COMMANDS) trong hoạt động mong muốn để bật lệnh thoại. Khi tính năng này được bật, biểu tượng micrô sẽ xuất hiện ở góc dưới cùng bên trái màn hình bất cứ khi nào hoạt động này nhận được tiêu điểm.
  2. Yêu cầu quyền RECORD_AUDIO trong ứng dụng của bạn.
  3. Ghi đè onCreatePanelMenu() và tăng cường một tài nguyên trình đơn.
  4. Ghi đè onContextItemSelected() để xử lý các lệnh thoại được phát hiện.

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

Sau đây là ví dụ về tài nguyên trình đơn mà hoạt động trước đó sử dụng:

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

Hãy đọc ứng dụng ghi chú mẫu để biết ví dụ đầy đủ.

Đang tải lại danh sách lệnh thoại

Bạn có thể tải lại danh sách lệnh thoại một cách linh động. Để làm như vậy, hãy thay thế tài nguyên menu trong phương thức onCreatePanelMenu() hoặc sửa đổi đối tượng trình đơn trong phương thức onPreparePanel(). Để áp dụng các thay đổi, hãy gọi phương thức 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();
}

Giải pháp AppCompatActivity

Để tải lại danh sách lệnh thoại trong một hoạt động mở rộng AppCompatActivity, hãy sử dụng phương thức sendBroadcast() với thao tác ý định reload-voice-commands:

Kotlin

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

Java

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

Bật và tắt lệnh thoại trong thời gian chạy

Bạn có thể bật và tắt lệnh thoại trong thời gian chạy. Để làm việc này, hãy trả về một giá trị thích hợp từ phương thức onCreatePanelMenu() như sau:

  • Đặt giá trị thành true để bật.
  • Đặt giá trị thành false để tắt.

Chế độ gỡ lỗi

Để bật chế độ gỡ lỗi cho các lệnh thoại, hãy gọi getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS) trong hoạt động mong muốn. Chế độ gỡ lỗi sẽ kích hoạt các tính năng sau:

  • Logcat chứa nhật ký có cụm từ được nhận dạng để gỡ lỗi.
  • Lớp phủ giao diện người dùng sẽ xuất hiện khi phát hiện thấy một lệnh không nhận dạng được, như minh hoạ:
  • Hình ảnh lệnh không nhận dạng được của tính năng nhận dạng giọng nói trên 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);
  }
  .
  .
  .
}

Để biết thông tin chi tiết về cách triển khai, hãy đọc ứng dụng tải lại lệnh thoại mẫu.

Chuyển văn bản sang lời nói (TTS)

Chức năng Chuyển văn bản sang lời nói sẽ chuyển đổi văn bản kỹ thuật số thành đầu ra lời nói được tổng hợp. Để biết thêm thông tin, hãy truy cập vào Tài liệu dành cho nhà phát triển Android về TextToSpeech.

Glass EE2 đã cài đặt công cụ Chuyển văn bản sang lời nói của Google. Ứng dụng này được đặt làm công cụ TTS mặc định và hoạt động ngoại tuyến.

Các ngôn ngữ sau đây được đi kèm với công cụ Chuyển văn bản sang lời nói của Google:

Tiếng Bengali
Tiếng Quan Thoại
Tiếng Séc
Tiếng Đan Mạch
Tiếng Đức
Tiếng Hy Lạp
Tiếng Anh
Tiếng Tây Ban Nha
Tiếng Estonia
Tiếng Phần Lan
Tiếng Pháp
Tiếng Gujarati
Tiếng Hindi
Tiếng Hungary
Tiếng Indonesia
Tiếng Ý
Tiếng Nhật
Tiếng Java
Tiếng Nam Đảo
Tiếng Nam Á
Tiếng Kannada
Tiếng Hàn
Tiếng Malayalam
Tiếng Na Uy
Tiếng Hà Lan
Tiếng Ba Lan
Tiếng Bồ Đào Nha
Tiếng Nga
Tiếng Slovakia
Tiếng Sundan
Tiếng Thuỵ Điển
Tiếng Tamil
Tiếng Telugu
Tiếng Thái
Tiếng Thổ Nhĩ Kỳ
Tiếng Ukraina
Tiếng Việt

Máy ảnh

Glass Enterprise Edition 2 được trang bị camera lấy nét cố định 8 megapixel có khẩu độ f/2.4, tỷ lệ khung hình cảm biến 4:3 và trường nhìn chéo 83° (71° x 57° ở hướng ngang). Bạn nên sử dụng CameraX hoặc API Camera2 tiêu chuẩn.

Để biết thông tin chi tiết về cách triển khai, hãy đọc ứng dụng máy ảnh mẫu.

Nút máy ảnh

Nút camera là nút vật lý trên bản lề của thiết bị Glass Enterprise Edition 2. Bạn có thể xử lý thao tác này giống như một thao tác tiêu chuẩn trên bàn phím và có thể xác định bằng mã phím KeyEvent#KEYCODE_CAMERA.

Kể từ bản cập nhật hệ điều hành OPM1.200313.001, ứng dụng Trình chạy sẽ gửi ý định với các thao tác sau:

Cảm biến

Nhà phát triển có thể sử dụng nhiều loại cảm biến khi phát triển các ứng dụng trong Glass EE2.

Glass hỗ trợ các cảm biến Android tiêu chuẩn sau:

Glass không hỗ trợ các cảm biến Android sau:

Hệ toạ độ cảm biến của Glass được minh hoạ trong hình sau. Vị trí này tương ứng với màn hình Glass. Để biết thêm thông tin, hãy xem phần Hệ toạ độ cảm biến.

Hệ toạ độ cảm biến của Glass xuất hiện ở đây, tương ứng với màn hình Glass.

Gia tốc kế, con quay hồi chuyển và từ kế nằm trên hộp quang học của thiết bị Glass. Người dùng xoay hộp này để căn chỉnh thiết bị với tầm nhìn của họ. Bạn không thể đo trực tiếp góc của hộp quang học, vì vậy hãy lưu ý điều này khi sử dụng các góc từ những cảm biến này cho các ứng dụng, chẳng hạn như hướng la bàn.

Để duy trì thời lượng pin, chỉ lắng nghe các cảm biến khi bạn cần. Hãy cân nhắc nhu cầu và vòng đời của ứng dụng khi bạn quyết định thời điểm bắt đầu và dừng nghe các cảm biến.

Các lệnh gọi lại sự kiện cảm biến chạy trên luồng giao diện người dùng, vì vậy, hãy xử lý các sự kiện và trả về nhanh nhất có thể. Nếu quá trình xử lý mất quá nhiều thời gian, hãy đẩy các sự kiện cảm biến vào một hàng đợi và sử dụng một luồng nền để xử lý các sự kiện đó.

50 Hz thường là tốc độ lấy mẫu đủ để theo dõi chuyển động đầu.

Để biết thêm thông tin về cách sử dụng cảm biến, hãy xem hướng dẫn dành cho nhà phát triển Android.

Dịch vụ vị trí

Thiết bị Glass Enterprise Edition 2 không được trang bị mô-đun GPS và không cung cấp vị trí của người dùng. Tuy nhiên, ứng dụng này có triển khai các dịch vụ vị trí, điều này là cần thiết để hiển thị danh sách các mạng Wi-Fi và thiết bị Bluetooth ở gần.

Nếu ứng dụng của bạn có đặc quyền của chủ sở hữu thiết bị, bạn có thể dùng đặc quyền này để thay đổi giá trị của chế độ cài đặt bảo mật tương ứng theo phương thức lập trình:

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

Nếu bạn sử dụng một giải pháp MDM của bên thứ ba, thì giải pháp MDM đó phải có thể thay đổi các chế độ cài đặt này cho bạn.