2016-09-18 122 views
0

首先,我只想指出,我现在只学习了几个星期的Android,所以请原谅任何破烂的代码。我试图制作一个应用程序,它将简单列出附近的蓝牙低功耗设备(现在在日志中)并最终连接到它们。Android 6.0上的蓝牙位置权限

开始这个项目几个星期后,我碰到了一个砖墙,它具有成功执行蓝牙扫描所需的Android 6.0位置权限。下面的代码是我目前有:

package com.benstechtips.bluetoothletest4; 

import android.annotation.TargetApi; 
import android.app.Activity; 
import android.bluetooth.BluetoothAdapter; 
import android.bluetooth.BluetoothDevice; 
import android.bluetooth.BluetoothGatt; 
import android.bluetooth.BluetoothGattCallback; 
import android.bluetooth.BluetoothGattCharacteristic; 
import android.bluetooth.BluetoothGattService; 
import android.bluetooth.BluetoothManager; 
import android.bluetooth.BluetoothProfile; 
import android.bluetooth.le.BluetoothLeScanner; 
import android.bluetooth.le.ScanCallback; 
import android.bluetooth.le.ScanFilter; 
import android.bluetooth.le.ScanResult; 
import android.bluetooth.le.ScanSettings; 
import android.content.Context; 
import android.content.Intent; 
import android.content.pm.PackageManager; 
import android.os.Build; 
import android.os.Bundle; 
import android.os.Handler; 
import android.support.v7.app.AppCompatActivity; 
import android.util.Log; 
import android.widget.Toast; 

import java.util.ArrayList; 
import java.util.List; 

@TargetApi(21) 
public class MainActivity extends AppCompatActivity { 

private BluetoothAdapter mBluetoothAdapter; 
private int REQUEST_ENABLE_BT = 1; 
private Handler mHandler; 
private static final long SCAN_PERIOD = 10000; 
private BluetoothLeScanner mLEScanner; 
private ScanSettings settings; 
private List<ScanFilter> filters; 
private BluetoothGatt mGatt; 

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

    mHandler = new Handler(); 
    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { 
     Toast.makeText(this, "BLE Not Supported", Toast.LENGTH_SHORT).show(); 
     finish(); 
    } 
    final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 
    mBluetoothAdapter = bluetoothManager.getAdapter(); 


} 

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

    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { 
     Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 
     startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); 
    } else { 
     if (Build.VERSION.SDK_INT >= 21) { 
      mLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); 
      settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); 
      filters = new ArrayList<ScanFilter>(); 
     } 
     scanLeDevice(true); 
    } 
} 

@Override 
protected void onPause() { 
    super.onPause(); 
    if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) { 
     scanLeDevice(false); 
    } 
} 

@Override 
protected void onDestroy() { 
    if (mGatt == null) { 
     return; 
    } 
    mGatt.close(); 
    mGatt = null; 
    super.onDestroy(); 
} 

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    if (requestCode == REQUEST_ENABLE_BT) { 
     if (resultCode == Activity.RESULT_CANCELED) { 
      //Bluetooth not enabled. 
      finish(); 
      return; 
     } 
    } 
    super.onActivityResult(requestCode, resultCode, data); 
} 

private void scanLeDevice(final boolean enable) { 
    if (enable) { 
     mHandler.postDelayed(new Runnable() { 
      @Override 
      public void run() { 
       Log.i("Scan Status", "Stopping Scan..."); 
       if (Build.VERSION.SDK_INT < 21) { 
        mBluetoothAdapter.stopLeScan(mLeScanCallback); 
       } else { 
        mLEScanner.stopScan(mScanCallback); 

       } 
      } 
     }, SCAN_PERIOD); 
     if (Build.VERSION.SDK_INT < 21) { 
      Log.i("Scan Status", "Starting Scan..."); 
      mBluetoothAdapter.startLeScan(mLeScanCallback); 
     } else { 
      mLEScanner.startScan(filters, settings, mScanCallback); 
     } 
    } else { 
     Log.i("Scan Status", "Stopping Scan..."); 
     if (Build.VERSION.SDK_INT < 21) { 
      mBluetoothAdapter.stopLeScan(mLeScanCallback); 
     } else { 
      mLEScanner.stopScan(mScanCallback); 
     } 
    } 
} 

private ScanCallback mScanCallback = new ScanCallback() { 
    @Override 
    public void onScanResult(int callbackType, ScanResult result) { 
     Log.i("callbackType", String.valueOf(callbackType)); 
     Log.i("result", result.toString()); 
     BluetoothDevice btDevice = result.getDevice(); 
     //connectToDevice(btDevice); 
    } 

    @Override 
    public void onBatchScanResults(List<ScanResult> results) { 
     for (ScanResult sr : results) { 
      Log.i("ScanResult - Results", sr.toString()); 
     } 
    } 

    @Override 
    public void onScanFailed(int errorCode) { 
     Log.e("Scan Failed", "Error Code: " + errorCode); 
    } 
}; 

private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { 
      @Override 
      public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { 
       runOnUiThread(new Runnable() { 
        @Override 
        public void run() { 
         Log.i("onLeScan", device.toString()); 
         //connectToDevice(device); 
        } 
       }); 
      } 
}; 

public void connectToDevice(BluetoothDevice device) { 
    if (mGatt == null) { 
     mGatt = device.connectGatt(this, false, gattCallback); 
     scanLeDevice(false);// will stop after first device detection 
    } 
} 

private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() { 
    @Override 
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 
     Log.i("onConnectionStateChange", "Status: " + status); 
     switch (newState) { 
      case BluetoothProfile.STATE_CONNECTED: 
       Log.i("gattCallback", "STATE_CONNECTED"); 
       gatt.discoverServices(); 
       break; 
      case BluetoothProfile.STATE_DISCONNECTED: 
       Log.e("gattCallback", "STATE_DISCONNECTED"); 
       break; 
      default: 
       Log.e("gattCallback", "STATE_OTHER"); 
     } 

    } 

    @Override 
    public void onServicesDiscovered(BluetoothGatt gatt, int status) { 
     List<BluetoothGattService> services = gatt.getServices(); 
     Log.i("onServicesDiscovered", services.toString()); 
     gatt.readCharacteristic(services.get(1).getCharacteristics().get 
       (0)); 
    } 

    @Override 
    public void onCharacteristicRead(BluetoothGatt gatt, 
            BluetoothGattCharacteristic 
              characteristic, int status) { 
     Log.i("onCharacteristicRead", characteristic.toString()); 
     gatt.disconnect(); 
    } 
}; 

现在,据我所知,在6.0版本之前的Android版本,该代码会工作,并显示搜索到的设备的名称(我暂时禁用了连接到设备的功能)。但是,在Android 6.0中,我得到以下错误:

java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results 

我明白,这是由于我的应用程序没有居民点的权限。虽然我在我的AndroidManifest.xml文件中有以下内容,但我知道我需要请求用户的许可。

<?xml version="1.0" encoding="utf-8"?> 

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

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> 

<application 
    android:allowBackup="true" 
    android:icon="@mipmap/ic_launcher" 
    android:label="@string/app_name" 
    android:supportsRtl="true" 
    android:theme="@style/AppTheme"> 
    <activity android:name=".MainActivity"> 
     <intent-filter> 
      <action android:name="android.intent.action.MAIN" /> 

      <category android:name="android.intent.category.LAUNCHER" /> 
     </intent-filter> 
    </activity> 
</application> 

但我的问题是,如何申请许可的用户?

回答

1

我遇到了同样的问题!这是Marshmallow最近添加的安全功能。以下是请求许可的样子:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
     if (this.checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 
      requestPermissions(new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, 
        PERMISSION_REQUEST_COARSE_LOCATION); 
     } 
} 

这样

private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1; 

我只是选择了粗略的位置。据我所知,选择哪一个并不重要。你可以把它放在你的主要活动的onCreate中。希望这可以帮助!