diff --git a/clients/android/NewsBlur/AndroidManifest.xml b/clients/android/NewsBlur/AndroidManifest.xml
index 2cf5b8fab..2d7e8643f 100644
--- a/clients/android/NewsBlur/AndroidManifest.xml
+++ b/clients/android/NewsBlur/AndroidManifest.xml
@@ -122,8 +122,6 @@
-
-
diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/Main.java b/clients/android/NewsBlur/src/com/newsblur/activity/Main.java
index 4efc10ec2..5856810a9 100644
--- a/clients/android/NewsBlur/src/com/newsblur/activity/Main.java
+++ b/clients/android/NewsBlur/src/com/newsblur/activity/Main.java
@@ -15,20 +15,17 @@ import android.view.Window;
import com.newsblur.R;
import com.newsblur.fragment.FolderListFragment;
import com.newsblur.fragment.LogoutDialogFragment;
-import com.newsblur.fragment.SyncUpdateFragment;
import com.newsblur.service.BootReceiver;
-import com.newsblur.service.SyncService;
import com.newsblur.service.NBSyncService;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.PrefsUtils;
import com.newsblur.view.StateToggleButton.StateChangedListener;
-public class Main extends NbActivity implements StateChangedListener, SyncUpdateFragment.SyncUpdateFragmentInterface {
+public class Main extends NbActivity implements StateChangedListener {
private ActionBar actionBar;
private FolderListFragment folderFeedList;
private FragmentManager fragmentManager;
- private SyncUpdateFragment syncFragment;
private static final String TAG = "MainActivity";
private Menu menu;
@@ -48,18 +45,9 @@ public class Main extends NbActivity implements StateChangedListener, SyncUpdate
fragmentManager = getFragmentManager();
folderFeedList = (FolderListFragment) fragmentManager.findFragmentByTag("folderFeedListFragment");
folderFeedList.setRetainInstance(true);
-
- syncFragment = (SyncUpdateFragment) fragmentManager.findFragmentByTag(SyncUpdateFragment.TAG);
- if (syncFragment == null) {
- syncFragment = new SyncUpdateFragment();
- fragmentManager.beginTransaction().add(syncFragment, SyncUpdateFragment.TAG).commit();
- // for our first sync, don't just trigger a heavyweight refresh, do it in two steps
- // so the UI appears more quickly (per the docs at newsblur.com/api)
- if (PrefsUtils.isTimeToAutoSync(this)) {
- triggerFirstSync();
- }
- }
+ //also make sure the interval sync is scheduled, in case it was just now enabled
+ BootReceiver.scheduleSyncService(this);
}
@Override
@@ -71,40 +59,11 @@ public class Main extends NbActivity implements StateChangedListener, SyncUpdate
// clear all stories from the DB, the story activities will load them.
FeedUtils.clearStories(this);
- //trigger an autosync right now, just in case
- Intent i = new Intent(this, NBSyncService.class);
- startService(i);
- //also make sure the interval sync is scheduled, in case it was just now enabled
- BootReceiver.scheduleSyncService(this);
}
- /**
- * Triggers an initial two-phase sync, so the UI can display quickly using /reader/feeds and
- * then call /reader/refresh_feeds to get updated counts.
- */
- private void triggerFirstSync() {
- PrefsUtils.updateLastSyncTime(this);
- setProgressBarIndeterminateVisibility(true);
- setRefreshEnabled(false);
-
- final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
- intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, syncFragment.receiver);
- intent.putExtra(SyncService.EXTRA_TASK_TYPE, SyncService.TaskType.FOLDER_UPDATE_TWO_STEP);
- startService(intent);
- }
-
- /**
- * Triggers a full, manually requested refresh of feed/folder data and counts.
- */
private void triggerRefresh() {
- PrefsUtils.updateLastSyncTime(this);
- setProgressBarIndeterminateVisibility(true);
- setRefreshEnabled(false);
-
- final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
- intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, syncFragment.receiver);
- intent.putExtra(SyncService.EXTRA_TASK_TYPE, SyncService.TaskType.FOLDER_UPDATE_WITH_COUNT);
- startService(intent);
+ Intent i = new Intent(this, NBSyncService.class);
+ startService(i);
}
private void setupActionBar() {
@@ -156,39 +115,18 @@ public class Main extends NbActivity implements StateChangedListener, SyncUpdate
}
}
- /**
- * Called after the sync service completely finishes a task.
- */
@Override
- public void updateAfterSync() {
+ public void handleUpdate() {
folderFeedList.hasUpdated();
- setProgressBarIndeterminateVisibility(false);
- setRefreshEnabled(true);
- }
-
- /**
- * Called when the sync service has made enough progress to update the UI but not
- * enough to stop the progress indicator.
- */
- @Override
- public void updatePartialSync() {
- // TODO: move 2-step sync to new async lib and remove this method entirely
- // folderFeedList.hasUpdated();
- }
-
- @Override
- public void updateSyncStatus(boolean syncRunning) {
- // TODO: the progress bar is activated manually elsewhere in this activity. this
- // interface method may be redundant.
- if (syncRunning) {
- setProgressBarIndeterminateVisibility(true);
+ if (NBSyncService.isSyncRunning()) {
+ setProgressBarIndeterminateVisibility(true);
setRefreshEnabled(false);
- }
+ } else {
+ setProgressBarIndeterminateVisibility(false);
+ setRefreshEnabled(true);
+ }
}
- @Override
- public void setNothingMoreToUpdate() { }
-
private void setRefreshEnabled(boolean enabled) {
if (menu != null) {
MenuItem item = menu.findItem(R.id.menu_refresh);
diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/NbActivity.java b/clients/android/NewsBlur/src/com/newsblur/activity/NbActivity.java
index 27cdbb006..8db0927da 100644
--- a/clients/android/NewsBlur/src/com/newsblur/activity/NbActivity.java
+++ b/clients/android/NewsBlur/src/com/newsblur/activity/NbActivity.java
@@ -7,10 +7,19 @@ import android.util.Log;
import com.newsblur.util.AppConstants;
import com.newsblur.util.PrefsUtils;
+import java.util.ArrayList;
+
public class NbActivity extends Activity {
private final static String UNIQUE_LOGIN_KEY = "uniqueLoginKey";
private String uniqueLoginKey;
+
+ /**
+ * Keep track of all activie activities so they can be notified when the sync service
+ * has updated the DB. This is essentially an ultra-lightweight implementation of a
+ * local, unfiltered broadcast manager.
+ */
+ private static ArrayList AllActivities = new ArrayList();
@Override
protected void onCreate(Bundle bundle) {
@@ -31,6 +40,20 @@ public class NbActivity extends Activity {
if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "onResume");
super.onResume();
finishIfNotLoggedIn();
+
+ synchronized (AllActivities) {
+ AllActivities.add(this);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ if (AppConstants.VERBOSE_LOG) Log.d(this.getClass().getName(), "onSuspend");
+ super.onPause();
+
+ synchronized (AllActivities) {
+ AllActivities.remove(this);
+ }
}
protected void finishIfNotLoggedIn() {
@@ -48,4 +71,31 @@ public class NbActivity extends Activity {
super.onSaveInstanceState(savedInstanceState);
}
+ /**
+ * Called on each NB activity after the DB has been updated by the sync service. This method
+ * should return as quickly as possible.
+ */
+ protected void handleUpdate() {;}
+
+ private void _handleUpdate() {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ handleUpdate();
+ }
+ });
+ }
+
+ /**
+ * Notify all activities in the app that the DB has been updated.
+ */
+ public static void updateAllActivities() {
+ Log.d(NbActivity.class.getName(), "updating all activities . . .");
+ synchronized (AllActivities) {
+ for (NbActivity activity : AllActivities) {
+ activity._handleUpdate();
+ }
+ }
+ Log.d(NbActivity.class.getName(), " . . . done updating all activities");
+ }
+
}
diff --git a/clients/android/NewsBlur/src/com/newsblur/fragment/DeleteFeedFragment.java b/clients/android/NewsBlur/src/com/newsblur/fragment/DeleteFeedFragment.java
index 9724f73d1..e1e145325 100644
--- a/clients/android/NewsBlur/src/com/newsblur/fragment/DeleteFeedFragment.java
+++ b/clients/android/NewsBlur/src/com/newsblur/fragment/DeleteFeedFragment.java
@@ -11,7 +11,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.DialogFragment;
-import android.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -35,20 +34,10 @@ public class DeleteFeedFragment extends DialogFragment {
return frag;
}
- private FragmentManager fragmentManager;
- private SyncUpdateFragment syncFragment;
-
@Override
public void onCreate(Bundle savedInstanceState) {
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.dialog);
super.onCreate(savedInstanceState);
-
- fragmentManager = super.getFragmentManager();
-
- syncFragment = (SyncUpdateFragment) fragmentManager.findFragmentByTag(SyncUpdateFragment.TAG);
- if (syncFragment == null) {
- syncFragment = new SyncUpdateFragment();
- }
}
@Override
@@ -66,7 +55,7 @@ public class DeleteFeedFragment extends DialogFragment {
// called from the feed view so finish
Activity activity = DeleteFeedFragment.this.getActivity();
if (activity instanceof Main) {
- ((Main)activity).updateAfterSync();
+ ((Main)activity).handleUpdate();
} else {
activity.finish();
}
diff --git a/clients/android/NewsBlur/src/com/newsblur/fragment/LoginRegisterFragment.java b/clients/android/NewsBlur/src/com/newsblur/fragment/LoginRegisterFragment.java
index 79675883b..d22cf3722 100644
--- a/clients/android/NewsBlur/src/com/newsblur/fragment/LoginRegisterFragment.java
+++ b/clients/android/NewsBlur/src/com/newsblur/fragment/LoginRegisterFragment.java
@@ -20,7 +20,6 @@ import com.newsblur.R;
import com.newsblur.activity.LoginProgress;
import com.newsblur.activity.RegisterProgress;
import com.newsblur.network.APIManager;
-import com.newsblur.service.DetachableResultReceiver;
public class LoginRegisterFragment extends Fragment implements OnClickListener {
@@ -28,10 +27,8 @@ public class LoginRegisterFragment extends Fragment implements OnClickListener {
private EditText username, password;
private ViewSwitcher viewSwitcher;
- DetachableResultReceiver receiver;
private EditText register_username, register_password, register_email;
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_loginregister, container, false);
diff --git a/clients/android/NewsBlur/src/com/newsblur/fragment/SyncUpdateFragment.java b/clients/android/NewsBlur/src/com/newsblur/fragment/SyncUpdateFragment.java
deleted file mode 100644
index 85da8132f..000000000
--- a/clients/android/NewsBlur/src/com/newsblur/fragment/SyncUpdateFragment.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.newsblur.fragment;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Handler;
-import android.app.Fragment;
-import android.util.Log;
-
-import com.newsblur.service.DetachableResultReceiver;
-import com.newsblur.service.DetachableResultReceiver.Receiver;
-import com.newsblur.service.SyncService;
-
-public class SyncUpdateFragment extends Fragment implements Receiver {
-
- public static final String TAG = SyncUpdateFragment.class.getName();
- public DetachableResultReceiver receiver;
- public boolean syncRunning = false;
-
- public SyncUpdateFragment() {
- receiver = new DetachableResultReceiver(new Handler());
- receiver.setReceiver(this);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setRetainInstance(true);
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- }
-
- @Override
- public void onReceiverResult(int resultCode, Bundle resultData) {
- final SyncService.SyncStatus resultStatus = SyncService.SyncStatus.values()[resultCode];
- switch (resultStatus) {
- case STATUS_FINISHED:
- syncRunning = false;
- if (getActivity() != null) {
- ((SyncUpdateFragmentInterface) getActivity()).updateAfterSync();
- }
- break;
- case STATUS_PARTIAL_PROGRESS:
- syncRunning = true;
- if (getActivity() != null) {
- ((SyncUpdateFragmentInterface) getActivity()).updatePartialSync();
- }
- break;
- case STATUS_RUNNING:
- syncRunning = true;
- break;
- case STATUS_NO_MORE_UPDATES:
- syncRunning = false;
- if (getActivity() != null) {
- ((SyncUpdateFragmentInterface) getActivity()).setNothingMoreToUpdate();
- }
- break;
- default:
- syncRunning = false;
- Log.e(this.getClass().getName(), "Sync completed with an unknown result.");
- break;
- }
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- ((SyncUpdateFragmentInterface) getActivity()).updateSyncStatus(syncRunning);
- }
-
-
- public interface SyncUpdateFragmentInterface {
- public void updateAfterSync();
- public void updatePartialSync();
- public void setNothingMoreToUpdate();
- public void updateSyncStatus(boolean syncRunning);
- }
-}
-
diff --git a/clients/android/NewsBlur/src/com/newsblur/service/DetachableResultReceiver.java b/clients/android/NewsBlur/src/com/newsblur/service/DetachableResultReceiver.java
deleted file mode 100644
index d13950bc2..000000000
--- a/clients/android/NewsBlur/src/com/newsblur/service/DetachableResultReceiver.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.newsblur.service;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
-import android.util.Log;
-
-/**
- * This class is based on the Google I/O 2011 app's class of the same name. It allows for
- * a resultreceiver to attach/detach to an activity and thus elegantly handle configuration changes.
- */
-public class DetachableResultReceiver extends ResultReceiver {
-
- private final static String TAG = "DetachableResultReceiver";
- private Receiver receiver;
-
- public DetachableResultReceiver(Handler handler) {
- super(handler);
- }
-
- public void clearReceiver() {
- receiver = null;
- }
-
- public void setReceiver(final Receiver resultReceiver) {
- receiver = resultReceiver;
- }
-
- public interface Receiver {
- public void onReceiverResult(int resultCode, Bundle resultData);
- }
-
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (receiver != null) {
- receiver.onReceiverResult(resultCode, resultData);
- } else {
- Log.w(TAG, "No receiver to handle result: " + resultCode + " " + resultData.toString());
- }
- }
-
-}
diff --git a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java
index 86453c59b..6d950b848 100644
--- a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java
+++ b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java
@@ -7,41 +7,73 @@ import android.os.PowerManager;
import android.util.Log;
import com.newsblur.R;
+import com.newsblur.activity.NbActivity;
+import com.newsblur.network.APIManager;
public class NBSyncService extends Service {
+ private static boolean SyncRunning = false;
+
+ private APIManager apiManager;
+
@Override
public void onCreate() {
super.onCreate();
Log.d(this.getClass().getName(), "onCreate");
+ apiManager = new APIManager(this);
}
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this.getClass().getName(), "onStartCommand");
// TODO: check for sync flag or realtime flag
- PowerManager pm = (PowerManager) getApplicationContext().getSystemService(POWER_SERVICE);
- PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getSimpleName());
- wl.acquire();
+ new Thread(new Runnable() {
+ public void run() {
+ doSync();
+ }
+ }).start();
- // TODO: stuff!
-
- wl.release();
Log.d(this.getClass().getName(), "onStartCommand complete");
return Service.START_NOT_STICKY;
}
+ private synchronized void doSync() {
+ Log.d(this.getClass().getName(), "starting sync . . .");
+
+ PowerManager pm = (PowerManager) getApplicationContext().getSystemService(POWER_SERVICE);
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getSimpleName());
+ try {
+ wl.acquire();
+
+ SyncRunning = true;
+ NbActivity.updateAllActivities();
+
+ apiManager.getFolderFeedMapping(true);
+
+ SyncRunning = false;
+ NbActivity.updateAllActivities();
+
+ } finally {
+ wl.release();
+ Log.d(this.getClass().getName(), " . . . sync done");
+ }
+ }
+
+ public static boolean isSyncRunning() {
+ return SyncRunning;
+ }
+
@Override
public void onDestroy() {
Log.d(this.getClass().getName(), "onDestroy");
}
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
}
diff --git a/clients/android/NewsBlur/src/com/newsblur/service/SyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/SyncService.java
deleted file mode 100644
index 0f2215a29..000000000
--- a/clients/android/NewsBlur/src/com/newsblur/service/SyncService.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package com.newsblur.service;
-
-import java.util.List;
-
-import android.app.IntentService;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.text.TextUtils;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.newsblur.R;
-import com.newsblur.database.FeedProvider;
-import com.newsblur.domain.ValueMultimap;
-import com.newsblur.network.APIConstants;
-import com.newsblur.network.APIManager;
-import com.newsblur.network.domain.SocialFeedResponse;
-import com.newsblur.network.domain.StoriesResponse;
-import com.newsblur.util.ReadFilter;
-import com.newsblur.util.StoryOrder;
-
-/**
- * A background-sync intent that, by virtue of extending IntentService, has several notable
- * features:
- * * invocations are FIFO and executed serially
- * * the OS picks an appropriate thread for execution that won't block the UI, but recycles
- * * supports callbacks where necessary
- */
-public class SyncService extends IntentService {
-
- public static final String EXTRA_TASK_TYPE = "syncServiceTaskType";
- public static final String EXTRA_STATUS_RECEIVER = "resultReceiverExtra";
-
- public enum SyncStatus {
- STATUS_RUNNING,
- STATUS_FINISHED,
- STATUS_NO_MORE_UPDATES,
- NOT_RUNNING,
- STATUS_PARTIAL_PROGRESS,
- };
-
- public enum TaskType {
- FOLDER_UPDATE_TWO_STEP,
- FOLDER_UPDATE_WITH_COUNT
- };
-
- private APIManager apiManager;
- private ContentResolver contentResolver;
-
- public SyncService() {
- super(SyncService.class.getName());
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- apiManager = new APIManager(this);
- contentResolver = getContentResolver();
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- final ResultReceiver receiver = intent.getParcelableExtra(EXTRA_STATUS_RECEIVER);
- try {
- TaskType taskType = (TaskType) intent.getSerializableExtra(EXTRA_TASK_TYPE);
- Log.d( this.getClass().getName(), "Sync Intent: " + taskType );
-
- if (receiver != null) {
- receiver.send(SyncStatus.STATUS_RUNNING.ordinal(), Bundle.EMPTY);
- }
-
- // an extra result code to callback before the final STATUS_FINISHED that is always sent
- SyncStatus resultStatus = null;
-
- switch (taskType) {
-
- case FOLDER_UPDATE_TWO_STEP:
- // do a quick fetch of folders/feeds
- apiManager.getFolderFeedMapping(false);
- // notify UI of progress
- if (receiver != null) {
- receiver.send(SyncStatus.STATUS_PARTIAL_PROGRESS.ordinal(), Bundle.EMPTY);
- }
- // update feed counts
- apiManager.refreshFeedCounts();
- // UI will be notified again by default
- break;
-
- case FOLDER_UPDATE_WITH_COUNT:
- apiManager.getFolderFeedMapping(true);
- break;
-
- default:
- Log.e(this.getClass().getName(), "SyncService called without relevant task assignment");
- break;
- }
-
- // send the first result code if it was set. The STATUS_FINISHED is sent below
- if ((receiver != null) && (resultStatus != null)) {
- receiver.send(resultStatus.ordinal(), Bundle.EMPTY);
- }
-
- Log.d( this.getClass().getName(), "Sync Intent complete");
-
- } catch (Exception e) {
- Log.e(this.getClass().getName(), "Couldn't synchronise with NewsBlur servers: " + e.getMessage(), e.getCause());
- e.printStackTrace();
- } finally {
- if (receiver != null) {
- receiver.send(SyncStatus.STATUS_FINISHED.ordinal(), Bundle.EMPTY);
- }
- }
-
- }
-
-}
diff --git a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java
index 1309779ec..acf0bab25 100644
--- a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java
+++ b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java
@@ -32,7 +32,6 @@ import com.newsblur.network.APIManager;
import com.newsblur.network.domain.NewsBlurResponse;
import com.newsblur.network.domain.SocialFeedResponse;
import com.newsblur.network.domain.StoriesResponse;
-import com.newsblur.service.SyncService;
import com.newsblur.util.AppConstants;
public class FeedUtils {