#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-messaging:20.2.0'
 | 
			
		||||
    implementation 'androidx.work:work-runtime:2.3.4'
 | 
			
		||||
    implementation "androidx.concurrent:concurrent-futures:1.0.0"
 | 
			
		||||
    testImplementation 'junit:junit:4.12'
 | 
			
		||||
    androidTestImplementation 'com.android.support.test:runner:1.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" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </receiver>
 | 
			
		||||
        <receiver android:name=".Autostart">
 | 
			
		||||
        <receiver android:name=".LocationUpdatesAfterReboot">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.BOOT_COMPLETED" />
 | 
			
		||||
            </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.content.Intent;
 | 
			
		||||
import android.location.Location;
 | 
			
		||||
import android.os.Binder;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.HandlerThread;
 | 
			
		||||
@@ -78,9 +77,6 @@ public class LocationUpdatesService extends Service {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        mLocationRequest = new LocationRequest();
 | 
			
		||||
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
 | 
			
		||||
 | 
			
		||||
        getLastLocation();
 | 
			
		||||
 | 
			
		||||
        HandlerThread handlerThread = new HandlerThread(TAG);
 | 
			
		||||
        handlerThread.start();
 | 
			
		||||
@@ -130,8 +126,10 @@ public class LocationUpdatesService extends Service {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void requestLocationUpdates() {
 | 
			
		||||
        long requestInterval = Utils.getLocationUpdateIntervals(getApplicationContext());
 | 
			
		||||
        Log.i(TAG, "Requesting location updates. Interval is " + requestInterval);
 | 
			
		||||
        long requestInterval = LocationUtils.getLocationUpdateIntervals(getApplicationContext());
 | 
			
		||||
        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.setFastestInterval(requestInterval);
 | 
			
		||||
        startForeground(NOTIFICATION_ID, getNotification());
 | 
			
		||||
@@ -146,7 +144,7 @@ public class LocationUpdatesService extends Service {
 | 
			
		||||
    private Notification getNotification() {
 | 
			
		||||
        Intent intent = new Intent(this, LocationUpdatesService.class);
 | 
			
		||||
 | 
			
		||||
        CharSequence text = Utils.getLocationText(mLocation);
 | 
			
		||||
        CharSequence text = LocationUtils.getLocationText(mLocation);
 | 
			
		||||
 | 
			
		||||
        intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true);
 | 
			
		||||
 | 
			
		||||
@@ -157,13 +155,13 @@ public class LocationUpdatesService extends Service {
 | 
			
		||||
                new Intent(this, MainActivity.class), 0);
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
                .addAction(R.drawable.blank_icon, "Stop",
 | 
			
		||||
                .addAction(R.drawable.blank_icon, "Stop tracking",
 | 
			
		||||
                        servicePendingIntent)
 | 
			
		||||
                .setContentText(text)
 | 
			
		||||
                .setPriority(-1)
 | 
			
		||||
                .setContentTitle(Utils.getLocationTitle(mLocation))
 | 
			
		||||
                .setContentTitle(LocationUtils.getLocationTitle(mLocation))
 | 
			
		||||
                .setOngoing(true)
 | 
			
		||||
                .setSmallIcon(R.drawable.mini_icon)
 | 
			
		||||
                .setWhen(System.currentTimeMillis());
 | 
			
		||||
@@ -171,21 +169,6 @@ public class LocationUpdatesService extends Service {
 | 
			
		||||
        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) {
 | 
			
		||||
        Log.i(TAG, "New location: " + location);
 | 
			
		||||
 | 
			
		||||
@@ -198,6 +181,7 @@ public class LocationUpdatesService extends Service {
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        Data locationData = new Data.Builder()
 | 
			
		||||
                .putInt(SendDataHomeWorker.DATA_TYPE_KEY, SendDataHomeWorker.DATA_TYPE_LOCATION)
 | 
			
		||||
                .putDouble("Lat", mLocation.getLatitude())
 | 
			
		||||
                .putDouble("Long", mLocation.getLongitude())
 | 
			
		||||
                .putFloat("Acc", mLocation.getAccuracy())
 | 
			
		||||
@@ -205,7 +189,7 @@ public class LocationUpdatesService extends Service {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        OneTimeWorkRequest uploadWorkRequest =
 | 
			
		||||
                new OneTimeWorkRequest.Builder(SendLocationWorker.class)
 | 
			
		||||
                new OneTimeWorkRequest.Builder(SendDataHomeWorker.class)
 | 
			
		||||
                        .setBackoffCriteria(
 | 
			
		||||
                                BackoffPolicy.EXPONENTIAL,
 | 
			
		||||
                                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.core.app.ActivityCompat;
 | 
			
		||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 | 
			
		||||
import androidx.work.WorkManager;
 | 
			
		||||
 | 
			
		||||
import io.flutter.embedding.android.FlutterActivity;
 | 
			
		||||
import io.flutter.embedding.engine.FlutterEngine;
 | 
			
		||||
import io.flutter.plugins.GeneratedPluginRegistrant;
 | 
			
		||||
 | 
			
		||||
import android.Manifest;
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.IntentFilter;
 | 
			
		||||
import android.content.ServiceConnection;
 | 
			
		||||
import android.content.SharedPreferences;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.location.Location;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
 | 
			
		||||
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 int locationUpdatesType = LocationUtils.LOCATION_UPDATES_DISABLED;
 | 
			
		||||
    private long locationUpdatesInterval = LocationUtils.DEFAULT_LOCATION_UPDATE_INTERVAL_S * 1000;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
 | 
			
		||||
        GeneratedPluginRegistrant.registerWith(flutterEngine);
 | 
			
		||||
@@ -46,8 +44,7 @@ public class MainActivity extends FlutterActivity {
 | 
			
		||||
                                        .addOnCompleteListener(task -> {
 | 
			
		||||
                                            if (task.isSuccessful()) {
 | 
			
		||||
                                                String token = task.getResult().getToken();
 | 
			
		||||
                                                UpdateTokenTask updateTokenTask = new UpdateTokenTask(context);
 | 
			
		||||
                                                updateTokenTask.execute(token);
 | 
			
		||||
                                                context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).edit().putString("flutter.npush-token", token).apply();
 | 
			
		||||
                                                result.success(token);
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                Exception ex = task.getException();
 | 
			
		||||
@@ -64,17 +61,25 @@ public class MainActivity extends FlutterActivity {
 | 
			
		||||
                            }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "startLocationService":
 | 
			
		||||
                            Utils.setRequestingLocationUpdates(this, true);
 | 
			
		||||
                            if (isNoLocationPermissions()) {
 | 
			
		||||
                                requestLocationPermissions();
 | 
			
		||||
                            } else {
 | 
			
		||||
                                startLocationService();
 | 
			
		||||
                            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()) {
 | 
			
		||||
                                    requestLocationPermissions();
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    startLocationUpdates();
 | 
			
		||||
                                }
 | 
			
		||||
                                result.success("");
 | 
			
		||||
                            } catch (Exception e) {
 | 
			
		||||
                                result.error("location_error", e.getMessage(), null);
 | 
			
		||||
                            }
 | 
			
		||||
                            result.success("");
 | 
			
		||||
                            break;
 | 
			
		||||
                        case "stopLocationService":
 | 
			
		||||
                            Utils.setRequestingLocationUpdates(this, false);
 | 
			
		||||
                            stopLocationService();
 | 
			
		||||
                            stopLocationUpdates();
 | 
			
		||||
                            result.success("");
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -86,24 +91,28 @@ public class MainActivity extends FlutterActivity {
 | 
			
		||||
        return (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void startLocationService() {
 | 
			
		||||
        Intent myService = new Intent(MainActivity.this, LocationUpdatesService.class);
 | 
			
		||||
        startService(myService);
 | 
			
		||||
    private void startLocationUpdates() {
 | 
			
		||||
        if (locationUpdatesType == LocationUtils.LOCATION_UPDATES_SERVICE) {
 | 
			
		||||
            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);
 | 
			
		||||
        stopService(myService);
 | 
			
		||||
        WorkManager.getInstance(this).cancelUniqueWork(LocationUtils.LOCATION_WORK_NAME);
 | 
			
		||||
        LocationUtils.setLocationUpdatesState(this, LocationUtils.LOCATION_UPDATES_DISABLED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        /*if (Utils.requestingLocationUpdates(this)) {
 | 
			
		||||
            if (isNoLocationPermissions()) {
 | 
			
		||||
                requestLocationPermissions();
 | 
			
		||||
            }
 | 
			
		||||
        }*/
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -142,7 +151,9 @@ public class MainActivity extends FlutterActivity {
 | 
			
		||||
                                           @NonNull int[] grantResults) {
 | 
			
		||||
        if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
 | 
			
		||||
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 | 
			
		||||
                startLocationService();
 | 
			
		||||
                startLocationUpdates();
 | 
			
		||||
            } else {
 | 
			
		||||
                stopLocationUpdates();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ package com.keyboardcrumbs.hassclient;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.net.URLConnection;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
import android.app.NotificationChannel;
 | 
			
		||||
@@ -14,6 +13,8 @@ import android.content.Intent;
 | 
			
		||||
import android.media.RingtoneManager;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.core.app.NotificationCompat;
 | 
			
		||||
 | 
			
		||||
import com.google.firebase.messaging.FirebaseMessagingService;
 | 
			
		||||
@@ -26,7 +27,7 @@ import android.webkit.URLUtil;
 | 
			
		||||
 | 
			
		||||
public class MessagingService extends FirebaseMessagingService {
 | 
			
		||||
 | 
			
		||||
    private static final String TAG = "MessagingService";
 | 
			
		||||
    public static final String NOTIFICATION_ACTION_BROADCAST = "com.keyboardcrumbs.hassclient.haNotificationAction";
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onMessageReceived(RemoteMessage remoteMessage) {
 | 
			
		||||
@@ -39,19 +40,19 @@ public class MessagingService extends FirebaseMessagingService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onNewToken(String token) {
 | 
			
		||||
        UpdateTokenTask updateTokenTask = new UpdateTokenTask(this);
 | 
			
		||||
        updateTokenTask.execute(token);
 | 
			
		||||
    public void onNewToken(@NonNull String token) {
 | 
			
		||||
        getApplicationContext().getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).edit().putString("flutter.npush-token", token).apply();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void sendNotification(Map<String, String> data) {
 | 
			
		||||
        String channelId, messageBody, messageTitle, imageUrl, nTag, channelDescription;
 | 
			
		||||
        boolean autoCancel;
 | 
			
		||||
        if (!data.containsKey("channelId")) {
 | 
			
		||||
        String customChannelId = data.get("channelId");
 | 
			
		||||
        if (customChannelId == null) {
 | 
			
		||||
            channelId = "ha_notify";
 | 
			
		||||
            channelDescription = "Default notification channel";
 | 
			
		||||
        } else {
 | 
			
		||||
            channelId = data.get("channelId");
 | 
			
		||||
            channelId = customChannelId;
 | 
			
		||||
            channelDescription = channelId;
 | 
			
		||||
        }
 | 
			
		||||
        if (!data.containsKey("body")) {
 | 
			
		||||
@@ -114,7 +115,7 @@ public class MessagingService extends FirebaseMessagingService {
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 1; i <= 3; 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) {
 | 
			
		||||
                    broadcastIntent.putExtra("tag", nTag);
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -7,19 +7,17 @@ import android.content.Intent;
 | 
			
		||||
 | 
			
		||||
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 androidx.work.WorkRequest;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public class NextAlarmBroadcastReceiver extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
    private static final String TAG = "NextAlarmReceiver";
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onReceive(Context context, Intent intent) {
 | 
			
		||||
        if (intent == null) {
 | 
			
		||||
@@ -35,12 +33,17 @@ public class NextAlarmBroadcastReceiver extends BroadcastReceiver {
 | 
			
		||||
                .setRequiredNetworkType(NetworkType.CONNECTED)
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        Data workerData = new Data.Builder()
 | 
			
		||||
                .putInt(SendDataHomeWorker.DATA_TYPE_KEY, SendDataHomeWorker.DATA_TYPE_NEXT_ALARM)
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        OneTimeWorkRequest uploadWorkRequest =
 | 
			
		||||
                new OneTimeWorkRequest.Builder(UpdateNextAlarmWorker.class)
 | 
			
		||||
                new OneTimeWorkRequest.Builder(SendDataHomeWorker.class)
 | 
			
		||||
                        .setBackoffCriteria(
 | 
			
		||||
                                BackoffPolicy.EXPONENTIAL,
 | 
			
		||||
                                10,
 | 
			
		||||
                                TimeUnit.SECONDS)
 | 
			
		||||
                        .setInputData(workerData)
 | 
			
		||||
                        .setConstraints(constraints)
 | 
			
		||||
                        .build();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,73 +1,58 @@
 | 
			
		||||
package com.keyboardcrumbs.hassclient;
 | 
			
		||||
 | 
			
		||||
import android.app.AlarmManager;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import android.content.BroadcastReceiver;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
 | 
			
		||||
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 android.content.SharedPreferences;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
public class NotificationActionReceiver extends BroadcastReceiver {
 | 
			
		||||
 | 
			
		||||
    private static final String TAG = "NotificationAction";
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onReceive(Context context, Intent intent) {
 | 
			
		||||
        if (intent == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String intentAction = intent.getAction();
 | 
			
		||||
        if (intentAction == null || !intentAction.equalsIgnoreCase(MessagingService.NOTIFICATION_ACTION_BROADCAST)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        String rawActionData = intent.getStringExtra("actionData");
 | 
			
		||||
        if (intent.hasExtra("tag")) {
 | 
			
		||||
            String notificationTag = intent.getStringExtra("tag");
 | 
			
		||||
            NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
 | 
			
		||||
            notificationManager.cancel(notificationTag, 0);
 | 
			
		||||
        }
 | 
			
		||||
        SharedPreferences prefs = context.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 actionData = new JSONObject(rawActionData);
 | 
			
		||||
                if (URLUtil.isValidUrl(requestUrl)) {
 | 
			
		||||
                    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);
 | 
			
		||||
                    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");
 | 
			
		||||
        }
 | 
			
		||||
        Constraints constraints = new Constraints.Builder()
 | 
			
		||||
                .setRequiredNetworkType(NetworkType.CONNECTED)
 | 
			
		||||
                .build();
 | 
			
		||||
        Data workerData = new Data.Builder()
 | 
			
		||||
                .putInt(SendDataHomeWorker.DATA_TYPE_KEY, SendDataHomeWorker.DATA_TYPE_NOTIFICATION_ACTION)
 | 
			
		||||
                .putString("rawActionData", rawActionData)
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        OneTimeWorkRequest uploadWorkRequest =
 | 
			
		||||
                new OneTimeWorkRequest.Builder(SendDataHomeWorker.class)
 | 
			
		||||
                        .setBackoffCriteria(
 | 
			
		||||
                                BackoffPolicy.EXPONENTIAL,
 | 
			
		||||
                                10,
 | 
			
		||||
                                TimeUnit.SECONDS)
 | 
			
		||||
                        .setInputData(workerData)
 | 
			
		||||
                        .setConstraints(constraints)
 | 
			
		||||
                        .build();
 | 
			
		||||
 | 
			
		||||
        WorkManager
 | 
			
		||||
                .getInstance(context)
 | 
			
		||||
                .enqueueUniqueWork("NotificationAction", ExistingWorkPolicy.APPEND, uploadWorkRequest);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
  AppTheme appTheme;
 | 
			
		||||
  final int defaultLocationUpdateIntervalMinutes = 20;
 | 
			
		||||
  final int defaultActiveLocationUpdateIntervalSeconds = 90;
 | 
			
		||||
  final int defaultActiveLocationUpdateIntervalSeconds = 900;
 | 
			
		||||
  Duration locationUpdateInterval;
 | 
			
		||||
  bool locationTrackingEnabled = false;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,12 @@ class IntegrationSettingsPage extends StatefulWidget {
 | 
			
		||||
class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
 | 
			
		||||
 | 
			
		||||
  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 _activeLocationInterval = AppSettings().defaultActiveLocationUpdateIntervalSeconds;
 | 
			
		||||
@@ -19,6 +25,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
 | 
			
		||||
  bool _foregroundLocationTrackingEnabled = false;
 | 
			
		||||
  bool _wait = false;
 | 
			
		||||
  bool _changedHere = false;
 | 
			
		||||
  int _accuracy = 102;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
@@ -33,10 +40,11 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
 | 
			
		||||
    SharedPreferences.getInstance().then((prefs) {
 | 
			
		||||
      setState(() {
 | 
			
		||||
        _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") ??
 | 
			
		||||
          AppSettings().defaultLocationUpdateIntervalMinutes;
 | 
			
		||||
        _activeLocationInterval = prefs.getInt("active-location-interval") ??
 | 
			
		||||
        _activeLocationInterval = prefs.getInt("location-updates-interval") ??
 | 
			
		||||
            AppSettings().defaultActiveLocationUpdateIntervalSeconds;
 | 
			
		||||
        if (_locationInterval < 15) {
 | 
			
		||||
          _locationInterval = 15;
 | 
			
		||||
@@ -94,9 +102,13 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _switchForegroundLocationTrackingState(bool state) async {
 | 
			
		||||
    await AppSettings().save({'active-location-interval': _activeLocationInterval});
 | 
			
		||||
    await AppSettings().save({'location-updates-interval': _activeLocationInterval, 'location-updates-priority': _accuracy});
 | 
			
		||||
    if (state) {
 | 
			
		||||
      await platform.invokeMethod('startLocationService');
 | 
			
		||||
      try {
 | 
			
		||||
        await platform.invokeMethod('startLocationService');
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        _foregroundLocationTrackingEnabled = false;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      await platform.invokeMethod('stopLocationService');
 | 
			
		||||
    }
 | 
			
		||||
@@ -154,11 +166,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
 | 
			
		||||
        ),
 | 
			
		||||
        Container(height: Sizes.rowPadding),
 | 
			
		||||
        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.",
 | 
			
		||||
          style: Theme.of(context).textTheme.caption,
 | 
			
		||||
          softWrap: true,
 | 
			
		||||
        ),
 | 
			
		||||
        Container(height: Sizes.rowPadding,),
 | 
			
		||||
        Container(height: Sizes.rowPadding),
 | 
			
		||||
        Row(
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Text("Enable"),
 | 
			
		||||
@@ -175,7 +183,26 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        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(
 | 
			
		||||
          mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
          mainAxisSize: MainAxisSize.max,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user