مصادر الإدخال وأجهزة الاستشعار

تتيح لك حزمة تطوير البرامج (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))
    }
}

جافا

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

جافا

  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 هو جهاز عادي يستند إلى مشروع Android مفتوح المصدر (AOSP) ويتوافق مع مصادر الصوت الأساسية.

تم تنفيذ معالجة الإشارات المتقدّمة لمصادر الصوت التالية:

التعرّف على الصوت

تتيح Glass Enterprise Edition 2 إمكانية التعرّف على الكلام بشكلٍ مدمج. تتوفّر هذه الميزة باللغة الإنجليزية فقط.

صورة لميزة التعرّف على الصوت في Glass

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

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

جافا

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)

جافا

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) في النشاط المطلوب بـ Call لتفعيل الأوامر الصوتية. عند تفعيل هذه الميزة، يظهر رمز الميكروفون في أسفل يمين الشاشة عندما يكون هذا النشاط في المقدّمة.
  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)
        }
    }
}

جافا

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

جافا

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

جافا

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

تفعيل الطلبات الصوتية وإيقافها أثناء التشغيل

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

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

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

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

  • يحتوي Logcat على السجلّ الذي يتضمّن العبارة التي تم التعرّف عليها لتصحيح الأخطاء.
  • يتم عرض تراكب واجهة المستخدم عند رصد أمر غير معروف، كما هو موضّح أدناه:
  • صورة لطلب صوتي غير معروف في 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
        )
    }
    .
    .
    .
}

جافا

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 لتحويل النص إلى كلام. يتم ضبطه كمحرّك تلقائي لتحويل النص إلى كلام ويعمل بلا اتصال بالإنترنت.

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

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

الكاميرا

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

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

زر الكاميرا

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

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

أجهزة الاستشعار

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

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

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

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

يظهر هنا نظام إحداثيات مستشعر Glass، بالنسبة إلى شاشة Glass.

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

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

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

في كثير من الأحيان، يكون معدّل أخذ العيّنات البالغ 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()
    )
}

جافا

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)، يجب أن يكون هذا الحلّ قادرًا على تغيير هذه الإعدادات.