package com.rockchip.smart.rockhome.fragment; import android.Manifest; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Color; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.text.method.HideReturnsTransformationMethod; import android.text.method.PasswordTransformationMethod; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.rockchip.smart.rockhome.R; import com.rockchip.smart.rockhome.Utils; import com.rockchip.smart.rockhome.WifiListDialogFragment; import com.rockchip.smart.rockhome.WifiListFragment; import com.rockchip.smart.rockhome.blue.BluetoothDeviceAdapter; import com.rockchip.smart.rockhome.blue.BluetoothGattManager; import com.rockchip.smart.rockhome.blue.Device; import com.rockchip.smart.rockhome.view.LoadingView; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; /** * Created by GJK on 2018/11/9. */ public class BlueFragment extends Fragment implements View.OnClickListener { private static final String TAG = BlueFragment.class.getSimpleName(); private static ContentFragment mContentFragment; private Handler mHandler; // UI private LinearLayout mLlGuide; private LinearLayout mLlDeviceList; private LinearLayout mLlNoDevice; private LinearLayout mLlWifi; private ListView mLvDeviceList; private Button mBtLeScan; private TextView mTvLlWifiAddr; private TextView mTvLlWifiSsid; private EditText mEtLlWifiPwd; private ImageView mIvPwdLock; private ImageView mIvExpand; private TextView mBtLlWifiConfirm; private WifiListDialogFragment mWifiListDialogFragment; private BluetoothGatt mBluetoothGatt; private BluetoothGattManager mBluetoothGattManager; private BluetoothGattCallback mBluetoothGattCallback; private LoadingView mLoadingView; private boolean mShowPwd; private boolean mIsActivityVisible; private STATE mState = STATE.Layout_Guide; private enum STATE { Layout_Guide, Layout_NoDevice, Layout_DeviceList, Layout_Wifi; } private int mMtu = 20; public static BlueFragment newInstance(ContentFragment contentFragment) { mContentFragment = contentFragment; BlueFragment fragment = new BlueFragment(); return fragment; } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate..."); mHandler = new Handler(); mBluetoothGattManager = new BluetoothGattManager(getContext()); mBluetoothGattManager.setBluetoothDeviceAdapterCallback(new BluetoothDeviceAdapter.BluetoothDeviceAdapterCallback() { @Override public void onLeScanStart() { getActivity().runOnUiThread(new Runnable() { @Override public void run() { mBtLeScan.setText(getActivity().getResources().getString(R.string.blue_stop_lescan)); } }); } @Override public void onLeScan(Device device, int position) { Log.d(TAG, "onLeScan position:" + position); } @Override public void onLeScanStop(final int size) { Log.d(TAG, "Scaned devices " + size); if (!mIsActivityVisible) return; getActivity().runOnUiThread(new Runnable() { @Override public void run() { mBtLeScan.setText(getActivity().getResources().getString(R.string.blue_stop_lescan)); if (size == 0) showState(STATE.Layout_NoDevice); } }); } @Override public void onBluetoothDeviceClick(Device device, int position) { Log.d(TAG, "onBluetoothDeviceClick name:" + device.getDevice().getName() + " - " + position); mLoadingView.show(getActivity().getResources().getString(R.string.dialog_connect_to) + device.getDevice().getAddress()); mHandler.removeCallbacks(mLeScanRunnable); if (mBluetoothGattManager.isLeScanning()) mBluetoothGattManager.stopLeScan(); mBluetoothGatt = mBluetoothGattManager.connectGatt(device.getDevice().getAddress(), mBluetoothGattCallback); } }); mBluetoothGattCallback = new BluetoothGattCallback() { private StringBuilder builder = new StringBuilder(); @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); boolean isOk = (status == 0); boolean isConnected = (newState == BluetoothAdapter.STATE_CONNECTED); Log.d(TAG, "onConnectionStateChange isOk:" + isOk + "; isConnected:" + isConnected); if (isOk && isConnected) { isOk = gatt.discoverServices(); } if (!isOk || !isConnected) { gatt.close(); updateLeScanState(false); showState(STATE.Layout_Guide); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); Log.d(TAG, "onServicesDiscovered status:" + status); if (status != 0) { gatt.close(); } else { mBluetoothGattManager.setNotify(false); mLoadingView.dismiss(); showState(STATE.Layout_Wifi); gatt.requestMtu(134 + 3); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); if (status == 0) { try { handleCharacteristicRead(gatt, characteristic); } catch (Exception e) { Log.d(TAG, Log.getStackTraceString(e)); } } mBluetoothGattManager.next(); } private void handleCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) throws JSONException, UnsupportedEncodingException { } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == 0) { try { handleCharacteristicWrite(gatt, characteristic); } catch (Exception e) { } } mBluetoothGattManager.next(); } private void handleCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); try { handleCharacteristicChanged(gatt, characteristic); } catch (JSONException | UnsupportedEncodingException e) { } } private void handleCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) throws JSONException, UnsupportedEncodingException { String value = characteristic.getStringValue(0); byte b[] = value.getBytes("utf-8"); // 忽略APK 发送出去的数据 if (b[0] == 0x01 && b[b.length - 1] == 0x04) return; if (b.length == 1) { if (b[0] == 0x01) { if (mLoadingView.isShowing()) mLoadingView.dismiss(); showMessage(getActivity().getResources().getString(R.string.toast_config_success)); mBluetoothGatt.disconnect(); } else if (b[0] == 0x02) { if (mLoadingView.isShowing()) mLoadingView.dismiss(); showMessage(getActivity().getResources().getString(R.string.toast_config_fail)); } return; } builder.append(value); Log.d(TAG, "handleCharacteristicChanged mtu:" + mMtu + "; bytes:" + value.getBytes("utf-8").length); if (value.getBytes("utf-8").length < mMtu || isJSONValid(builder.toString())) { Utils.saveFile("ble_wifilist.txt", builder.toString()); final List wifis = new ArrayList<>(); JSONObject jsonRoot = (JSONObject) JSON.parse(builder.toString()); if (jsonRoot == null || jsonRoot.getString("cmd") == null) return; String cmd = jsonRoot.getString("cmd"); if (cmd.equals("wifilists")) { JSONArray content = jsonRoot.getJSONArray("ret"); for (int i = 0, size = content.size(); i < size; i++) { JSONObject object = content.getJSONObject(i); com.rockchip.smart.rockhome.WifiInfo wifi = new com.rockchip.smart.rockhome.WifiInfo(); String ssid = object.getString("ssid"); if (ssid == null || ssid.isEmpty()) continue; wifi.setSsid(object.getString("ssid")); wifi.setSignalLevel(object.getString("rssi")); wifi.setFlags(object.getString("flags")); wifis.add(wifi); } builder = new StringBuilder(); mLoadingView.dismiss(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { mWifiListDialogFragment.setWifiList(wifis); mWifiListDialogFragment.show(getActivity().getFragmentManager(), "WifiList"); } }); } } } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); if (status == 0) { try { handleDescriptorRead(gatt, descriptor); } catch (Exception e) { } } mBluetoothGattManager.next(); } private void handleDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) { } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); if (status == 0) { try { handleDescriptorWrite(gatt, descriptor); } catch (Exception e) { } } mBluetoothGattManager.next(); } private void handleDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) { } @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); Log.d(TAG, "onMtuChanged mtu:" + mtu + "; status:" + status); // mBluetoothGattManager.notify(gatt); mMtu = mtu - 3; } }; mLoadingView = new LoadingView(getActivity()); mWifiListDialogFragment = WifiListDialogFragment.createDialog(new WifiListFragment.WifiCallback() { @Override public void onWifiPicked(com.rockchip.smart.rockhome.WifiInfo info) { Log.d(TAG, "onWifiPicked " + info.getSsid()); mWifiListDialogFragment.dismiss(); mTvLlWifiSsid.setText(info.getSsid()); } @Override public void onWifiPickCancelled() { Log.d(TAG, "onWifiPickCancelled"); } }); } private View mHeader; private View initHeader() { TextView tv = new TextView(getContext()); tv.setText(getActivity().getResources().getString(R.string.devices_searched)); tv.setTextColor(Color.BLACK); return (mHeader = tv); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.d(TAG, "onCreateView..."); View rootView = inflater.inflate(R.layout.fragment_activity_rockhome_blue, container, false); mLlGuide = (LinearLayout) rootView.findViewById(R.id.fragment_activity_rockhome_blue_ll_guide); mLlDeviceList = (LinearLayout) rootView.findViewById(R.id.fragment_activity_rockhome_blue_ll_deviceList); mLvDeviceList = (ListView) rootView.findViewById(R.id.fragment_activity_rockhome_blue_lv_deviceList); mLvDeviceList.setAdapter(mBluetoothGattManager.getBluetoothDeviceAdapter()); mBtLeScan = (Button) rootView.findViewById(R.id.fragment_activity_rockhome_blue_bt_search); mBtLeScan.setOnClickListener(this); mLlNoDevice = (LinearLayout) rootView.findViewById(R.id.fragment_activity_rockhome_blue_ll_nodevice); mLlWifi = (LinearLayout) rootView.findViewById(R.id.fragment_activity_rockhome_blue_ll_wifi); mTvLlWifiAddr = (TextView) rootView.findViewById(R.id.fragment_activity_rockhome_blue_tv_addr); mTvLlWifiSsid = (TextView) rootView.findViewById(R.id.fragment_activity_rockhome_blue_tv_input_ssid); mIvExpand = (ImageView) rootView.findViewById(R.id.fragment_activity_rockhome_blue_iv_expand); mIvExpand.setOnClickListener(this); mIvPwdLock = (ImageView) rootView.findViewById(R.id.fragment_activity_rockhome_blue_iv_pwd_lock); mIvPwdLock.setOnClickListener(this); mEtLlWifiPwd = (EditText) rootView.findViewById(R.id.fragment_activity_rockhome_blue_et_pwd); mBtLlWifiConfirm = (TextView) rootView.findViewById(R.id.fragment_activity_rockhome_blue_tv_confirm); mBtLlWifiConfirm.setOnClickListener(this); requestPermission(getActivity()); return rootView; } @Override public void onStart() { super.onStart(); mIsActivityVisible = true; } @Override public void onStop() { super.onStop(); mIsActivityVisible = false; mHandler.removeCallbacks(mLeScanRunnable); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.fragment_activity_rockhome_blue_bt_search: handleLeScan(); break; case R.id.fragment_activity_rockhome_blue_iv_expand: mLoadingView.show(getActivity().getResources().getString(R.string.dialog_getting_wifilist)); mBluetoothGattManager.notify(mBluetoothGatt); mBluetoothGattManager.sendCmdWifiLists(mBluetoothGatt); break; case R.id.fragment_activity_rockhome_blue_tv_confirm: String ssid = mTvLlWifiSsid.getText().toString().trim(); String pwd = mEtLlWifiPwd.getText().toString().trim(); if (ssid == null || ssid.isEmpty() || ssid.equals(getString(R.string.no_wifi))) { showMessage(getString(R.string.select_wifi)); return; } else if (!mContentFragment.verifyWifiPwd(pwd)) { showMessage(getString(R.string.pwd_invalid)); return; } mLoadingView.show(getActivity().getResources().getString(R.string.dialog_wifi_configuring)); Log.d(TAG, "ssid:" + ssid + "; pwd:" + pwd); mBluetoothGattManager.notify(mBluetoothGatt); mBluetoothGattManager.sendCmdWifiSetup(mBluetoothGatt, ssid, pwd); break; case R.id.fragment_activity_rockhome_blue_iv_pwd_lock: mShowPwd = !mShowPwd; if (mShowPwd) { mIvPwdLock.setImageResource(R.drawable.password_input_02); mEtLlWifiPwd.setTransformationMethod(HideReturnsTransformationMethod.getInstance()); } else { mIvPwdLock.setImageResource(R.drawable.password_input_01); mEtLlWifiPwd.setTransformationMethod(PasswordTransformationMethod.getInstance()); } break; } } private Runnable mLeScanRunnable = new Runnable() { @Override public void run() { handleLeScan(); } }; private void handleLeScan() { if (mBluetoothGattManager.isLeScanning()) { mBluetoothGattManager.stopLeScan(); updateLeScanState(false); } else { if (mBluetoothGattManager.startLeScan()) { mLvDeviceList.removeHeaderView(mHeader); initHeader(); mLvDeviceList.addHeaderView(mHeader); showState(STATE.Layout_DeviceList); mHandler.postDelayed(mLeScanRunnable, 10 * 1000); updateLeScanState(true); } } } private void updateLeScanState(boolean scanning) { mBtLeScan.setText(getActivity().getResources().getString(scanning ? R.string.blue_stop_lescan : R.string.blue_start_lescan)); } private void showState(final STATE state) { mState = state; getActivity().runOnUiThread(new Runnable() { @Override public void run() { switch (state) { case Layout_Guide: mBtLeScan.setVisibility(View.VISIBLE); mLlGuide.setVisibility(View.VISIBLE); mLlNoDevice.setVisibility(View.GONE); mLlDeviceList.setVisibility(View.GONE); mLlWifi.setVisibility(View.GONE); break; case Layout_NoDevice: mBtLeScan.setVisibility(View.VISIBLE); mLlGuide.setVisibility(View.GONE); mLlNoDevice.setVisibility(View.VISIBLE); mLlDeviceList.setVisibility(View.GONE); mLlWifi.setVisibility(View.GONE); mBtLeScan.setText(getActivity().getResources().getString(R.string.blue_start_lescan)); break; case Layout_DeviceList: mBtLeScan.setVisibility(View.VISIBLE); mLlGuide.setVisibility(View.GONE); mLlNoDevice.setVisibility(View.GONE); mLlDeviceList.setVisibility(View.VISIBLE); mLlWifi.setVisibility(View.GONE); break; case Layout_Wifi: mBtLeScan.setVisibility(View.GONE); mLlGuide.setVisibility(View.GONE); mLlNoDevice.setVisibility(View.GONE); mLlDeviceList.setVisibility(View.GONE); mLlWifi.setVisibility(View.VISIBLE); mTvLlWifiAddr.setText(mBluetoothGatt.getDevice().getAddress()); initWifi(); break; } } }); } private void showMessage(final String msg) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show(); } }); } private void initWifi() { WifiManager wifiManager = (WifiManager) getActivity().getApplicationContext().getSystemService(Context.WIFI_SERVICE); WifiInfo wifiInfo = wifiManager.getConnectionInfo(); if (wifiManager.isWifiEnabled() && wifiInfo != null && !wifiInfo.getSSID().isEmpty()) { mTvLlWifiSsid.setText(removeQuote(wifiInfo.getSSID().toString())); } else { mTvLlWifiSsid.setText(getActivity().getResources().getString(R.string.no_wifi)); } mEtLlWifiPwd.setText(""); } private String removeQuote(String value) { if (!TextUtils.isEmpty(value)) { if (value.startsWith("\"")) { value = value.substring(1); } if (value.endsWith("\"")) { value = value.substring(0, value.length() - 1); } return value; } return value; } private void requestPermission(Activity activity) { if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 1); } } private boolean isJSONValid(String str) { try { JSONObject.parseObject(str); } catch (JSONException ex) { try { JSONObject.parseArray(str); } catch (JSONException ex1) { return false; } } return true; } }