无法启动发现(Android Nearby Connections API v2)

时间:2017-10-17 17:50:35

标签: java android google-client

我试图让我的设备同时做广告和发现,有点像Varun使用Nearby Connections API v2描述的here

问题是我能够在切换开关时开始做广告,但startDiscovery()方法总是返回失败。此外,onResult()回调中收到的状态有null条消息。这是我的代码:

    public class HomeActivity extends AppCompatActivity implements HomeMvp.View{

        private HomePresenter presenter;
        private HomeVH viewHolder;
        private HomeActionBarVH actionBarVH;

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.home_activity);
            viewHolder = new HomeVH(findViewById(android.R.id.content));
            setUpViews();

            presenter = new HomePresenter(this);
            presenter.onViewCreate();
        }

        @Override
        protected void onStart() {
            super.onStart();
            presenter.startGoogleApiClient();
        }

        private void setUpActionBar() {
            ActionBar actionBar = getSupportActionBar();
            if (actionBar == null) {
                return;
            }
            actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
            actionBar.setDisplayShowCustomEnabled(true);
            actionBar.setCustomView(R.layout.home_action_bar);

            actionBarVH = new HomeActionBarVH(actionBar.getCustomView());
            actionBarVH.startSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    if (isChecked) {
                        showToastShort(getResources().getString(R.string.toast_starting_services));
                        presenter.startAdvertising();
                        presenter.startDiscovery();
                    } else {
                        showToastShort(getResources().getString(R.string.toast_stopping_services));
                        presenter.stopAdvertising();
                        presenter.stopDiscovery();
                    }
                }
            });
            actionBarVH.startSwitch.setEnabled(false);

            actionBarVH.shareButton.setOnClickListener(getShareButtonClickListener());
        }

        private View.OnClickListener getShareButtonClickListener() {
            return new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent filePickerIntent = new Intent(Intent.ACTION_GET_CONTENT);
                    filePickerIntent.setType("*/*");
                    startActivityForResult(filePickerIntent, Constants.PICK_FILE);
                }
            };
        }

        private void setUpViews() {
            setUpActionBar();
            HomeRvAdapter adapter = new HomeRvAdapter(getCuratedDeviceList());
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
            viewHolder.deviceListRv.setLayoutManager(linearLayoutManager);
            viewHolder.deviceListRv.setAdapter(adapter);
            viewHolder.googleApiStatus.setText(getResources().getString(R.string.msg_please_wait));
        }

        private ArrayList<BaseRvData> getCuratedDeviceList() {
            ArrayList<BaseRvData> deviceList = new ArrayList<>();
            deviceList.add(new EmptyListData());
            return deviceList;
        }

        @Override
        protected void onStop() {
            super.onStop();
            presenter.stopGoogleApiClient();
        }

        @Override
        protected void onDestroy() {
            presenter.onViewDestroy();
            super.onDestroy();
        }

        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == Constants.PICK_FILE && resultCode == RESULT_OK) {
                if (data.getData() != null) {
                    String filePath = data.getData().getPath();
                    presenter.onNewFileSelected(filePath);
                    showToastShort(filePath);
                }
            }
        }

        @Override
        public void onInitialDataLoaded() {

        }

        @Override
        public void onGoogleApiConnected() {
            viewHolder.googleApiStatus.setText(getResources().getString(R.string.msg_ready));
            actionBarVH.startSwitch.setEnabled(true);
        }

        @Override
        public void onGoogleApiConnectionFailed() {
            viewHolder.googleApiStatus.setText(getResources().getString(R.string.msg_please_wait));
            actionBarVH.startSwitch.setEnabled(false);
        }

        @Override
        public void onStartedAdvertising() {
            viewHolder.advertiseStatus.setText(R.string.msg_advert_service_started);
        }

        @Override
        public void onAdvertisingFailed() {
            viewHolder.advertiseStatus.setText(R.string.msg_advert_service_failed);
        }

        @Override
        public void onAdvertisingStopped() {
            viewHolder.advertiseStatus.setText(R.string.msg_advert_service_stopped);
        }

        @Override
        public void onDiscoveryStarted() {
            viewHolder.discoveryStatus.setText(R.string.msg_discovery_started);
        }

        @Override
        public void onDiscoveryFailure() {
            viewHolder.discoveryStatus.setText(R.string.msg_discovery_failed);
        }

        @Override
        public void onDiscoveryStopped() {
            viewHolder.discoveryStatus.setText(R.string.msg_discovery_stopped);
        }

        @Override
        public void onConnectionInitiated(String msg) {
            showToastShort(msg);
        }

        @Override
        public void onNewConnection(String msg) {
            showToastShort(msg);
        }

        @Override
        public void onDisconnected(String msg) {
            showToastShort(msg);
        }

        @Override
        public void showToastShort(String msg) {
            Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        }
    }


    class HomePresenter implements GoogleApiClient.ConnectionCallbacks,
            GoogleApiClient.OnConnectionFailedListener, HomeMvp.Presenter {

        private boolean isGoogleApiConnected;
        private HomeMvp.View activityInterface;
        private GoogleApiClient googleApiClient;
        private EndpointDiscoveryCallback endpointDiscoveryCallback = new EndpointDiscoveryCallback() {
            @Override
            public void onEndpointFound(String endpointId,
                                        DiscoveredEndpointInfo discoveredEndpointInfo) {
                // An endpoint was found!
                // Connect to the end point
                // TODO make a dialog to decide whether to connect ??
                Nearby.Connections.requestConnection(googleApiClient, "listener", endpointId,
                        connectionLifecycleCallback)
                        .setResultCallback(new ResultCallback<Status>() {
                            @Override
                            public void onResult(@NonNull Status status) {
                                if (status.isSuccess()) {
                                    // Successfully requested for a connection
                                } else {
                                    // failed to request the connection
                                    // TODO view should show a msg here
                                }
                            }
                        });
            }

            @Override
            public void onEndpointLost(String endpointId) {
                // A previously discovered endpoint has gone away.
                // TODO update view here along with cleanup
            }
        };

        private final ConnectionLifecycleCallback connectionLifecycleCallback =
                new ConnectionLifecycleCallback() {
                    @Override
                    public void onConnectionInitiated(String endpointId, ConnectionInfo connectionInfo) {
                        onEndpointConnectionInitiated(endpointId, connectionInfo);
                    }

                    @Override
                    public void onConnectionResult(String endpointId, ConnectionResolution connectionResolution) {
                        switch (connectionResolution.getStatus().getStatusCode()) {
                            case ConnectionsStatusCodes.STATUS_OK:
                                onNewEndpointConnection(endpointId, connectionResolution);
                                break;
                            case ConnectionsStatusCodes.STATUS_CONNECTION_REJECTED:
                                onNewEndpointConnectionReject(endpointId, connectionResolution);
                                break;
                        }
                    }

                    @Override
                    public void onDisconnected(String endpointId) {
                        onEndpointDisconnected(endpointId);
                    }
                };

        private void acceptConnection(String endpointId, ConnectionInfo connectionInfo) {
            Nearby.Connections.acceptConnection(googleApiClient, endpointId, null);
        }

        HomePresenter(HomeMvp.View activityInterface) {
            this.activityInterface = activityInterface;
        }

        @Override
        public void onViewCreate() {
            isGoogleApiConnected = false;

            googleApiClient = new GoogleApiClient.Builder((Context) activityInterface)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(Nearby.CONNECTIONS_API).build();

            activityInterface.onInitialDataLoaded();
        }

        @Override
        public void onViewDestroy() {
            stopAdvertising();
            stopDiscovery();
        }

        @Override
        public void onConnected(@Nullable Bundle bundle) {
            isGoogleApiConnected = true;
            activityInterface.onGoogleApiConnected();
        }

        @Override
        public void onConnectionSuspended(int i) {

        }

        @Override
        public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
            isGoogleApiConnected = false;
            activityInterface.onGoogleApiConnectionFailed();
        }

        /**
         * Sets the device to advertising mode. It will broadcast to other devices in discovery mode.
         * Either {@link #onAdvertisingStarted()} or {@link #onAdvertisingFailed(Status)} will be called once
         * we've found out if we successfully entered this mode.
         */
        @Override
        public void startAdvertising() {
            if (isGoogleApiConnected && googleApiClient != null) {
                Logger.d(HomeConstants.TAG, HomeConstants.STARTING_ADVERTISING);
                Nearby.Connections.startAdvertising(
                        googleApiClient, "NickName", Constants.NEARBY_API_SERVICE_ID,
                        connectionLifecycleCallback, new AdvertisingOptions(Constants.CLUSTER_STRATEGY))
                        .setResultCallback(new ResultCallback<Connections.StartAdvertisingResult>() {
                            @Override
                            public void onResult(@NonNull Connections.StartAdvertisingResult startAdvertisingResult) {
                                if (startAdvertisingResult.getStatus().isSuccess()) {
                                    onAdvertisingStarted();
                                } else {
                                    onAdvertisingFailed(startAdvertisingResult.getStatus());
                                }
                            }
                        });
            }
        }

        @Override
        public void stopAdvertising() {
            if (isGoogleApiConnected && googleApiClient != null) {
                Logger.d(HomeConstants.TAG, HomeConstants.STOPPING_ADVERTISING);
                Nearby.Connections.stopAdvertising(googleApiClient);
                activityInterface.onAdvertisingStopped();
            }
        }

        /**
         * Sets the device to discovery mode. It will now listen for devices in advertising mode. Either
         * {@link #onDiscoveryStarted()} ()} or {@link #onDiscoveryFailed(Status)} ()} will be called once we've
         * found out if we successfully entered this mode.
         */
        @Override
        public void startDiscovery() {
            if (isGoogleApiConnected && googleApiClient != null && endpointDiscoveryCallback != null) {
                Logger.d(HomeConstants.TAG, HomeConstants.STARTING_DISCOVERY);
                Nearby.Connections.startDiscovery(googleApiClient, Constants.NEARBY_API_SERVICE_ID,
                        endpointDiscoveryCallback, new DiscoveryOptions(Constants.CLUSTER_STRATEGY))
                        .setResultCallback(new ResultCallback<Status>() {
                            @Override
                            public void onResult(@NonNull Status status) {
                                if (status.isSuccess()) {
                                    onDiscoveryStarted();
                                } else {
                                    onDiscoveryFailed(status);
                                }
                            }
                        });
            }
        }

        @Override
        public void stopDiscovery() {
            if (isGoogleApiConnected && googleApiClient != null) {
                Logger.d(HomeConstants.TAG, HomeConstants.STOPPING_DISCOVERY);
                Nearby.Connections.stopDiscovery(googleApiClient);
                activityInterface.onDiscoveryStopped();
            }
        }

        @Override
        public void onNewFileSelected(String filePath) {
            // add the new file to our list of files
        }

        @Override
        public void startGoogleApiClient() {
            if (googleApiClient != null && !googleApiClient.isConnected()) {
                googleApiClient.connect();
                Logger.d(HomeConstants.TAG, HomeConstants.GOOGLE_API_CONNECTED);
            } else {
                Logger.d(HomeConstants.TAG, HomeConstants.GOOGLE_API_NULL_OR_CONNECTED);
            }
        }

        @Override
        public void stopGoogleApiClient() {
            if (googleApiClient != null && googleApiClient.isConnected()) {
                googleApiClient.disconnect();
            }
        }

        // ======================= private methods ================================

        private void onAdvertisingStarted() {
            Logger.d(HomeConstants.TAG, HomeConstants.NEARBY_RESULT_ADVERT_SUCCESS);
            activityInterface.onStartedAdvertising();
        }

        private void onAdvertisingFailed(Status status) {
            if (status.isCanceled()) {
                Logger.d(HomeConstants.TAG, HomeConstants.NEARBY_RESULT_ADVERT_CANCELLED);
            } else if (status.isInterrupted()) {
                Logger.d(HomeConstants.TAG, HomeConstants.NEARBY_RESULT_ADVERT_INTERRUPTED);
            }
            activityInterface.onAdvertisingFailed();
        }

        private void onDiscoveryStarted() {
            Logger.d(HomeConstants.TAG, HomeConstants.NEARBY_RESULT_DISCOVERY_SUCCESS);
            activityInterface.onDiscoveryStarted();
        }

        private void onDiscoveryFailed(Status status) {
            Logger.d(HomeConstants.TAG, HomeConstants.NEARBY_RESULT_DISCOVERY_FAILURE);
            Logger.d(HomeConstants.TAG, status.getStatusMessage());
            activityInterface.onDiscoveryFailure();
        }

        private void onEndpointConnectionInitiated(String endpointId, ConnectionInfo connectionInfo) {
            activityInterface.onConnectionInitiated("Connection initiated " + endpointId);
            // TODO auto accept connection here
            acceptConnection(endpointId, connectionInfo);
        }

        private void onNewEndpointConnection(String endpointId, ConnectionResolution connectionResolution) {
            // connected now transfer data
            activityInterface.onNewConnection("Connected with " + endpointId);
            Logger.d(HomeConstants.TAG,
                    HomeConstants.NEARBY_NEW_CONNECTION + endpointId);
        }

        private void onNewEndpointConnectionReject(String endpointId, ConnectionResolution connectionResolution) {
            Logger.d(HomeConstants.TAG, HomeConstants.NEARBY_CONNECTION_REJECTED + endpointId);
        }

        private void onEndpointDisconnected(String endpointId) {
            activityInterface.onDisconnected("Disconnected " + endpointId);
        }
    }

我无法理解为什么行为是这样的。此外,每当我存在活动时,我都会收到错误:

Unable to destroy activity {com.forkit.arka.rapidsync/com.forkit.arka.rapidsync.home.HomeActivity}: java.lang.IllegalStateException: GoogleApiClient is not connected yet.

修改

以下是我的明确权限:

        <uses-permission android:name="android.permission.BLUETOOTH" />
            <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
            <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
            <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
            <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

修改

无法启动发现问题似乎已得到解决。问题是ACCESS_COARSE_LOCATION是一个危险的权限,并且仅在清单中使用它是不够的。我们必须明确询问用户。

0 个答案:

没有答案