蓝牙(BLE)实现自动配对

最近项目需求要做蓝牙自动配对,也就是在首次配对的时候跳过用户输入PIN码。网上有很多分享的如何实现自动配对。
以下做一个记录,方便以后查阅。
注意:
蓝牙开启需要开启蓝牙的相关权限还有 定位 权限
在AndroidManifest.xml中添加
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
并且开启运行时权限;
private void initPermission() {
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
//判断是否需要向用户解释为何要此权限
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.READ_CONTACTS)) {
showMessageOKCancel("你必须允许这个权限,否则无法搜索到BLE设备", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},100);
}
});
return;
}
//请求权限
requestPermissions(new String[]{Manifest.permission.WRITE_CONTACTS},100);
}
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", okListener)
.create()
.show();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 100) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0,PERMISSION_DENIED = -1
//这里进行授权被允许的处理
//可以弹个Toast,感谢用户允许了。
scanLeDevice(true);
Toast.makeText(MainActivity.this, "谢谢!", Toast.LENGTH_SHORT).show();
} else {
//这里进行权限被拒绝的处理,就跳转到本应用的程序管理器
Toast.makeText(MainActivity.this, "请开启位置权限", Toast.LENGTH_SHORT).show();
Intent i = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
String pkg = "com.android.settings";
String cls = "com.android.settings.applications.InstalledAppDetails";
i.setComponent(new ComponentName(pkg, cls));
i.setData(Uri.parse("package:" + getPackageName()));
startActivity(i);
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}

A、获取蓝牙,有两种方式:
/**

  • 两种获取BluetoothAdapter方式
    **/
    //方式一:通过BluetoothManager获取
    BluetoothManager mBm = (BluetoothManager)this.getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = mBm.getAdapter();
    //方式二:通过getDefaultAdapter()获取
    //mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter ==null) {
    Log.e("没有蓝牙模块");
    ToastUtils.showShort("没有蓝牙模块");
    return;
    }

B、打开蓝牙,有两种方式:
/**

  • 打开蓝牙的两种方式
    */
    //一:直接打开,不通过用户
    // if (!mBluetoothAdapter.isEnabled()) {
    // mBluetoothAdapter.enable();
    // Log.e("开启蓝牙~");
    // }

//二:优雅的打开
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enabIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabIntent, 100);
LogUtils.e("开启蓝牙~");
}

第二种打开会调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 100) {
Toast.makeText(this, "蓝牙已启用", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "蓝牙未启用", Toast.LENGTH_SHORT).show();
}
}

C、搜索设备
//15秒搜索时间
private Handler mHandler = new Handler();
//15秒搜索时间
private static final long SCAN_PERIOD = 15000;
private void scanLeDevice(final boolean enable) {
mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
}
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() {
//在这里可以把搜索到的设备保存起来
//device.getName();获取蓝牙设备名字
//device.getAddress();获取蓝牙设备mac地址
//这里的rssi即信号强度,即手机与设备之间的信号强度。
list.add(device);
mBleAdapter.setNewData(list);
LogUtils.e("name:" + device.getName());
}
});
}
};

搜索到设备后我是用List装,然后用RecyclerView和BaseRecyclerViewAdapterHelper配合显示

D、连接设备
mBleAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
//ToastUtils.showShort("点击了:" + position);
//这里只需要createBond就行了
BluetoothDevice device = list.get(position);
try {
//创建createBond
ClsUtils.createBond(device.getClass(), device);
} catch (Exception e) {
e.printStackTrace();
}
//建立蓝牙连接
mBluetoothGatt = device.connectGatt(MainActivity.this, false, mGattCallback);
}
});

因为跳过用户输入需要用到反射,用到了ClsUtils这个工具类,感谢一下大牛们得无私分享。在连接的时候,创建createBond。
蓝牙连接是通过广播判断状态,所以需要自定义
public class BluetoothConnectActivityReceiver extends BroadcastReceiver {
String strPsw = "111111";
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (intent.getAction().equals("android.bluetooth.device.action.PAIRING_REQUEST")) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
try {
/**

  • cancelPairingUserInput()取消用户输入密钥框,
  • 个人觉得一般情况下不要和setPin(setPasskey、setPairingConfirmation、
  • setRemoteOutOfBandData)一起用,
  • 这几个方法都会remove掉map里面的key:value(<<<<<也就是互斥的>>>>>>)。
    */
    ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对
    //ClsUtils.setPasskey(device.getClass(), device, strPsw);
    ToastUtils.showShort("配对信息===>>>>成功了~");
    abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。
    } catch (Exception e) {
    LogUtils.e("反射异常:"+e);
    // TODO Auto-generated catch block
    ToastUtils.showShort("请求连接错误");
    }
    }
    }
    }
    }
    以上基本就是这样了,表述不清楚待提高。最后附上Activity主要代码。
    public class MainActivity extends AppCompatActivity {
    @BindView(R.id.list_view)
    RecyclerView listView;
    BleAdapter mBleAdapter;
    @BindView(R.id.swipeRefreshLayout)
    SwipeRefreshLayout swipeRefreshLayout;
    private BluetoothAdapter mBluetoothAdapter;
    List<BluetoothDevice> list;
    private BluetoothGatt mBluetoothGatt;
    private BluetoothGattCharacteristic mCharacteristic;
    private BluetoothGattCharacteristic rssiCharacteristic;
    @SuppressLint("NewApi")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // View注入
    ButterKnife.bind(this);
    initPermission();
    initView();
    init();
    // 注册Receiver来获取蓝牙设备相关的结果
    IntentFilter intent = new IntentFilter();
    intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver来取得搜索结果
    intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
    intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
    intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
    registerReceiver(searchDevices,intent);
    }
    private void initView() {
    list = new ArrayList<>();
    mBleAdapter = new BleAdapter(R.layout.item_ble_view, list);
    // 设置布局管理器
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
    listView.setLayoutManager(linearLayoutManager);
    listView.setAdapter(mBleAdapter);
    mBleAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
    //ToastUtils.showShort("点击了:" + position);
    //这里只需要createBond就行了
    BluetoothDevice device = list.get(position);
    try {
    //创建createBond
    ClsUtils.createBond(device.getClass(), device);
    } catch (Exception e) {
    e.printStackTrace();
    }
    //建立蓝牙连接
    mBluetoothGatt = device.connectGatt(MainActivity.this, false, mGattCallback);
    }
    });
    //下拉刷新
    swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {
    //停止刷新
    swipeRefreshLayout.setRefreshing(false);
    list.clear();
    }
    });
    }

private void init() {
/**
* 两种获取BluetoothAdapter方式
/
//方式一:通过BluetoothManager获取
BluetoothManager mBm = (BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBm.getAdapter();
//方式二:通过getDefaultAdapter()获取
//mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
LogUtils.e("没有蓝牙模块");
ToastUtils.showShort("没有蓝牙模块");
return;
}
/

* 打开蓝牙的两种方式
*/
//一:直接打开,不通过用户
// if (!mBluetoothAdapter.isEnabled()) {
// mBluetoothAdapter.enable();
// LogUtils.e("开启蓝牙~");
// }
//二:优雅的打开
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enabIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabIntent, 100);
LogUtils.e("开启蓝牙~");
}
}

private boolean mScanning;//是否正在搜索
private Handler mHandler = new Handler();
//15秒搜索时间
private static final long SCAN_PERIOD = 15000;
private void scanLeDevice(final boolean enable) {
mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
}

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() {
//在这里可以把搜索到的设备保存起来
//device.getName();获取蓝牙设备名字
//device.getAddress();获取蓝牙设备mac地址
//这里的rssi即信号强度,即手机与设备之间的信号强度。
list.add(device);
mBleAdapter.setNewData(list);
LogUtils.e("name:" + device.getName());
}
});
}
};

@Override
protected void onResume() {
list.clear();
scanLeDevice(true);
super.onResume();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 100) {
Toast.makeText(this, "蓝牙已启用", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "蓝牙未启用", Toast.LENGTH_SHORT).show();
}
}

@RequiresApi(api = Build.VERSION_CODES.M)
private void initPermission() {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//判断是否需要向用户解释为何要此权限
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.READ_CONTACTS)) {
showMessageOKCancel("你必须允许这个权限,否则无法搜索到BLE设备", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
100);
}
});
return;
}
//请求权限
requestPermissions(new String[]{Manifest.permission.WRITE_CONTACTS},
100);
}
}
/**
*显示权限申请框
*/
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", okListener)
.create()
.show();
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 100) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0, PERMISSION_DENIED = -1
//这里进行授权被允许的处理
//可以弹个Toast,感谢用户爸爸允许了。
scanLeDevice(true);
Toast.makeText(MainActivity.this, "谢谢!", Toast.LENGTH_SHORT).show();
} else {
//这里进行权限被拒绝的处理,就跳转到本应用的程序管理器
Toast.makeText(MainActivity.this, "请开启位置权限", Toast.LENGTH_SHORT).show();
Intent i = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
String pkg = "com.android.settings";
String cls = "com.android.settings.applications.InstalledAppDetails";
i.setComponent(new ComponentName(pkg, cls));
i.setData(Uri.parse("package:" + getPackageName()));
startActivity(i);
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}

/**

  • gatt连接结果的返回
    */
    private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
    if (status == BluetoothProfile.STATE_DISCONNECTED) { //蓝牙连接
    LogUtils.e("onConnectionStateChange" + "连接成功");
    ToastUtils.showShort("连接成功");

          }
      }
    

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
}

@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
}

/**

  • Callback triggered as a result of a remote characteristic notification.
  • @param gatt
  • @param characteristic
    */
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
    System.out.println("onCharacteristicChanged");
    LogUtils.e("onCharacteristicChanged");
    }

/**

  • 写入数据时操作
  • @param gatt
  • @param characteristic
  • @param status
    */
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    LogUtils.e("onCharacteristicWrite");
    }

/**

  • 读取返回值时操作
  • @param gatt
  • @param characteristic
  • @param status
    */
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    LogUtils.e("onCharacteristicRead");
    }

/**

  • Callback indicating the result of a descriptor write operation.

  • @param gatt

  • @param descriptor

  • @param status
    */
    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
    LogUtils.e("onDescriptorWrite");
    }
    };

    /**

    • 搜索BroadcastReceiver
      */
      private final BroadcastReceiver searchDevices = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
      String action = intent.getAction();
      Bundle b = intent.getExtras();
      Object[] lstName = b.keySet().toArray();
      // 显示所有收到的消息及其细节
      for (int i = 0; i < lstName.length; i++) {
      String keyName = lstName.toString();
      LogUtils.e(keyName, String.valueOf(b.get(keyName)));
      }
      BluetoothDevice device = null;
      // 搜索设备时,取得设备的MAC地址
      if (BluetoothDevice.ACTION_FOUND.equals(action)) {
      device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
      if (device.getBondState() == BluetoothDevice.BOND_NONE) {
      String str = " 未配对|" + device.getName() + "|"
      + device.getAddress();
      LogUtils.e(str);
      // if (lstDevices.indexOf(str) == -1)// 防止重复添加
      // lstDevices.add(str); // 获取设备名称和mac地址
      // adtDevices.notifyDataSetChanged();
      }
      } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
      device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
      switch (device.getBondState()) {
      case BluetoothDevice.BOND_BONDING:
      LogUtils.e("BlueToothTestActivity", "正在配对......");
      break;
      case BluetoothDevice.BOND_BONDED:
      LogUtils.e("BlueToothTestActivity", "完成配对");
      //connect(device);//连接设备
      break;
      case BluetoothDevice.BOND_NONE:
      LogUtils.e("BlueToothTestActivity", "取消配对");
      default:
      break;
      }
      }
      }
      };

}
再贴上ClsUtils类代码,也可以直接搜ClsUtils,可以搜到。
public class ClsUtils {
static BluetoothDevice remoteDevice;
/**
* 与设备配对 参考源码:platform/packages/apps/Settings.git
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
/
static public boolean createBond(Class btClass, BluetoothDevice btDevice)
throws Exception {
Method createBondMethod = btClass.getMethod("createBond");
Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
/
*
* 与设备解除配对 参考源码:platform/packages/apps/Settings.git
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
/
static public boolean removeBond(Class btClass, BluetoothDevice btDevice)
throws Exception {
Method removeBondMethod = btClass.getMethod("removeBond");
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
static public boolean setPin(Class btClass, BluetoothDevice btDevice,String str) throws Exception {
try {
Method removeBondMethod = btClass.getDeclaredMethod("setPin",new Class[]{byte[].class});
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
new Object[]
{str.getBytes()});
Log.e("returnValue", "" + returnValue);
} catch (SecurityException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (IllegalArgumentException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
static public boolean setPasskey (Class btClass, BluetoothDevice btDevice,String str) throws Exception {
try {
Method removeBondMethod = btClass.getDeclaredMethod("setPasskey", new Class[]{byte[].class});
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice, new Object[]{str.getBytes()});
Log.e("returnValue", "" + returnValue);
} catch (SecurityException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (IllegalArgumentException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
// 取消用户输入
static public boolean cancelPairingUserInput(Class btClass,BluetoothDevice device)throws Exception {
Method createBondMethod = btClass.getMethod("cancelPairingUserInput");
// cancelBondProcess()
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
return returnValue.booleanValue();
}
// 取消配对
static public boolean cancelBondProcess(Class btClass, BluetoothDevice device)throws Exception {
Method createBondMethod = btClass.getMethod("cancelBondProcess");
Boolean returnValue = (Boolean) createBondMethod.invoke(device);
return returnValue.booleanValue();
}
//确认配对
static public void setPairingConfirmation(Class<?> btClass, BluetoothDevice device, boolean isConfirm) throws Exception {
Method setPairingConfirmation = btClass.getDeclaredMethod("setPairingConfirmation", boolean.class);
setPairingConfirmation.invoke(device, isConfirm);
}
/
*
* @param clsShow
*/
static public void printAllInform(Class clsShow) {
try {
// 取得所有方法
Method[] hideMethod = clsShow.getMethods();
int i = 0;
for (; i < hideMethod.length; i++) {
Log.e("method name", hideMethod[i].getName() + ";and the i is:"
+ i);
}
// 取得所有常量
Field[] allFields = clsShow.getFields();
for (i = 0; i < allFields.length; i++) {
Log.e("Field name", allFields[i].getName());
}
} catch (SecurityException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (IllegalArgumentException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 执行时直接使用:
public static boolean pair(String strAddr, String strPsw) {
boolean result = false;
BluetoothAdapter bluetoothAdapter = BluetoothAdapter .getDefaultAdapter();
bluetoothAdapter.cancelDiscovery();
if (!bluetoothAdapter.isEnabled()) {
bluetoothAdapter.enable();
}
if (!BluetoothAdapter.checkBluetoothAddress(strAddr)) { // 检查蓝牙地址是否有效
Log.d("mylog", "devAdd un effient!");
}
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(strAddr);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
try {
Log.d("mylog", "NOT BOND_BONDED");
ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对
ClsUtils.createBond(device.getClass(), device);
remoteDevice = device; // 配对完毕就把这个设备对象传给全局的remoteDevice
result = true;
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d("mylog", "setPiN failed!");
e.printStackTrace();
} //
} else {
Log.d("mylog", "HAS BOND_BONDED");
try {
ClsUtils.createBond(device.getClass(), device);
ClsUtils.setPin(device.getClass(), device, strPsw); // 手机和蓝牙采集器配对
ClsUtils.createBond(device.getClass(), device);
remoteDevice = device; // 如果绑定成功,就直接把这个设备对象传给全局的remoteDevice
result = true;
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d("mylog", "setPiN failed!");
e.printStackTrace();
}
}
return result;
}
}

以上在红米not4x 、 华为荣耀8 、 华为荣耀青春版(不知道具体啥型号)、小米5、vivoX20、华为p9、华为荣耀9、华为Mate 10、小米5x、红米3X、红米not5A测试OK。听说在三星上不行,到时候找个测试下。

如有错误,请留言提出。谢谢~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容