diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/ble/BlePreCheck.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/ble/BlePreCheck.kt new file mode 100644 index 0000000000..2d623902bb --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/ble/BlePreCheck.kt @@ -0,0 +1,88 @@ +package info.nightscout.androidaps.plugins.pump.common.ble + +import android.Manifest +import android.bluetooth.BluetoothAdapter +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.location.LocationManager +import android.provider.Settings +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.OKDialog.showConfirmation +import info.nightscout.androidaps.utils.resources.ResourceHelper +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class BlePreCheck @Inject constructor( + val resourceHelper: ResourceHelper +) { + + companion object { + private const val PERMISSION_REQUEST_COARSE_LOCATION = 30241 // arbitrary. + } + + fun prerequisitesCheck(activity: AppCompatActivity): Boolean { + if (!activity.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + OKDialog.show(activity, resourceHelper.gs(R.string.app_name), resourceHelper.gs(R.string.rileylink_scanner_ble_not_supported)) + return false + } else { + // Use this check to determine whether BLE is supported on the device. Then + // you can selectively disable BLE-related features. + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + // your code that requires permission + ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), PERMISSION_REQUEST_COARSE_LOCATION) + } + + val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() + // Ensures Bluetooth is available on the device and it is enabled. If not, + // displays a dialog requesting user permission to enable Bluetooth. + if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled) { + OKDialog.show(activity, resourceHelper.gs(R.string.app_name), resourceHelper.gs(R.string.rileylink_scanner_ble_not_enabled)) + return false + } else { + // Will request that GPS be enabled for devices running Marshmallow or newer. + if (!isLocationEnabled(activity)) { + requestLocation(activity) + return false + } + } + } + return true + } + + /** + * Determine if GPS is currently enabled. + * + * + * On Android 6 (Marshmallow), location needs to be enabled for Bluetooth discovery to work. + * + * @param context The current app context. + * @return true if location is enabled, false otherwise. + */ + private fun isLocationEnabled(context: Context): Boolean { + val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager + return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || + locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + } + + /** + * Prompt the user to enable GPS location if it isn't already on. + * + * @param activity The currently visible activity. + */ + private fun requestLocation(activity: AppCompatActivity) { + if (isLocationEnabled(activity)) { + return + } + + // Shamelessly borrowed from http://stackoverflow.com/a/10311877/868533 + showConfirmation(activity, resourceHelper.gs(R.string.location_not_found_title), resourceHelper.gs(R.string.location_not_found_message), Runnable { + activity.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEScanActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEScanActivity.java index e292808ded..7695344183 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEScanActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEScanActivity.java @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.pump.common.dialog; -import android.Manifest; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.le.BluetoothLeScanner; @@ -10,7 +9,6 @@ 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.Bundle; import android.os.Handler; import android.os.ParcelUuid; @@ -26,13 +24,12 @@ import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.widget.Toolbar; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -43,10 +40,10 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; +import info.nightscout.androidaps.plugins.pump.common.ble.BlePreCheck; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes; -import info.nightscout.androidaps.plugins.pump.common.utils.LocationHelper; import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpConfigurationChanged; import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; @@ -60,9 +57,7 @@ public class RileyLinkBLEScanActivity extends NoSplashAppCompatActivity { @Inject SP sp; @Inject RxBusWrapper rxBus; @Inject ResourceHelper resourceHelper; - - private static final int PERMISSION_REQUEST_COARSE_LOCATION = 30241; // arbitrary. - private static final int REQUEST_ENABLE_BT = 30242; // arbitrary + @Inject BlePreCheck blePrecheck; private static String TAG = "RileyLinkBLEScanActivity"; @@ -156,36 +151,13 @@ public class RileyLinkBLEScanActivity extends NoSplashAppCompatActivity { public void prepareForScanning() { - // https://developer.android.com/training/permissions/requesting.html - // http://developer.radiusnetworks.com/2015/09/29/is-your-beacon-app-ready-for-android-6.html - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { - Toast.makeText(this, R.string.rileylink_scanner_ble_not_supported, Toast.LENGTH_SHORT).show(); - } else { - // Use this check to determine whether BLE is supported on the device. Then - // you can selectively disable BLE-related features. - if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - // your code that requires permission - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, - PERMISSION_REQUEST_COARSE_LOCATION); - } + boolean checkOK = blePrecheck.prerequisitesCheck(this); - // Ensures Bluetooth is available on the device and it is enabled. If not, - // displays a dialog requesting user permission to enable Bluetooth. - if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { - Toast.makeText(this, R.string.rileylink_scanner_ble_not_enabled, Toast.LENGTH_SHORT).show(); - } else { - - // Will request that GPS be enabled for devices running Marshmallow or newer. - if (!LocationHelper.isLocationEnabled(this)) { - LocationHelper.requestLocationForBluetooth(this); - } - - mLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); - settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); - filters = Arrays.asList(new ScanFilter.Builder().setServiceUuid( - ParcelUuid.fromString(GattAttributes.SERVICE_RADIO)).build()); - - } + if (checkOK) { + mLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); + settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); + filters = Collections.singletonList(new ScanFilter.Builder().setServiceUuid( + ParcelUuid.fromString(GattAttributes.SERVICE_RADIO)).build()); } // disable currently selected RL, so that we can discover it @@ -193,19 +165,6 @@ public class RileyLinkBLEScanActivity extends NoSplashAppCompatActivity { } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_ENABLE_BT) { - if (resultCode == RESULT_OK) { - // User allowed Bluetooth to turn on - } else if (resultCode == RESULT_CANCELED) { - // Error, or user said "NO" - finish(); - } - } - } - private ScanCallback mScanCallback2 = new ScanCallback() { @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java deleted file mode 100644 index 065f0ec4d6..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java +++ /dev/null @@ -1,70 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.common.utils; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.location.LocationManager; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.utils.OKDialog; - -/** - * Helper for checking if location services are enabled on the device. - */ -public class LocationHelper { - - /** - * Determine if GPS is currently enabled. - *

- * On Android 6 (Marshmallow), location needs to be enabled for Bluetooth discovery to work. - * - * @param context The current app context. - * @return true if location is enabled, false otherwise. - */ - public static boolean isLocationEnabled(Context context) { - LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); - - return (locationManager != null && // - (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || // - locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))); - - // return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); - } - - - /** - * Prompt the user to enable GPS location if it isn't already on. - * - * @param parent The currently visible activity. - */ - public static void requestLocation(final Activity parent) { - if (LocationHelper.isLocationEnabled(parent)) { - return; - } - - // Shamelessly borrowed from http://stackoverflow.com/a/10311877/868533 - OKDialog.showConfirmation(parent, MainApp.gs(R.string.location_not_found_title), MainApp.gs(R.string.location_not_found_message), () -> { - parent.startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); - }); - } - - - /** - * Prompt the user to enable GPS location on devices that need it for Bluetooth discovery. - *

- * Android 6 (Marshmallow) needs GPS enabled for Bluetooth discovery to work. - * - * @param activity The currently visible activity. - */ - public static void requestLocationForBluetooth(Activity activity) { - // Location needs to be enabled for Bluetooth discovery on Marshmallow. - LocationHelper.requestLocation(activity); - } - - // public static Boolean locationPermission(ActivityWithMenu act) { - // return ActivityCompat.checkSelfPermission(act, Manifest.permission.ACCESS_FINE_LOCATION) == - // PackageManager.PERMISSION_GRANTED; - // } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java index b9d4af7497..3e865cb0e4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java @@ -61,6 +61,7 @@ import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.Round; import info.nightscout.androidaps.utils.T; +import info.nightscout.androidaps.utils.ToastUtils; import info.nightscout.androidaps.utils.resources.ResourceHelper; import info.nightscout.androidaps.utils.sharedPreferences.SP; import io.reactivex.disposables.CompositeDisposable; @@ -77,16 +78,16 @@ public class DanaRSPlugin extends PumpPluginBase implements PumpInterface, DanaR private final TreatmentsPlugin treatmentsPlugin; private final SP sp; private final RxBusWrapper rxBus; - private final CommandQueueProvider commandQueue; private final DanaRPump danaRPump; private final DetailedBolusInfoStorage detailedBolusInfoStorage; + private final FabricPrivacy fabricPrivacy; - private static DanaRSService danaRSService; + private DanaRSService danaRSService; - private static String mDeviceAddress = ""; - public static String mDeviceName = ""; + private String mDeviceAddress = ""; + public String mDeviceName = ""; - public static PumpDescription pumpDescription = new PumpDescription(); + public PumpDescription pumpDescription = new PumpDescription(); // Bolus & history handling public int bolusStartErrorCode; // from start message @@ -112,7 +113,8 @@ public class DanaRSPlugin extends PumpPluginBase implements PumpInterface, DanaR SP sp, CommandQueueProvider commandQueue, DanaRPump danaRPump, - DetailedBolusInfoStorage detailedBolusInfoStorage + DetailedBolusInfoStorage detailedBolusInfoStorage, + FabricPrivacy fabricPrivacy ) { super(new PluginDescription() .mainType(PluginType.PUMP) @@ -130,9 +132,9 @@ public class DanaRSPlugin extends PumpPluginBase implements PumpInterface, DanaR this.profileFunction = profileFunction; this.treatmentsPlugin = treatmentsPlugin; this.sp = sp; - this.commandQueue = commandQueue; this.danaRPump = danaRPump; this.detailedBolusInfoStorage = detailedBolusInfoStorage; + this.fabricPrivacy = fabricPrivacy; pumpDescription.setPumpDescription(PumpType.DanaRS); } @@ -153,12 +155,12 @@ public class DanaRSPlugin extends PumpPluginBase implements PumpInterface, DanaR disposable.add(rxBus .toObservable(EventAppExit.class) .observeOn(Schedulers.io()) - .subscribe(event -> context.unbindService(mConnection), exception -> FabricPrivacy.getInstance().logException(exception)) + .subscribe(event -> context.unbindService(mConnection), fabricPrivacy::logException) ); disposable.add(rxBus .toObservable(EventDanaRSDeviceChange.class) .observeOn(Schedulers.io()) - .subscribe(event -> loadAddress(), exception -> FabricPrivacy.getInstance().logException(exception)) + .subscribe(event -> loadAddress(), fabricPrivacy::logException) ); loadAddress(); // load device name super.onStart(); @@ -195,7 +197,9 @@ public class DanaRSPlugin extends PumpPluginBase implements PumpInterface, DanaR public void connect(String from) { getAapsLogger().debug(LTag.PUMP, "RS connect from: " + from); if (danaRSService != null && !mDeviceAddress.equals("") && !mDeviceName.equals("")) { - danaRSService.connect(from, mDeviceAddress); + boolean success = danaRSService.connect(from, mDeviceAddress); + if (!success) + ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.rileylink_scanner_ble_not_supported)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java deleted file mode 100644 index 89f794259e..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java +++ /dev/null @@ -1,217 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaRS.activities; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.le.BluetoothLeScanner; -import android.bluetooth.le.ScanCallback; -import android.bluetooth.le.ScanResult; -import android.content.pm.ActivityInfo; -import android.os.Bundle; -import android.os.Handler; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ListView; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSDeviceChange; -import info.nightscout.androidaps.utils.SP; - -public class BLEScanActivity extends NoSplashAppCompatActivity { - private ListAdapter mListAdapter = null; - private ArrayList mDevices = new ArrayList<>(); - - private BluetoothLeScanner mBluetoothLeScanner = null; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.danars_blescanner_activity); - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - - mListAdapter = new ListAdapter(); - - ListView listView = findViewById(R.id.danars_blescanner_listview); - listView.setEmptyView(findViewById(R.id.danars_blescanner_nodevice)); - listView.setAdapter(mListAdapter); - - mListAdapter.notifyDataSetChanged(); - } - - @Override - protected void onResume() { - super.onResume(); - - BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - if (mBluetoothAdapter != null) { - if (!mBluetoothAdapter.isEnabled()) mBluetoothAdapter.enable(); - mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); - - if (mBluetoothLeScanner == null) { - mBluetoothAdapter.enable(); - mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); - } - startScan(); - } - } - - @Override - protected void onPause() { - super.onPause(); - - stopScan(); - } - - private void startScan() { - if (mBluetoothLeScanner != null) - mBluetoothLeScanner.startScan(mBleScanCallback); - } - - private void stopScan() { - if (mBluetoothLeScanner != null) - mBluetoothLeScanner.stopScan(mBleScanCallback); - } - - private void addBleDevice(BluetoothDevice device) { - if (device == null || device.getName() == null || device.getName().equals("")) { - return; - } - BluetoothDeviceItem item = new BluetoothDeviceItem(device); - if (!isSNCheck(device.getName()) || mDevices.contains(item)) { - return; - } - - mDevices.add(item); - new Handler().post(() -> mListAdapter.notifyDataSetChanged()); - } - - private ScanCallback mBleScanCallback = new ScanCallback() { - @Override - public void onScanResult(int callbackType, ScanResult result) { - addBleDevice(result.getDevice()); - } - }; - - class ListAdapter extends BaseAdapter { - - @Override - public int getCount() { - return mDevices.size(); - } - - @Override - public BluetoothDeviceItem getItem(int i) { - return mDevices.get(i); - } - - @Override - public long getItemId(int i) { - return 0; - } - - @Override - public View getView(int i, View convertView, ViewGroup parent) { - View v = convertView; - ViewHolder holder; - - if (v == null) { - v = View.inflate(getApplicationContext(), R.layout.danars_blescanner_item, null); - holder = new ViewHolder(v); - v.setTag(holder); - } else { - holder = (ViewHolder) v.getTag(); - } - - BluetoothDeviceItem item = getItem(i); - holder.setData(item); - return v; - } - - private class ViewHolder implements View.OnClickListener { - private BluetoothDeviceItem item = null; - - private TextView mName; - private TextView mAddress; - - ViewHolder(View v) { - mName = v.findViewById(R.id.ble_name); - mAddress = v.findViewById(R.id.ble_address); - - v.setOnClickListener(ViewHolder.this); - } - - @Override - public void onClick(View v) { - SP.putString(R.string.key_danars_address, item.device.getAddress()); - SP.putString(R.string.key_danars_name, mName.getText().toString()); - item.device.createBond(); - RxBus.Companion.getINSTANCE().send(new EventDanaRSDeviceChange()); - finish(); - } - - public void setData(BluetoothDeviceItem data) { - if (data != null) { - try { - String tTitle = data.device.getName(); - if (tTitle == null || tTitle.equals("")) { - tTitle = "(unknown)"; - } else if (tTitle.length() > 10) { - tTitle = tTitle.substring(0, 10); - } - mName.setText(tTitle); - - mAddress.setText(data.device.getAddress()); - - item = data; - } catch (Exception ignored) { - } - } - } - } - } - - // - private class BluetoothDeviceItem { - private BluetoothDevice device; - - BluetoothDeviceItem(BluetoothDevice device) { - super(); - this.device = device; - } - - @Override - public boolean equals(Object o) { - if (device == null || !(o instanceof BluetoothDeviceItem)) { - return false; - } - BluetoothDeviceItem checkItem = (BluetoothDeviceItem) o; - if (checkItem.device == null) { - return false; - } - return stringEquals(device.getAddress(), checkItem.device.getAddress()); - } - - boolean stringEquals(String arg1, String arg2) { - try { - return arg1.equals(arg2); - } catch (Exception e) { - return false; - } - } - } - - public static boolean isSNCheck(String sn) { - String regex = "^([a-zA-Z]{3})([0-9]{5})([a-zA-Z]{2})$"; - Pattern p = Pattern.compile(regex); - Matcher m = p.matcher(sn); - - return m.matches(); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.kt new file mode 100644 index 0000000000..d51dc5ab05 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.kt @@ -0,0 +1,172 @@ +package info.nightscout.androidaps.plugins.pump.danaRS.activities + +import android.annotation.SuppressLint +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.le.BluetoothLeScanner +import android.bluetooth.le.ScanCallback +import android.bluetooth.le.ScanResult +import android.content.pm.ActivityInfo +import android.os.Bundle +import android.os.Handler +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.TextView +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.pump.common.ble.BlePreCheck +import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSDeviceChange +import info.nightscout.androidaps.utils.sharedPreferences.SP +import kotlinx.android.synthetic.main.danars_blescanner_activity.* +import java.util.* +import java.util.regex.Pattern +import javax.inject.Inject + +class BLEScanActivity : NoSplashAppCompatActivity() { + + @Inject lateinit var sp: SP + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var blePreCheck: BlePreCheck + + private var listAdapter: ListAdapter? = null + private val devices = ArrayList() + private var bluetoothLeScanner: BluetoothLeScanner? = null + + @SuppressLint("SourceLockedOrientationActivity") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.danars_blescanner_activity) + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + + blePreCheck.prerequisitesCheck(this) + + listAdapter = ListAdapter() + danars_blescanner_listview.emptyView = findViewById(R.id.danars_blescanner_nodevice) + danars_blescanner_listview.adapter = listAdapter + listAdapter?.notifyDataSetChanged() + } + + override fun onResume() { + super.onResume() + + BluetoothAdapter.getDefaultAdapter()?.let { bluetoothAdapter -> + if (!bluetoothAdapter.isEnabled) bluetoothAdapter.enable() + bluetoothLeScanner = bluetoothAdapter.bluetoothLeScanner + startScan() + } + } + + override fun onPause() { + super.onPause() + stopScan() + } + + private fun startScan() { + if (bluetoothLeScanner != null) bluetoothLeScanner!!.startScan(mBleScanCallback) + } + + private fun stopScan() { + if (bluetoothLeScanner != null) bluetoothLeScanner!!.stopScan(mBleScanCallback) + } + + private fun addBleDevice(device: BluetoothDevice?) { + if (device == null || device.name == null || device.name == "") { + return + } + val item = BluetoothDeviceItem(device) + if (!isSNCheck(device.name) || devices.contains(item)) { + return + } + devices.add(item) + Handler().post { listAdapter!!.notifyDataSetChanged() } + } + + private val mBleScanCallback: ScanCallback = object : ScanCallback() { + override fun onScanResult(callbackType: Int, result: ScanResult) { + addBleDevice(result.device) + } + } + + internal inner class ListAdapter : BaseAdapter() { + override fun getCount(): Int = devices.size + override fun getItem(i: Int): BluetoothDeviceItem = devices[i] + override fun getItemId(i: Int): Long = 0 + + override fun getView(i: Int, convertView: View?, parent: ViewGroup?): View { + var v = convertView + val holder: ViewHolder + if (v == null) { + v = View.inflate(applicationContext, R.layout.danars_blescanner_item, null) + holder = ViewHolder(v) + v.tag = holder + } else { + // reuse view if already exists + holder = v.tag as ViewHolder + } + val item = getItem(i) + holder.setData(item) + return v!! + } + + private inner class ViewHolder internal constructor(v: View) : View.OnClickListener { + private lateinit var item: BluetoothDeviceItem + private val name: TextView = v.findViewById(R.id.ble_name) + private val address: TextView = v.findViewById(R.id.ble_address) + + init { + v.setOnClickListener(this@ViewHolder) + } + + override fun onClick(v: View) { + sp.putString(R.string.key_danars_address, item.device.address) + sp.putString(R.string.key_danars_name, name.text.toString()) + item.device.createBond() + rxBus.send(EventDanaRSDeviceChange()) + finish() + } + + fun setData(data: BluetoothDeviceItem) { + var tTitle = data.device.name + if (tTitle == null || tTitle == "") { + tTitle = "(unknown)" + } else if (tTitle.length > 10) { + tTitle = tTitle.substring(0, 10) + } + name.text = tTitle + address.text = data.device.address + item = data + } + + } + } + + // + inner class BluetoothDeviceItem internal constructor(val device: BluetoothDevice) { + + override fun equals(other: Any?): Boolean { + if (other !is BluetoothDeviceItem) { + return false + } + return stringEquals(device.address, other.device.address) + } + + private fun stringEquals(arg1: String, arg2: String): Boolean { + return try { + arg1 == arg2 + } catch (e: Exception) { + false + } + } + + override fun hashCode(): Int = device.hashCode() + } + + private fun isSNCheck(sn: String?): Boolean { + val regex = "^([a-zA-Z]{3})([0-9]{5})([a-zA-Z]{2})$" + val p = Pattern.compile(regex) + val m = p.matcher(sn) + return m.matches() + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.kt index ba73e1b368..03d31c10a2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.kt @@ -1,11 +1,13 @@ package info.nightscout.androidaps.plugins.pump.danaRS.activities +import android.annotation.SuppressLint import android.content.pm.ActivityInfo import android.os.Bundle import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.plugins.pump.danaRS.dialogs.PairingProgressDialog class PairingHelperActivity : NoSplashAppCompatActivity() { + @SuppressLint("SourceLockedOrientationActivity") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) PairingProgressDialog() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.kt index b079d5c241..1260573c34 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.kt @@ -37,6 +37,7 @@ class BLEComm @Inject internal constructor( private val sp: SP, private val danaRSMessageHashTable: DanaRSMessageHashTable, private val danaRPump: DanaRPump, + private val danaRSPlugin: DanaRSPlugin, private val bleEncryption: BleEncryption ) { @@ -52,66 +53,51 @@ class BLEComm @Inject internal constructor( private var scheduledDisconnection: ScheduledFuture<*>? = null private var processedMessage: DanaRS_Packet? = null private val mSendQueue = ArrayList() - private var mBluetoothManager: BluetoothManager? = null - private var mBluetoothAdapter: BluetoothAdapter? = null + private var bluetoothManager: BluetoothManager? = null + private var bluetoothAdapter: BluetoothAdapter? = null private var connectDeviceName: String? = null - private var mBluetoothGatt: BluetoothGatt? = null + private var bluetoothGatt: BluetoothGatt? = null var isConnected = false var isConnecting = false private var uartRead: BluetoothGattCharacteristic? = null private var uartWrite: BluetoothGattCharacteristic? = null - init { - initialize() - } - - private fun initialize(): Boolean { + @Synchronized + fun connect(from: String, address: String?): Boolean { aapsLogger.debug(LTag.PUMPBTCOMM, "Initializing BLEComm.") - if (mBluetoothManager == null) { - mBluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager - if (mBluetoothManager == null) { + if (bluetoothManager == null) { + bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + if (bluetoothManager == null) { aapsLogger.error("Unable to initialize BluetoothManager.") return false } } - mBluetoothAdapter = mBluetoothManager?.adapter - if (mBluetoothAdapter == null) { + bluetoothAdapter = bluetoothManager?.adapter + if (bluetoothAdapter == null) { aapsLogger.error("Unable to obtain a BluetoothAdapter.") return false } - return true - } - - fun connect(from: String, address: String?): Boolean { - // test existing BT device - val tBluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager? - ?: return false - tBluetoothManager.adapter ?: return false if (address == null) { aapsLogger.error("unspecified address.") return false } - if (mBluetoothAdapter == null) { - if (!initialize()) { - return false - } - } isConnecting = true - val device = mBluetoothAdapter?.getRemoteDevice(address) + val device = bluetoothAdapter?.getRemoteDevice(address) if (device == null) { aapsLogger.error("Device not found. Unable to connect from: $from") return false } aapsLogger.debug(LTag.PUMPBTCOMM, "Trying to create a new connection from: $from") connectDeviceName = device.name - mBluetoothGatt = device.connectGatt(context, false, mGattCallback) + bluetoothGatt = device.connectGatt(context, false, mGattCallback) setCharacteristicNotification(uartReadBTGattChar, true) return true } + @Synchronized fun stopConnecting() { isConnecting = false } @@ -124,21 +110,21 @@ class BLEComm @Inject internal constructor( scheduledDisconnection?.cancel(false) scheduledDisconnection = null - if (mBluetoothAdapter == null || mBluetoothGatt == null) { - aapsLogger.error("disconnect not possible: (mBluetoothAdapter == null) " + (mBluetoothAdapter == null)) - aapsLogger.error("disconnect not possible: (mBluetoothGatt == null) " + (mBluetoothGatt == null)) + if (bluetoothAdapter == null || bluetoothGatt == null) { + aapsLogger.error("disconnect not possible: (mBluetoothAdapter == null) " + (bluetoothAdapter == null)) + aapsLogger.error("disconnect not possible: (mBluetoothGatt == null) " + (bluetoothGatt == null)) return } setCharacteristicNotification(uartReadBTGattChar, false) - mBluetoothGatt?.disconnect() + bluetoothGatt?.disconnect() isConnected = false SystemClock.sleep(2000) } @Synchronized fun close() { aapsLogger.debug(LTag.PUMPBTCOMM, "BluetoothAdapter close") - mBluetoothGatt?.close() - mBluetoothGatt = null + bluetoothGatt?.close() + bluetoothGatt = null } private val mGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() { @@ -185,20 +171,20 @@ class BLEComm @Inject internal constructor( @Synchronized private fun setCharacteristicNotification(characteristic: BluetoothGattCharacteristic?, enabled: Boolean) { aapsLogger.debug(LTag.PUMPBTCOMM, "setCharacteristicNotification") - if (mBluetoothAdapter == null || mBluetoothGatt == null) { + if (bluetoothAdapter == null || bluetoothGatt == null) { aapsLogger.error("BluetoothAdapter not initialized_ERROR") isConnecting = false isConnected = false return } - mBluetoothGatt!!.setCharacteristicNotification(characteristic, enabled) + bluetoothGatt!!.setCharacteristicNotification(characteristic, enabled) } @Synchronized private fun writeCharacteristicNoResponse(characteristic: BluetoothGattCharacteristic, data: ByteArray) { Thread(Runnable { SystemClock.sleep(WRITE_DELAY_MILLIS) - if (mBluetoothAdapter == null || mBluetoothGatt == null) { + if (bluetoothAdapter == null || bluetoothGatt == null) { aapsLogger.error("BluetoothAdapter not initialized_ERROR") isConnecting = false isConnected = false @@ -207,7 +193,7 @@ class BLEComm @Inject internal constructor( characteristic.value = data characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE aapsLogger.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data)) - mBluetoothGatt!!.writeCharacteristic(characteristic) + bluetoothGatt!!.writeCharacteristic(characteristic) }).start() } @@ -220,15 +206,15 @@ class BLEComm @Inject internal constructor( ?: BluetoothGattCharacteristic(UUID.fromString(UART_WRITE_UUID), BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, 0).also { uartWrite = it } private fun getSupportedGattServices(): List? { - aapsLogger.debug(LTag.PUMPBTCOMM, "getSupportedGattServices") - if (mBluetoothAdapter == null || mBluetoothGatt == null) { - aapsLogger.error("BluetoothAdapter not initialized_ERROR") - isConnecting = false - isConnected = false - return null - } - return mBluetoothGatt?.services + aapsLogger.debug(LTag.PUMPBTCOMM, "getSupportedGattServices") + if (bluetoothAdapter == null || bluetoothGatt == null) { + aapsLogger.error("BluetoothAdapter not initialized_ERROR") + isConnecting = false + isConnected = false + return null } + return bluetoothGatt?.services + } private fun findCharacteristic() { val gattServices = getSupportedGattServices() ?: return @@ -339,7 +325,7 @@ class BLEComm @Inject internal constructor( BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK.toByte() -> if (decryptedBuffer.size == 4 && decryptedBuffer[2] == 'O'.toByte() && decryptedBuffer[3] == 'K'.toByte()) { aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (OK)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) // Grab pairing key from preferences if exists - val pairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_pairingkey) + DanaRSPlugin.mDeviceName, "") + val pairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName, "") aapsLogger.debug(LTag.PUMPBTCOMM, "Using stored pairing key: $pairingKey") if (pairingKey.isNotEmpty()) { val encodedPairingKey = DanaRS_Packet.hexToBytes(pairingKey) @@ -366,7 +352,7 @@ class BLEComm @Inject internal constructor( aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) mSendQueue.clear() rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, resourceHelper.gs(R.string.connectionerror))) - sp.remove(resourceHelper.gs(R.string.key_danars_pairingkey) + DanaRSPlugin.mDeviceName) + sp.remove(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName) val n = Notification(Notification.WRONGSERIALNUMBER, resourceHelper.gs(R.string.wrongpassword), Notification.URGENT) rxBus.send(EventNewNotification(n)) } @@ -396,7 +382,7 @@ class BLEComm @Inject internal constructor( sendTimeInfo() val pairingKey = byteArrayOf(decryptedBuffer[2], decryptedBuffer[3]) // store pairing key to preferences - sp.putString(resourceHelper.gs(R.string.key_danars_pairingkey) + DanaRSPlugin.mDeviceName, DanaRS_Packet.bytesToHex(pairingKey)) + sp.putString(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName, DanaRS_Packet.bytesToHex(pairingKey)) aapsLogger.debug(LTag.PUMPBTCOMM, "Got pairing key: " + DanaRS_Packet.bytesToHex(pairingKey)) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java index 84beb5871d..17f7b03a17 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java @@ -53,9 +53,6 @@ public class RileyLinkMedtronicService extends RileyLinkService { public RileyLinkMedtronicService() { super(); instance = this; - aapsLogger.debug(LTag.PUMPCOMM, "RileyLinkMedtronicService newly constructed"); - MedtronicUtil.setMedtronicService(this); - pumpStatus = (MedtronicPumpStatus) medtronicPumpPlugin.getPumpStatusData(); } @@ -68,6 +65,12 @@ public class RileyLinkMedtronicService extends RileyLinkService { return instance.medtronicCommunicationManager; } + @Override public void onCreate() { + super.onCreate(); + aapsLogger.debug(LTag.PUMPCOMM, "RileyLinkMedtronicService newly created"); + MedtronicUtil.setMedtronicService(this); + pumpStatus = (MedtronicPumpStatus) medtronicPumpPlugin.getPumpStatusData(); + } @Override public void onConfigurationChanged(Configuration newConfig) {