مصادر الإدخال والمستشعرات

تمنحك حزمة تطوير البرامج (SDK) لنظام التشغيل Android إمكانية الوصول إلى المدخلات وأجهزة الاستشعار المختلفة المتاحة مع Glass EE2. تقدّم لك هذه الصفحة نظرة عامة حول الميزات المتاحة وتفاصيل التنفيذ ونصائح مفيدة.

إيماءات اللمس

يمكنك استخدام حزمة تطوير البرامج (SDK) لنظام التشغيل Android لتفعيل الوصول إلى البيانات الأولية من لوحة لمس Glass. ويتم تنفيذ ذلك من خلال أداة رصد الإيماءات التي ترصد تلقائيًا الإيماءات الشائعة على Glass، مثل النقر والتمرير والتنقّل.

يمكنك أيضًا استخدام أداة رصد الإيماءات هذه في تطبيقاتك لمراعاة النقر والتمرير السريع للأمام والتمرير سريعًا للخلف والتمرير سريعًا للأسفل. ويشبه هذا أجهزة Glass السابقة.

من الأفضل استخدام هذه الإيماءات بالطرق التالية:

  • النقر: التأكيد أو الدخول.
  • التمرير السريع للأمام والتمرير سريعًا للخلف: التنقُّل بين البطاقات والشاشات.
  • التمرير السريع للأسفل: للرجوع أو الخروج.

وللحصول على تفاصيل التنفيذ، اطّلع على نموذج أداة رصد الإيماءات.

رصد الإيماءات باستخدام أداة رصد الإيماءات في Android

يتيح لك GestureDetector Android اكتشاف إيماءات بسيطة ومعقدة، مثل الإيماءات التي تستخدم أصابعًا أو تستخدم عدّة أصابع.

رصد الإيماءات على مستوى النشاط

اكتشاف الإيماءات على مستوى النشاط فقط عندما لا يكون ذلك ممكنًا، بغض النظر عن الجزء الذي محل التركيز في واجهة المستخدم. على سبيل المثال، إذا كنت تريد إظهار قائمة عندما ينقر المستخدم على لوحة اللمس، بغض النظر عن العرض الذي يركّز عليه، يجب التعامل مع 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. أضِف البيان التالي إلى ملف البيان داخل بيان التطبيق. يؤدي ذلك إلى تفعيل تطبيقك من تلقّي 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 استخدام طريقة تنفيذ أصلية لميزة "التعرّف على الكلام". تتوفّر هذه الميزة للغة الإنجليزية فقط.

صورة التعرف على الصوت الزجاجي.

في انتظار واجهة مستخدم التعرّف على الكلام حتى يتحدث المستخدمون ثم عرض النص الذي تم تحويله إلى نص بعد الانتهاء. لبدء النشاط، اتّبع الخطوات التالية:

  1. اطلب startActivityForResult() بقصد ACTION_RECOGNIZE_SPEECH. تتوفّر الميزات الإضافية التالية المستندة إلى النية بالشراء عند بدء النشاط:
  2. يمكنك إلغاء استدعاء onActivityResult() لتلقي النص الذي تم تحويله إلى نص من نية الإضافة EXTRA_RESULTS كما هو موضح في نموذج الرمز التالي. يتم استدعاء هذه المكالمة عند انتهاء المستخدم من التحدّث.

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 العادية، ولكن يمكن للمستخدمين استدعاء عناصر القائمة باستخدام الطلبات الصوتية بدلاً من اللمس.

لتفعيل الأوامر الصوتية لنشاط معيَّن، اتبع الخطوات التالية:

  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 المقصود:

Kotlin

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

Java

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

تفعيل الأوامر الصوتية وإيقافها في وقت التشغيل

يمكنك تفعيل الطلبات الصوتية وإيقافها في وقت التشغيل. لإجراء ذلك، اعرض قيمة مناسبة من طريقة onCreatePanelMenu() كما يلي:

  • اضبط القيمة على true لتفعيلها.
  • ويمكنك ضبط القيمة على false لإيقافها.

وضع تصحيح الأخطاء

لتفعيل وضع تصحيح الأخطاء للطلبات الصوتية، انقر على getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS) في النشاط المطلوب. يفعِّل وضع تصحيح الأخطاء الميزات التالية:

  • يحتوي Logcat على السجلّ الذي يتضمّن العبارة التي تم التعرّف عليها لتصحيح الأخطاء.
  • يتم عرض تراكب واجهة المستخدم عند اكتشاف أمر غير معروف، كما هو موضح:
  • صورة أمر غير معروف للتعرّف على الصوت من الزجاج.

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)

تحوّل وظيفة تحويل النص إلى كلام النص الرقمي إلى إخراج كلام مجمّع. للاطّلاع على مزيد من المعلومات، يُرجى الانتقال إلى TextToSpeech الذي يخص مستندات Android Developers.

تم تثبيت محرك تحويل النص إلى كلام من Google على جهاز Glass EE2. ويتم ضبطه كمحرّك تحويل النص إلى كلام التلقائي ويتم تشغيله بلا إنترنت.

يتم تضمين اللغات التالية مع محرّك تحويل النص إلى كلام من Google:

البنغالية
الماندرين الصينية
التشيكية
الدانمركية
الألمانية
اليونانية
الإنجليزية
الإسبانية
الإستونية
الفنلندية
الفرنسية
الغوجاراتية
الهندية
المجرية
الإندونيسية
الإيطالية
اليابانية
الجاوية
الأسترونية
النمساوية
الكنادية
الكورية
الملايلامية
النرويجية
الهولندية
البولندية
البرتغالية
الروسية
السلوفاكية
السوندانية
السويدية
التاميلية
التيلوغوية
التايلاندية
التركية
الأوكرانية
الفيتنامية

الكاميرا

إنّ الإصدار 2 من Glass Enterprise مجهّز بكاميرا 8 ميغا بكسل بتركيز ثابت مع فتحة عدسة F/2.4 وبنسبة عرض إلى ارتفاع تبلغ 4:3 ونطاق قطري يبلغ 83 درجة (71 × 57 درجة في الاتجاه الأفقي). ننصح باستخدام واجهة برمجة التطبيقات الكاميرا X العادية أو الكاميرا 2.

للحصول على تفاصيل التنفيذ، يُرجى قراءة نموذج تطبيق الكاميرا.

زر الكاميرا

زر الكاميرا هو الزر الفعلي على مُفصَّلة جهاز Glass Enterprise Edition 2. ويمكن التعامل معها تمامًا مثل إجراء لوحة المفاتيح العادي ويمكن التعرّف عليها من خلال رمز المفتاح KeyEvent#KEYCODE_CAMERA.

بدءًا من تحديث OPM1.200313.001 لنظام التشغيل، يتم إرسال النية بإجراء ات الإجراءات التالية من تطبيق "مشغّل التطبيقات":

أدوات الاستشعار

هناك مجموعة متنوعة من أجهزة الاستشعار المتاحة لمطوّري البرامج أثناء تطوير التطبيقات في Glass EE2.

تتوافق أجهزة الاستشعار المتوافقة مع Android العادية التالية مع Glass:

لا تتوافق أجهزة استشعار Android التالية مع Glass:

يتم عرض نظام إحداثيات جهاز استشعار الزجاج في الرسم التوضيحي التالي. هذه النسبة مقارنةً بشاشة Glass. لمزيد من المعلومات، يمكنك الاطّلاع على نظام إحداثيات أجهزة الاستشعار.

يتم عرض نظام إحداثيات جهاز استشعار Glass هنا، مقارنةً بعرض جهاز Glass.

يتوفّر مقياس التسارع والجيروسكوب ومقياس المغناطيسية في مجموعة المرئيات من جهاز Glass الذي يجري المستخدمون تدويرًا لمحاذاة الجهاز مع رؤيتهم. لا يمكنك قياس زاوية لوحة المواد البصرية مباشرةً، لذا يُرجى الانتباه إليها عند استخدام زوايا من أجهزة الاستشعار هذه للتطبيقات، مثل عنوان البوصلة.

للحفاظ على عمر البطارية، لا تستمع إلى أجهزة الاستشعار إلا عند الحاجة إليها. يمكنك الاستناد إلى احتياجات التطبيق ومراحل نشاطه عندما تقرّر موعد بدء تشغيل أجهزة الاستشعار والتوقّف عنها.

يتم تنفيذ استدعاءات أحداث جهاز الاستشعار على سلسلة واجهة المستخدم، لذا يمكنك معالجة الأحداث والمرتجعات في أسرع وقت ممكن. إذا استغرقت عملية المعالجة وقتًا طويلاً جدًا، أرسِل أحداث أداة الاستشعار إلى قائمة الانتظار واستخدِم سلسلة محادثات في الخلفية للتعامل معها.

غالبًا ما يمثل معدل 50 هرتز معدلًا كافيًا لأخذ العينات لتتبع حركة الرأس.

للاطّلاع على مزيد من المعلومات حول طريقة استخدام أجهزة الاستشعار، يُرجى الاطّلاع على دليل مطوّري برامج 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));
}

إذا كنت تستخدم حلًّا لإدارة الأجهزة الجوّالة من جهة خارجية، يجب أن يتمكّن حل إدارة الأجهزة الجوّالة من تغيير هذه الإعدادات نيابةً عنك.