ورودی ها و سنسورها

کیت توسعه نرم‌افزار اندروید (SDK) به شما امکان دسترسی به ورودی‌ها و حسگرهای مختلف موجود در Glass EE2 را می‌دهد. این صفحه مروری بر ویژگی‌های موجود، جزئیات پیاده‌سازی و نکات مفید ارائه می‌دهد.

حرکات لمسی

شما می‌توانید از Android SDK برای فعال کردن دسترسی به داده‌های خام از صفحه لمسی Glass استفاده کنید. این کار از طریق یک حسگر حرکت انجام می‌شود که به طور خودکار حرکات رایج روی Glass مانند ضربه زدن، پرتاب کردن و پیمایش را تشخیص می‌دهد.

همچنین می‌توانید از این حسگر حرکتی در برنامه‌های خود برای ثبت لمس، کشیدن انگشت به جلو، کشیدن انگشت به عقب و کشیدن انگشت به پایین استفاده کنید. این قابلیت مشابه دستگاه‌های قبلی گلس است.

بهتر است از این حرکات به روش‌های زیر استفاده کنید:

  • ضربه بزنید : تأیید یا ورود.
  • کشیدن انگشت به جلو ، کشیدن انگشت به عقب : در کارت‌ها و صفحه‌ها حرکت کنید.
  • کشیدن انگشت به پایین : برگشت یا خروج.

برای جزئیات پیاده‌سازی، نمونه آشکارساز حرکت را مطالعه کنید.

تشخیص حرکات با استفاده از تشخیص دهنده حرکات اندروید

GestureDetector اندروید (Android GestureDetector) به شما امکان می‌دهد حرکات ساده و پیچیده، مانند حرکاتی که از چندین انگشت استفاده می‌کنند یا اسکرول کردن، را تشخیص دهید.

تشخیص حرکات در سطح فعالیت

حرکات را فقط در سطح فعالیت تشخیص دهید، زمانی که مهم نیست کدام بخش از رابط کاربری شما فوکوس دارد. برای مثال، اگر می‌خواهید وقتی کاربر روی صفحه لمسی ضربه می‌زند، صرف نظر از اینکه کدام نمای فوکوس دارد، یک منو نمایش داده شود، MotionEvent را درون فعالیت مدیریت کنید.

در ادامه مثالی از تشخیص حرکت در سطح فعالیت آمده است که از GestureDetector استفاده می‌کند و GestureDetector.OnGestureListener را برای پردازش حرکات شناسایی‌شده پیاده‌سازی می‌کند که سپس مدیریت و به صورت زیر ترجمه می‌شوند:

  • TAP
  • SWIPE_FORWARD
  • SWIPE_BACKWARD
  • SWIPE_UP
  • SWIPE_DOWN

کاتلین

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 در activity دریافت کند:
    <application>
    <!-- Copy below declaration into your manifest file -->
    <meta-data
      android:name="com.google.android.glass.TouchEnabledApplication"
      android:value="true" />
    </application>
  2. متد dispatchTouchEvent(motionEvent) مربوط به activity را برای ارسال رویدادهای حرکتی به متد onTouchEvent(motionEvent) مربوط به حسگر حرکت، بازنویسی (override) کنید.
  3. GlassGestureDetector.OnGestureListener را در اکتیویتی خود پیاده‌سازی کنید.

در زیر مثالی از یک آشکارساز ژست در سطح فعالیت آمده است:

کاتلین

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 یک دستگاه استاندارد مبتنی بر AOSP است که از منابع صوتی پایه پشتیبانی می‌کند.

منابع صوتی زیر پردازش سیگنال پیشرفته‌ای را پیاده‌سازی کرده‌اند:

تشخیص صدا

نسخه ۲ نرم‌افزار Glass Enterprise از پیاده‌سازی بومی برای تشخیص گفتار پشتیبانی می‌کند. این قابلیت فقط برای زبان انگلیسی پشتیبانی می‌شود.

تصویر تشخیص صدا روی شیشه.

رابط کاربری تشخیص گفتار منتظر می‌ماند تا کاربران صحبت کنند و سپس متن رونویسی شده را پس از اتمام صحبت آنها برمی‌گرداند. برای شروع فعالیت، این مراحل را دنبال کنید:

  1. تابع startActivityForResult() را با اینتنت ACTION_RECOGNIZE_SPEECH فراخوانی کنید. اینتنت‌های اضافی زیر هنگام شروع اکتیویتی پشتیبانی می‌شوند:
  2. تابع فراخوانی onActivityResult() را برای دریافت متن رونویسی شده از EXTRA_RESULTS intent اضافی، همانطور که در نمونه کد زیر نشان داده شده است، لغو کنید. این تابع فراخوانی زمانی فراخوانی می‌شود که کاربر صحبت خود را تمام کند.

کاتلین

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

برای جزئیات پیاده‌سازی، نمونه برنامه تشخیص صدا را مطالعه کنید.

سوگیری برای کلمات کلیدی

تشخیص گفتار در گوگل گلس می‌تواند برای فهرستی از کلمات کلیدی بایاس شود. بایاس کردن، دقت تشخیص کلمات کلیدی را افزایش می‌دهد. برای فعال کردن بایاس کردن برای کلمات کلیدی، از موارد زیر استفاده کنید:

کاتلین

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

دستورات صوتی

دستورات صوتی به کاربران اجازه می‌دهند تا از طریق اکتیویتی‌ها (Activity) اقدامات لازم را انجام دهند. شما دستورات صوتی را با APIهای استاندارد منوی اندروید می‌سازید، اما کاربران می‌توانند به جای لمس کردن، آیتم‌های منو را با دستورات صوتی فراخوانی کنند.

برای فعال کردن دستورات صوتی برای یک فعالیت خاص، این مراحل را دنبال کنید:

  1. برای فعال کردن دستورات صوتی، تابع getWindow().requestFeature(FEATURE_VOICE_COMMANDS) در اکتیویتی مورد نظر فراخوانی کنید. با فعال بودن این ویژگی، هر زمان که این اکتیویتی فوکوس (focus) دریافت کند، آیکون میکروفون در گوشه پایین سمت چپ صفحه نمایش داده می‌شود.
  2. درخواست مجوز RECORD_AUDIO را در برنامه خود انجام دهید.
  3. تابع onCreatePanelMenu() را بازنویسی کرده و یک منبع منو را افزایش دهید.
  4. برای مدیریت فرمان‌های صوتی شناسایی‌شده، تابع onContextItemSelected() را نادیده بگیرید.

کاتلین

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() را فراخوانی کنید.

کاتلین

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

برای بارگذاری مجدد لیست دستورات صوتی در یک activity که AppCompatActivity ارث بری می‌کند، از متد sendBroadcast() با اکشن reload-voice-commands استفاده کنید:

کاتلین

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

جاوا

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

فعال و غیرفعال کردن دستورات صوتی در زمان اجرا

شما می‌توانید دستورات صوتی را در زمان اجرا فعال و غیرفعال کنید. برای انجام این کار، مقدار مناسبی را از متد onCreatePanelMenu() به صورت زیر برگردانید:

  • برای فعال کردن، مقدار را روی true تنظیم کنید.
  • برای غیرفعال کردن، مقدار را روی false تنظیم کنید.

حالت اشکال‌زدایی

برای فعال کردن حالت اشکال‌زدایی برای دستورات صوتی، تابع getWindow().requestFeature(FEATURE_DEBUG_VOICE_COMMANDS) در اکتیویتی مورد نظر فراخوانی کنید. حالت اشکال‌زدایی ویژگی‌های زیر را فعال می‌کند:

  • Logcat شامل گزارش با عبارت شناسایی‌شده برای اشکال‌زدایی است.
  • همانطور که نشان داده شده است، وقتی یک دستور ناشناخته شناسایی می‌شود، رابط کاربری نمایش داده می‌شود:
  • تشخیص صدای شیشه، تصویر فرمان ناشناخته.

کاتلین

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)

قابلیت تبدیل متن به گفتار، متن دیجیتال را به خروجی گفتار ترکیبی تبدیل می‌کند. برای اطلاعات بیشتر، به مستندات توسعه‌دهندگان اندروید، TextToSpeech، مراجعه کنید.

گلس EE2 موتور تبدیل متن به گفتار گوگل را نصب دارد. این موتور به عنوان موتور پیش‌فرض TTS تنظیم شده و به صورت آفلاین نیز کار می‌کند.

زبان‌های محلی زیر با موتور تبدیل متن به گفتار گوگل همراه هستند:

بنگالی
چینی ماندارین
چک
دانمارکی
آلمانی
یونانی
انگلیسی
اسپانیایی
استونیایی
فنلاندی
فرانسوی
گجراتی
هندی
مجارستانی
اندونزیایی
ایتالیایی
ژاپنی
جاوه ای
آسترونزیایی
استراآسیایی
کانارا
کره ای
مالایالامی
نروژی
هلندی
لهستانی
پرتغالی
روسی
اسلواکی
سوندایی
سوئدی
تامیل
تلوگو
تایلندی
ترکی
اوکراینی
ویتنامی

دوربین

گلس اینترپرایز ادیشن ۲ مجهز به یک دوربین ۸ مگاپیکسلی با فوکوس ثابت است که دارای دیافراگم f/2.4، نسبت تصویر حسگر ۴:۳ و میدان دید مورب ۸۳ درجه (۷۱ درجه در ۵۷ درجه در جهت افقی) می‌باشد. توصیه می‌کنیم از رابط برنامه‌نویسی استاندارد CameraX یا Camera2 API استفاده کنید.

برای جزئیات پیاده‌سازی، برنامه دوربین نمونه را مطالعه کنید.

دکمه دوربین

دکمه دوربین، دکمه فیزیکی روی لولای دستگاه Glass Enterprise Edition 2 است. می‌توان آن را درست مانند یک عملکرد استاندارد صفحه کلید کنترل کرد و با کد کلید KeyEvent#KEYCODE_CAMERA قابل شناسایی است.

از زمان به‌روزرسانی سیستم عامل OPM1.200313.001 ، اینتنت‌هایی با اقدامات زیر از برنامه‌ی لانچر ارسال می‌شوند:

  • دکمه دوربین را برای مدت کوتاهی فشار دهید. (برای این کار، روی دکمه دوربین کلیک کنید تا MediaStore#ACTION_IMAGE_CAPTURE باز شود.)
  • دکمه دوربین را برای چند لحظه نگه دارید. (برای این کار، روی دکمه دوربین MediaStore#ACTION_VIDEO_CAPTURE کلیک کنید.)

حسگرها

توسعه‌دهندگان می‌توانند با استفاده از Glass EE2 اپلیکیشن‌های متنوعی را توسعه دهند که شامل حسگرهای متنوعی است.

سنسورهای استاندارد اندروید زیر در گلس پشتیبانی می‌شوند:

سنسورهای اندروید زیر در گلس پشتیبانی نمی‌شوند:

سیستم مختصات سنسور گلس در تصویر زیر نشان داده شده است. این سیستم نسبت به صفحه نمایش گلس است. برای اطلاعات بیشتر، به بخش سیستم مختصات سنسور مراجعه کنید.

سیستم مختصات حسگر گلس در اینجا، نسبت به نمایشگر گلس، نشان داده شده است.

شتاب‌سنج، ژیروسکوپ و مغناطیس‌سنج روی غلاف اپتیکی دستگاه گلس قرار دارند که کاربران با چرخاندن آنها می‌توانند دستگاه را با دید خود هم‌تراز کنند. شما نمی‌توانید زاویه غلاف اپتیکی را مستقیماً اندازه‌گیری کنید، بنابراین هنگام استفاده از زوایای این حسگرها برای کاربردهایی مانند جهت‌یابی قطب‌نما، به این نکته توجه داشته باشید.

برای حفظ عمر باتری، فقط زمانی که به حسگرها نیاز دارید به آنها گوش دهید. هنگام تصمیم‌گیری در مورد زمان شروع و پایان گوش دادن به حسگرها، نیازها و چرخه عمر برنامه را در نظر بگیرید.

فراخوانی‌های رویداد حسگر روی نخ رابط کاربری اجرا می‌شوند، بنابراین رویدادها و بازگشت‌ها را در اسرع وقت پردازش کنید. اگر پردازش خیلی طول می‌کشد، رویدادهای حسگر را در یک صف قرار دهید و از یک نخ پس‌زمینه برای مدیریت آنها استفاده کنید.

۵۰ هرتز اغلب نرخ نمونه‌برداری کافی برای ردیابی حرکت سر است.

برای اطلاعات بیشتر در مورد نحوه استفاده از حسگرها، به راهنمای توسعه‌دهندگان اندروید مراجعه کنید.

خدمات مکان

دستگاه Glass Enterprise Edition 2 به ماژول GPS مجهز نیست و موقعیت مکانی کاربر را ارائه نمی‌دهد. با این حال، سرویس‌های موقعیت مکانی در آن پیاده‌سازی شده است که برای نمایش لیستی از شبکه‌های Wi-Fi و دستگاه‌های بلوتوث نزدیک ضروری است.

اگر برنامه شما دارای امتیازات مالک دستگاه است، می‌توانید از آن برای تغییر مقدار تنظیمات امن مربوطه به صورت برنامه‌نویسی استفاده کنید:

کاتلین

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 شخص ثالث استفاده می‌کنید، راهکار MDM باید بتواند این تنظیمات را برای شما تغییر دهد.