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

SDK Android cho phép bạn truy cập vào các đầu vào và cảm biến khác nhau có sẵn bằng 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 triển khai và các mẹo hữu ích.

Cử chỉ chạm

Bạn có thể sử dụng SDK Android để cho phép truy cập vào dữ liệu thô trên bàn di chuột của Glass. Quá trình này được thực hiện thông qua một trình phát hiện cử chỉ giúp tự động phát hiện các cử chỉ phổ biến trên ứng dụng Glass 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 để 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. Thao tác này tương tự như các thiết bị Glass trước đây.

Tốt nhất bạn nên sử dụng các cử chỉ này theo các 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ỉ của Android

Android GestureDetector 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ư các cử chỉ dùng nhiều ngón tay hoặc cuộn.

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

Chỉ phát hiện các cử chỉ ở cấp hoạt động khi vật dụng không 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ể chế độ xem nào có 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ỉ được nhận dạng, sau đó xử lý và dịch các nội dung 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ề mức 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 thành các thao tác sau:

  1. Thêm phần khai báo sau vào tệp kê khai bên trong phần khai báo ứng dụng. Điều này cho phép ứng dụng của bạn nhận được 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 để chuyển các sự kiện chuyển động tới 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.

Sau đây là ví dụ về 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);
   }
  }

Thiết bị đầu vào âm thanh

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

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

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

Glass Enterprise Edition 2 hỗ trợ triển khai gốc để nhận dạng giọng nói. Thư này chỉ được hỗ trợ cho tiếng Anh.

Hình ảnh nhận dạng giọng nói của Glass.

Giao diện người dùng của tính năng nhận dạng lời nói chờ người dùng nói và sau đó trả về văn bản đã chép lời sau khi hoàn tất. Để bắt đầu hoạt động, hãy làm theo các bước sau:

  1. Gọi startActivityForResult() với ý định ACTION_RECOGNIZE_SPEECH. Các thành phần bổ sung ý định sau đây đượ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 đã chép lời từ ý định EXTRA_RESULTS như được hiển thị trong 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.

Xu hướng cho từ khóa

Tính năng nhận dạng lời nói trên Glass có thể thiên về danh sách từ khóa. Xu hướng làm tăng độ chính xác của tính năng nhận dạng từ khóa. Để bật tính năng xu hướng cho các từ khoá, hãy sử dụng:

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 hành động từ các hoạt động. Bạn tạo các lệnh thoại bằng các 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ì 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 của 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.
  3. Ghi đè onCreatePanelMenu() và tăng cường tài nguyên trình đơn.
  4. Ghi đè onContextItemSelected() để xử lý các lệnh thoại mà Google phát hiện được.

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ụ hoàn chỉnh.

Tải lại danh sách lệnh thoại

Bạn có thể tự động tải lại danh sách lệnh thoại. Để thực hiện việc nà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 theo ý đị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 các lệnh thoại trong thời gian chạy. Để thực hiện 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ị này thành false để tắt tính năng này.

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 kích hoạt các tính năng sau:

  • Logcat chứa nhật ký chứa cụm từ được công nhận để gỡ lỗi.
  • Lớp phủ giao diện người dùng hiển thị khi phát hiện một lệnh không xác định, như sau:
  • Hình ảnh lệnh nhận dạng bằng giọng nói của 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 chuyển đổi văn bản kỹ thuật số thành đầu ra bằng giọng nói 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 TextToSpeak.

Glass EE2 đã cài đặt công cụ Chuyển văn bản sang lời nói của Google. Nó đượ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 đi kèm với công cụ Chuyển văn bản sang lời nói của Google:

Tiếng Bengal
Tiếng Trung
Tiếng Hungary
Tiếng Indonesia
Tiếng Ý
Tiếng Nhật
Tiếng Java
Tiếng Úc Austron
Tiếng Ba Lan
Tiếng Bồ Đào Nha
Tiếng Nga
Tiếng Slovak
Tiếng Sunda
Tiếng Thụy Đ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ị máy ảnh 8 megapixel, lấy nét cố định có khẩu độ f/2, 4, tỷ lệ khung hình cảm biến 4:3 và trường nhìn đường chéo 83° (71° x 57° theo hướng ngang). Bạn nên sử dụng API CameraX hoặc Camera2 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 máy ảnh 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 trên bàn phím chuẩn và có thể được 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, ý định có các thao tác sau đây sẽ được gửi qua ứng dụng Trình chạy:

Cảm biến

Có nhiều loại cảm biến dành cho nhà phát triển trong quá trình phát triển các ứng dụng trong Glass EE2.

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

Các cảm biến Android sau đây không được hỗ trợ trên Glass:

Hệ thống toạ độ cảm biến Glass được hiển thị trong hình minh hoạ sau. Nó tương đối với màn hình Glass. Để biết thêm thông tin, hãy xem nội dung Hệ thống toạ độ cảm biến.

Hệ thống toạ độ cảm biến Glass được hiển thị ở đây, so với màn hình Glass.

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

Để duy trì tuổi thọ pin, chỉ nghe các cảm biến khi bạn cần. 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ảm biến.

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 hàng đợi và sử dụng luồng nền để xử lý.

Tần số lấy mẫu 50 Hz thường là đủ để theo dõi chuyển động của đầ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 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, tính năng này đã triển khai các dịch vụ vị trí. Việc 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ó đặc quyền của chủ sở hữu thiết bị, bạn có thể sử dụng ứng dụng đó để 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 giải pháp MDM bên thứ ba, thì giải pháp MDM phải có khả năng thay đổi các tùy chọn cài đặt này cho bạn.