首页 > 解决方案 > 获取 Google Beacon 的距离和 UUID

问题描述

我已经在谷歌信标平台上注册了我的信标,并下载了“信标工具”安卓应用程序来扫描信标。下面我附上了它的截图。

在此处输入图像描述

现在我正在为该信标开发一个 android 应用程序,并使用这个应用程序我可以获得我为信标设置的附件。我正在为此使用附近的消息 API。现在我想获取 UUID 以及用户和信标之间的距离。我对这些东西很陌生,并且阅读了许多 stackoverflow 问题和答案。但那些并没有解决我的问题

下面我提到了我的课程。

MainActivity.java

public class MainActivity extends AppCompatActivity implements 
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
SharedPreferences.OnSharedPreferenceChangeListener {

private static final String TAG = MainActivity.class.getSimpleName();

private static final int PERMISSIONS_REQUEST_CODE = 1111;

private static final String KEY_SUBSCRIBED = "subscribed";

/**
 * The entry point to Google Play Services.
 */
private GoogleApiClient mGoogleApiClient;

/**
 * The container {@link android.view.ViewGroup} for the minimal UI associated with this sample.
 */
private RelativeLayout mContainer;

/**
 * Tracks subscription state. Set to true when a call to
 * {@link Messages#subscribe(GoogleApiClient, MessageListener)} succeeds.
 */
private boolean mSubscribed = false;

/**
 * Adapter for working with messages from nearby beacons.
 */
private ArrayAdapter<String> mNearbyMessagesArrayAdapter;

/**
 * Backing data structure for {@code mNearbyMessagesArrayAdapter}.
 */
private List<String> mNearbyMessagesList = new ArrayList<>();
FirebaseDatabase db = FirebaseDatabase.getInstance();
DatabaseReference databaseReference;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);



    setContentView(R.layout.activity_main);
    //Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    //setSupportActionBar(toolbar);

    if (savedInstanceState != null) {
        mSubscribed = savedInstanceState.getBoolean(KEY_SUBSCRIBED, false);
    }

    mContainer = (RelativeLayout) findViewById(R.id.main_activity_container);

    if (!havePermissions()) {
        Log.i(TAG, "Requesting permissions needed for this app.");
        requestPermissions();
    }

    final List<String> cachedMessages = Utils.getCachedMessages(this);
    if (cachedMessages != null) {
        mNearbyMessagesList.addAll(cachedMessages);
    }

    final ListView nearbyMessagesListView = (ListView) findViewById(
            R.id.nearby_messages_list_view);
    mNearbyMessagesArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
            mNearbyMessagesList);
    if (nearbyMessagesListView != null) {
        nearbyMessagesListView.setAdapter(mNearbyMessagesArrayAdapter);
    }

}

@Override
protected void onResume() {
    super.onResume();

    getSharedPreferences(getApplicationContext().getPackageName(), Context.MODE_PRIVATE)
            .registerOnSharedPreferenceChangeListener(this);

    if (havePermissions()) {
        buildGoogleApiClient();
    }
}

@TargetApi(Build.VERSION_CODES.M)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    if (requestCode != PERMISSIONS_REQUEST_CODE) {
        return;
    }
    for (int i = 0; i < permissions.length; i++) {
        String permission = permissions[i];
        if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
            if (shouldShowRequestPermissionRationale(permission)) {
                Log.i(TAG, "Permission denied without 'NEVER ASK AGAIN': " + permission);
                showRequestPermissionsSnackbar();
            } else {
                Log.i(TAG, "Permission denied with 'NEVER ASK AGAIN': " + permission);
                showLinkToSettingsSnackbar();
            }
        } else {
            Log.i(TAG, "Permission granted, building GoogleApiClient");
            buildGoogleApiClient();
        }
    }
}

private synchronized void buildGoogleApiClient() {
    if (mGoogleApiClient == null) {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Nearby.MESSAGES_API, new MessagesOptions.Builder()
                    .setPermissions(NearbyPermissions.BLE).build())
                .addConnectionCallbacks(this)
                .enableAutoManage(this, this)
                .build();
    }
}

@Override
protected void onPause() {
    getSharedPreferences(getApplicationContext().getPackageName(), Context.MODE_PRIVATE)
            .unregisterOnSharedPreferenceChangeListener(this);
    super.onPause();
}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
    if (mContainer != null) {
        Snackbar.make(mContainer, "Exception while connecting to Google Play services: " +
                        connectionResult.getErrorMessage(),
                Snackbar.LENGTH_INDEFINITE).show();
    }
}

@Override
public void onConnectionSuspended(int i) {
    Log.w(TAG, "Connection suspended. Error code: " + i);
}

@Override
public void onConnected(@Nullable Bundle bundle) {
    Log.i(TAG, "GoogleApiClient connected");
    subscribe();
}

@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    if (TextUtils.equals(key, Utils.KEY_CACHED_MESSAGES)) {
        mNearbyMessagesList.clear();
        mNearbyMessagesList.addAll(Utils.getCachedMessages(this));
        mNearbyMessagesArrayAdapter.notifyDataSetChanged();
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(KEY_SUBSCRIBED, mSubscribed);
}

private boolean havePermissions() {
    return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED;
}

private void requestPermissions() {
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_CODE);
}

/**
 * Calls {@link Messages#subscribe(GoogleApiClient, MessageListener, SubscribeOptions)},
 * using a {@link Strategy} for BLE scanning. Attaches a {@link ResultCallback} to monitor
 * whether the call to {@code subscribe()} succeeded or failed.
 */
private void subscribe() {
    // In this sample, we subscribe when the activity is launched, but not on device orientation
    // change.
    if (mSubscribed) {
        Log.i(TAG, "Already subscribed.");
        return;
    }

    SubscribeOptions options = new SubscribeOptions.Builder()
            .setStrategy(Strategy.BLE_ONLY)
            .build();

    Nearby.Messages.subscribe(mGoogleApiClient, getPendingIntent(), options)
            .setResultCallback(new ResultCallback<Status>() {
                @Override
                public void onResult(@NonNull Status status) {
                    if (status.isSuccess()) {
                        Log.i(TAG, "Subscribed successfully.");
                        startService(getBackgroundSubscribeServiceIntent());
                    } else {
                        Log.e(TAG, "Operation failed. Error: " +
                                NearbyMessagesStatusCodes.getStatusCodeString(
                                        status.getStatusCode()));
                    }
                }
            });
}

private PendingIntent getPendingIntent() {
    return PendingIntent.getService(this, 0,
            getBackgroundSubscribeServiceIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
}

private Intent getBackgroundSubscribeServiceIntent() {
    return new Intent(this, BackgroundSubscribeIntentService.class);
}

/**
 * Displays {@link Snackbar} instructing user to visit Settings to grant permissions required by
 * this application.
 */
private void showLinkToSettingsSnackbar() {
    if (mContainer == null) {
        return;
    }
    Snackbar.make(mContainer,
            R.string.permission_denied_explanation,
            Snackbar.LENGTH_INDEFINITE)
            .setAction(R.string.settings, new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // Build intent that displays the App settings screen.
                    Intent intent = new Intent();
                    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                    Uri uri = Uri.fromParts("package",
                            BuildConfig.APPLICATION_ID, null);
                    intent.setData(uri);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                }
            }).show();
}

/**
 * Displays {@link Snackbar} with button for the user to re-initiate the permission workflow.
 */
private void showRequestPermissionsSnackbar() {
    if (mContainer == null) {
        return;
    }
    Snackbar.make(mContainer, R.string.permission_rationale,
            Snackbar.LENGTH_INDEFINITE)
            .setAction(R.string.ok, new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // Request permission.
                    ActivityCompat.requestPermissions(MainActivity.this,
                            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                            PERMISSIONS_REQUEST_CODE);
                }
            }).show();
      }
  }

BackgroundSubscribeIntentService.java

public class BackgroundSubscribeIntentService extends IntentService {
private static final String TAG = "BackSubIntentService";

private static final int MESSAGES_NOTIFICATION_ID = 1;
private static final int NUM_MESSAGES_IN_NOTIFICATION = 5;

public BackgroundSubscribeIntentService() {
    super("BackgroundSubscribeIntentService");
}

@Override
public void onCreate() {
    super.onCreate();
    updateNotification();
}


@Override
protected void onHandleIntent(Intent intent) {
    if (intent != null) {
        Nearby.Messages.handleIntent(intent, new MessageListener() {
            @Override
            public void onFound(Message message) {
                Utils.saveFoundMessage(getApplicationContext(), message);
                updateNotification();
            }

            @Override
            public void onLost(Message message) {
                Utils.removeLostMessage(getApplicationContext(), message);
                updateNotification();
            }
        });
    }
}

private void updateNotification() {
    List<String> messages = Utils.getCachedMessages(getApplicationContext());
    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    Intent launchIntent = new Intent(getApplicationContext(), MainActivity.class);
    launchIntent.setAction(Intent.ACTION_MAIN);
    launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
            launchIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    String contentTitle = getContentTitle(messages);
    String contentText = getContentText(messages);

    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
            .setSmallIcon(android.R.drawable.star_on)
            .setContentTitle(contentTitle)
            .setContentText(contentText)
            .setStyle(new NotificationCompat.BigTextStyle().bigText(contentText))
            .setOngoing(true)
            .setContentIntent(pi);
    notificationManager.notify(MESSAGES_NOTIFICATION_ID, notificationBuilder.build());
}

private String getContentTitle(List<String> messages) {
    switch (messages.size()) {
        case 0:
            return getResources().getString(R.string.scanning);
        case 1:
            return getResources().getString(R.string.one_message);
        default:
            return getResources().getString(R.string.many_messages, messages.size());
    }
}

private String getContentText(List<String> messages) {
    String newline = System.getProperty("line.separator");
    if (messages.size() < NUM_MESSAGES_IN_NOTIFICATION) {
        return TextUtils.join(newline, messages);
    }
    return TextUtils.join(newline, messages.subList(0, NUM_MESSAGES_IN_NOTIFICATION)) +
            newline + "&#8230;";
    }
 }

实用程序.java

public final class Utils {
static final String KEY_CACHED_MESSAGES = "cached-messages";

/**
 * Fetches message strings stored in {@link SharedPreferences}.
 *
 * @param context The context.
 * @return  A list (possibly empty) containing message strings.
 */
static List<String> getCachedMessages(Context context) {
    SharedPreferences sharedPrefs = getSharedPreferences(context);
    String cachedMessagesJson = sharedPrefs.getString(KEY_CACHED_MESSAGES, "");
    if (TextUtils.isEmpty(cachedMessagesJson)) {
        return Collections.emptyList();
    } else {
        Type type = new TypeToken<List<String>>() {}.getType();
        return new Gson().fromJson(cachedMessagesJson, type);
    }
}

/**
 * Saves a message string to {@link SharedPreferences}.
 *
 * @param context The context.
 * @param message The Message whose payload (as string) is saved to SharedPreferences.
 */
static void saveFoundMessage(Context context, Message message) {
    ArrayList<String> cachedMessages = new ArrayList<>(getCachedMessages(context));
    Set<String> cachedMessagesSet = new HashSet<>(cachedMessages);
    String messageString = new String(message.getContent());
    if (!cachedMessagesSet.contains(messageString)) {
        cachedMessages.add(0, new String(message.getContent()));
        getSharedPreferences(context)
                .edit()
                .putString(KEY_CACHED_MESSAGES, new Gson().toJson(cachedMessages))
                .apply();
    }
}

/**
 * Removes a message string from {@link SharedPreferences}.
 * @param context The context.
 * @param message The Message whose payload (as string) is removed from SharedPreferences.
 */
static void removeLostMessage(Context context, Message message) {
    ArrayList<String> cachedMessages = new ArrayList<>(getCachedMessages(context));
    cachedMessages.remove(new String(message.getContent()));
    getSharedPreferences(context)
            .edit()
            .putString(KEY_CACHED_MESSAGES, new Gson().toJson(cachedMessages))
            .apply();
}

/**
 * Gets the SharedPReferences object that is used for persisting data in this application.
 *
 * @param context The context.
 * @return The single {@link SharedPreferences} instance that can be used to retrieve and 
   modify  values.
 */
static SharedPreferences getSharedPreferences(Context context) {
    return context.getSharedPreferences(
            context.getApplicationContext().getPackageName(),
            Context.MODE_PRIVATE);
  }
}

如何获取信标的 UUID 和距离以及可以在哪里应用代码。我之前没有使用信标的经验。

标签: androidandroid-studiogoogle-beacon-platformgoogle-nearby-messages

解决方案


问题中显示的代码设置了对 BLE 信标附近消息的后台订阅,这些消息由 PendingIntent 传递给服务:Nearby.Messages.subscribe(mGoogleApiClient, getPendingIntent(), options).setResultCallback(...);

遗憾的是,Google Nearby 不支持向后台订阅提供信号强度或距离估计。您只能对前台订阅执行此操作:

RSSI 和距离回调 除了找到和丢失的回调之外,前台订阅可以在 Nearby 具有与消息关联的 BLE 信号的新信息时更新您的 MessageListener。注意:这些额外的回调目前仅针对 BLE 信标消息(附件和信标 ID)传递。 这些额外的回调不会传递到后台 (PendingIntent) 订阅。

请参阅https://developers.google.com/nearby/messages/android/advanced

如果您想获得信号强度和距离估计值,您必须使用 Google Nearby 重写您的代码以使用仅前台订阅。仅当您的应用程序在前台时才会交付结果。

MessageListener messageListener = new MessageListener() {
    @Override
    public void onFound(final Message message) {
      Log.i(TAG, "Found message: " + message);
    }
   @Override
    public void onBleSignalChanged(final Message message, final BleSignal bleSignal) {
      Log.i(TAG, "Message: " + message + " has new BLE signal information: " + bleSignal);
    }
    @Override
    public void onDistanceChanged(final Message message, final Distance distance) {
      Log.i(TAG, "Distance changed, message: " + message + ", new distance: " + distance);
    }
    @Override
    public void onLost(final Message message) {
      Log.i(TAG, "Lost message: " + message);
    }
};
Nearby.getMessagesClient(this).subscribe(messageListener, options);

上面的链接显示了如何做到这一点的完整示例。


推荐阅读