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