Nearby

repository·main·Indexed 21 days ago

https://github.com/google/nearby

A suite of connectivity-focused projects for cross-device experiences. It includes Nearby Connections, a medium-agnostic peer-to-peer networking protocol supporting Bluetooth and Wi-Fi; Nearby Presence, which adds identity models and proximity detection; and Nearby for Embedded Systems, a lightweight Fast Pair implementation. The project provides support for Android, ChromeOS, Windows, iOS, and macOS, with specific implementations for data transfer, device discovery, and Quick Share integration on Android.

Tokens
4K
Snippets
11
Records
17
Agent score
76%

What's inside Nearby

  1. Overview of Nearby projects

    main

    Nearby is a collection of connectivity projects designed to enable cross-device experiences. It consists of three primary project areas:

    1. Nearby Connections: A peer-to-peer networking API for real-time device discovery, connection, and data exchange, functioning independently of traditional network connectivity.
    2. Nearby Presence: An extension of Nearby Connections that adds an extensible identity model for authentication, restricted visibility, resource management for system health, and proximity detection via sensor fusion.
    3. Nearby for Embedded Systems: A lightweight implementation of Fast Pair specifically designed for embedded systems.
  2. What is Nearby Connections?

    main

    Nearby Connections is a high-level, medium-agnostic protocol built on top of Bluetooth and Wi-Fi. It functions like a socket that allows devices to advertise, scan, and connect regardless of the underlying shared medium.

    Key features include:

    • Automatic Medium Upgrading: Once connected, devices negotiate and attempt to upgrade to the medium with the highest bandwidth (e.g., moving from Bluetooth to Wi-Fi).
    • Security: Connections are encrypted, reliable, and fully duplex.
    • Payload Support: It natively supports BYTE, FILE, and STREAM payloads, which are automatically chunked, transferred, and recombined on the receiving device.
  3. Understand the Nearby Share sample app architecture

    main

    The sample app demonstrates how to bind to and use the Nearby Share slice. The implementation is centered around two main components:

    • MainViewModel: Manages the business logic for processing and binding to the slice, and handles the slice's lifecycle.
    • MainActivity: Hosts the MainView composable. It is responsible for providing the Intent used to fill in the slice's PendingIntent action, which contains the data to be sent via Nearby Share.
  4. Check if Quick Share (Google) is supported on an Android device

    main

    Before attempting to use Quick Share launch intents, verify that the device supports them. You can do this by querying the PackageManager for the com.google.android.gms.SHARE_NEARBY intent. If queryIntentActivities() returns a non-empty list, the device can handle the intent.

    val shareIntent = Intent("com.google.android.gms.SHARE_NEARBY")
            .setType("text/plain")
            .putExtra(Intent.EXTRA_TEXT, "Hello Nearby");
    
    val packageManager = context.packageManager;
    val activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    // If the activities list is not empty, the intent can be handled and the intent can be called!
  5. Build Nearby Connections for Linux

    main

    Building for Linux uses the Bazel build system.

    Note: Linux currently has no mediums implemented.

    Prerequisites

    • Bazel
    • clang with C++17 support
    • OpenSSL libcrypto.so (-lssl and -lcrypto)

    Build Command

    To build the Nearby Connection Core library, run:

    CC=clang CXX=clang++ bazel build -s --check_visibility=false //connections:core  --spawn_strategy=standalone --verbose_failures
  6. Use the Quick Share Slice API to show nearby targets

    main

    The Nearby module in GMSCore provides a Slice at content://com.google.android.gms.nearby.sharing/scan. This allows your app to display live data of nearby Quick Share targets. Clicking a target in the slice opens the main Quick Share screen to begin the transfer.

    Implementation Steps

    1. Derive the Slice URI: content://com.google.android.gms.nearby.sharing/scan.
    2. Get an instance of SliceViewManager.
    3. Register a callback using registerSliceCallback to handle incoming Slice updates.
    4. Crucial: Bind the slice using bindSlice(sliceUri) after registering the callback to avoid race conditions.

    When parsing the slice, look for items with LIST_ITEM and ACTIVITY hints. Target device names are found in items with the TEXT format and TITLE hint. Actions are found in items with SHORT_CUT and TITLE hints.

    // Derive the Slice URI.
    val sliceUri = Uri.parse("content://com.google.android.gms.nearby.sharing/scan")
    // Get the SliceViewManager
    val sliceManager = SliceViewManager.getInstance(context)
    // Pin the slice and register your callback.
    sliceManager.registerSliceCallback(sliceUri, { slice: Slice? ->
        if (slice == null) {
            return
        }
        for (targetItem in slice.items.reversed()) {
          // Each row containing a target has the hints LIST_ITEM and ACTIVITY.
          if (!(targetItem.format == SLICE && targetItem.hints.containsAll(listOf(LIST_ITEM, ACTIVITY)))) {
            continue
          }
          val targetSlice = targetItem.slice
          var deviceName: String? = null
          var action: PendingIntent? = null
          var profileIcon: IconCompat? = null
    
          for (item in targetSlice.items) {
            // The slice item of the target's device name contains the TITLE hint.
            if (item.format == TEXT && item.hints.contains(TITLE)) {
              deviceName = item.text.toString()
            }
            // The slice item of the target action contains the SHORTCUT and TITLE hints.
            if (item.format == ACTION && item.hints.containsAll(listOf(SHORTCUT, TITLE))) {
              action = item.action
    
              val iconSlice: Slice? = item.slice
              if (iconSlice != null) {
                for (iconitem in iconSlice.items) {
                  // The target's icon is indicated by the IMAGE slice item format and the NO_TINT hint.
                  if (iconitem.format == IMAGE && iconitem.hints.contains(NO_TINT)) {
                    profileIcon = iconitem.icon
                  }
                }
              }
            }
          }
          // Returns null if the data parsed from the slice is incomplete.
          if (deviceName == null || action == null || profileIcon == null) {
            continue
          }
        }
    })
    // Remember to bind the slice after you pin it to avoid race conditions!
    val slice = sliceManager.bindSlice(sliceUri)
  7. Build Nearby Connections for macOS and iOS

    main

    Building for macOS and iOS uses the Swift Package Manager.

    Note: The only supported medium for these platforms is Wi-Fi LAN.

    Prerequisites

    • Xcode (available from the Apple App Store)
    • Google Protobuf Compiler (protoc). You can install this via Homebrew using brew install protobuf.

    Build Command

    To build the library, run:

    swift build
  8. Set up the Nearby Connections iOS Example app

    main

    To run the Nearby Connections sample application on iOS, follow these steps:

    1. Clone the repository: Ensure you include all submodules required for the project.
    2. Open in Xcode: Use the provided .xcodeproj file to open the project.
    3. Configure Signing: You must select a development team in Xcode to run the app on a physical device or simulator. Navigate to iOS Example > iOS Example under Targets > Signing & Capabilities and select your team from the team drop-down menu.
    git clone --recurse-submodules https://github.com/google/nearby.git
    open nearby/connections/swift/NearbyConnections/Example/iOS\ Example.xcodeproj
  9. Build the Nearby Share sample app

    main

    To build the Nearby Share sample app, navigate to the example directory from the repository root and use the Gradle wrapper. It is recommended to use Gradle 8.0 or higher.

    Follow these steps:

    1. Change directory to sharing/android/example.
    2. Generate the Gradle wrapper.
    3. Run the build command.
    $ cd sharing/android/example
    $ gradle wrapper
    $ ./gradlew build
  10. Send a folder via Quick Share

    main

    To send entire folders, use the custom intent com.google.android.gms.nearby.SEND_FOLDER. This requires a ContentProvider to supply the file metadata and a specific intent structure.

    Mandatory Extras

    • com.google.android.gms.nearby.PARENT_FOLDER: A string specifying the destination folder under /sdcard/Download/Quick Share. Use an empty string if not desired.
    • com.google.android.gms.nearby.FILE_COUNT: The number of files in the folder. Defaults to 0 if not supplied.
    • com.google.android.gms.nearby.SEND_FOLDER_CONTENT_URI: The URI of your ContentProvider containing the folder contents. The transfer will fail if this is missing.
    val sendFolderIntent = Intent("com.google.android.gms.nearby.SEND_FOLDER")
            .putExtra("com.google.android.gms.nearby.PARENT_FOLDER", /* The folder to save to under /sdcard/Download/Quick Share */)
            .putExtra("com.google.android.gms.nearby.FILE_COUNT", /* The file count in the folder */)
            .putExtra("com.google.android.gms.nearby.SEND_FOLDER_CONTENT_URI", /* Your content provider URI here */)