#571 Finish native part
This commit is contained in:
		| @@ -81,6 +81,7 @@ dependencies { | |||||||
|     implementation 'com.google.firebase:firebase-analytics:17.2.2' |     implementation 'com.google.firebase:firebase-analytics:17.2.2' | ||||||
|     implementation 'com.google.firebase:firebase-messaging:20.2.0' |     implementation 'com.google.firebase:firebase-messaging:20.2.0' | ||||||
|     implementation 'androidx.work:work-runtime:2.3.4' |     implementation 'androidx.work:work-runtime:2.3.4' | ||||||
|  |     implementation "androidx.concurrent:concurrent-futures:1.0.0" | ||||||
|     testImplementation 'junit:junit:4.12' |     testImplementation 'junit:junit:4.12' | ||||||
|     androidTestImplementation 'com.android.support.test:runner:1.0.2' |     androidTestImplementation 'com.android.support.test:runner:1.0.2' | ||||||
|     androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' |     androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' | ||||||
|   | |||||||
| @@ -72,7 +72,7 @@ | |||||||
|                 <action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" /> |                 <action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" /> | ||||||
|             </intent-filter> |             </intent-filter> | ||||||
|         </receiver> |         </receiver> | ||||||
|         <receiver android:name=".Autostart"> |         <receiver android:name=".LocationUpdatesAfterReboot"> | ||||||
|             <intent-filter> |             <intent-filter> | ||||||
|                 <action android:name="android.intent.action.BOOT_COMPLETED" /> |                 <action android:name="android.intent.action.BOOT_COMPLETED" /> | ||||||
|             </intent-filter> |             </intent-filter> | ||||||
|   | |||||||
| @@ -1,23 +0,0 @@ | |||||||
| package com.keyboardcrumbs.hassclient; |  | ||||||
|  |  | ||||||
| import android.content.BroadcastReceiver; |  | ||||||
| import android.content.ComponentName; |  | ||||||
| import android.content.Context; |  | ||||||
| import android.content.Intent; |  | ||||||
| import android.content.ServiceConnection; |  | ||||||
| import android.os.Build; |  | ||||||
| import android.os.IBinder; |  | ||||||
|  |  | ||||||
| public class Autostart extends BroadcastReceiver { |  | ||||||
|  |  | ||||||
|     public void onReceive(Context context, Intent intent) { |  | ||||||
|         if (Utils.requestingLocationUpdates(context) && Intent.ACTION_BOOT_COMPLETED.equalsIgnoreCase(intent.getAction())) { |  | ||||||
|             Intent serviceIntent = new Intent(context, LocationUpdatesService.class); |  | ||||||
|             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |  | ||||||
|                 context.startForegroundService(serviceIntent); |  | ||||||
|             } else { |  | ||||||
|                 context.startService(serviceIntent); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,14 @@ | |||||||
|  | package com.keyboardcrumbs.hassclient; | ||||||
|  |  | ||||||
|  | import android.content.BroadcastReceiver; | ||||||
|  | import android.content.Context; | ||||||
|  | import android.content.Intent; | ||||||
|  |  | ||||||
|  | public class LocationUpdatesAfterReboot extends BroadcastReceiver { | ||||||
|  |  | ||||||
|  |     public void onReceive(Context context, Intent intent) { | ||||||
|  |         if (LocationUtils.getLocationUpdatesState(context) == LocationUtils.LOCATION_UPDATES_SERVICE && Intent.ACTION_BOOT_COMPLETED.equalsIgnoreCase(intent.getAction())) { | ||||||
|  |             LocationUtils.startServiceFromBroadcast(context); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -7,7 +7,6 @@ import android.app.PendingIntent; | |||||||
| import android.app.Service; | import android.app.Service; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.location.Location; | import android.location.Location; | ||||||
| import android.os.Binder; |  | ||||||
| import android.os.Build; | import android.os.Build; | ||||||
| import android.os.Handler; | import android.os.Handler; | ||||||
| import android.os.HandlerThread; | import android.os.HandlerThread; | ||||||
| @@ -78,9 +77,6 @@ public class LocationUpdatesService extends Service { | |||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         mLocationRequest = new LocationRequest(); |         mLocationRequest = new LocationRequest(); | ||||||
|         mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); |  | ||||||
|  |  | ||||||
|         getLastLocation(); |  | ||||||
|  |  | ||||||
|         HandlerThread handlerThread = new HandlerThread(TAG); |         HandlerThread handlerThread = new HandlerThread(TAG); | ||||||
|         handlerThread.start(); |         handlerThread.start(); | ||||||
| @@ -130,8 +126,10 @@ public class LocationUpdatesService extends Service { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void requestLocationUpdates() { |     private void requestLocationUpdates() { | ||||||
|         long requestInterval = Utils.getLocationUpdateIntervals(getApplicationContext()); |         long requestInterval = LocationUtils.getLocationUpdateIntervals(getApplicationContext()); | ||||||
|         Log.i(TAG, "Requesting location updates. Interval is " + requestInterval); |         int priority = LocationUtils.getLocationUpdatesPriority(getApplicationContext()); | ||||||
|  |         Log.i(TAG, "Requesting location updates. Every " + requestInterval + "ms with priority of " + priority); | ||||||
|  |         mLocationRequest.setPriority(priority); | ||||||
|         mLocationRequest.setInterval(requestInterval); |         mLocationRequest.setInterval(requestInterval); | ||||||
|         mLocationRequest.setFastestInterval(requestInterval); |         mLocationRequest.setFastestInterval(requestInterval); | ||||||
|         startForeground(NOTIFICATION_ID, getNotification()); |         startForeground(NOTIFICATION_ID, getNotification()); | ||||||
| @@ -146,7 +144,7 @@ public class LocationUpdatesService extends Service { | |||||||
|     private Notification getNotification() { |     private Notification getNotification() { | ||||||
|         Intent intent = new Intent(this, LocationUpdatesService.class); |         Intent intent = new Intent(this, LocationUpdatesService.class); | ||||||
|  |  | ||||||
|         CharSequence text = Utils.getLocationText(mLocation); |         CharSequence text = LocationUtils.getLocationText(mLocation); | ||||||
|  |  | ||||||
|         intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true); |         intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true); | ||||||
|  |  | ||||||
| @@ -157,13 +155,13 @@ public class LocationUpdatesService extends Service { | |||||||
|                 new Intent(this, MainActivity.class), 0); |                 new Intent(this, MainActivity.class), 0); | ||||||
|  |  | ||||||
|         NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID) |         NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID) | ||||||
|                 .addAction(R.drawable.blank_icon, "Open HA Client", |                 .addAction(R.drawable.blank_icon, "Open app", | ||||||
|                         activityPendingIntent) |                         activityPendingIntent) | ||||||
|                 .addAction(R.drawable.blank_icon, "Stop", |                 .addAction(R.drawable.blank_icon, "Stop tracking", | ||||||
|                         servicePendingIntent) |                         servicePendingIntent) | ||||||
|                 .setContentText(text) |                 .setContentText(text) | ||||||
|                 .setPriority(-1) |                 .setPriority(-1) | ||||||
|                 .setContentTitle(Utils.getLocationTitle(mLocation)) |                 .setContentTitle(LocationUtils.getLocationTitle(mLocation)) | ||||||
|                 .setOngoing(true) |                 .setOngoing(true) | ||||||
|                 .setSmallIcon(R.drawable.mini_icon) |                 .setSmallIcon(R.drawable.mini_icon) | ||||||
|                 .setWhen(System.currentTimeMillis()); |                 .setWhen(System.currentTimeMillis()); | ||||||
| @@ -171,21 +169,6 @@ public class LocationUpdatesService extends Service { | |||||||
|         return builder.build(); |         return builder.build(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void getLastLocation() { |  | ||||||
|         try { |  | ||||||
|             mFusedLocationClient.getLastLocation() |  | ||||||
|                     .addOnCompleteListener(task -> { |  | ||||||
|                         if (task.isSuccessful() && task.getResult() != null) { |  | ||||||
|                             mLocation = task.getResult(); |  | ||||||
|                         } else { |  | ||||||
|                             Log.w(TAG, "Failed to get location."); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|         } catch (SecurityException unlikely) { |  | ||||||
|             Log.e(TAG, "Lost location permission." + unlikely); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void onNewLocation(Location location) { |     private void onNewLocation(Location location) { | ||||||
|         Log.i(TAG, "New location: " + location); |         Log.i(TAG, "New location: " + location); | ||||||
|  |  | ||||||
| @@ -198,6 +181,7 @@ public class LocationUpdatesService extends Service { | |||||||
|                 .build(); |                 .build(); | ||||||
|  |  | ||||||
|         Data locationData = new Data.Builder() |         Data locationData = new Data.Builder() | ||||||
|  |                 .putInt(SendDataHomeWorker.DATA_TYPE_KEY, SendDataHomeWorker.DATA_TYPE_LOCATION) | ||||||
|                 .putDouble("Lat", mLocation.getLatitude()) |                 .putDouble("Lat", mLocation.getLatitude()) | ||||||
|                 .putDouble("Long", mLocation.getLongitude()) |                 .putDouble("Long", mLocation.getLongitude()) | ||||||
|                 .putFloat("Acc", mLocation.getAccuracy()) |                 .putFloat("Acc", mLocation.getAccuracy()) | ||||||
| @@ -205,7 +189,7 @@ public class LocationUpdatesService extends Service { | |||||||
|  |  | ||||||
|  |  | ||||||
|         OneTimeWorkRequest uploadWorkRequest = |         OneTimeWorkRequest uploadWorkRequest = | ||||||
|                 new OneTimeWorkRequest.Builder(SendLocationWorker.class) |                 new OneTimeWorkRequest.Builder(SendDataHomeWorker.class) | ||||||
|                         .setBackoffCriteria( |                         .setBackoffCriteria( | ||||||
|                                 BackoffPolicy.EXPONENTIAL, |                                 BackoffPolicy.EXPONENTIAL, | ||||||
|                                 10, |                                 10, | ||||||
|   | |||||||
| @@ -0,0 +1,100 @@ | |||||||
|  | package com.keyboardcrumbs.hassclient; | ||||||
|  |  | ||||||
|  | import android.content.Context; | ||||||
|  | import android.location.Location; | ||||||
|  | import android.os.Looper; | ||||||
|  |  | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.concurrent.futures.CallbackToFutureAdapter; | ||||||
|  | import androidx.work.BackoffPolicy; | ||||||
|  | import androidx.work.Constraints; | ||||||
|  | import androidx.work.Data; | ||||||
|  | import androidx.work.ExistingWorkPolicy; | ||||||
|  | import androidx.work.ListenableWorker; | ||||||
|  | import androidx.work.NetworkType; | ||||||
|  | import androidx.work.OneTimeWorkRequest; | ||||||
|  | import androidx.work.WorkManager; | ||||||
|  | import androidx.work.WorkerParameters; | ||||||
|  |  | ||||||
|  | import com.google.android.gms.location.FusedLocationProviderClient; | ||||||
|  | import com.google.android.gms.location.LocationCallback; | ||||||
|  | import com.google.android.gms.location.LocationRequest; | ||||||
|  | import com.google.android.gms.location.LocationResult; | ||||||
|  | import com.google.android.gms.location.LocationServices; | ||||||
|  | import com.google.common.util.concurrent.ListenableFuture; | ||||||
|  |  | ||||||
|  | import java.util.concurrent.TimeUnit; | ||||||
|  |  | ||||||
|  | public class LocationUpdatesWorker extends ListenableWorker { | ||||||
|  |  | ||||||
|  |     private Context currentContext; | ||||||
|  |     private LocationCallback callback; | ||||||
|  |     private FusedLocationProviderClient fusedLocationClient; | ||||||
|  |  | ||||||
|  |     public LocationUpdatesWorker(Context context, WorkerParameters params) { | ||||||
|  |         super(context, params); | ||||||
|  |         currentContext = context; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void finish() { | ||||||
|  |         fusedLocationClient.removeLocationUpdates(callback); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     @NonNull | ||||||
|  |     @Override | ||||||
|  |     public ListenableFuture<Result> startWork() { | ||||||
|  |         return CallbackToFutureAdapter.getFuture(completer -> { | ||||||
|  |             fusedLocationClient = LocationServices.getFusedLocationProviderClient(currentContext); | ||||||
|  |  | ||||||
|  |             callback = new LocationCallback() { | ||||||
|  |                 @Override | ||||||
|  |                 public void onLocationResult(LocationResult locationResult) { | ||||||
|  |                     super.onLocationResult(locationResult); | ||||||
|  |                     Location location = locationResult.getLastLocation(); | ||||||
|  |                     Constraints constraints = new Constraints.Builder() | ||||||
|  |                             .setRequiredNetworkType(NetworkType.CONNECTED) | ||||||
|  |                             .build(); | ||||||
|  |  | ||||||
|  |                     Data locationData = new Data.Builder() | ||||||
|  |                             .putInt(SendDataHomeWorker.DATA_TYPE_KEY, SendDataHomeWorker.DATA_TYPE_LOCATION) | ||||||
|  |                             .putDouble("Lat", location.getLatitude()) | ||||||
|  |                             .putDouble("Long", location.getLongitude()) | ||||||
|  |                             .putFloat("Acc", location.getAccuracy()) | ||||||
|  |                             .build(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                     OneTimeWorkRequest uploadWorkRequest = | ||||||
|  |                             new OneTimeWorkRequest.Builder(SendDataHomeWorker.class) | ||||||
|  |                                     .setBackoffCriteria( | ||||||
|  |                                             BackoffPolicy.EXPONENTIAL, | ||||||
|  |                                             10, | ||||||
|  |                                             TimeUnit.SECONDS) | ||||||
|  |                                     .setConstraints(constraints) | ||||||
|  |                                     .setInputData(locationData) | ||||||
|  |                                     .build(); | ||||||
|  |  | ||||||
|  |                     WorkManager | ||||||
|  |                             .getInstance(getApplicationContext()) | ||||||
|  |                             .enqueueUniqueWork("SendLocationUpdate", ExistingWorkPolicy.REPLACE, uploadWorkRequest); | ||||||
|  |                     finish(); | ||||||
|  |                     completer.set(Result.success()); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             LocationRequest locationRequest = new LocationRequest(); | ||||||
|  |             int accuracy = LocationUtils.getLocationUpdatesPriority(getApplicationContext()); | ||||||
|  |             locationRequest.setPriority(accuracy); | ||||||
|  |             locationRequest.setInterval(5000); | ||||||
|  |             locationRequest.setFastestInterval(1000); | ||||||
|  |             try { | ||||||
|  |                 fusedLocationClient.requestLocationUpdates(locationRequest, | ||||||
|  |                         callback, Looper.myLooper()); | ||||||
|  |             } catch (SecurityException e) { | ||||||
|  |                 completer.setException(e); | ||||||
|  |             } | ||||||
|  |             return callback; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,78 @@ | |||||||
|  | package com.keyboardcrumbs.hassclient; | ||||||
|  |  | ||||||
|  | import android.content.Context; | ||||||
|  | import android.content.Intent; | ||||||
|  | import android.location.Location; | ||||||
|  | import android.os.Build; | ||||||
|  |  | ||||||
|  | import androidx.work.ExistingPeriodicWorkPolicy; | ||||||
|  | import androidx.work.PeriodicWorkRequest; | ||||||
|  | import androidx.work.WorkManager; | ||||||
|  |  | ||||||
|  | import java.text.DateFormat; | ||||||
|  | import java.util.Date; | ||||||
|  | import java.util.concurrent.TimeUnit; | ||||||
|  |  | ||||||
|  | class LocationUtils { | ||||||
|  |  | ||||||
|  |     static final String KEY_REQUESTING_LOCATION_UPDATES = "flutter.location-updates-state"; | ||||||
|  |     static final String KEY_LOCATION_UPDATE_INTERVAL = "flutter.location-updates-interval"; | ||||||
|  |     static final String KEY_LOCATION_UPDATE_PRIORITY = "flutter.location-updates-priority"; | ||||||
|  |  | ||||||
|  |     static final String LOCATION_WORK_NAME = "HALocationWorker"; | ||||||
|  |  | ||||||
|  |     static final int LOCATION_UPDATES_DISABLED = 0; | ||||||
|  |     static final int LOCATION_UPDATES_SERVICE = 1; | ||||||
|  |     static final int LOCATION_UPDATES_WORKER = 2; | ||||||
|  |  | ||||||
|  |     static final int DEFAULT_LOCATION_UPDATE_INTERVAL_S = 900; //15 minutes | ||||||
|  |     static final long MIN_WORKER_LOCATION_UPDATE_INTERVAL_MS = 900000; //15 minutes | ||||||
|  |  | ||||||
|  |     static int getLocationUpdatesState(Context context) { | ||||||
|  |         return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getInt(KEY_REQUESTING_LOCATION_UPDATES, LOCATION_UPDATES_DISABLED); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static long getLocationUpdateIntervals(Context context) { | ||||||
|  |         return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getLong(KEY_LOCATION_UPDATE_INTERVAL, DEFAULT_LOCATION_UPDATE_INTERVAL_S) * 1000; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static int getLocationUpdatesPriority(Context context) { | ||||||
|  |         return (int) context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getLong(KEY_LOCATION_UPDATE_PRIORITY, 102); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static void setLocationUpdatesState(Context context, int locationUpdatesState) { | ||||||
|  |         context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) | ||||||
|  |                 .edit() | ||||||
|  |                 .putInt(KEY_REQUESTING_LOCATION_UPDATES, locationUpdatesState) | ||||||
|  |                 .apply(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static String getLocationText(Location location) { | ||||||
|  |         return location == null ? "Accuracy: unknown" : | ||||||
|  |                 "Accuracy: " + location.getAccuracy(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static String getLocationTitle(Location location) { | ||||||
|  |         return location == null ? "Requesting location..." : "Location updated at " + DateFormat.getDateTimeInstance().format(new Date(location.getTime())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static void startService(Context context) { | ||||||
|  |         Intent myService = new Intent(context, LocationUpdatesService.class); | ||||||
|  |         context.startService(myService); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static void startServiceFromBroadcast(Context context) { | ||||||
|  |         Intent serviceIntent = new Intent(context, LocationUpdatesService.class); | ||||||
|  |         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||||
|  |             context.startForegroundService(serviceIntent); | ||||||
|  |         } else { | ||||||
|  |             context.startService(serviceIntent); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static void startWorker(Context context, long interval) { | ||||||
|  |         PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(LocationUpdatesWorker.class, interval, TimeUnit.MILLISECONDS) | ||||||
|  |                 .build(); | ||||||
|  |         WorkManager.getInstance(context).enqueueUniquePeriodicWork(LocationUtils.LOCATION_WORK_NAME, ExistingPeriodicWorkPolicy.REPLACE, periodicWork); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -2,24 +2,19 @@ package com.keyboardcrumbs.hassclient; | |||||||
|  |  | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.core.app.ActivityCompat; | import androidx.core.app.ActivityCompat; | ||||||
| import androidx.localbroadcastmanager.content.LocalBroadcastManager; | import androidx.work.WorkManager; | ||||||
|  |  | ||||||
| import io.flutter.embedding.android.FlutterActivity; | import io.flutter.embedding.android.FlutterActivity; | ||||||
| import io.flutter.embedding.engine.FlutterEngine; | import io.flutter.embedding.engine.FlutterEngine; | ||||||
| import io.flutter.plugins.GeneratedPluginRegistrant; | import io.flutter.plugins.GeneratedPluginRegistrant; | ||||||
|  |  | ||||||
| import android.Manifest; | import android.Manifest; | ||||||
| import android.content.BroadcastReceiver; |  | ||||||
| import android.content.ComponentName; |  | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
|  |  | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.IntentFilter; | import android.content.SharedPreferences; | ||||||
| import android.content.ServiceConnection; |  | ||||||
| import android.content.pm.PackageManager; | import android.content.pm.PackageManager; | ||||||
| import android.location.Location; |  | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.os.IBinder; |  | ||||||
|  |  | ||||||
| import io.flutter.plugin.common.MethodChannel; | import io.flutter.plugin.common.MethodChannel; | ||||||
|  |  | ||||||
| @@ -33,6 +28,9 @@ public class MainActivity extends FlutterActivity { | |||||||
|  |  | ||||||
|     private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34; |     private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34; | ||||||
|  |  | ||||||
|  |     private int locationUpdatesType = LocationUtils.LOCATION_UPDATES_DISABLED; | ||||||
|  |     private long locationUpdatesInterval = LocationUtils.DEFAULT_LOCATION_UPDATE_INTERVAL_S * 1000; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { |     public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { | ||||||
|         GeneratedPluginRegistrant.registerWith(flutterEngine); |         GeneratedPluginRegistrant.registerWith(flutterEngine); | ||||||
| @@ -46,8 +44,7 @@ public class MainActivity extends FlutterActivity { | |||||||
|                                         .addOnCompleteListener(task -> { |                                         .addOnCompleteListener(task -> { | ||||||
|                                             if (task.isSuccessful()) { |                                             if (task.isSuccessful()) { | ||||||
|                                                 String token = task.getResult().getToken(); |                                                 String token = task.getResult().getToken(); | ||||||
|                                                 UpdateTokenTask updateTokenTask = new UpdateTokenTask(context); |                                                 context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).edit().putString("flutter.npush-token", token).apply(); | ||||||
|                                                 updateTokenTask.execute(token); |  | ||||||
|                                                 result.success(token); |                                                 result.success(token); | ||||||
|                                             } else { |                                             } else { | ||||||
|                                                 Exception ex = task.getException(); |                                                 Exception ex = task.getException(); | ||||||
| @@ -64,17 +61,25 @@ public class MainActivity extends FlutterActivity { | |||||||
|                             } |                             } | ||||||
|                             break; |                             break; | ||||||
|                         case "startLocationService": |                         case "startLocationService": | ||||||
|                             Utils.setRequestingLocationUpdates(this, true); |                             try { | ||||||
|  |                                 locationUpdatesInterval = LocationUtils.getLocationUpdateIntervals(this); | ||||||
|  |                                 if (locationUpdatesInterval >= LocationUtils.MIN_WORKER_LOCATION_UPDATE_INTERVAL_MS) { | ||||||
|  |                                     locationUpdatesType = LocationUtils.LOCATION_UPDATES_WORKER; | ||||||
|  |                                 } else { | ||||||
|  |                                     locationUpdatesType = LocationUtils.LOCATION_UPDATES_SERVICE; | ||||||
|  |                                 } | ||||||
|                                 if (isNoLocationPermissions()) { |                                 if (isNoLocationPermissions()) { | ||||||
|                                     requestLocationPermissions(); |                                     requestLocationPermissions(); | ||||||
|                                 } else { |                                 } else { | ||||||
|                                 startLocationService(); |                                     startLocationUpdates(); | ||||||
|                                 } |                                 } | ||||||
|                                 result.success(""); |                                 result.success(""); | ||||||
|  |                             } catch (Exception e) { | ||||||
|  |                                 result.error("location_error", e.getMessage(), null); | ||||||
|  |                             } | ||||||
|                             break; |                             break; | ||||||
|                         case "stopLocationService": |                         case "stopLocationService": | ||||||
|                             Utils.setRequestingLocationUpdates(this, false); |                             stopLocationUpdates(); | ||||||
|                             stopLocationService(); |  | ||||||
|                             result.success(""); |                             result.success(""); | ||||||
|                             break; |                             break; | ||||||
|                     } |                     } | ||||||
| @@ -86,24 +91,28 @@ public class MainActivity extends FlutterActivity { | |||||||
|         return (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS); |         return (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void startLocationService() { |     private void startLocationUpdates() { | ||||||
|         Intent myService = new Intent(MainActivity.this, LocationUpdatesService.class); |         if (locationUpdatesType == LocationUtils.LOCATION_UPDATES_SERVICE) { | ||||||
|         startService(myService); |             LocationUtils.startService(this); | ||||||
|  |             LocationUtils.setLocationUpdatesState(this, locationUpdatesType); | ||||||
|  |         } else if (locationUpdatesType == LocationUtils.LOCATION_UPDATES_WORKER) { | ||||||
|  |             LocationUtils.startWorker(this, locationUpdatesInterval); | ||||||
|  |             LocationUtils.setLocationUpdatesState(this, locationUpdatesType); | ||||||
|  |         } else { | ||||||
|  |             stopLocationUpdates(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void stopLocationService() { |     private void stopLocationUpdates() { | ||||||
|         Intent myService = new Intent(MainActivity.this, LocationUpdatesService.class); |         Intent myService = new Intent(MainActivity.this, LocationUpdatesService.class); | ||||||
|         stopService(myService); |         stopService(myService); | ||||||
|  |         WorkManager.getInstance(this).cancelUniqueWork(LocationUtils.LOCATION_WORK_NAME); | ||||||
|  |         LocationUtils.setLocationUpdatesState(this, LocationUtils.LOCATION_UPDATES_DISABLED); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |     protected void onCreate(Bundle savedInstanceState) { | ||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
|         /*if (Utils.requestingLocationUpdates(this)) { |  | ||||||
|             if (isNoLocationPermissions()) { |  | ||||||
|                 requestLocationPermissions(); |  | ||||||
|             } |  | ||||||
|         }*/ |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -142,7 +151,9 @@ public class MainActivity extends FlutterActivity { | |||||||
|                                            @NonNull int[] grantResults) { |                                            @NonNull int[] grantResults) { | ||||||
|         if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) { |         if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) { | ||||||
|             if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { |             if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { | ||||||
|                 startLocationService(); |                 startLocationUpdates(); | ||||||
|  |             } else { | ||||||
|  |                 stopLocationUpdates(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ package com.keyboardcrumbs.hassclient; | |||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.net.URL; | import java.net.URL; | ||||||
| import java.net.URLConnection; | import java.net.URLConnection; | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
|  |  | ||||||
| import android.app.NotificationChannel; | import android.app.NotificationChannel; | ||||||
| @@ -14,6 +13,8 @@ import android.content.Intent; | |||||||
| import android.media.RingtoneManager; | import android.media.RingtoneManager; | ||||||
| import android.net.Uri; | import android.net.Uri; | ||||||
| import android.os.Build; | import android.os.Build; | ||||||
|  |  | ||||||
|  | import androidx.annotation.NonNull; | ||||||
| import androidx.core.app.NotificationCompat; | import androidx.core.app.NotificationCompat; | ||||||
|  |  | ||||||
| import com.google.firebase.messaging.FirebaseMessagingService; | import com.google.firebase.messaging.FirebaseMessagingService; | ||||||
| @@ -26,7 +27,7 @@ import android.webkit.URLUtil; | |||||||
|  |  | ||||||
| public class MessagingService extends FirebaseMessagingService { | public class MessagingService extends FirebaseMessagingService { | ||||||
|  |  | ||||||
|     private static final String TAG = "MessagingService"; |     public static final String NOTIFICATION_ACTION_BROADCAST = "com.keyboardcrumbs.hassclient.haNotificationAction"; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onMessageReceived(RemoteMessage remoteMessage) { |     public void onMessageReceived(RemoteMessage remoteMessage) { | ||||||
| @@ -39,19 +40,19 @@ public class MessagingService extends FirebaseMessagingService { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onNewToken(String token) { |     public void onNewToken(@NonNull String token) { | ||||||
|         UpdateTokenTask updateTokenTask = new UpdateTokenTask(this); |         getApplicationContext().getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).edit().putString("flutter.npush-token", token).apply(); | ||||||
|         updateTokenTask.execute(token); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void sendNotification(Map<String, String> data) { |     private void sendNotification(Map<String, String> data) { | ||||||
|         String channelId, messageBody, messageTitle, imageUrl, nTag, channelDescription; |         String channelId, messageBody, messageTitle, imageUrl, nTag, channelDescription; | ||||||
|         boolean autoCancel; |         boolean autoCancel; | ||||||
|         if (!data.containsKey("channelId")) { |         String customChannelId = data.get("channelId"); | ||||||
|  |         if (customChannelId == null) { | ||||||
|             channelId = "ha_notify"; |             channelId = "ha_notify"; | ||||||
|             channelDescription = "Default notification channel"; |             channelDescription = "Default notification channel"; | ||||||
|         } else { |         } else { | ||||||
|             channelId = data.get("channelId"); |             channelId = customChannelId; | ||||||
|             channelDescription = channelId; |             channelDescription = channelId; | ||||||
|         } |         } | ||||||
|         if (!data.containsKey("body")) { |         if (!data.containsKey("body")) { | ||||||
| @@ -114,7 +115,7 @@ public class MessagingService extends FirebaseMessagingService { | |||||||
|         } |         } | ||||||
|         for (int i = 1; i <= 3; i++) { |         for (int i = 1; i <= 3; i++) { | ||||||
|             if (data.containsKey("action" + i)) { |             if (data.containsKey("action" + i)) { | ||||||
|                 Intent broadcastIntent = new Intent(this, NotificationActionReceiver.class); |                 Intent broadcastIntent = new Intent(this, NotificationActionReceiver.class).setAction(NOTIFICATION_ACTION_BROADCAST); | ||||||
|                 if (autoCancel) { |                 if (autoCancel) { | ||||||
|                     broadcastIntent.putExtra("tag", nTag); |                     broadcastIntent.putExtra("tag", nTag); | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -7,19 +7,17 @@ import android.content.Intent; | |||||||
|  |  | ||||||
| import androidx.work.BackoffPolicy; | import androidx.work.BackoffPolicy; | ||||||
| import androidx.work.Constraints; | import androidx.work.Constraints; | ||||||
|  | import androidx.work.Data; | ||||||
| import androidx.work.ExistingWorkPolicy; | import androidx.work.ExistingWorkPolicy; | ||||||
| import androidx.work.NetworkType; | import androidx.work.NetworkType; | ||||||
| import androidx.work.OneTimeWorkRequest; | import androidx.work.OneTimeWorkRequest; | ||||||
| import androidx.work.WorkManager; | import androidx.work.WorkManager; | ||||||
| import androidx.work.WorkRequest; |  | ||||||
|  |  | ||||||
| import java.util.concurrent.TimeUnit; | import java.util.concurrent.TimeUnit; | ||||||
|  |  | ||||||
|  |  | ||||||
| public class NextAlarmBroadcastReceiver extends BroadcastReceiver { | public class NextAlarmBroadcastReceiver extends BroadcastReceiver { | ||||||
|  |  | ||||||
|     private static final String TAG = "NextAlarmReceiver"; |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onReceive(Context context, Intent intent) { |     public void onReceive(Context context, Intent intent) { | ||||||
|         if (intent == null) { |         if (intent == null) { | ||||||
| @@ -35,12 +33,17 @@ public class NextAlarmBroadcastReceiver extends BroadcastReceiver { | |||||||
|                 .setRequiredNetworkType(NetworkType.CONNECTED) |                 .setRequiredNetworkType(NetworkType.CONNECTED) | ||||||
|                 .build(); |                 .build(); | ||||||
|  |  | ||||||
|  |         Data workerData = new Data.Builder() | ||||||
|  |                 .putInt(SendDataHomeWorker.DATA_TYPE_KEY, SendDataHomeWorker.DATA_TYPE_NEXT_ALARM) | ||||||
|  |                 .build(); | ||||||
|  |  | ||||||
|         OneTimeWorkRequest uploadWorkRequest = |         OneTimeWorkRequest uploadWorkRequest = | ||||||
|                 new OneTimeWorkRequest.Builder(UpdateNextAlarmWorker.class) |                 new OneTimeWorkRequest.Builder(SendDataHomeWorker.class) | ||||||
|                         .setBackoffCriteria( |                         .setBackoffCriteria( | ||||||
|                                 BackoffPolicy.EXPONENTIAL, |                                 BackoffPolicy.EXPONENTIAL, | ||||||
|                                 10, |                                 10, | ||||||
|                                 TimeUnit.SECONDS) |                                 TimeUnit.SECONDS) | ||||||
|  |                         .setInputData(workerData) | ||||||
|                         .setConstraints(constraints) |                         .setConstraints(constraints) | ||||||
|                         .build(); |                         .build(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,73 +1,58 @@ | |||||||
| package com.keyboardcrumbs.hassclient; | package com.keyboardcrumbs.hassclient; | ||||||
|  |  | ||||||
| import android.app.AlarmManager; |  | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.util.Log; |  | ||||||
| import android.content.BroadcastReceiver; | import android.content.BroadcastReceiver; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
|  |  | ||||||
| import android.app.NotificationManager; | import android.app.NotificationManager; | ||||||
|  |  | ||||||
| import android.webkit.URLUtil; | import androidx.work.BackoffPolicy; | ||||||
|  | import androidx.work.Constraints; | ||||||
|  | import androidx.work.Data; | ||||||
|  | import androidx.work.ExistingWorkPolicy; | ||||||
|  | import androidx.work.NetworkType; | ||||||
|  | import androidx.work.OneTimeWorkRequest; | ||||||
|  | import androidx.work.WorkManager; | ||||||
|  |  | ||||||
| import org.json.JSONObject; | import java.util.concurrent.TimeUnit; | ||||||
| import android.content.SharedPreferences; |  | ||||||
|  |  | ||||||
| public class NotificationActionReceiver extends BroadcastReceiver { | public class NotificationActionReceiver extends BroadcastReceiver { | ||||||
|  |  | ||||||
|     private static final String TAG = "NotificationAction"; |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onReceive(Context context, Intent intent) { |     public void onReceive(Context context, Intent intent) { | ||||||
|         if (intent == null) { |         if (intent == null) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |         String intentAction = intent.getAction(); | ||||||
|  |         if (intentAction == null || !intentAction.equalsIgnoreCase(MessagingService.NOTIFICATION_ACTION_BROADCAST)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|         String rawActionData = intent.getStringExtra("actionData"); |         String rawActionData = intent.getStringExtra("actionData"); | ||||||
|         if (intent.hasExtra("tag")) { |         if (intent.hasExtra("tag")) { | ||||||
|             String notificationTag = intent.getStringExtra("tag"); |             String notificationTag = intent.getStringExtra("tag"); | ||||||
|             NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); |             NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); | ||||||
|             notificationManager.cancel(notificationTag, 0); |             notificationManager.cancel(notificationTag, 0); | ||||||
|         } |         } | ||||||
|         SharedPreferences prefs = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE); |         Constraints constraints = new Constraints.Builder() | ||||||
|         String webhookId = prefs.getString("flutter.app-webhook-id", null); |                 .setRequiredNetworkType(NetworkType.CONNECTED) | ||||||
|         if (webhookId != null) { |                 .build(); | ||||||
|             try { |         Data workerData = new Data.Builder() | ||||||
|                 String requestUrl = prefs.getString("flutter.hassio-res-protocol", "") + |                 .putInt(SendDataHomeWorker.DATA_TYPE_KEY, SendDataHomeWorker.DATA_TYPE_NOTIFICATION_ACTION) | ||||||
|                     "://" + |                 .putString("rawActionData", rawActionData) | ||||||
|                     prefs.getString("flutter.hassio-domain", "") + |                 .build(); | ||||||
|                     ":" + |  | ||||||
|                     prefs.getString("flutter.hassio-port", "") + "/api/webhook/" + webhookId; |         OneTimeWorkRequest uploadWorkRequest = | ||||||
|                 JSONObject actionData = new JSONObject(rawActionData); |                 new OneTimeWorkRequest.Builder(SendDataHomeWorker.class) | ||||||
|                 if (URLUtil.isValidUrl(requestUrl)) { |                         .setBackoffCriteria( | ||||||
|                     JSONObject dataToSend = new JSONObject(); |                                 BackoffPolicy.EXPONENTIAL, | ||||||
|                     JSONObject requestData = new JSONObject(); |                                 10, | ||||||
|                     if (actionData.getString("action").equals("call-service")) { |                                 TimeUnit.SECONDS) | ||||||
|                         dataToSend.put("type", "call_service"); |                         .setInputData(workerData) | ||||||
|                         requestData.put("domain", actionData.getString("service").split("\\.")[0]); |                         .setConstraints(constraints) | ||||||
|                         requestData.put("service", actionData.getString("service").split("\\.")[1]); |                         .build(); | ||||||
|                         if (actionData.has("service_data")) { |  | ||||||
|                             requestData.put("service_data", actionData.get("service_data")); |         WorkManager | ||||||
|                         } |                 .getInstance(context) | ||||||
|                     } else { |                 .enqueueUniqueWork("NotificationAction", ExistingWorkPolicy.APPEND, uploadWorkRequest); | ||||||
|                         dataToSend.put("type", "fire_event"); |  | ||||||
|                         requestData.put("event_type", "ha_client_event"); |  | ||||||
|                         JSONObject eventData = new JSONObject(); |  | ||||||
|                         eventData.put("action", actionData.getString("action")); |  | ||||||
|                         requestData.put("event_data", eventData); |  | ||||||
|                     } |  | ||||||
|                     dataToSend.put("data", requestData); |  | ||||||
|                     String stringRequest = dataToSend.toString(); |  | ||||||
|                     SendTask sendTask = new SendTask(); |  | ||||||
|                     sendTask.execute(requestUrl, stringRequest); |  | ||||||
|                 } else { |  | ||||||
|                     Log.w(TAG, "Invalid HA url"); |  | ||||||
|                 } |  | ||||||
|             } catch (Exception e) { |  | ||||||
|                 Log.e(TAG, "Error handling notification action", e);     |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             Log.w(TAG, "Webhook id not found"); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -0,0 +1,218 @@ | |||||||
|  | package com.keyboardcrumbs.hassclient; | ||||||
|  |  | ||||||
|  | import android.app.AlarmManager; | ||||||
|  | import android.content.Context; | ||||||
|  | import android.content.SharedPreferences; | ||||||
|  | import android.os.BatteryManager; | ||||||
|  | import android.util.Log; | ||||||
|  | import android.webkit.URLUtil; | ||||||
|  |  | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.work.Worker; | ||||||
|  | import androidx.work.WorkerParameters; | ||||||
|  |  | ||||||
|  | import org.json.JSONArray; | ||||||
|  | import org.json.JSONObject; | ||||||
|  |  | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.net.HttpURLConnection; | ||||||
|  | import java.net.URL; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
|  | import java.text.SimpleDateFormat; | ||||||
|  | import java.util.Calendar; | ||||||
|  | import java.util.Locale; | ||||||
|  |  | ||||||
|  | public class SendDataHomeWorker extends Worker { | ||||||
|  |     public static final String DATA_TYPE_KEY = "dataType"; | ||||||
|  |  | ||||||
|  |     public static final int DATA_TYPE_LOCATION = 1; | ||||||
|  |     public static final int DATA_TYPE_NEXT_ALARM = 2; | ||||||
|  |     public static final int DATA_TYPE_NOTIFICATION_ACTION = 3; | ||||||
|  |  | ||||||
|  |     private Context currentContext; | ||||||
|  |     private static final String TAG = "SendDataHomeWorker"; | ||||||
|  |  | ||||||
|  |     public static final String KEY_LAT_ARG = "Lat"; | ||||||
|  |     public static final String KEY_LONG_ARG = "Long"; | ||||||
|  |     public static final String KEY_ACC_ARG = "Acc"; | ||||||
|  |  | ||||||
|  |     private static final SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:00", Locale.ENGLISH); | ||||||
|  |     private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH); | ||||||
|  |     private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:00", Locale.ENGLISH); | ||||||
|  |  | ||||||
|  |     public SendDataHomeWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { | ||||||
|  |         super(context, workerParams); | ||||||
|  |         currentContext = context; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @NonNull | ||||||
|  |     @Override | ||||||
|  |     public Result doWork() { | ||||||
|  |         Log.d(TAG, "Start sending data home"); | ||||||
|  |         SharedPreferences prefs = currentContext.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE); | ||||||
|  |         String webhookId = prefs.getString("flutter.app-webhook-id", null); | ||||||
|  |         if (webhookId != null) { | ||||||
|  |             try { | ||||||
|  |                 String requestUrl = prefs.getString("flutter.hassio-res-protocol", "") + | ||||||
|  |                         "://" + | ||||||
|  |                         prefs.getString("flutter.hassio-domain", "") + | ||||||
|  |                         ":" + | ||||||
|  |                         prefs.getString("flutter.hassio-port", "") + "/api/webhook/" + webhookId; | ||||||
|  |                 if (URLUtil.isValidUrl(requestUrl)) { | ||||||
|  |                     int dataType = getInputData().getInt(DATA_TYPE_KEY, 0); | ||||||
|  |                     String stringRequest; | ||||||
|  |                     if (dataType == DATA_TYPE_LOCATION) { | ||||||
|  |                         Log.d(TAG, "Location data"); | ||||||
|  |                         stringRequest = getLocationDataToSend(); | ||||||
|  |                     } else if (dataType == DATA_TYPE_NEXT_ALARM) { | ||||||
|  |                         Log.d(TAG, "Next alarm data"); | ||||||
|  |                         stringRequest = getNextAlarmDataToSend(); | ||||||
|  |                     } else if (dataType == DATA_TYPE_NOTIFICATION_ACTION) { | ||||||
|  |                         Log.d(TAG, "Notification action data"); | ||||||
|  |                         stringRequest = getNotificationActionData(); | ||||||
|  |                     } else { | ||||||
|  |                         Log.e(TAG, "doWork() unknown data type: " + dataType); | ||||||
|  |                         return Result.failure(); | ||||||
|  |                     } | ||||||
|  |                     try { | ||||||
|  |                         URL url = new URL(requestUrl); | ||||||
|  |                         HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); | ||||||
|  |                         urlConnection.setRequestMethod("POST"); | ||||||
|  |                         urlConnection.setRequestProperty("Content-Type", "application/json"); | ||||||
|  |                         urlConnection.setDoOutput(true); | ||||||
|  |                         assert stringRequest != null; | ||||||
|  |                         byte[] outputBytes = stringRequest.getBytes(StandardCharsets.UTF_8); | ||||||
|  |                         OutputStream os = urlConnection.getOutputStream(); | ||||||
|  |                         os.write(outputBytes); | ||||||
|  |  | ||||||
|  |                         int responseCode = urlConnection.getResponseCode(); | ||||||
|  |                         urlConnection.disconnect(); | ||||||
|  |                         if (responseCode >= 300) { | ||||||
|  |                             return Result.retry(); | ||||||
|  |                         } | ||||||
|  |                     } catch (Exception e) { | ||||||
|  |                         Log.e(TAG, "Error sending data", e); | ||||||
|  |                         return Result.retry(); | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     Log.w(TAG, "Invalid HA url"); | ||||||
|  |                     return Result.failure(); | ||||||
|  |                 } | ||||||
|  |             } catch (Exception e) { | ||||||
|  |                 Log.e(TAG, "Error =(", e); | ||||||
|  |                 return Result.failure(); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             Log.w(TAG, "Webhook id not found"); | ||||||
|  |             return Result.failure(); | ||||||
|  |         } | ||||||
|  |         return Result.success(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String getLocationDataToSend() { | ||||||
|  |         try { | ||||||
|  |             JSONObject dataToSend = new JSONObject(); | ||||||
|  |             dataToSend.put("type", "update_location"); | ||||||
|  |             JSONObject dataObject = new JSONObject(); | ||||||
|  |  | ||||||
|  |             JSONArray gps = new JSONArray(); | ||||||
|  |             gps.put(0, getInputData().getDouble(KEY_LAT_ARG, 0)); | ||||||
|  |             gps.put(1, getInputData().getDouble(KEY_LONG_ARG, 0)); | ||||||
|  |  | ||||||
|  |             dataObject.put("gps", gps); | ||||||
|  |             dataObject.put("gps_accuracy", getInputData().getFloat(KEY_ACC_ARG, 0)); | ||||||
|  |  | ||||||
|  |             BatteryManager bm; | ||||||
|  |             if (android.os.Build.VERSION.SDK_INT >= 23) { | ||||||
|  |                 bm = currentContext.getSystemService(BatteryManager.class); | ||||||
|  |             } else { | ||||||
|  |                 bm = (BatteryManager)currentContext.getSystemService(Context.BATTERY_SERVICE); | ||||||
|  |             } | ||||||
|  |             int batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); | ||||||
|  |  | ||||||
|  |             dataObject.put("battery", batLevel); | ||||||
|  |  | ||||||
|  |             dataToSend.put("data", dataObject); | ||||||
|  |             return dataToSend.toString(); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             Log.e(TAG,"getLocationDataToSend", e); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String getNotificationActionData() { | ||||||
|  |         try { | ||||||
|  |             String rawActionData = getInputData().getString("rawActionData"); | ||||||
|  |             if (rawActionData == null || rawActionData.length() == 0) { | ||||||
|  |                 Log.e(TAG,"getNotificationActionData rawAction data is empty"); | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |             JSONObject actionData = new JSONObject(rawActionData); | ||||||
|  |             JSONObject dataToSend = new JSONObject(); | ||||||
|  |             JSONObject requestData = new JSONObject(); | ||||||
|  |             if (actionData.getString("action").equals("call-service")) { | ||||||
|  |                 dataToSend.put("type", "call_service"); | ||||||
|  |                 requestData.put("domain", actionData.getString("service").split("\\.")[0]); | ||||||
|  |                 requestData.put("service", actionData.getString("service").split("\\.")[1]); | ||||||
|  |                 if (actionData.has("service_data")) { | ||||||
|  |                     requestData.put("service_data", actionData.get("service_data")); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 dataToSend.put("type", "fire_event"); | ||||||
|  |                 requestData.put("event_type", "ha_client_event"); | ||||||
|  |                 JSONObject eventData = new JSONObject(); | ||||||
|  |                 eventData.put("action", actionData.getString("action")); | ||||||
|  |                 requestData.put("event_data", eventData); | ||||||
|  |             } | ||||||
|  |             dataToSend.put("data", requestData); | ||||||
|  |             return dataToSend.toString(); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             Log.e(TAG,"getNotificationActionData", e); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String getNextAlarmDataToSend() { | ||||||
|  |         try { | ||||||
|  |             final AlarmManager alarmManager; | ||||||
|  |             if (android.os.Build.VERSION.SDK_INT >= 23) { | ||||||
|  |                 alarmManager = currentContext.getSystemService(AlarmManager.class); | ||||||
|  |             } else { | ||||||
|  |                 alarmManager = (AlarmManager)currentContext.getSystemService(Context.ALARM_SERVICE); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             final AlarmManager.AlarmClockInfo alarmClockInfo = alarmManager.getNextAlarmClock(); | ||||||
|  |  | ||||||
|  |             JSONObject dataToSend = new JSONObject(); | ||||||
|  |             dataToSend.put("type", "update_sensor_states"); | ||||||
|  |             JSONArray dataArray = new JSONArray(); | ||||||
|  |             JSONObject sensorData = new JSONObject(); | ||||||
|  |             JSONObject sensorAttrs = new JSONObject(); | ||||||
|  |             sensorData.put("unique_id", "next_alarm"); | ||||||
|  |             sensorData.put("type", "sensor"); | ||||||
|  |             final long triggerTimestamp; | ||||||
|  |             if (alarmClockInfo != null) { | ||||||
|  |                 triggerTimestamp = alarmClockInfo.getTriggerTime(); | ||||||
|  |                 final Calendar calendar = Calendar.getInstance(); | ||||||
|  |                 calendar.setTimeInMillis(triggerTimestamp); | ||||||
|  |                 sensorData.put("state", DATE_TIME_FORMAT.format(calendar.getTime())); | ||||||
|  |                 sensorAttrs.put("date", DATE_FORMAT.format(calendar.getTime())); | ||||||
|  |                 sensorAttrs.put("time", TIME_FORMAT.format(calendar.getTime())); | ||||||
|  |                 sensorAttrs.put("timestamp", triggerTimestamp); | ||||||
|  |             } else { | ||||||
|  |                 sensorData.put("state", ""); | ||||||
|  |                 sensorAttrs.put("date", ""); | ||||||
|  |                 sensorAttrs.put("time", ""); | ||||||
|  |                 sensorAttrs.put("timestamp", 0); | ||||||
|  |             } | ||||||
|  |             sensorData.put("icon", "mdi:alarm"); | ||||||
|  |             sensorData.put("attributes", sensorAttrs); | ||||||
|  |             dataArray.put(0, sensorData); | ||||||
|  |             dataToSend.put("data", dataArray); | ||||||
|  |             return dataToSend.toString(); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             Log.e(TAG,"getNextAlarmDataToSend", e); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,98 +0,0 @@ | |||||||
| package com.keyboardcrumbs.hassclient; |  | ||||||
|  |  | ||||||
| import android.app.AlarmManager; |  | ||||||
| import android.content.Context; |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.util.Log; |  | ||||||
| import android.webkit.URLUtil; |  | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; |  | ||||||
| import androidx.work.Worker; |  | ||||||
| import androidx.work.WorkerParameters; |  | ||||||
|  |  | ||||||
| import org.json.JSONArray; |  | ||||||
| import org.json.JSONObject; |  | ||||||
|  |  | ||||||
| import java.io.OutputStream; |  | ||||||
| import java.net.HttpURLConnection; |  | ||||||
| import java.net.URL; |  | ||||||
| import java.text.SimpleDateFormat; |  | ||||||
| import java.util.Calendar; |  | ||||||
| import java.util.Locale; |  | ||||||
|  |  | ||||||
| public class SendLocationWorker extends Worker { |  | ||||||
|  |  | ||||||
|     private Context currentContext; |  | ||||||
|     private static final String TAG = "SendLocationWorker"; |  | ||||||
|  |  | ||||||
|     public static final String KEY_LAT_ARG = "Lat"; |  | ||||||
|     public static final String KEY_LONG_ARG = "Long"; |  | ||||||
|     public static final String KEY_ACC_ARG = "Acc"; |  | ||||||
|  |  | ||||||
|     public SendLocationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { |  | ||||||
|         super(context, workerParams); |  | ||||||
|         currentContext = context; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     @Override |  | ||||||
|     public Result doWork() { |  | ||||||
|         SharedPreferences prefs = currentContext.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE); |  | ||||||
|         String webhookId = prefs.getString("flutter.app-webhook-id", null); |  | ||||||
|         if (webhookId != null) { |  | ||||||
|             try { |  | ||||||
|                 String requestUrl = prefs.getString("flutter.hassio-res-protocol", "") + |  | ||||||
|                         "://" + |  | ||||||
|                         prefs.getString("flutter.hassio-domain", "") + |  | ||||||
|                         ":" + |  | ||||||
|                         prefs.getString("flutter.hassio-port", "") + "/api/webhook/" + webhookId; |  | ||||||
|                 JSONObject dataToSend = new JSONObject(); |  | ||||||
|                 if (URLUtil.isValidUrl(requestUrl)) { |  | ||||||
|                     dataToSend.put("type", "update_location"); |  | ||||||
|                     JSONObject dataObject = new JSONObject(); |  | ||||||
|  |  | ||||||
|                     JSONArray gps = new JSONArray(); |  | ||||||
|                     gps.put(0, getInputData().getDouble(KEY_LAT_ARG, 0)); |  | ||||||
|                     gps.put(1, getInputData().getDouble(KEY_LONG_ARG, 0)); |  | ||||||
|  |  | ||||||
|                     dataObject.put("gps", gps); |  | ||||||
|                     dataObject.put("gps_accuracy", getInputData().getFloat(KEY_ACC_ARG, 0)); |  | ||||||
|                     dataObject.put("battery", 41); |  | ||||||
|  |  | ||||||
|                     dataToSend.put("data", dataObject); |  | ||||||
|  |  | ||||||
|                     String stringRequest = dataToSend.toString(); |  | ||||||
|                     try { |  | ||||||
|                         URL url = new URL(requestUrl); |  | ||||||
|                         HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); |  | ||||||
|                         urlConnection.setRequestMethod("POST"); |  | ||||||
|                         urlConnection.setRequestProperty("Content-Type", "application/json"); |  | ||||||
|                         urlConnection.setDoOutput(true); |  | ||||||
|                         byte[] outputBytes = stringRequest.getBytes("UTF-8"); |  | ||||||
|                         OutputStream os = urlConnection.getOutputStream(); |  | ||||||
|                         os.write(outputBytes); |  | ||||||
|  |  | ||||||
|                         int responseCode = urlConnection.getResponseCode(); |  | ||||||
|                         urlConnection.disconnect(); |  | ||||||
|                         if (responseCode >= 300) { |  | ||||||
|                             return Result.retry(); |  | ||||||
|                         } |  | ||||||
|                     } catch (Exception e) { |  | ||||||
|                         Log.e(TAG, "Error sending data", e); |  | ||||||
|                         return Result.retry(); |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     Log.w(TAG, "Invalid HA url"); |  | ||||||
|                     return Result.failure(); |  | ||||||
|                 } |  | ||||||
|             } catch (Exception e) { |  | ||||||
|                 Log.e(TAG, "Error =(", e); |  | ||||||
|                 return Result.failure(); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             Log.w(TAG, "Webhook id not found"); |  | ||||||
|             return Result.failure(); |  | ||||||
|         } |  | ||||||
|         return Result.success(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| package com.keyboardcrumbs.hassclient; |  | ||||||
|  |  | ||||||
| import android.util.Log; |  | ||||||
| import android.os.AsyncTask; |  | ||||||
|  |  | ||||||
| import java.net.URL; |  | ||||||
| import java.net.HttpURLConnection; |  | ||||||
| import java.io.OutputStream; |  | ||||||
|  |  | ||||||
| public class SendTask extends AsyncTask<String, String, String> { |  | ||||||
|  |  | ||||||
|     private static final String TAG = "SendTask"; |  | ||||||
|  |  | ||||||
|     public SendTask(){ |  | ||||||
|         //set context variables if required |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected void onPreExecute() { |  | ||||||
|         super.onPreExecute(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected String doInBackground(String... params) { |  | ||||||
|         String urlString = params[0]; |  | ||||||
|         String data = params[1]; |  | ||||||
|  |  | ||||||
|         try { |  | ||||||
|             URL url = new URL(urlString); |  | ||||||
|             HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); |  | ||||||
|             urlConnection.setRequestMethod("POST"); |  | ||||||
|             urlConnection.setRequestProperty("Content-Type", "application/json"); |  | ||||||
|             urlConnection.setDoOutput(true); |  | ||||||
|             byte[] outputBytes = data.getBytes("UTF-8"); |  | ||||||
|             OutputStream os = urlConnection.getOutputStream(); |  | ||||||
|             os.write(outputBytes); |  | ||||||
|  |  | ||||||
|             int responseCode = urlConnection.getResponseCode(); |  | ||||||
|  |  | ||||||
|             urlConnection.disconnect(); |  | ||||||
|         } catch (Exception e) { |  | ||||||
|             Log.e(TAG, "Error sending data", e);  |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,119 +0,0 @@ | |||||||
| package com.keyboardcrumbs.hassclient; |  | ||||||
|  |  | ||||||
| import android.app.AlarmManager; |  | ||||||
| import android.content.Context; |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.util.Log; |  | ||||||
| import android.webkit.URLUtil; |  | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; |  | ||||||
| import androidx.work.Worker; |  | ||||||
| import androidx.work.WorkerParameters; |  | ||||||
|  |  | ||||||
| import org.json.JSONArray; |  | ||||||
| import org.json.JSONObject; |  | ||||||
|  |  | ||||||
| import java.io.OutputStream; |  | ||||||
| import java.net.HttpURLConnection; |  | ||||||
| import java.net.URL; |  | ||||||
| import java.text.SimpleDateFormat; |  | ||||||
| import java.util.Calendar; |  | ||||||
| import java.util.Locale; |  | ||||||
|  |  | ||||||
| public class UpdateNextAlarmWorker extends Worker { |  | ||||||
|  |  | ||||||
|     private Context currentContext; |  | ||||||
|     private static final String TAG = "NextAlarmWorker"; |  | ||||||
|     private static final SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:00", Locale.ENGLISH); |  | ||||||
|     private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH); |  | ||||||
|     private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:00", Locale.ENGLISH); |  | ||||||
|  |  | ||||||
|     public UpdateNextAlarmWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { |  | ||||||
|         super(context, workerParams); |  | ||||||
|         currentContext = context; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @NonNull |  | ||||||
|     @Override |  | ||||||
|     public Result doWork() { |  | ||||||
|         final AlarmManager alarmManager; |  | ||||||
|         if (android.os.Build.VERSION.SDK_INT >= 23) { |  | ||||||
|             alarmManager = currentContext.getSystemService(AlarmManager.class); |  | ||||||
|         } else { |  | ||||||
|             alarmManager = (AlarmManager)currentContext.getSystemService(Context.ALARM_SERVICE); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         final AlarmManager.AlarmClockInfo alarmClockInfo = alarmManager.getNextAlarmClock(); |  | ||||||
|  |  | ||||||
|         SharedPreferences prefs = currentContext.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE); |  | ||||||
|         String webhookId = prefs.getString("flutter.app-webhook-id", null); |  | ||||||
|         if (webhookId != null) { |  | ||||||
|             try { |  | ||||||
|                 String requestUrl = prefs.getString("flutter.hassio-res-protocol", "") + |  | ||||||
|                         "://" + |  | ||||||
|                         prefs.getString("flutter.hassio-domain", "") + |  | ||||||
|                         ":" + |  | ||||||
|                         prefs.getString("flutter.hassio-port", "") + "/api/webhook/" + webhookId; |  | ||||||
|                 JSONObject dataToSend = new JSONObject(); |  | ||||||
|                 if (URLUtil.isValidUrl(requestUrl)) { |  | ||||||
|                     dataToSend.put("type", "update_sensor_states"); |  | ||||||
|                     JSONArray dataArray = new JSONArray(); |  | ||||||
|                     JSONObject sensorData = new JSONObject(); |  | ||||||
|                     JSONObject sensorAttrs = new JSONObject(); |  | ||||||
|                     sensorData.put("unique_id", "next_alarm"); |  | ||||||
|                     sensorData.put("type", "sensor"); |  | ||||||
|                     final long triggerTimestamp; |  | ||||||
|                     if (alarmClockInfo != null) { |  | ||||||
|                         triggerTimestamp = alarmClockInfo.getTriggerTime(); |  | ||||||
|                         final Calendar calendar = Calendar.getInstance(); |  | ||||||
|                         calendar.setTimeInMillis(triggerTimestamp); |  | ||||||
|                         sensorData.put("state", DATE_TIME_FORMAT.format(calendar.getTime())); |  | ||||||
|                         sensorAttrs.put("date", DATE_FORMAT.format(calendar.getTime())); |  | ||||||
|                         sensorAttrs.put("time", TIME_FORMAT.format(calendar.getTime())); |  | ||||||
|                         sensorAttrs.put("timestamp", triggerTimestamp); |  | ||||||
|                     } else { |  | ||||||
|                         sensorData.put("state", ""); |  | ||||||
|                         sensorAttrs.put("date", ""); |  | ||||||
|                         sensorAttrs.put("time", ""); |  | ||||||
|                         sensorAttrs.put("timestamp", 0); |  | ||||||
|                     } |  | ||||||
|                     sensorData.put("icon", "mdi:alarm"); |  | ||||||
|                     sensorData.put("attributes", sensorAttrs); |  | ||||||
|                     dataArray.put(0, sensorData); |  | ||||||
|                     dataToSend.put("data", dataArray); |  | ||||||
|  |  | ||||||
|                     String stringRequest = dataToSend.toString(); |  | ||||||
|                     try { |  | ||||||
|                         URL url = new URL(requestUrl); |  | ||||||
|                         HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); |  | ||||||
|                         urlConnection.setRequestMethod("POST"); |  | ||||||
|                         urlConnection.setRequestProperty("Content-Type", "application/json"); |  | ||||||
|                         urlConnection.setDoOutput(true); |  | ||||||
|                         byte[] outputBytes = stringRequest.getBytes("UTF-8"); |  | ||||||
|                         OutputStream os = urlConnection.getOutputStream(); |  | ||||||
|                         os.write(outputBytes); |  | ||||||
|  |  | ||||||
|                         int responseCode = urlConnection.getResponseCode(); |  | ||||||
|                         urlConnection.disconnect(); |  | ||||||
|                         if (responseCode >= 300) { |  | ||||||
|                             return Result.retry(); |  | ||||||
|                         } |  | ||||||
|                     } catch (Exception e) { |  | ||||||
|                         Log.e(TAG, "Error sending data", e); |  | ||||||
|                         return Result.retry(); |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     Log.w(TAG, "Invalid HA url"); |  | ||||||
|                     return Result.failure(); |  | ||||||
|                 } |  | ||||||
|             } catch (Exception e) { |  | ||||||
|                 Log.e(TAG, "Error setting next alarm", e); |  | ||||||
|                 return Result.failure(); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             Log.w(TAG, "Webhook id not found"); |  | ||||||
|             return Result.failure(); |  | ||||||
|         } |  | ||||||
|         return Result.success(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| package com.keyboardcrumbs.hassclient; |  | ||||||
|  |  | ||||||
| import android.util.Log; |  | ||||||
| import android.os.AsyncTask; |  | ||||||
|  |  | ||||||
| import java.net.URL; |  | ||||||
| import java.net.HttpURLConnection; |  | ||||||
| import java.io.OutputStream; |  | ||||||
|  |  | ||||||
| import android.webkit.URLUtil; |  | ||||||
|  |  | ||||||
| import org.json.JSONObject; |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.content.Context; |  | ||||||
| import java.lang.ref.WeakReference; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| public class UpdateTokenTask extends AsyncTask<String, String, String> { |  | ||||||
|  |  | ||||||
|     private static final String TAG = "UpdateTokenTask"; |  | ||||||
|  |  | ||||||
|     private WeakReference<Context> contextRef; |  | ||||||
|  |  | ||||||
|     public UpdateTokenTask(Context context){ |  | ||||||
|         contextRef = new WeakReference<>(context); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected void onPreExecute() { |  | ||||||
|         super.onPreExecute(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected String doInBackground(String... params) { |  | ||||||
|         Log.d(TAG, "Updating push token"); |  | ||||||
|         Context context = contextRef.get(); |  | ||||||
|         if (context != null) { |  | ||||||
|             String token = params[0]; |  | ||||||
|             SharedPreferences prefs = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE); |  | ||||||
|             SharedPreferences.Editor editor = prefs.edit(); |  | ||||||
|             editor.putString("flutter.npush-token", token); |  | ||||||
|             editor.commit(); |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,38 +0,0 @@ | |||||||
| package com.keyboardcrumbs.hassclient; |  | ||||||
|  |  | ||||||
| import android.content.Context; |  | ||||||
| import android.location.Location; |  | ||||||
| import android.preference.PreferenceManager; |  | ||||||
|  |  | ||||||
| import java.text.DateFormat; |  | ||||||
| import java.util.Date; |  | ||||||
|  |  | ||||||
| class Utils { |  | ||||||
|  |  | ||||||
|     static final String KEY_REQUESTING_LOCATION_UPDATES = "flutter.foreground-location-service"; |  | ||||||
|     static final String KEY_LOCATION_UPDATE_INTERVAL = "flutter.active-location-interval"; |  | ||||||
|  |  | ||||||
|     static boolean requestingLocationUpdates(Context context) { |  | ||||||
|         return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getBoolean(KEY_REQUESTING_LOCATION_UPDATES, false); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static long getLocationUpdateIntervals(Context context) { |  | ||||||
|         return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getLong(KEY_LOCATION_UPDATE_INTERVAL, 90) * 1000; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static void setRequestingLocationUpdates(Context context, boolean requestingLocationUpdates) { |  | ||||||
|         context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) |  | ||||||
|                 .edit() |  | ||||||
|                 .putBoolean(KEY_REQUESTING_LOCATION_UPDATES, requestingLocationUpdates) |  | ||||||
|                 .apply(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static String getLocationText(Location location) { |  | ||||||
|         return location == null ? "Accuracy: unknown" : |  | ||||||
|                 "Accuracy: " + location.getAccuracy(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static String getLocationTitle(Location location) { |  | ||||||
|         return location == null ? "Requesting location..." : "Location updated at " + DateFormat.getDateTimeInstance().format(new Date(location.getTime())); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -32,7 +32,7 @@ class AppSettings { | |||||||
|   DisplayMode displayMode; |   DisplayMode displayMode; | ||||||
|   AppTheme appTheme; |   AppTheme appTheme; | ||||||
|   final int defaultLocationUpdateIntervalMinutes = 20; |   final int defaultLocationUpdateIntervalMinutes = 20; | ||||||
|   final int defaultActiveLocationUpdateIntervalSeconds = 90; |   final int defaultActiveLocationUpdateIntervalSeconds = 900; | ||||||
|   Duration locationUpdateInterval; |   Duration locationUpdateInterval; | ||||||
|   bool locationTrackingEnabled = false; |   bool locationTrackingEnabled = false; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,6 +12,12 @@ class IntegrationSettingsPage extends StatefulWidget { | |||||||
| class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> { | class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> { | ||||||
|  |  | ||||||
|   static const platform = const MethodChannel('com.keyboardcrumbs.hassclient/native'); |   static const platform = const MethodChannel('com.keyboardcrumbs.hassclient/native'); | ||||||
|  |   static final locationAccuracy = { | ||||||
|  |     100: "Highest", | ||||||
|  |     102: "Balanced (about 100 meters)", | ||||||
|  |     104: "Low (up to 10 kilometers)", | ||||||
|  |     105: "Passive (last known location)", | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   int _locationInterval = AppSettings().defaultLocationUpdateIntervalMinutes; |   int _locationInterval = AppSettings().defaultLocationUpdateIntervalMinutes; | ||||||
|   int _activeLocationInterval = AppSettings().defaultActiveLocationUpdateIntervalSeconds; |   int _activeLocationInterval = AppSettings().defaultActiveLocationUpdateIntervalSeconds; | ||||||
| @@ -19,6 +25,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> { | |||||||
|   bool _foregroundLocationTrackingEnabled = false; |   bool _foregroundLocationTrackingEnabled = false; | ||||||
|   bool _wait = false; |   bool _wait = false; | ||||||
|   bool _changedHere = false; |   bool _changedHere = false; | ||||||
|  |   int _accuracy = 102; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   void initState() { |   void initState() { | ||||||
| @@ -33,10 +40,11 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> { | |||||||
|     SharedPreferences.getInstance().then((prefs) { |     SharedPreferences.getInstance().then((prefs) { | ||||||
|       setState(() { |       setState(() { | ||||||
|         _locationTrackingEnabled = prefs.getBool("location-enabled") ?? false; |         _locationTrackingEnabled = prefs.getBool("location-enabled") ?? false; | ||||||
|         _foregroundLocationTrackingEnabled = prefs.getBool("foreground-location-service") ?? false; |         _accuracy = prefs.getInt("location-updates-priority") ?? 102; | ||||||
|  |         _foregroundLocationTrackingEnabled = (prefs.getInt("location-updates-state") ?? 0) > 0; | ||||||
|         _locationInterval = prefs.getInt("location-interval") ?? |         _locationInterval = prefs.getInt("location-interval") ?? | ||||||
|           AppSettings().defaultLocationUpdateIntervalMinutes; |           AppSettings().defaultLocationUpdateIntervalMinutes; | ||||||
|         _activeLocationInterval = prefs.getInt("active-location-interval") ?? |         _activeLocationInterval = prefs.getInt("location-updates-interval") ?? | ||||||
|             AppSettings().defaultActiveLocationUpdateIntervalSeconds; |             AppSettings().defaultActiveLocationUpdateIntervalSeconds; | ||||||
|         if (_locationInterval < 15) { |         if (_locationInterval < 15) { | ||||||
|           _locationInterval = 15; |           _locationInterval = 15; | ||||||
| @@ -94,9 +102,13 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   _switchForegroundLocationTrackingState(bool state) async { |   _switchForegroundLocationTrackingState(bool state) async { | ||||||
|     await AppSettings().save({'active-location-interval': _activeLocationInterval}); |     await AppSettings().save({'location-updates-interval': _activeLocationInterval, 'location-updates-priority': _accuracy}); | ||||||
|     if (state) { |     if (state) { | ||||||
|  |       try { | ||||||
|         await platform.invokeMethod('startLocationService'); |         await platform.invokeMethod('startLocationService'); | ||||||
|  |       } catch (e) { | ||||||
|  |         _foregroundLocationTrackingEnabled = false; | ||||||
|  |       } | ||||||
|     } else { |     } else { | ||||||
|       await platform.invokeMethod('stopLocationService'); |       await platform.invokeMethod('stopLocationService'); | ||||||
|     } |     } | ||||||
| @@ -154,11 +166,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> { | |||||||
|         ), |         ), | ||||||
|         Container(height: Sizes.rowPadding), |         Container(height: Sizes.rowPadding), | ||||||
|         Text("Active location tracking", style: Theme.of(context).textTheme.title), |         Text("Active location tracking", style: Theme.of(context).textTheme.title), | ||||||
|         Text("Works in foreground noticeably affecting phone battery. Sends most accurate location getting it from phone if possible. Can be very frequent.", |         Container(height: Sizes.rowPadding), | ||||||
|           style: Theme.of(context).textTheme.caption, |  | ||||||
|           softWrap: true, |  | ||||||
|         ), |  | ||||||
|         Container(height: Sizes.rowPadding,), |  | ||||||
|         Row( |         Row( | ||||||
|           children: <Widget>[ |           children: <Widget>[ | ||||||
|             Text("Enable"), |             Text("Enable"), | ||||||
| @@ -175,7 +183,26 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> { | |||||||
|           ], |           ], | ||||||
|         ), |         ), | ||||||
|         Container(height: Sizes.rowPadding), |         Container(height: Sizes.rowPadding), | ||||||
|         Text("Update device location every"), |         Text("Accuracy:", style: Theme.of(context).textTheme.body2), | ||||||
|  |         Container(height: Sizes.rowPadding), | ||||||
|  |         DropdownButton<int>( | ||||||
|  |           value: _accuracy, | ||||||
|  |           iconSize: 30.0, | ||||||
|  |           isExpanded: true, | ||||||
|  |           items: locationAccuracy.keys.map((value) { | ||||||
|  |             return new DropdownMenuItem<int>( | ||||||
|  |               value: value, | ||||||
|  |               child: Text('${locationAccuracy[value]}'), | ||||||
|  |             ); | ||||||
|  |           }).toList(), | ||||||
|  |           onChanged: _foregroundLocationTrackingEnabled ? null : (val) { | ||||||
|  |             setState(() { | ||||||
|  |               _accuracy = val; | ||||||
|  |             }); | ||||||
|  |           }, | ||||||
|  |         ), | ||||||
|  |         Container(height: Sizes.rowPadding), | ||||||
|  |         Text("Update intervals"), | ||||||
|         Row( |         Row( | ||||||
|           mainAxisAlignment: MainAxisAlignment.center, |           mainAxisAlignment: MainAxisAlignment.center, | ||||||
|           mainAxisSize: MainAxisSize.max, |           mainAxisSize: MainAxisSize.max, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user