diff --git a/android/gradle.properties b/android/gradle.properties
index 6e98ecfec..1f783d1bc 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
-VERSION_CODE = 51
-VERSION_NAME = 1.8.8
+VERSION_CODE = 52
+VERSION_NAME = 1.8.9
MIN_SDK_VERSION = 21
TARGET_SDK_VERSION = 29
diff --git a/android/uhabits-android/src/main/AndroidManifest.xml b/android/uhabits-android/src/main/AndroidManifest.xml
index f2dbda768..66e733339 100644
--- a/android/uhabits-android/src/main/AndroidManifest.xml
+++ b/android/uhabits-android/src/main/AndroidManifest.xml
@@ -1,10 +1,27 @@
+
-
-
-
-
\ No newline at end of file
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java
index 8c110ab26..cc5e55b20 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java
@@ -34,7 +34,6 @@ import org.isoron.uhabits.core.ui.screens.habits.list.*;
import org.isoron.uhabits.core.utils.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.receivers.*;
-import org.isoron.uhabits.sync.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.widgets.*;
@@ -81,8 +80,6 @@ public interface HabitsApplicationComponent
ReminderController getReminderController();
- SyncManager getSyncManager();
-
TaskRunner getTaskRunner();
WidgetPreferences getWidgetPreferences();
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java
index 049f6e319..7f9430b7b 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java
@@ -155,8 +155,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
// Temporarily disable this; we now always ask
findPreference("reminderSound").setVisible(false);
findPreference("pref_snooze_interval").setVisible(false);
-
- updateSync();
}
private void updateWeekdayPreference()
@@ -183,7 +181,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
if (key.equals("pref_first_weekday")) updateWeekdayPreference();
BackupManager.dataChanged("org.isoron.uhabits");
- updateSync();
}
private void setResultOnPreferenceClick(String key, final int result)
@@ -218,24 +215,4 @@ public class SettingsFragment extends PreferenceFragmentCompat
Preference ringtonePreference = findPreference("reminderSound");
ringtonePreference.setSummary(ringtoneName);
}
-
- private void updateSync()
- {
- if (prefs == null) return;
- boolean enabled = prefs.isSyncEnabled();
-
- Preference syncKey = findPreference("pref_sync_key");
- if (syncKey != null)
- {
- syncKey.setSummary(prefs.getSyncKey());
- syncKey.setVisible(enabled);
- }
-
- Preference syncAddress = findPreference("pref_sync_address");
- if (syncAddress != null)
- {
- syncAddress.setSummary(prefs.getSyncAddress());
- syncAddress.setVisible(enabled);
- }
- }
}
\ No newline at end of file
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ConnectivityReceiver.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ConnectivityReceiver.kt
deleted file mode 100644
index 0f861ae57..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ConnectivityReceiver.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.receivers
-
-import android.content.*
-import android.content.Context.*
-import android.net.*
-import org.isoron.uhabits.*
-
-class ConnectivityReceiver : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- if (context == null || intent == null) return
- val app = context.applicationContext as HabitsApplication
- val networkInfo = (context.getSystemService(CONNECTIVITY_SERVICE)
- as ConnectivityManager).activeNetworkInfo
- val isConnected = (networkInfo != null) &&
- networkInfo.isConnectedOrConnecting
- val syncManager = app.component.syncManager
- syncManager.onNetworkStatusChanged(isConnected)
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java
index 20e0f65a7..2942f8141 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java
@@ -26,7 +26,6 @@ import org.isoron.uhabits.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.ui.widgets.*;
import org.isoron.uhabits.intents.*;
-import org.isoron.uhabits.sync.*;
import org.isoron.uhabits.widgets.activities.*;
import dagger.*;
@@ -72,9 +71,6 @@ public class WidgetReceiver extends BroadcastReceiver
Log.i(TAG, String.format("Received intent: %s", intent.toString()));
- if (prefs.isSyncEnabled())
- context.startService(new Intent(context, SyncService.class));
-
try
{
IntentParser.CheckmarkIntentData data;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java
deleted file mode 100644
index 3d15d0d3e..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.sync;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.isoron.uhabits.core.database.*;
-
-@Table(name = "Events")
-public class Event
-{
- @Nullable
- @Column
- public Long id;
-
- @NonNull
- @Column(name = "timestamp")
- public Long timestamp;
-
- @NonNull
- @Column(name = "message")
- public String message;
-
- @NonNull
- @Column(name = "server_id")
- public String serverId;
-
- public Event()
- {
- timestamp = 0L;
- message = "";
- serverId = "";
- }
-
- public Event(@NonNull String serverId, long timestamp, @NonNull String message)
- {
- this.serverId = serverId;
- this.timestamp = timestamp;
- this.message = message;
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java
deleted file mode 100644
index 88fecb6bf..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.sync;
-
-import android.util.*;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.isoron.androidbase.*;
-import org.isoron.uhabits.BuildConfig;
-import org.isoron.uhabits.core.*;
-import org.isoron.uhabits.core.commands.*;
-import org.isoron.uhabits.core.database.*;
-import org.isoron.uhabits.core.preferences.*;
-import org.isoron.uhabits.database.*;
-import org.isoron.uhabits.utils.*;
-import org.json.*;
-
-import java.net.*;
-import java.util.*;
-
-import javax.inject.*;
-
-import io.socket.client.*;
-import io.socket.client.Socket;
-import io.socket.emitter.*;
-
-import static io.socket.client.Socket.EVENT_CONNECT;
-import static io.socket.client.Socket.EVENT_CONNECTING;
-import static io.socket.client.Socket.EVENT_CONNECT_ERROR;
-import static io.socket.client.Socket.EVENT_CONNECT_TIMEOUT;
-import static io.socket.client.Socket.EVENT_DISCONNECT;
-import static io.socket.client.Socket.EVENT_PING;
-import static io.socket.client.Socket.EVENT_PONG;
-import static io.socket.client.Socket.EVENT_RECONNECT;
-import static io.socket.client.Socket.EVENT_RECONNECT_ATTEMPT;
-import static io.socket.client.Socket.EVENT_RECONNECT_ERROR;
-import static io.socket.client.Socket.EVENT_RECONNECT_FAILED;
-
-@AppScope
-public class SyncManager implements CommandRunner.Listener
-{
- public static final String EVENT_AUTH = "auth";
-
- public static final String EVENT_AUTH_OK = "authOK";
-
- public static final String EVENT_EXECUTE_EVENT = "execute";
-
- public static final String EVENT_FETCH = "fetch";
-
- public static final String EVENT_FETCH_OK = "fetchOK";
-
- public static final String EVENT_POST_EVENT = "postEvent";
-
- @NonNull
- private String clientId;
-
- @NonNull
- private String groupKey;
-
- @NonNull
- private Socket socket;
-
- @NonNull
- private LinkedList pendingConfirmation;
-
- @NonNull
- private LinkedList pendingEmit;
-
- private boolean readyToEmit = false;
-
- @NonNull
- private final Preferences prefs;
-
- @NonNull
- private CommandRunner commandRunner;
-
- @NonNull
- private CommandParser commandParser;
-
- private boolean isListening;
-
- private SSLContextProvider sslProvider;
-
- private final Repository repository;
-
- @Inject
- public SyncManager(@NonNull SSLContextProvider sslProvider,
- @NonNull Preferences prefs,
- @NonNull CommandRunner commandRunner,
- @NonNull CommandParser commandParser)
- {
- Log.i("SyncManager", this.toString());
-
- this.sslProvider = sslProvider;
- this.prefs = prefs;
- this.commandRunner = commandRunner;
- this.commandParser = commandParser;
- this.isListening = false;
-
- repository = new Repository<>(Event.class,
- new AndroidDatabase(DatabaseUtils.openDatabase()));
- pendingConfirmation = new LinkedList<>();
- pendingEmit = new LinkedList<>(repository.findAll("order by timestamp"));
-
- groupKey = prefs.getSyncKey();
- clientId = prefs.getSyncClientId();
- String serverURL = prefs.getSyncAddress();
-
- Log.d("SyncManager", clientId);
- connect(serverURL);
- }
-
- @Override
- public void onCommandExecuted(@NonNull Command command,
- @Nullable Long refreshKey)
- {
- if (command.isRemote()) return;
-
- JSONObject msg = toJSONObject(command.toJson());
- Long now = new Date().getTime();
- Event e = new Event(command.getId(), now, msg.toString());
- repository.save(e);
-
- Log.i("SyncManager", "Adding to outbox: " + msg.toString());
-
- pendingEmit.add(e);
- if (readyToEmit) emitPending();
- }
-
- public void onNetworkStatusChanged(boolean isConnected)
- {
- if (!isListening) return;
- if (isConnected) socket.connect();
- else socket.disconnect();
- }
-
- public void startListening()
- {
- if (!prefs.isSyncEnabled()) return;
- if (groupKey.isEmpty()) return;
- if (isListening) return;
-
- isListening = true;
- socket.connect();
- commandRunner.addListener(this);
- }
-
- public void stopListening()
- {
- if (!isListening) return;
-
- commandRunner.removeListener(this);
- socket.close();
- isListening = false;
- }
-
- private void connect(String serverURL)
- {
- try
- {
- IO.setDefaultSSLContext(sslProvider.getCACertSSLContext());
- socket = IO.socket(serverURL);
-
- logSocketEvent(socket, EVENT_CONNECT, "Connected");
- logSocketEvent(socket, EVENT_CONNECT_TIMEOUT, "Connect timeout");
- logSocketEvent(socket, EVENT_CONNECTING, "Connecting...");
- logSocketEvent(socket, EVENT_CONNECT_ERROR, "Connect error");
- logSocketEvent(socket, EVENT_DISCONNECT, "Disconnected");
- logSocketEvent(socket, EVENT_RECONNECT, "Reconnected");
- logSocketEvent(socket, EVENT_RECONNECT_ATTEMPT, "Reconnecting...");
- logSocketEvent(socket, EVENT_RECONNECT_ERROR, "Reconnect error");
- logSocketEvent(socket, EVENT_RECONNECT_FAILED, "Reconnect failed");
- logSocketEvent(socket, EVENT_DISCONNECT, "Disconnected");
- logSocketEvent(socket, EVENT_PING, "Ping");
- logSocketEvent(socket, EVENT_PONG, "Pong");
-
- socket.on(EVENT_CONNECT, new OnConnectListener());
- socket.on(EVENT_DISCONNECT, new OnDisconnectListener());
- socket.on(EVENT_EXECUTE_EVENT, new OnExecuteCommandListener());
- socket.on(EVENT_AUTH_OK, new OnAuthOKListener());
- socket.on(EVENT_FETCH_OK, new OnFetchOKListener());
- }
- catch (URISyntaxException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- private void emitPending()
- {
- try
- {
- for (Event e : pendingEmit)
- {
- Log.i("SyncManager", "Emitting: " + e.message);
- socket.emit(EVENT_POST_EVENT, new JSONObject(e.message));
- pendingConfirmation.add(e);
- }
-
- pendingEmit.clear();
- }
- catch (JSONException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- private void logSocketEvent(Socket socket, String event, final String msg)
- {
- socket.on(event, args ->
- {
- Log.i("SyncManager", msg);
- for (Object o : args)
- if (o instanceof SocketIOException)
- ((SocketIOException) o).printStackTrace();
- });
- }
-
- private JSONObject toJSONObject(String json)
- {
- try
- {
- return new JSONObject(json);
- }
- catch (JSONException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- private void updateLastSync(Long timestamp)
- {
- prefs.setLastSync(timestamp + 1);
- }
-
- private class OnAuthOKListener implements Emitter.Listener
- {
- @Override
- public void call(Object... args)
- {
- Log.i("SyncManager", "Auth OK");
- Log.i("SyncManager", "Requesting commands since last sync");
-
- Long lastSync = prefs.getLastSync();
- socket.emit(EVENT_FETCH, buildFetchMessage(lastSync));
- }
-
- private JSONObject buildFetchMessage(Long lastSync)
- {
- try
- {
- JSONObject json = new JSONObject();
- json.put("since", lastSync);
- return json;
- }
- catch (JSONException e)
- {
- throw new RuntimeException(e);
- }
- }
- }
-
- private class OnConnectListener implements Emitter.Listener
- {
- @Override
- public void call(Object... args)
- {
- Log.i("SyncManager", "Sending auth message");
- socket.emit(EVENT_AUTH, buildAuthMessage());
- }
-
- private JSONObject buildAuthMessage()
- {
- try
- {
- JSONObject json = new JSONObject();
- json.put("groupKey", groupKey);
- json.put("clientId", clientId);
- json.put("version", BuildConfig.VERSION_NAME);
- return json;
- }
- catch (JSONException e)
- {
- throw new RuntimeException(e);
- }
- }
- }
-
- private class OnDisconnectListener implements Emitter.Listener
- {
- @Override
- public void call(Object... args)
- {
- readyToEmit = false;
- for (Event e : pendingConfirmation) pendingEmit.add(e);
- pendingConfirmation.clear();
- }
- }
-
- private class OnExecuteCommandListener implements Emitter.Listener
- {
- @Override
- public void call(Object... args)
- {
- try
- {
- Log.d("SyncManager",
- String.format("Received command: %s", args[0].toString()));
- JSONObject root = new JSONObject(args[0].toString());
- updateLastSync(root.getLong("timestamp"));
- executeCommand(root);
- }
- catch (JSONException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- private void executeCommand(JSONObject root) throws JSONException
- {
- Command received = commandParser.parse(root.toString());
- received.setRemote(true);
-
- for (Event e : pendingConfirmation)
- {
- if (e.serverId.equals(received.getId()))
- {
- Log.i("SyncManager", "Pending command confirmed");
- pendingConfirmation.remove(e);
- repository.remove(e);
- return;
- }
- }
-
- Log.d("SyncManager", "Executing received command");
- commandRunner.execute(received, null);
- }
- }
-
- private class OnFetchOKListener implements Emitter.Listener
- {
- @Override
- public void call(Object... args)
- {
- try
- {
- Log.i("SyncManager", "Fetch OK");
-
- JSONObject json = (JSONObject) args[0];
- updateLastSync(json.getLong("timestamp"));
-
- emitPending();
- readyToEmit = true;
- }
- catch (JSONException e)
- {
- throw new RuntimeException(e);
- }
- }
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java
deleted file mode 100644
index 12adf46f2..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.sync;
-
-import android.app.*;
-import android.content.*;
-import android.net.*;
-import android.os.*;
-import androidx.core.app.*;
-
-import org.isoron.uhabits.*;
-import org.isoron.uhabits.core.preferences.*;
-import org.isoron.uhabits.receivers.*;
-
-public class SyncService extends Service implements Preferences.Listener
-{
- private SyncManager syncManager;
-
- private Preferences prefs;
-
- private ConnectivityReceiver connectivityReceiver;
-
- public SyncService()
- {
- }
-
- @Override
- public IBinder onBind(Intent intent)
- {
- return null;
- }
-
- @Override
- public void onCreate()
- {
- Intent notificationIntent = new Intent(this, SyncService.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
-
- Notification notification = new NotificationCompat.Builder(this)
- .setContentTitle("Loop Habit Tracker")
- .setContentText("Sync service running")
- .setSmallIcon(R.drawable.ic_notification)
- .setPriority(NotificationCompat.PRIORITY_MIN)
- .setContentIntent(pendingIntent)
- .build();
-
- startForeground(99999, notification);
-
- connectivityReceiver = new ConnectivityReceiver();
- IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
- this.registerReceiver(connectivityReceiver, filter);
-
- HabitsApplication app = (HabitsApplication) getApplicationContext();
- syncManager = app.getComponent().getSyncManager();
- syncManager.startListening();
-
- prefs = app.getComponent().getPreferences();
- prefs.addListener(this);
- }
-
- @Override
- public void onSyncFeatureChanged()
- {
- if(!prefs.isSyncEnabled()) stopSelf();
- }
-
- @Override
- public void onDestroy()
- {
- unregisterReceiver(connectivityReceiver);
- syncManager.stopListening();
- }
-}
diff --git a/android/uhabits-android/src/main/play/release-notes/en-US/alpha.txt b/android/uhabits-android/src/main/play/release-notes/en-US/alpha.txt
index 6cb4c5aa1..59075b54b 100644
--- a/android/uhabits-android/src/main/play/release-notes/en-US/alpha.txt
+++ b/android/uhabits-android/src/main/play/release-notes/en-US/alpha.txt
@@ -1,10 +1,10 @@
-1.8.8
-* Small tweaks to the habit scheduling algorithm
-* Fix some crashes
+1.8.9
+* Remove unused permissions
+* Notification bundling
1.8:
* New bar chart showing number of repetitions performed each week, month or year
* Performing habits on irregular weekdays will no longer break your streak
-* More colors to choose from (now 20 in total)
+* More colors
* Customize how transparent the widgets are
* Customize the first day of the week
* Yes/No buttons on notifications
diff --git a/android/uhabits-android/src/main/res/xml/preferences.xml b/android/uhabits-android/src/main/res/xml/preferences.xml
index 032dda1d7..2d24e2863 100644
--- a/android/uhabits-android/src/main/res/xml/preferences.xml
+++ b/android/uhabits-android/src/main/res/xml/preferences.xml
@@ -190,22 +190,6 @@
android:title="Enable widget stacks"
app:iconSpaceReserved="false" />
-
-
-
-
-
-
\ No newline at end of file