Move Sync to foreground service

pull/286/head
Alinson S. Xavier 9 years ago
parent 3da996b8a4
commit 293d838d80

@ -16,37 +16,30 @@
~
~ You should have received a copy of the GNU General Public License along
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<manifest
package="org.isoron.uhabits"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="28"
android:versionName="1.7.1">
-->
<manifest package="org.isoron.uhabits"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="28"
android:versionName="1.7.1">
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name="HabitsApplication"
android:name=".HabitsApplication"
android:allowBackup="true"
android:backupAgent=".HabitsBackupAgent"
android:icon="@mipmap/ic_launcher"
android:label="@string/main_activity_title"
android:supportsRtl="true"
android:theme="@style/AppBaseTheme">
<meta-data
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI6aeWncbnMNo8E5GWeZ44dlc5cQ7tCROwFhOtiw"/>
@ -64,6 +57,7 @@
android:targetActivity=".activities.habits.list.ListHabitsActivity">
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
@ -75,7 +69,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.habits.list.ListHabitsActivity"/>
</activity>
<activity
android:name=".activities.settings.SettingsActivity"
android:label="@string/settings">
@ -83,12 +76,10 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.habits.list.ListHabitsActivity"/>
</activity>
<activity
android:name=".activities.intro.IntroActivity"
android:label=""
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
<activity
android:name=".widgets.HabitPickerDialog"
android:theme="@style/Theme.AppCompat.Light.Dialog">
@ -96,7 +87,6 @@
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
<activity
android:name=".activities.about.AboutActivity"
android:label="@string/about">
@ -116,7 +106,6 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_checkmark_info"/>
</receiver>
<receiver
android:name=".widgets.HistoryWidgetProvider"
android:label="@string/history">
@ -128,7 +117,6 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_history_info"/>
</receiver>
<receiver
android:name=".widgets.ScoreWidgetProvider"
android:label="@string/habit_strength">
@ -140,7 +128,6 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_score_info"/>
</receiver>
<receiver
android:name=".widgets.StreakWidgetProvider"
android:label="@string/streaks">
@ -152,7 +139,6 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_streak_info"/>
</receiver>
<receiver
android:name=".widgets.FrequencyWidgetProvider"
android:label="@string/frequency">
@ -164,33 +150,35 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_frequency_info"/>
</receiver>
<receiver android:name=".receivers.ReminderReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<receiver android:name=".receivers.WidgetReceiver">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="org.isoron.uhabits.ACTION_TOGGLE_REPETITION"/>
<data
android:host="org.isoron.uhabits"
android:scheme="content"/>
</intent-filter>
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="org.isoron.uhabits.ACTION_ADD_REPETITION"/>
<data
android:host="org.isoron.uhabits"
android:scheme="content"/>
</intent-filter>
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="org.isoron.uhabits.ACTION_REMOVE_REPETITION"/>
<data
android:host="org.isoron.uhabits"
android:scheme="content"/>
@ -211,8 +199,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<intent-filter>
<action
android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING"/>
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING"/>
</intent-filter>
</activity>
@ -232,8 +219,14 @@
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
android:resource="@xml/file_paths"/>
</provider>
<service
android:name=".sync.SyncService"
android:enabled="true"
android:exported="false">
</service>
</application>
</manifest>
</manifest>

@ -19,6 +19,7 @@
package org.isoron.uhabits.activities.habits.list;
import android.content.*;
import android.os.*;
import org.isoron.uhabits.*;
@ -47,8 +48,6 @@ public class ListHabitsActivity extends BaseActivity
private MidnightTimer midnightTimer;
private SyncManager syncManager;
public ListHabitsComponent getListHabitsComponent()
{
return component;
@ -84,7 +83,9 @@ public class ListHabitsActivity extends BaseActivity
rootView.setController(controller, selectionMenu);
midnightTimer = component.getMidnightTimer();
syncManager = app.getComponent().getSyncManager();
if(prefs.isSyncFeatureEnabled())
startService(new Intent(this, SyncService.class));
setScreen(screen);
controller.onStartup();
@ -93,7 +94,6 @@ public class ListHabitsActivity extends BaseActivity
@Override
protected void onPause()
{
syncManager.stopListening();
midnightTimer.onPause();
screen.onDettached();
adapter.cancelRefresh();
@ -107,7 +107,6 @@ public class ListHabitsActivity extends BaseActivity
screen.onAttached();
rootView.postInvalidate();
midnightTimer.onResume();
syncManager.startListening();
if (prefs.getTheme() == ThemeSwitcher.THEME_DARK &&
prefs.isPureBlackEnabled() != pureBlack)

@ -242,9 +242,10 @@ public class Preferences
}
if (key.equals("pref_sticky_notifications"))
{
for (Listener l : listeners) l.onNotificationsChanged();
}
if (key.equals("pref_feature_sync"))
for (Listener l : listeners) l.onSyncFeatureChanged();
}
public void removeListener(Listener listener)
@ -306,5 +307,7 @@ public class Preferences
default void onCheckmarkOrderChanged() {}
default void onNotificationsChanged() {}
default void onSyncFeatureChanged() {}
}
}

@ -30,6 +30,8 @@ import com.getpebble.android.kit.util.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.sync.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.utils.*;
@ -51,6 +53,8 @@ public class PebbleReceiver extends PebbleDataReceiver
private HabitList filteredHabits;
private Preferences prefs;
public PebbleReceiver()
{
super(WATCHAPP_UUID);
@ -69,9 +73,14 @@ public class PebbleReceiver extends PebbleDataReceiver
HabitsApplication app =
(HabitsApplication) context.getApplicationContext();
commandRunner = app.getComponent().getCommandRunner();
taskRunner = app.getComponent().getTaskRunner();
allHabits = app.getComponent().getHabitList();
AppComponent component = app.getComponent();
commandRunner = component.getCommandRunner();
taskRunner = component.getTaskRunner();
allHabits = component.getHabitList();
prefs = component.getPreferences();
if(prefs.isSyncFeatureEnabled())
context.startService(new Intent(context, SyncService.class));
HabitMatcher build = new HabitMatcherBuilder()
.setArchivedAllowed(false)

@ -24,6 +24,8 @@ import android.util.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.sync.*;
import dagger.*;
@ -59,6 +61,10 @@ public class WidgetReceiver extends BroadcastReceiver
IntentParser parser = app.getComponent().getIntentParser();
WidgetController controller = component.getWidgetController();
Preferences prefs = app.getComponent().getPreferences();
if(prefs.isSyncFeatureEnabled())
context.startService(new Intent(context, SyncService.class));
try
{

@ -26,17 +26,13 @@ import android.util.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.utils.*;
import org.json.*;
import java.io.*;
import java.net.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.*;
import java.util.*;
import javax.inject.*;
import javax.net.ssl.*;
import io.socket.client.*;
import io.socket.client.Socket;
@ -58,8 +54,10 @@ public class SyncManager implements CommandRunner.Listener
public static final String EVENT_POST_EVENT = "postEvent";
@NonNull
private String clientId;
@NonNull
private String groupKey;
@NonNull
@ -69,16 +67,17 @@ public class SyncManager implements CommandRunner.Listener
private LinkedList<Event> pendingConfirmation;
@NonNull
private List<Event> pendingEmit;
private LinkedList<Event> pendingEmit;
private boolean readyToEmit = false;
private Context context;
@NonNull
private final Preferences prefs;
@NonNull
private CommandRunner commandRunner;
@NonNull
private CommandParser commandParser;
@Inject
@ -87,54 +86,26 @@ public class SyncManager implements CommandRunner.Listener
@NonNull CommandRunner commandRunner,
@NonNull CommandParser commandParser)
{
this.context = context;
this.prefs = prefs;
this.commandRunner = commandRunner;
this.commandParser = commandParser;
pendingConfirmation = new LinkedList<>();
pendingEmit = Event.getAll();
pendingEmit = new LinkedList<>(Event.getAll());
groupKey = prefs.getSyncKey();
clientId = prefs.getSyncClientId();
String serverURL = prefs.getSyncAddress();
Log.d("SyncManager", clientId);
try
{
IO.setDefaultSSLContext(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);
}
connect(context, serverURL);
}
@Override
public void onCommandExecuted(@NonNull Command command,
@Nullable Long refreshKey)
{
if(command.isRemote()) return;
if (command.isRemote()) return;
JSONObject msg = command.toJson();
Long now = new Date().getTime();
@ -149,8 +120,8 @@ public class SyncManager implements CommandRunner.Listener
public void startListening()
{
if(!prefs.isSyncFeatureEnabled()) return;
if(groupKey.isEmpty()) return;
if (!prefs.isSyncFeatureEnabled()) return;
if (groupKey.isEmpty()) return;
socket.connect();
commandRunner.addListener(this);
@ -162,47 +133,52 @@ public class SyncManager implements CommandRunner.Listener
socket.close();
}
private void emitPending()
private void connect(@AppContext @NonNull Context context, String serverURL)
{
try
{
for (Event e : pendingEmit)
{
Log.i("SyncManager", "Emitting: " + e.message);
socket.emit(EVENT_POST_EVENT, new JSONObject(e.message));
pendingConfirmation.add(e);
}
IO.setDefaultSSLContext(SSLUtils.getCACertSSLContext(context));
socket = IO.socket(serverURL);
pendingEmit.clear();
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 (JSONException e)
catch (URISyntaxException e)
{
throw new RuntimeException(e);
}
}
private SSLContext getCACertSSLContext()
private void emitPending()
{
try
{
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = context.getAssets().open("cacert.pem");
Certificate ca = cf.generateCertificate(caInput);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("ca", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
for (Event e : pendingEmit)
{
Log.i("SyncManager", "Emitting: " + e.message);
socket.emit(EVENT_POST_EVENT, new JSONObject(e.message));
pendingConfirmation.add(e);
}
return ctx;
pendingEmit.clear();
}
catch (Exception e)
catch (JSONException e)
{
throw new RuntimeException(e);
}
@ -283,6 +259,8 @@ public class SyncManager implements CommandRunner.Listener
public void call(Object... args)
{
readyToEmit = false;
for(Event e : pendingConfirmation) pendingEmit.add(e);
pendingConfirmation.clear();
}
}

@ -0,0 +1,81 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.sync;
import android.app.*;
import android.content.*;
import android.os.*;
import android.support.v7.app.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.preferences.*;
public class SyncService extends Service implements Preferences.Listener
{
private SyncManager syncManager;
private Preferences prefs;
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);
HabitsApplication app = (HabitsApplication) getApplicationContext();
syncManager = app.getComponent().getSyncManager();
syncManager.startListening();
prefs = app.getComponent().getPreferences();
prefs.addListener(this);
}
@Override
public void onSyncFeatureChanged()
{
if(!prefs.isSyncFeatureEnabled()) stopSelf();
}
@Override
public void onDestroy()
{
syncManager.stopListening();
}
}

@ -0,0 +1,61 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.utils;
import android.content.*;
import android.support.annotation.*;
import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.*;
import javax.net.ssl.*;
public abstract class SSLUtils
{
public static SSLContext getCACertSSLContext(@NonNull Context context)
{
try
{
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = context.getAssets().open("cacert.pem");
Certificate ca = cf.generateCertificate(caInput);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("ca", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
return ctx;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
Loading…
Cancel
Save