diff --git a/app/build.gradle b/app/build.gradle index d359e0d..858b712 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 27 - buildToolsVersion "27.0.3" - + compileSdkVersion 29 defaultConfig { applicationId "com.dataart.btle_android" - minSdkVersion 19 - targetSdkVersion 27 - versionCode 3 - versionName "3.0" + minSdkVersion 21 + targetSdkVersion 29 + versionCode 4 + versionName "4.0" multiDexEnabled true } buildTypes { @@ -27,23 +25,24 @@ android { ext { devicehive = "3.1.2" commons = "1.11" - rxandroid = "2.0.1" - rxjava = "2.1.7" - guava = "23.6-android" - gson = "2.8.2" - timber = "4.6.0" + rxandroid = "2.1.1" + rxjava = "2.2.19" + guava = "24.1-jre" + gson = "2.8.6" + timber = "4.7.1" support = "27.0.2" - gms = "11.8.0" - multidex = "1.0.2" + gms = "17.0.0" + materialVersion = '1.3.0-alpha02' + constraintLayoutVersion = '2.0.1' } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "com.android.support:appcompat-v7:${support}" - implementation "com.android.support:design:${support}" - + implementation "com.google.android.material:material:$materialVersion" implementation "com.google.code.gson:gson:${gson}" + implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion" + implementation "com.jakewharton.timber:timber:${timber}" implementation "commons-codec:commons-codec:${commons}" @@ -58,5 +57,4 @@ dependencies { implementation "com.google.guava:guava:${guava}" implementation "com.github.devicehive:devicehive-java:${devicehive}" - implementation "com.android.support:multidex:${multidex}" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 65318a6..f47f990 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ + diff --git a/app/src/main/java/com/dataart/btle_android/BTLEApplication.java b/app/src/main/java/com/dataart/btle_android/BTLEApplication.java index 0e63f57..68be216 100644 --- a/app/src/main/java/com/dataart/btle_android/BTLEApplication.java +++ b/app/src/main/java/com/dataart/btle_android/BTLEApplication.java @@ -1,17 +1,14 @@ package com.dataart.btle_android; -import android.support.multidex.MultiDex; -import android.support.multidex.MultiDexApplication; - +import android.app.Application; import com.dataart.btle_android.devicehive.BTLEDevicePreferences; - import timber.log.Timber; /** * Created by alrybakov */ -public class BTLEApplication extends MultiDexApplication { +public class BTLEApplication extends Application { private static BTLEApplication application; @@ -23,7 +20,6 @@ public static BTLEApplication getApplication() { public void onCreate() { super.onCreate(); application = this; - MultiDex.install(this); BTLEDevicePreferences.getInstance().init(this); if (BuildConfig.DEBUG){ Timber.plant(new Timber.DebugTree()); diff --git a/app/src/main/java/com/dataart/btle_android/MainActivity.java b/app/src/main/java/com/dataart/btle_android/MainActivity.java index 6511d29..d945ec7 100644 --- a/app/src/main/java/com/dataart/btle_android/MainActivity.java +++ b/app/src/main/java/com/dataart/btle_android/MainActivity.java @@ -8,22 +8,24 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.View; import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.EditText; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; + import com.dataart.btle_android.btle_gateway.BluetoothLeService; import com.dataart.btle_android.devicehive.BTLEDevicePreferences; import com.dataart.btle_android.helpers.BleHelpersFactory; import com.dataart.btle_android.helpers.ble.base.BleInitializer; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.textfield.TextInputEditText; +import com.google.android.material.textfield.TextInputLayout; import java.util.Objects; import java.util.UUID; @@ -36,12 +38,15 @@ public class MainActivity extends AppCompatActivity { private BleInitializer bleInitializer; private BluetoothManager mBluetoothManager; - private EditText serverUrlEditText; - private EditText gatewayIdEditText; - private EditText refreshTokenEditText; + private TextInputEditText serverUrlEditText; + private TextInputLayout serverUrlEditTextParent; + private TextInputEditText gatewayIdEditText; + private TextInputLayout gatewayIdEditTextParent; + private TextInputEditText refreshTokenEditText; + private TextInputLayout refreshTokenEditTextParent; private TextView hintText; - private Button serviceButton; - private Button restartServiceButton; + private FloatingActionButton serviceButton; + private FloatingActionButton restartServiceButton; private BTLEDevicePreferences prefs; private boolean isServiceStarted; private final View.OnClickListener restartClickListener = new View.OnClickListener() { @@ -83,11 +88,6 @@ public void afterTextChanged(Editable editable) { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); - Toolbar myToolbar = findViewById(R.id.toolbar); - setSupportActionBar(myToolbar); - if (getSupportActionBar() != null) { - getSupportActionBar().setTitle(R.string.app_name); - } // BleInitializer will start service on initialization success bleInitializer = BleHelpersFactory.getInitializer(this, bluetoothAdapter -> startService()); init(); @@ -114,8 +114,11 @@ private void init() { prefs = BTLEDevicePreferences.getInstance(); serverUrlEditText = findViewById(R.id.server_url_edit); + serverUrlEditTextParent = findViewById(R.id.server_url_parent); gatewayIdEditText = findViewById(R.id.settings_gateway_id); + gatewayIdEditTextParent = findViewById(R.id.settings_gateway_id_parent); refreshTokenEditText = findViewById(R.id.refresh_token_edit); + refreshTokenEditTextParent = findViewById(R.id.refresh_token_parent); hintText = findViewById(R.id.hintText); resetValues(); @@ -212,12 +215,12 @@ private void startService() { private void onServiceRunning() { isServiceStarted = true; - serviceButton.setText(R.string.button_stop); + serviceButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_stop)); } private void onServiceStopped() { isServiceStarted = false; - serviceButton.setText(R.string.button_start); + serviceButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_play_arrow)); } private boolean isRestartRequired() { @@ -234,7 +237,7 @@ private void onDataChanged() { if (isServiceStarted && isRestartRequired()) { hintText.setVisibility(View.VISIBLE); restartServiceButton.setVisibility(View.VISIBLE); - serviceButton.setVisibility(View.GONE); + serviceButton.setVisibility(View.INVISIBLE); } else { hintText.setVisibility(View.GONE); restartServiceButton.setVisibility(View.GONE); @@ -254,7 +257,7 @@ private void resetValues() { gatewayIdEditText.setText( TextUtils.isEmpty(gatewayId) ? getString(R.string.default_gateway_id) + "-" + - UUID.randomUUID().toString().substring(0, 4) + UUID.randomUUID().toString().substring(0, 4) : gatewayId ); @@ -267,9 +270,9 @@ private void resetValues() { } private void resetErrors() { - serverUrlEditText.setError(null); - gatewayIdEditText.setError(null); - refreshTokenEditText.setError(null); + serverUrlEditTextParent.setError(null); + gatewayIdEditTextParent.setError(null); + refreshTokenEditTextParent.setError(null); } private boolean validateValues() { @@ -280,13 +283,13 @@ private boolean validateValues() { final String refreshToken = refreshTokenEditText.getText().toString(); if (TextUtils.isEmpty(serverUrl)) { - serverUrlEditText.setError(getString(R.string.error_message_empty_server_url)); + serverUrlEditTextParent.setError(getString(R.string.error_message_empty_server_url)); serverUrlEditText.requestFocus(); } else if (TextUtils.isEmpty(gatewayId)) { - gatewayIdEditText.setError(getString(R.string.error_message_empty_gateway_id)); + gatewayIdEditTextParent.setError(getString(R.string.error_message_empty_gateway_id)); gatewayIdEditText.requestFocus(); } else if (TextUtils.isEmpty(refreshToken)) { - refreshTokenEditText.setError(getString(R.string.error_message_empty_refresh_token)); + refreshTokenEditTextParent.setError(getString(R.string.error_message_empty_refresh_token)); refreshTokenEditText.requestFocus(); } else { return true; diff --git a/app/src/main/java/com/dataart/btle_android/btle_gateway/BluetoothLeService.java b/app/src/main/java/com/dataart/btle_android/btle_gateway/BluetoothLeService.java index f5ac9ee..9f5470e 100644 --- a/app/src/main/java/com/dataart/btle_android/btle_gateway/BluetoothLeService.java +++ b/app/src/main/java/com/dataart/btle_android/btle_gateway/BluetoothLeService.java @@ -1,5 +1,7 @@ package com.dataart.btle_android.btle_gateway; +import android.app.Notification; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; @@ -10,16 +12,15 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.Build; import android.os.IBinder; -import android.support.v4.app.NotificationCompat; import android.widget.Toast; - +import androidx.core.app.NotificationCompat; import com.dataart.btle_android.MainActivity; import com.dataart.btle_android.R; import com.dataart.btle_android.btle_gateway.server.BluetoothServer; import com.dataart.btle_android.devicehive.BTLEDeviceHive; import com.github.devicehive.client.service.DeviceCommand; - import timber.log.Timber; /** @@ -33,6 +34,8 @@ public class BluetoothLeService extends Service { .concat("ACTION_BT_PERMISSION_REQUEST"); private final static int LE_NOTIFICATION_ID = 1; + private final static String LE_NOTIFICATION_NAME = "DeviceHive"; + private final static String LE_NOTIFICATION_CHANNEL_ID = "devicehive"; private NotificationManager mNotificationManager; private NotificationCompat.Builder mBuilder; @@ -49,7 +52,11 @@ public BluetoothLeService() { } public static void start(final Context context) { - context.startService(new Intent(context, BluetoothLeService.class)); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + context.startForegroundService(new Intent(context, BluetoothLeService.class)); + } else { + context.startService(new Intent(context, BluetoothLeService.class)); + } } public static void stop(final Context context) { @@ -86,7 +93,13 @@ public int onStartCommand(Intent intent, int flags, int startId) { mDeviceHive.registerDevice(); - setNotification(); + Notification notification = prepareNotification(); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + startForeground(LE_NOTIFICATION_ID, notification); + } else { + mNotificationManager.notify(LE_NOTIFICATION_ID, mBuilder.build()); + } return START_NOT_STICKY; } @@ -156,13 +169,20 @@ private void send(final String action) { sendBroadcast(intent); } - private void setNotification() { + private Notification prepareNotification() { final Intent resultIntent = new Intent(this, MainActivity.class); final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addParentStack(MainActivity.class); stackBuilder.addNextIntent(resultIntent); final PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); - mBuilder = new NotificationCompat.Builder(this, "channel_id_1") + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel(LE_NOTIFICATION_CHANNEL_ID, LE_NOTIFICATION_NAME, NotificationManager.IMPORTANCE_HIGH); + channel.enableVibration(false); + channel.setShowBadge(true); + mNotificationManager.createNotificationChannel(channel); + } + + mBuilder = new NotificationCompat.Builder(this, LE_NOTIFICATION_CHANNEL_ID) .setContentText(getString(R.string.notification_bt_on)) .setContentTitle(getString(R.string.device_hive)) .setSmallIcon(R.drawable.ic_le_service) @@ -170,7 +190,11 @@ private void setNotification() { .setOngoing(true) .setContentIntent(resultPendingIntent); - mNotificationManager.notify(LE_NOTIFICATION_ID, mBuilder.build()); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + mBuilder.setChannelId(LE_NOTIFICATION_CHANNEL_ID); + } + + return mBuilder.build(); } } diff --git a/app/src/main/java/com/dataart/btle_android/btle_gateway/server/BluetoothServer.java b/app/src/main/java/com/dataart/btle_android/btle_gateway/server/BluetoothServer.java index b67e271..5956fd0 100644 --- a/app/src/main/java/com/dataart/btle_android/btle_gateway/server/BluetoothServer.java +++ b/app/src/main/java/com/dataart/btle_android/btle_gateway/server/BluetoothServer.java @@ -241,7 +241,12 @@ DeviceConnection connectAndSave(String address, BluetoothDevice device, Interact private DeviceConnection connectAndSave(String address, BluetoothDevice device, InteractiveGattCallback.DisconnectListener disconnectListener, InteractiveGattCallback.StatusListener statusListener, InteractiveGattCallback.OnConnectedListener connectedListener) { final InteractiveGattCallback callback = new InteractiveGattCallback(address, statusListener, context, disconnectListener, connectedListener); - BluetoothGatt gatt = device.connectGatt(context, false, callback); + BluetoothGatt gatt = null; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + gatt = device.connectGatt(context, false, callback, BluetoothDevice.TRANSPORT_LE); + } else { + gatt = device.connectGatt(context, false, callback); + } DeviceConnection connection = new DeviceConnection(address, gatt, callback); activeConnections.put(address, connection); return connection; diff --git a/app/src/main/java/com/dataart/btle_android/devicehive/BTLEDeviceHive.java b/app/src/main/java/com/dataart/btle_android/devicehive/BTLEDeviceHive.java index 5efc9ea..6957412 100644 --- a/app/src/main/java/com/dataart/btle_android/devicehive/BTLEDeviceHive.java +++ b/app/src/main/java/com/dataart/btle_android/devicehive/BTLEDeviceHive.java @@ -88,10 +88,11 @@ public void run() { deviceListener.onDeviceReceived(device); device.subscribeCommands(new CommandFilter(), new DeviceCommandsCallback() { public void onSuccess(List commands) { - for(DeviceCommand command: commands) { + for (DeviceCommand command : commands) { notifyListenersCommandReceived(command); } } + public void onFail(FailureData failureData) { } }); @@ -107,7 +108,7 @@ public void disconnect() { Thread thread = new Thread() { public void run() { deviceListener.onDeviceReceived(null); - deviceHive.unsubscribeAllCommands(); + if (deviceHive != null) deviceHive.unsubscribeAllCommands(); deviceHive = null; } }; diff --git a/app/src/main/java/com/dataart/btle_android/devicehive/DeviceHiveConfig.java b/app/src/main/java/com/dataart/btle_android/devicehive/DeviceHiveConfig.java index a826b42..80d04a3 100644 --- a/app/src/main/java/com/dataart/btle_android/devicehive/DeviceHiveConfig.java +++ b/app/src/main/java/com/dataart/btle_android/devicehive/DeviceHiveConfig.java @@ -2,6 +2,6 @@ public final class DeviceHiveConfig { - public static final String API_ENDPOINT = "http://playground.devicehive.com/api/rest"; + public static final String API_ENDPOINT = "https://playground.devicehive.com/api/rest"; } diff --git a/app/src/main/java/com/dataart/btle_android/helpers/LocationHelper.java b/app/src/main/java/com/dataart/btle_android/helpers/LocationHelper.java index 61481bf..e7052f3 100644 --- a/app/src/main/java/com/dataart/btle_android/helpers/LocationHelper.java +++ b/app/src/main/java/com/dataart/btle_android/helpers/LocationHelper.java @@ -1,6 +1,5 @@ package com.dataart.btle_android.helpers; - import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -9,9 +8,8 @@ import android.location.LocationManager; import android.os.Bundle; import android.provider.Settings; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.dataart.btle_android.R; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; @@ -26,7 +24,6 @@ import com.google.android.gms.location.LocationSettingsStatusCodes; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; - import timber.log.Timber; /** diff --git a/app/src/main/res/drawable/ic_play_arrow.xml b/app/src/main/res/drawable/ic_play_arrow.xml new file mode 100644 index 0000000..a92dd19 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_arrow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_repeat.xml b/app/src/main/res/drawable/ic_repeat.xml new file mode 100644 index 0000000..ca56235 --- /dev/null +++ b/app/src/main/res/drawable/ic_repeat.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_stop.xml b/app/src/main/res/drawable/ic_stop.xml new file mode 100644 index 0000000..a9e8c66 --- /dev/null +++ b/app/src/main/res/drawable/ic_stop.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 0f52517..c967b3f 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -1,132 +1,155 @@ - - - - + android:fillViewport="true" + android:scrollbars="none" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - - - - - - - + android:layout_height="match_parent" + android:padding="@dimen/padding"> + + + + - - - - - - - - + android:hint="@string/settings_server_url" + android:imeOptions="actionNext" + android:lines="1" + android:inputType="text" + android:maxLines="1" /> + + + - - - - - - + - - - - - - - - - -