อินพุตและเซ็นเซอร์

Android SDK ช่วยให้คุณเข้าถึงอินพุตและเซ็นเซอร์ต่างๆ ที่มีใน Glass EE2 ได้ หน้านี้จะแสดงภาพรวมของฟีเจอร์ที่มี รายละเอียดการติดตั้งใช้งาน และเคล็ดลับที่เป็นประโยชน์

ท่าทางแตะสัมผัส

คุณสามารถใช้ Android SDK เพื่อเปิดใช้การเข้าถึงข้อมูลดิบจากทัชแพดของ Glass ได้ ซึ่งทำได้ผ่านเครื่องตรวจจับท่าทางสัมผัสที่ตรวจจับท่าทางสัมผัสทั่วไปบน Glass โดยอัตโนมัติ เช่น แตะ ปัด และเลื่อน

คุณยังใช้เครื่องตรวจจับท่าทางสัมผัสนี้ในแอปเพื่อรองรับการแตะ ปัดไปข้างหน้า ปัดไปข้างหลัง และปัดลงได้ด้วย ซึ่งคล้ายกับอุปกรณ์ Glass รุ่นก่อนๆ

คุณควรใช้ท่าทางสัมผัสเหล่านี้ในลักษณะต่อไปนี้

  • แตะ: ยืนยันหรือป้อน
  • ปัดไปข้างหน้า ปัดไปข้างหลัง: เลื่อนดูการ์ดและหน้าจอ
  • ปัดลง: กลับหรือออก

ดูรายละเอียดการใช้งานได้ที่ตัวอย่างเครื่องตรวจจับท่าทางสัมผัส

ตรวจหาท่าทางสัมผัสด้วยเครื่องตรวจจับท่าทางสัมผัสของ Android

GestureDetector ของ Android ช่วยให้คุณตรวจจับท่าทางสัมผัสที่เรียบง่ายและซับซ้อนได้ เช่น ท่าทางสัมผัสที่ใช้นิ้วหลายนิ้วหรือการเลื่อน

ตรวจจับท่าทางสัมผัสระดับกิจกรรม

ตรวจจับท่าทางสัมผัสที่ระดับกิจกรรมเฉพาะเมื่อไม่สำคัญว่าส่วนใดของ UI จะมีโฟกัส ตัวอย่างเช่น หากต้องการแสดงเมนูเมื่อผู้ใช้แตะทัชแพด ไม่ว่ามุมมองใดจะโฟกัสอยู่ ให้จัดการ MotionEvent ภายในกิจกรรม

ต่อไปนี้เป็นตัวอย่างการตรวจจับท่าทางสัมผัสระดับกิจกรรมที่ใช้ GestureDetector และใช้ GestureDetector.OnGestureListener เพื่อประมวลผลท่าทางสัมผัสที่รับรู้ ซึ่งจะ ได้รับการจัดการและแปลเป็นรายการต่อไปนี้

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

ตัวอย่างการใช้งาน

หากต้องการใช้การตรวจจับท่าทางสัมผัสระดับกิจกรรม คุณต้องทําภารกิจต่อไปนี้ให้เสร็จสมบูรณ์

  1. เพิ่มการประกาศต่อไปนี้ลงในไฟล์ Manifest ภายในประกาศของแอปพลิเคชัน ซึ่งจะช่วยให้แอปได้รับ MotionEvent ในกิจกรรม
    <application>
    <!-- Copy below declaration into your manifest file -->
    <meta-data
      android:name="com.google.android.glass.TouchEnabledApplication"
      android:value="true" />
    </application>
  2. แทนที่เมธอด dispatchTouchEvent(motionEvent) ของกิจกรรม เพื่อส่งเหตุการณ์การเคลื่อนไหวไปยังเมธอด onTouchEvent(motionEvent) ของเครื่องตรวจจับท่าทางสัมผัส
  3. ใช้ GlassGestureDetector.OnGestureListener ในกิจกรรม

ต่อไปนี้เป็นตัวอย่างเครื่องตรวจจับท่าทางสัมผัสระดับกิจกรรม

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

อินพุตเสียง

Glass Enterprise Edition 2 เป็นอุปกรณ์มาตรฐานที่ใช้ AOSP ซึ่งรองรับแหล่งเสียงพื้นฐาน

แหล่งเสียงต่อไปนี้มีการประมวลผลสัญญาณขั้นสูง

การจดจำเสียงพูด

Glass Enterprise Edition 2 รองรับการติดตั้งใช้งานการจดจำเสียงพูดโดยตรง ฟีเจอร์นี้รองรับเฉพาะภาษาอังกฤษ

รูปภาพการจดจำเสียงของ Glass

UI การจดจำเสียงพูดจะรอให้ผู้ใช้พูด แล้วแสดงข้อความที่ถอดเสียงหลังจากที่ผู้ใช้พูดจบ หากต้องการเริ่มกิจกรรม ให้ทำตามขั้นตอนต่อไปนี้

  1. เรียกใช้ startActivityForResult() ด้วยความตั้งใจ ACTION_RECOGNIZE_SPEECH ระบบรองรับส่วนเสริมของ Intent ต่อไปนี้เมื่อคุณเริ่มกิจกรรม
  2. ลบล้าง onActivityResult() การเรียกกลับเพื่อรับข้อความที่ถอดเสียงจาก EXTRA_RESULTS Intent Extra ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้ ระบบจะเรียกใช้ Callback นี้เมื่อผู้ใช้พูดจบ

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

ดูรายละเอียดการใช้งานได้ที่แอปตัวอย่างการจดจำเสียง

การให้ความสำคัญกับคีย์เวิร์ด

การจดจำเสียงพูดใน Glass อาจมีอคติสำหรับรายการคีย์เวิร์ด การเอนเอียงจะเพิ่มความแม่นยำในการจดจำคีย์เวิร์ด หากต้องการเปิดใช้การเอนเอียงสำหรับคีย์เวิร์ด ให้ใช้สิ่งต่อไปนี้

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

คำสั่งเสียง

คำสั่งเสียงช่วยให้ผู้ใช้ดำเนินการจากกิจกรรมต่างๆ ได้ คุณสร้างคำสั่งเสียง ด้วย Android Menu API มาตรฐาน แต่ผู้ใช้จะเรียกใช้รายการเมนูด้วยคำสั่งเสียง แทนการแตะได้

หากต้องการเปิดใช้คำสั่งเสียงสำหรับกิจกรรมหนึ่งๆ ให้ทำตามขั้นตอนต่อไปนี้

  1. โทร getWindow().requestFeature(FEATURE_VOICE_COMMANDS) ในกิจกรรมที่ต้องการเพื่อเปิดใช้คำสั่งเสียง เมื่อเปิดใช้ฟีเจอร์นี้ ไอคอนไมโครโฟน จะปรากฏที่มุมซ้ายล่างของหน้าจอเมื่อใดก็ตามที่กิจกรรมนี้ได้รับโฟกัส
  2. ขอสิทธิ์ RECORD_AUDIO ในแอป
  3. แทนที่ onCreatePanelMenu() และขยายทรัพยากรเมนู
  4. แทนที่ onContextItemSelected() เพื่อจัดการคำสั่งเสียงที่ตรวจพบ

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

ต่อไปนี้คือตัวอย่างของทรัพยากรเมนูที่กิจกรรมก่อนหน้าใช้

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

อ่านแอปจดบันทึกตัวอย่างเพื่อดูตัวอย่างที่สมบูรณ์

กำลังโหลดรายการคำสั่งเสียงอีกครั้ง

คุณโหลดรายการคำสั่งเสียงซ้ำแบบไดนามิกได้ โดยแทนที่menuในเมธอด onCreatePanelMenu() หรือแก้ไขออบเจ็กต์เมนูในเมธอด onPreparePanel() หากต้องการใช้การเปลี่ยนแปลง ให้เรียกใช้เมธอด 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();
}

โซลูชัน AppCompatActivity

หากต้องการโหลดรายการคำสั่งเสียงในกิจกรรมที่ขยาย AppCompatActivity ให้ใช้เมธอด sendBroadcast() กับreload-voice-commands การดำเนินการของ Intent ดังนี้

Kotlin

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

Java

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

เปิดและปิดใช้คำสั่งเสียงในรันไทม์

คุณเปิดและปิดใช้คำสั่งเสียงได้ในรันไทม์ โดยให้ส่งคืนค่าที่เหมาะสมจากเมธอด onCreatePanelMenu() ดังนี้

  • ตั้งค่าเป็น true เพื่อเปิดใช้
  • ตั้งค่าเป็น false เพื่อปิดใช้

โหมดแก้ไขข้อบกพร่อง

หากต้องการเปิดใช้โหมดแก้ไขข้อบกพร่องสำหรับคำสั่งเสียง ให้เรียกใช้ getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS) ในกิจกรรมที่ต้องการ โหมดแก้ไขข้อบกพร่องจะเปิดใช้งานฟีเจอร์ต่อไปนี้

  • Logcat มีบันทึกพร้อมวลีที่ระบบจดจำได้สำหรับการแก้ไขข้อบกพร่อง
  • การซ้อนทับ UI จะแสดงขึ้นเมื่อตรวจพบคำสั่งที่ไม่รู้จัก ดังที่แสดง
  • รูปภาพคำสั่งที่ไม่รู้จักของการจดจำเสียงของ 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);
  }
  .
  .
  .
}

ดูรายละเอียดการใช้งานได้ที่แอปตัวอย่างที่โหลดซ้ำด้วยคำสั่งเสียง

การอ่านออกเสียงข้อความ (TTS)

ฟังก์ชันการอ่านออกเสียงข้อความจะแปลงข้อความดิจิทัลเป็นเอาต์พุตเสียงพูดที่สังเคราะห์ขึ้น ดูข้อมูลเพิ่มเติมได้ที่ เอกสารประกอบสำหรับนักพัฒนาแอป Android เกี่ยวกับ TextToSpeech

Glass EE2 มีเครื่องมือการอ่านออกเสียงข้อความของ Google ติดตั้งไว้ โดยจะตั้งเป็นเครื่องมือ TTS เริ่มต้นและทำงานแบบออฟไลน์

เครื่องมืออ่านออกเสียงข้อความของ Google มาพร้อมกับภาษาต่อไปนี้

เบงกาลี
จีนกลาง
เช็ก
เดนมาร์ก
เยอรมัน
กรีก
อังกฤษ
สเปน
เอสโตเนีย
ฟินแลนด์
ฝรั่งเศส
คุชราต
ฮินดี
ฮังการี
อินโดนีเซีย
อิตาลี
ญี่ปุ่น
ชวา
ออสโตรนีเซียน
ออสโตรเอเชียติก
กันนาดา
เกาหลี
มาลายาลัม
นอร์เวย์
ดัตช์
โปแลนด์
โปรตุเกส
รัสเซีย
สโลวัก
ซุนดา
สวีเดน
ทมิฬ
เตลูกู
ไทย
ตุรกี
ยูเครน
เวียดนาม

กล้อง

Glass Enterprise Edition 2 มาพร้อมกล้อง 8 ล้านพิกเซลที่มีโฟกัสคงที่ รูรับแสง f/2.4 สัดส่วนภาพของเซ็นเซอร์ 4:3 และขอบเขตการมองเห็นแนวทแยง 83° (71° x 57° ในแนวนอน) เราขอแนะนำให้คุณใช้ API CameraX หรือ Camera2 มาตรฐาน

ดูรายละเอียดการนำไปใช้งานได้ที่แอปกล้องตัวอย่าง

ปุ่มกล้องถ่ายรูป

ปุ่มกล้องคือปุ่มจริงที่บานพับของอุปกรณ์ Glass Enterprise Edition 2 คุณจัดการได้เช่นเดียวกับการดำเนินการของแป้นพิมพ์มาตรฐาน และระบุได้ด้วยรหัสคีย์ KeyEvent#KEYCODE_CAMERA

ตั้งแต่การอัปเดต OS OPM1.200313.001 เป็นต้นไป ระบบจะส่ง Intent ที่มี การดำเนินการต่อไปนี้จากแอปพลิเคชัน Launcher

เซ็นเซอร์

นักพัฒนาแอปมีเซ็นเซอร์หลากหลายประเภทให้ใช้งานขณะพัฒนาแอปพลิเคชันใน Glass EE2

Glass รองรับเซ็นเซอร์ Android มาตรฐานต่อไปนี้

Glass ไม่รองรับเซ็นเซอร์ Android ต่อไปนี้

ระบบพิกัดเซ็นเซอร์ของ Glass แสดงอยู่ในภาพต่อไปนี้ โดยจะสัมพันธ์กับจอแสดงผลของ Glass ดูข้อมูลเพิ่มเติมได้ที่ ระบบพิกัดของเซ็นเซอร์

ระบบพิกัดเซ็นเซอร์ของ Glass จะแสดงที่นี่โดยสัมพันธ์กับจอแสดงผลของ Glass

มาตรความเร่ง ไจโรสโคป และแมกนีโตมิเตอร์จะอยู่ที่พ็อดเลนส์ของอุปกรณ์ Glass ซึ่งผู้ใช้จะหมุนเพื่อจัดแนวอุปกรณ์ให้ตรงกับสายตา คุณไม่สามารถวัดมุมของพ็อดเลนส์ได้โดยตรง ดังนั้นโปรดทราบถึงข้อจำกัดนี้เมื่อใช้มุมจากเซ็นเซอร์เหล่านี้สำหรับแอปพลิเคชัน เช่น ทิศทางเข็มทิศ

ฟังเซ็นเซอร์เฉพาะเมื่อจำเป็นเท่านั้นเพื่อช่วยยืดอายุการใช้งานแบตเตอรี่ พิจารณาความต้องการและวงจรของแอปเมื่อตัดสินใจว่าจะเริ่มและหยุดฟังเซ็นเซอร์ เมื่อใด

Callback ของเหตุการณ์เซ็นเซอร์จะทำงานใน UI เธรด ดังนั้นให้ประมวลผลเหตุการณ์และส่งคืนค่าให้เร็วที่สุด เท่าที่จะทำได้ หากการประมวลผลใช้เวลานานเกินไป ให้ส่งเหตุการณ์เซ็นเซอร์ไปยังคิวและใช้เธรดเบื้องหลัง เพื่อจัดการเหตุการณ์เหล่านั้น

โดยทั่วไปแล้ว 50 Hz เป็นอัตราการสุ่มตัวอย่างที่เพียงพอต่อการติดตามการเคลื่อนไหวของศีรษะ

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้เซ็นเซอร์ได้ที่ คู่มือนักพัฒนาแอป Android

บริการตำแหน่ง

อุปกรณ์ Glass Enterprise Edition 2 ไม่มีโมดูล GPS และไม่ได้ระบุตำแหน่งของผู้ใช้ อย่างไรก็ตาม แอปนี้มีบริการตำแหน่งที่จำเป็นต่อการ แสดงรายการเครือข่าย Wi-Fi และอุปกรณ์บลูทูธที่อยู่ใกล้เคียง

หากแอปพลิเคชันมีสิทธิ์ระดับเจ้าของอุปกรณ์ คุณจะใช้แอปพลิเคชันดังกล่าวเพื่อเปลี่ยนค่าของการตั้งค่าที่ปลอดภัยที่เกี่ยวข้องโดยอัตโนมัติได้โดยทำดังนี้

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

หากคุณใช้โซลูชัน MDM ของบุคคลที่สาม โซลูชัน MDM ต้องเปลี่ยนการตั้งค่าเหล่านี้ให้คุณได้