diff --git a/AndroidManifest.xml b/AndroidManifest.xml index cdeca242e1..73fddfea2f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -269,6 +269,13 @@ + + + + + + + diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index 9de2d7029b..66969cf4af 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -24,6 +24,7 @@ import com.actionbarsherlock.view.MenuItem; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; +import org.thoughtcrime.securesms.service.DirectoryRefreshListener; import org.whispersystems.textsecure.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.ThreadDatabase; @@ -66,6 +67,8 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment initializeSenderReceiverService(); initializeResources(); initializeContactUpdatesReceiver(); + + DirectoryRefreshListener.schedule(this); } @Override diff --git a/src/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java b/src/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java new file mode 100644 index 0000000000..b617f7e5c1 --- /dev/null +++ b/src/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java @@ -0,0 +1,60 @@ +package org.thoughtcrime.securesms.service; + + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import org.thoughtcrime.securesms.util.TextSecurePreferences; + +public class DirectoryRefreshListener extends BroadcastReceiver { + + private static final String REFRESH_EVENT = "org.whispersystems.whisperpush.DIRECTORY_REFRESH"; + private static final String BOOT_EVENT = "android.intent.action.BOOT_COMPLETED"; + + private static final long INTERVAL = 24 * 60 * 60 * 1000; // 24 hours. + + @Override + public void onReceive(Context context, Intent intent) { + if (REFRESH_EVENT.equals(intent.getAction())) handleRefreshAction(context); + else if (BOOT_EVENT.equals(intent.getAction())) handleBootEvent(context); + } + + private void handleBootEvent(Context context) { + schedule(context); + } + + private void handleRefreshAction(Context context) { + schedule(context); + } + + public static void schedule(Context context) { + if (!TextSecurePreferences.isPushRegistered(context)) return; + + AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(DirectoryRefreshListener.REFRESH_EVENT); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); + long time = TextSecurePreferences.getDirectoryRefreshTime(context); + + if (time <= System.currentTimeMillis()) { + if (time != 0) { + Intent serviceIntent = new Intent(context, DirectoryRefreshService.class); + serviceIntent.setAction(DirectoryRefreshService.REFRESH_ACTION); + context.startService(serviceIntent); + } + + time = System.currentTimeMillis() + INTERVAL; + } + + Log.w("DirectoryRefreshListener", "Scheduling for: " + time); + + alarmManager.cancel(pendingIntent); + alarmManager.set(AlarmManager.RTC_WAKEUP, time, pendingIntent); + + TextSecurePreferences.setDirectoryRefreshTime(context, time); + } + +} diff --git a/src/org/thoughtcrime/securesms/service/DirectoryRefreshService.java b/src/org/thoughtcrime/securesms/service/DirectoryRefreshService.java new file mode 100644 index 0000000000..72dd9cff36 --- /dev/null +++ b/src/org/thoughtcrime/securesms/service/DirectoryRefreshService.java @@ -0,0 +1,79 @@ +package org.thoughtcrime.securesms.service; + + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.PowerManager; +import android.util.Log; + +import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.whispersystems.textsecure.directory.Directory; +import org.whispersystems.textsecure.push.PushServiceSocket; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public class DirectoryRefreshService extends Service { + + public static final String REFRESH_ACTION = "org.whispersystems.whisperpush.REFRESH_ACTION"; + + private static final Executor executor = Executors.newSingleThreadExecutor(); + + @Override + public int onStartCommand (Intent intent, int flags, int startId) { + if (REFRESH_ACTION.equals(intent.getAction())) { + handleRefreshAction(); + } + return START_NOT_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void handleRefreshAction() { + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Directory Refresh"); + wakeLock.acquire(); + + executor.execute(new RefreshRunnable(wakeLock)); + } + + private class RefreshRunnable implements Runnable { + private final PowerManager.WakeLock wakeLock; + private final Context context; + + public RefreshRunnable(PowerManager.WakeLock wakeLock) { + this.wakeLock = wakeLock; + this.context = DirectoryRefreshService.this.getApplicationContext(); + } + + public void run() { + try { + Log.w("DirectoryRefreshService", "Refreshing directory..."); + Directory directory = Directory.getInstance(context); + String localNumber = TextSecurePreferences.getLocalNumber(context); + String password = TextSecurePreferences.getPushServerPassword(context); + PushServiceSocket socket = new PushServiceSocket(context, localNumber, password); + + Set eligibleContactTokens = directory.getPushEligibleContactTokens(localNumber); + List activeTokens = socket.retrieveDirectory(eligibleContactTokens); + + if (activeTokens != null) { + eligibleContactTokens.removeAll(activeTokens); + directory.setTokens(activeTokens, eligibleContactTokens); + } + + Log.w("DirectoryRefreshService", "Directory refresh complete..."); + } finally { + if (wakeLock != null && wakeLock.isHeld()) + wakeLock.release(); + } + } + } +} diff --git a/src/org/thoughtcrime/securesms/service/RegistrationService.java b/src/org/thoughtcrime/securesms/service/RegistrationService.java index 8f5e138dd7..53fa4051ad 100644 --- a/src/org/thoughtcrime/securesms/service/RegistrationService.java +++ b/src/org/thoughtcrime/securesms/service/RegistrationService.java @@ -289,6 +289,8 @@ public class RegistrationService extends Service { contactTokens.removeAll(activeTokens); Directory.getInstance(this).setTokens(activeTokens, contactTokens); } + + DirectoryRefreshListener.schedule(this); } private synchronized String waitForChallenge() throws AccountVerificationTimeoutException { diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 009d36f1e1..2e54f6af2c 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -38,6 +38,15 @@ public class TextSecurePreferences { private static final String GCM_PASSWORD_PREF = "pref_gcm_password"; private static final String PROMPTED_PUSH_REGISTRATION_PREF = "pref_prompted_push_registration"; private static final String SIGNALING_KEY_PREF = "pref_signaling_key"; + private static final String DIRECTORY_FRESH_TIME_PREF = "pref_directory_refresh_time"; + + public static long getDirectoryRefreshTime(Context context) { + return getLongPreference(context, DIRECTORY_FRESH_TIME_PREF, 0L); + } + + public static void setDirectoryRefreshTime(Context context, long value) { + setLongPreference(context, DIRECTORY_FRESH_TIME_PREF, value); + } public static String getLocalNumber(Context context) { return getStringPreference(context, LOCAL_NUMBER_PREF, "No Stored Number"); @@ -224,4 +233,13 @@ public class TextSecurePreferences { PreferenceManager.getDefaultSharedPreferences(context).edit().putInt(key, value).commit(); } + private static long getLongPreference(Context context, String key, long defaultValue) { + return PreferenceManager.getDefaultSharedPreferences(context).getLong(key, defaultValue); + } + + private static void setLongPreference(Context context, String key, long value) { + PreferenceManager.getDefaultSharedPreferences(context).edit().putLong(key, value).commit(); + } + + }