การจับคู่อุปกรณ์ที่ใช้ร่วมกัน

สำหรับอุปกรณ์ที่ใช้ Android 8.0 (API ระดับ 26) ขึ้นไป อุปกรณ์ที่ใช้ร่วมกัน การจับคู่จะสแกนหาบลูทูธหรือ Wi-Fi ของอุปกรณ์ที่อยู่ใกล้เคียงในนามของ โดยไม่ต้องติดตั้ง ACCESS_FINE_LOCATION สิทธิ์ วิธีนี้จะช่วยเพิ่มการปกป้องความเป็นส่วนตัวของผู้ใช้ให้ได้สูงสุด ใช้วิธีนี้เพื่อ กำหนดค่าเริ่มต้นของอุปกรณ์ที่ใช้ร่วมกัน เช่น สามารถใช้ BLE สมาร์ทวอทช์ นอกจากนี้ การจับคู่อุปกรณ์ที่ใช้ร่วมกันจะต้องใช้บริการตำแหน่งเพื่อ ได้

การจับคู่อุปกรณ์ที่ใช้ร่วมกันจะไม่สร้างการเชื่อมต่อด้วยตนเองหรือเปิดใช้ การสแกนอย่างต่อเนื่อง แอปสามารถใช้ API การเชื่อมต่อบลูทูธหรือ Wi-Fi เพื่อ สร้างการเชื่อมต่อ

หลังจากจับคู่อุปกรณ์แล้ว อุปกรณ์จะสามารถใช้ REQUEST_COMPANION_RUN_IN_BACKGROUND และ REQUEST_COMPANION_USE_DATA_IN_BACKGROUND สิทธิ์ในการเริ่มแอปจากเบื้องหลัง แอปยังสามารถใช้ REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND สิทธิ์ในการเริ่มบริการที่ทำงานอยู่เบื้องหน้าจากเบื้องหลัง

ผู้ใช้สามารถเลือกอุปกรณ์จากรายการและให้สิทธิ์แก่แอปในการเข้าถึงได้ อุปกรณ์ ระบบจะเพิกถอนสิทธิ์เหล่านี้หากคุณถอนการติดตั้งแอปหรือโทร disassociate() แอปที่ใช้ร่วมกันมีหน้าที่ล้างการเชื่อมโยงของตนเองหากผู้ใช้ ก็ไม่จำเป็นอีกต่อไป เช่น เมื่อออกจากระบบหรือนำอุปกรณ์ที่เชื่อมโยงออก

ใช้การจับคู่อุปกรณ์ที่ใช้ร่วมกัน

ส่วนนี้จะอธิบายวิธีใช้CompanionDeviceManagerเพื่อจับคู่ แอปพร้อมอุปกรณ์ที่ใช้ร่วมกันผ่านบลูทูธ, BLE และ Wi-Fi

ระบุอุปกรณ์ที่ใช้ร่วมกัน

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีเพิ่ม <uses-feature> แจ้งว่า Manifest ข้อมูลนี้จะบอกระบบว่าแอปของคุณต้องการตั้งค่าโฆษณาที่แสดงร่วม อุปกรณ์

<uses-feature android:name="android.software.companion_device_setup"/>

แสดงรายการอุปกรณ์ตาม DeviceFilter

คุณสามารถแสดงอุปกรณ์ที่ใช้ร่วมกันในระยะใกล้ทั้งหมดที่ตรงกับ DeviceFilter ที่คุณระบุ (แสดงในรูปที่ 1) หากต้องการจำกัดการสแกนไว้เพียงรายการเดียว คุณสามารถ setSingleDevice() เป็น true (แสดงในรูปที่ 2)

การจับคู่อุปกรณ์ที่ใช้ร่วมกัน
รูปที่ 1 การจับคู่อุปกรณ์ที่ใช้ร่วมกัน
การจับคู่อุปกรณ์เดียว
รูปที่ 2 การจับคู่อุปกรณ์เดียว

ต่อไปนี้คือคลาสย่อยของ DeviceFilter ที่ สามารถระบุใน AssociationRequest:

คลาสย่อยทั้ง 3 คลาสมีเครื่องมือสร้างที่ช่วยให้การกำหนดค่าตัวกรองมีประสิทธิภาพยิ่งขึ้น ในตัวอย่างต่อไปนี้ อุปกรณ์จะสแกนหาอุปกรณ์บลูทูธที่มี BluetoothDeviceFilter

Kotlin

val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder()
        // Match only Bluetooth devices whose name matches the pattern.
        .setNamePattern(Pattern.compile("My device"))
        // Match only Bluetooth devices whose service UUID matches this pattern.
        .addServiceUuid(ParcelUuid(UUID(0x123abcL, -1L)), null)
        .build()

Java

BluetoothDeviceFilter deviceFilter = new BluetoothDeviceFilter.Builder()
        // Match only Bluetooth devices whose name matches the pattern.
        .setNamePattern(Pattern.compile("My device"))
        // Match only Bluetooth devices whose service UUID matches this pattern.
        .addServiceUuid(new ParcelUuid(new UUID(0x123abcL, -1L)), null)
        .build();

ตั้งค่า DeviceFilter เป็น AssociationRequest เพื่อ CompanionDeviceManager สามารถระบุประเภทอุปกรณ์ที่ต้องการกรอได้

Kotlin

val pairingRequest: AssociationRequest = AssociationRequest.Builder()
        // Find only devices that match this request filter.
        .addDeviceFilter(deviceFilter)
        // Stop scanning as soon as one device matching the filter is found.
        .setSingleDevice(true)
        .build()

Java

AssociationRequest pairingRequest = new AssociationRequest.Builder()
        // Find only devices that match this request filter.
        .addDeviceFilter(deviceFilter)
        // Stop scanning as soon as one device matching the filter is found.
        .setSingleDevice(true)
        .build();

หลังจากแอปเริ่มต้น AssociationRequest ให้เรียกใช้ associate() ใน CompanionDeviceManager ฟังก์ชัน associate() ใช้เวลาใน AssociationRequest และ Callback

Callback แสดงค่า IntentSender ในช่วง onAssociationPending เมื่อ CompanionDeviceManager หาอุปกรณ์ เท่านี้ก็พร้อมเปิดกล่องโต้ตอบความยินยอมของผู้ใช้แล้ว หลังจากผู้ใช้ยืนยันอุปกรณ์แล้ว AssociationInfo ของอุปกรณ์จะส่งคืนใน onAssociationCreated หากแอปไม่พบอุปกรณ์ใดเลย Callback จะแสดง onFailure ด้วยข้อความแสดงข้อผิดพลาด

สำหรับอุปกรณ์ที่ใช้ Android 13 (API ระดับ 33) ขึ้นไป ให้ทำดังนี้

Kotlin

val deviceManager =
  requireContext().getSystemService(Context.COMPANION_DEVICE_SERVICE)

val executor: Executor =  Executor { it.run() }

deviceManager.associate(pairingRequest,
    executor,
    object : CompanionDeviceManager.Callback() {
    // Called when a device is found. Launch the IntentSender so the user
    // can select the device they want to pair with.
    override fun onAssociationPending(intentSender: IntentSender) {
        intentSender?.let {
             startIntentSenderForResult(it, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
        }
    }

    override fun onAssociationCreated(associationInfo: AssociationInfo) {
        // An association is created.
    }

    override fun onFailure(errorMessage: CharSequence?) {
        // To handle the failure.
     }
})

Java

CompanionDeviceManager deviceManager =
        (CompanionDeviceManager) getSystemService(Context.COMPANION_DEVICE_SERVICE);

Executor executor = new Executor() {
            @Override
            public void execute(Runnable runnable) {
                runnable.run();
            }
        };
deviceManager.associate(pairingRequest, new CompanionDeviceManager.Callback() {
    executor,
    // Called when a device is found. Launch the IntentSender so the user can
    // select the device they want to pair with.
    @Override
    public void onDeviceFound(IntentSender chooserLauncher) {
        try {
            startIntentSenderForResult(
                    chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0
            );
        } catch (IntentSender.SendIntentException e) {
            Log.e("MainActivity", "Failed to send intent");
        }
    }

    @Override
    public void onAssociationCreated(AssociationInfo associationInfo) {
        // An association is created.
    }

    @Override
    public void onFailure(CharSequence errorMessage) {
        // To handle the failure.
    });

ในอุปกรณ์ที่ใช้ Android 12L (API ระดับ 32) หรือต่ำกว่า (เลิกใช้งานแล้ว) ให้ทำดังนี้

Kotlin

val deviceManager =
      requireContext().getSystemService(Context.COMPANION_DEVICE_SERVICE)

deviceManager.associate(pairingRequest,
    object : CompanionDeviceManager.Callback() {
        // Called when a device is found. Launch the IntentSender so the user
        // can select the device they want to pair with.
        override fun onDeviceFound(chooserLauncher: IntentSender) {
            startIntentSenderForResult(chooserLauncher,
                SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
        }

        override fun onFailure(error: CharSequence?) {
            // To handle the failure.
        }
    }, null)

Java

CompanionDeviceManager deviceManager =
        (CompanionDeviceManager) getSystemService(Context.COMPANION_DEVICE_SERVICE);
deviceManager.associate(pairingRequest, new CompanionDeviceManager.Callback() {
    // Called when a device is found. Launch the IntentSender so the user can
    // select the device they want to pair with.
    @Override
    public void onDeviceFound(IntentSender chooserLauncher) {
        try {
            startIntentSenderForResult(
                    chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0
            );
        } catch (IntentSender.SendIntentException e) {
            Log.e("MainActivity", "Failed to send intent");
        }
    }

    @Override
    public void onFailure(CharSequence error) {
        // To handle the failure.
    }
}, null);

ระบบจะส่งผลการเลือกผู้ใช้กลับไปยังส่วนย่อยใน onActivityResult() กิจกรรมของคุณ จากนั้นคุณจะเข้าถึงอุปกรณ์ที่เลือกได้

เมื่อผู้ใช้เลือกอุปกรณ์บลูทูธ คาดหวังว่า BluetoothDevice เมื่อผู้ใช้เลือกอุปกรณ์บลูทูธ LE ต้องการ android.bluetooth.le.ScanResult เมื่อผู้ใช้เลือกอุปกรณ์ Wi-Fi จะต้องมี android.net.wifi.ScanResult

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    when (requestCode) {
        SELECT_DEVICE_REQUEST_CODE -> when(resultCode) {
            Activity.RESULT_OK -> {
                // The user chose to pair the app with a Bluetooth device.
                val deviceToPair: BluetoothDevice? =
data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)
                deviceToPair?.let { device ->
                    device.createBond()
                    // Continue to interact with the paired device.
                }
            }
        }
        else -> super.onActivityResult(requestCode, resultCode, data)
    }
}

Java

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    if (resultCode != Activity.RESULT_OK) {
        return;
    }
    if (requestCode == SELECT_DEVICE_REQUEST_CODE && data != null) {
        BluetoothDevice deviceToPair =
data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE);
        if (deviceToPair != null) {
            deviceToPair.createBond();
            // Continue to interact with the paired device.
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

ดูตัวอย่างที่สมบูรณ์

สำหรับอุปกรณ์ที่ใช้ Android 13 (API ระดับ 33) ขึ้นไป ให้ทำดังนี้

Kotlin

private const val SELECT_DEVICE_REQUEST_CODE = 0

class MainActivity : AppCompatActivity() {

    private val deviceManager: CompanionDeviceManager by lazy {
        getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager
    }
    val mBluetoothAdapter: BluetoothAdapter by lazy {
        val java = BluetoothManager::class.java
        getSystemService(java)!!.adapter }
    val executor: Executor =  Executor { it.run() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // To skip filters based on names and supported feature flags (UUIDs),
        // omit calls to setNamePattern() and addServiceUuid()
        // respectively, as shown in the following  Bluetooth example.
        val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder()
            .setNamePattern(Pattern.compile("My device"))
            .addServiceUuid(ParcelUuid(UUID(0x123abcL, -1L)), null)
            .build()

        // The argument provided in setSingleDevice() determines whether a single
        // device name or a list of them appears.
        val pairingRequest: AssociationRequest = AssociationRequest.Builder()
            .addDeviceFilter(deviceFilter)
            .setSingleDevice(true)
            .build()

        // When the app tries to pair with a Bluetooth device, show the
        // corresponding dialog box to the user.
        deviceManager.associate(pairingRequest,
            executor,
            object : CompanionDeviceManager.Callback() {
                // Called when a device is found. Launch the IntentSender so the user
                // can select the device they want to pair with.
                override fun onAssociationPending(intentSender: IntentSender) {
                intentSender?.let {
                    startIntentSenderForResult(it, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
              }
            }

             override fun onAssociationCreated(associationInfo: AssociationInfo) {
                 // AssociationInfo object is created and get association id and the
                 // macAddress.
                 var associationId: int = associationInfo.id
                 var macAddress: MacAddress = associationInfo.deviceMacAddress
             }
             override fun onFailure(errorMessage: CharSequence?) {
                // Handle the failure.
            }
    )

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            SELECT_DEVICE_REQUEST_CODE -> when(resultCode) {
                Activity.RESULT_OK -> {
                    // The user chose to pair the app with a Bluetooth device.
                    val deviceToPair: BluetoothDevice? =
                        data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)
                    deviceToPair?.let { device ->
                        device.createBond()
                        // Maintain continuous interaction with a paired device.
                    }
                }
            }
            else -> super.onActivityResult(requestCode, resultCode, data)
        }
    }
}

Java

class MainActivityJava extends AppCompatActivity {

    private static final int SELECT_DEVICE_REQUEST_CODE = 0;
    Executor executor = new Executor() {
        @Override
        public void execute(Runnable runnable) {
            runnable.run();
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CompanionDeviceManager deviceManager =
            (CompanionDeviceManager) getSystemService(
                Context.COMPANION_DEVICE_SERVICE
            );

        // To skip filtering based on name and supported feature flags,
        // do not include calls to setNamePattern() and addServiceUuid(),
        // respectively. This example uses Bluetooth.
        BluetoothDeviceFilter deviceFilter =
            new BluetoothDeviceFilter.Builder()
                .setNamePattern(Pattern.compile("My device"))
                .addServiceUuid(
                    new ParcelUuid(new UUID(0x123abcL, -1L)), null
                )
                .build();

        // The argument provided in setSingleDevice() determines whether a single
        // device name or a list of device names is presented to the user as
        // pairing options.
        AssociationRequest pairingRequest = new AssociationRequest.Builder()
            .addDeviceFilter(deviceFilter)
            .setSingleDevice(true)
            .build();

        // When the app tries to pair with the Bluetooth device, show the
        // appropriate pairing request dialog to the user.
        deviceManager.associate(pairingRequest, new CompanionDeviceManager.Callback() {
            executor,
           // Called when a device is found. Launch the IntentSender so the user can
           // select the device they want to pair with.
           @Override
           public void onDeviceFound(IntentSender chooserLauncher) {
               try {
                   startIntentSenderForResult(
                       chooserLauncher, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0
                   );
               } catch (IntentSender.SendIntentException e) {
                   Log.e("MainActivity", "Failed to send intent");
               }
           }

          @Override
          public void onAssociationCreated(AssociationInfo associationInfo) {
                 // AssociationInfo object is created and get association id and the
                 // macAddress.
                 int associationId = associationInfo.getId();
                 MacAddress macAddress = associationInfo.getDeviceMacAddress();
          }

          @Override
          public void onFailure(CharSequence errorMessage) {
             // Handle the failure.
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        if (requestCode == SELECT_DEVICE_REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK && data != null) {
                BluetoothDevice deviceToPair = data.getParcelableExtra(
                    CompanionDeviceManager.EXTRA_DEVICE
                );

                if (deviceToPair != null) {
                    deviceToPair.createBond();
                    // ... Continue interacting with the paired device.
                }
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }
}

ในอุปกรณ์ที่ใช้ Android 12L (API ระดับ 32) หรือต่ำกว่า (เลิกใช้งานแล้ว) ให้ทำดังนี้

Kotlin

private const val SELECT_DEVICE_REQUEST_CODE = 0

class MainActivity : AppCompatActivity() {

    private val deviceManager: CompanionDeviceManager by lazy {
        getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // To skip filters based on names and supported feature flags (UUIDs),
        // omit calls to setNamePattern() and addServiceUuid()
        // respectively, as shown in the following  Bluetooth example.
        val deviceFilter: BluetoothDeviceFilter = BluetoothDeviceFilter.Builder()
            .setNamePattern(Pattern.compile("My device"))
            .addServiceUuid(ParcelUuid(UUID(0x123abcL, -1L)), null)
            .build()

        // The argument provided in setSingleDevice() determines whether a single
        // device name or a list of them appears.
        val pairingRequest: AssociationRequest = AssociationRequest.Builder()
            .addDeviceFilter(deviceFilter)
            .setSingleDevice(true)
            .build()

        // When the app tries to pair with a Bluetooth device, show the
        // corresponding dialog box to the user.
        deviceManager.associate(pairingRequest,
            object : CompanionDeviceManager.Callback() {

                override fun onDeviceFound(chooserLauncher: IntentSender) {
                    startIntentSenderForResult(chooserLauncher,
                        SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
                }

                override fun onFailure(error: CharSequence?) {
                    // Handle the failure.
                }
            }, null)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            SELECT_DEVICE_REQUEST_CODE -> when(resultCode) {
                Activity.RESULT_OK -> {
                    // The user chose to pair the app with a Bluetooth device.
                    val deviceToPair: BluetoothDevice? =
                        data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)
                    deviceToPair?.let { device ->
                        device.createBond()
                        // Maintain continuous interaction with a paired device.
                    }
                }
            }
            else -> super.onActivityResult(requestCode, resultCode, data)
        }
    }
}

Java

class MainActivityJava extends AppCompatActivity {

    private static final int SELECT_DEVICE_REQUEST_CODE = 0;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CompanionDeviceManager deviceManager =
            (CompanionDeviceManager) getSystemService(
                Context.COMPANION_DEVICE_SERVICE
            );

        // To skip filtering based on name and supported feature flags,
        // don't include calls to setNamePattern() and addServiceUuid(),
        // respectively. This example uses Bluetooth.
        BluetoothDeviceFilter deviceFilter =
            new BluetoothDeviceFilter.Builder()
                .setNamePattern(Pattern.compile("My device"))
                .addServiceUuid(
                    new ParcelUuid(new UUID(0x123abcL, -1L)), null
                )
                .build();

        // The argument provided in setSingleDevice() determines whether a single
        // device name or a list of device names is presented to the user as
        // pairing options.
        AssociationRequest pairingRequest = new AssociationRequest.Builder()
            .addDeviceFilter(deviceFilter)
            .setSingleDevice(true)
            .build();

        // When the app tries to pair with the Bluetooth device, show the
        // appropriate pairing request dialog to the user.
        deviceManager.associate(pairingRequest,
            new CompanionDeviceManager.Callback() {
                @Override
                public void onDeviceFound(IntentSender chooserLauncher) {
                    try {
                        startIntentSenderForResult(chooserLauncher,
                            SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0);
                    } catch (IntentSender.SendIntentException e) {
                        // failed to send the intent
                    }
                }

                @Override
                public void onFailure(CharSequence error) {
                    // handle failure to find the companion device
                }
            }, null);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (requestCode == SELECT_DEVICE_REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK && data != null) {
                BluetoothDevice deviceToPair = data.getParcelableExtra(
                    CompanionDeviceManager.EXTRA_DEVICE
                );

                if (deviceToPair != null) {
                    deviceToPair.createBond();
                    // ... Continue interacting with the paired device.
                }
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }
}

โปรไฟล์อุปกรณ์ที่ใช้ร่วมกัน

ใน Android 12 (API ระดับ 31) ขึ้นไป แอปที่ใช้ร่วมกันซึ่งจัดการอุปกรณ์ต่างๆ เช่น นาฬิกาสามารถใช้โปรไฟล์อุปกรณ์ที่ใช้ร่วมกันเพื่อปรับปรุงกระบวนการตั้งค่าได้โดย ให้สิทธิ์ที่จำเป็นขณะจับคู่ สำหรับข้อมูลเพิ่มเติม โปรดดู โปรไฟล์อุปกรณ์ที่ใช้ร่วมกัน

ให้แอปที่ใช้ร่วมกันทำงาน

ใน Android 12 (API ระดับ 31) ขึ้นไป คุณสามารถใช้ API เพิ่มเติมเพื่อช่วย แอปที่ใช้ร่วมกันจะยังทํางานต่อไปเมื่ออุปกรณ์ที่ใช้ร่วมกันอยู่ในระยะสัญญาณ API เหล่านี้ช่วยให้คุณทำสิ่งต่อไปนี้ได้

  • ปลุกระบบแอปเมื่ออุปกรณ์ที่ใช้ร่วมกันอยู่ในระยะสัญญาณ

    โปรดดูรายละเอียดที่หัวข้อ CompanionDeviceManager.startObservingDevicePresence()

  • รับประกันว่ากระบวนการของแอปจะยังคงทำงานต่อไปตราบใดที่แอปที่ใช้ร่วมกัน อุปกรณ์จะอยู่ในระยะสัญญาณ

    โปรดดูรายละเอียดที่หัวข้อ CompanionDeviceService.onDeviceAppeared()