位置與感應器

您可以使用標準 Android 平台 API 存取位置和感應器資料。

位置

Glass 上的位置功能是使用標準 Android 平台 API 從可用的定位服務供應商取得位置資料。

您將使用以下 Android SDK 類別取得位置資料:

  • LocationManager - 提供處理與 LocationProvider 通訊的 Android 定位系統服務的存取權。

  • LocationProvider - 根據部分條件提供位置資料。Glass 提供特殊的「遠端」提供者,可讓您從已安裝 MyGlass 隨附應用程式的配對裝置上取得位置資料。

  • Criteria – 您可以建立一組條件,根據您設定的條件選取最佳 LocationProvider

總覽

如要取得位置資料,您必須使用 LocationManager 類別取得一或多個位置提供者的資料。

Android 手機或平板電腦上的應用程式會從裝置上的本機 GPS 和網路位置供應商擷取位置資料。但是在 Glass 上,可用的位置集是動態的,其中可能包含「遠端」定位提供者,可提供其他來源的位置資料,例如已安裝 MyGlass 隨附應用程式的藍牙配對裝置。如要處理這些額外供應商,請監聽多個供應商 (而非單一供應商) 的位置更新。

如何要求所有可用定位服務供應商的資料:

  1. 建立包含位置資訊要求的 Criteria 物件。
  2. 呼叫 getProviders() 以擷取符合條件的已啟用提供者清單。
  3. 疊代供應商清單並向所有供應商要求更新。以確保您能收到遠端供應商提供的更新 (如果有的話),以及來自 Glass 的本機供應商 (例如無線網路供應商) 的更新。
  4. 您可以使用每次更新提供的準確性和時間資訊,判斷更新是否足夠或應等待更新。

    LocationManager locationManager; // initialized elsewhere
    
    // This example requests fine accuracy and requires altitude, but
    // these criteria could be whatever you want.
    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    criteria.setAltitudeRequired(true);
    
    List<String> providers = locationManager.getProviders(
            criteria, true /* enabledOnly */);
    
    for (String provider : providers) {
        locationManager.requestLocationUpdates(provider, minTime,
                minDistance, listener);
    }
    

感應器

玻璃

Glass 具有專用的感應器,可偵測裝置是否位於使用者頭部。啟用後,這項設定可在裝置處於閒置狀態時節省電力。您可以在 Glassware 中使用這項功能,以停用或限制背景服務。首先,請實作 BroadcastReceiver 來偵測 ACTION_ON_HEAD_STATE_CHANGE 事件。

下列範例會根據使用者是否從頭部移除了 Glass,來延遲及停用遊戲分數更新:

  1. 實作 BroadcastReceiver 來處理狀態變更。
  2. 在服務中實作 onCreate() 方法,並註冊監聽 ACTION_ON_HEAD_STATE_CHANGE 意圖的接收器。
  3. onDestroy() 方法中,取消註冊接收器。

    import com.google.android.glass.content.Intents;
    ...
    
    public class LiveCardService extends Service {
    
        ...
        private boolean mIsStopped = false;
    
        private final BroadcastReceiver broadCastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
    
                if (Intents.ACTION_ON_HEAD_STATE_CHANGED.equals(intent.getAction())) {
                    boolean onHead = intent.getBooleanExtra(Intents.EXTRA_IS_ON_HEAD,
                            false);
                    if (onHead) {
                        mDelay = LiveCardService.DELAY_MILLIS;
                        if (isStopped()) {
                            // Resume updating scores
                            setStop(false);
    
                            // Restart immediately to get a refreshed score
                            mHandler.postDelayed(mUpdateLiveCardRunnable, 0);
                        }
                    } else {
                        // Increase the delay when the device is off head
                        mDelay = LiveCardService.DELAY_MILLIS_EXT;
                    }
                }
            }
        };
    
        private final Runnable mUpdateLiveCardRunnable = new Runnable() {
    
            @Override
            public void run() {
    
                if (mDelay == DELAY_MILLIS_EXT) {
                    // Count the increased delay as a retry attempt
                    mRetryCount++;
                } else if (mDelay == DELAY_MILLIS) {
                    mRetryCount = 0;
                }
    
                if (mRetryCount > MAX_RETRIES) {
                    // Stop updating scores
                    mIsStopped = true;
                }
    
                if (!isStopped()) {
                    // Generate fake points.
                    homeScore += mPointsGenerator.nextInt(3);
                    awayScore += mPointsGenerator.nextInt(3);
    
                    // Update the remote view with the new scores.
                    mLiveCardView = getRemoteViews(homeScore, awayScore);
    
                    // Always call setViews() to update the live card's RemoteViews.
                    mLiveCard.setViews(mLiveCardView);
    
                    // Queue another score update in 30 seconds.
                    mHandler.postDelayed(mUpdateLiveCardRunnable, mDelay);
                }
            }
        };
    
        @Override
        public void onCreate() {
            super.onCreate();
            mPointsGenerator = new Random();
            mDelay = DELAY_MILLIS;
    
            registerReceiver(broadCastReceiver, new IntentFilter(
                    Intents.ACTION_ON_HEAD_STATE_CHANGED));
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            if (mLiveCard == null) {
    
                // Get an instance of a live card
                mLiveCard = new LiveCard(this, LIVE_CARD_TAG);
    
                // Inflate a layout into a remote view
                mLiveCardView = new RemoteViews(getPackageName(),
                        R.layout.live_card);
    
                // Set up initial RemoteViews values
                homeScore = 0;
                awayScore = 0;
                mLiveCardView = getRemoteViews(homeScore, awayScore);
    
                // Set up the live card's action with a pending intent
                // to show a menu when tapped
                Intent menuIntent = new Intent(this, LiveCardMenuActivity.class);
                menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                        Intent.FLAG_ACTIVITY_CLEAR_TASK);
                mLiveCard.setAction(PendingIntent.getActivity(
                        this, 0, menuIntent, 0));
    
                // Publish the live card
                mLiveCard.publish(PublishMode.REVEAL);
    
                // Queue the update text runnable
                mHandler.post(mUpdateLiveCardRunnable);
            }
    
            return START_STICKY;
        }
    
        @Override
        public void onDestroy() {
            if (mLiveCard != null && mLiveCard.isPublished()) {
                //Stop the handler from queuing more Runnable jobs
                setStop(true);
    
                mLiveCard.unpublish();
                mLiveCard = null;
            }
    
            unregisterReceiver(broadCastReceiver);
    
            super.onDestroy();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        private RemoteViews getRemoteViews(int homeScore, int awayScore) {
            RemoteViews remoteViews = new RemoteViews(getPackageName(),
                    R.layout.live_card);
    
            remoteViews.setTextViewText(R.id.home_team_name_text_view,
                    getString(R.string.home_team));
            remoteViews.setTextViewText(R.id.away_team_name_text_view,
                    getString(R.string.away_team));
            remoteViews.setTextViewText(R.id.footer_text,
                    getString(R.string.game_quarter));
    
            remoteViews.setTextViewText(R.id.home_score_text_view,
                    String.valueOf(homeScore));
            remoteViews.setTextViewText(R.id.away_score_text_view,
                    String.valueOf(awayScore));
            return remoteViews;
        }
    
        public boolean isStopped() {
            return mIsStopped;
        }
    
        public void setStop(boolean isStopped) {
            mIsStopped = isStopped;
        }
    }
    

Android

Glass 支援下列 Android 感應器:

系統不支援下列 Android 感應器:

在 Glass 上使用感應器時,可參考以下幾個訣竅:

  • Glass 感應器的座標系統是相對於 Glass 螢幕的下方。 詳情請參閱感應器座標系統一文。

  • 加速計、陀螺儀和磁力儀位於 Glass 裝置的光學 Pod 上,使用者會旋轉,使裝置與視線方向對齊。您無法直接測量光學 Pod 的角度,因此在為應用程式 (例如指南針方向) 使用這類感應器的角度時,請注意這一點。

  • 為了延長電池續航力,建議你只在有需要時監聽感應器。舉例來說,如果您的 Glassware 使用 Service 轉譯 LiveCard,且只有在顯示即時資訊卡時,才需要使用感應器,請使用 LiveCard 顯示回呼方法,開始和停止監聽感應器。

  • 感應器事件回呼會在 UI 執行緒上執行,因此請處理事件並盡快傳回。如果處理處理時間過長,請考慮將感應器事件推送到佇列,並使用背景執行緒來處理。

  • 50 Hz 通常足以提供追蹤頭部動作的取樣率。

如要進一步瞭解如何使用感應器,請參閱 Android 開發人員指南