位置和传感器

您可以使用标准的 Android 平台 API 访问位置信息和传感器数据。

位置

Glass on Location 涉及使用标准 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 intent 的接收器。
  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 设备的光学仪器板上,用户可旋转该设备以使设备与他们的视野相符。您无法直接测量光学广告连播的角度,因此在针对罗盘方向等应用使用这些传感器的角度时,请注意这一点。

  • 为了延长电池续航时间,请仅在需要时监听传感器。例如,如果您的 Glassware 使用 Service 来渲染 LiveCard,并且您仅在实时卡片可见时才需要传感器,请使用 LiveCard Surface 回调方法来开始和停止监听传感器。

  • 传感器事件回调在界面线程上运行,因此请处理事件并尽快返回。请考虑将传感器事件推送到队列中,并在处理时间过长时使用后台线程来处理这些事件。

  • 50 Hz 通常足以用来跟踪头部移动。

如需详细了解如何使用传感器,请参阅 Android 开发者指南