From bbec509caaad507a6ae54f16616bd75f91c7008c Mon Sep 17 00:00:00 2001 From: Tim Su Date: Thu, 21 Oct 2010 18:08:17 -0700 Subject: [PATCH] update service + test --- .../todoroo/astrid/service/AddOnService.java | 13 +- .../astrid/service/StartupService.java | 6 +- .../astrid/service/UpdateMessageService.java | 163 +++++++++++ .../astrid/service/UpgradeService.java | 13 +- .../astrid/utility/AstridPreferences.java | 11 + .../service/UpdateMessageServiceTest.java | 265 ++++++++++++++++++ 6 files changed, 457 insertions(+), 14 deletions(-) create mode 100644 astrid/src/com/todoroo/astrid/service/UpdateMessageService.java create mode 100644 tests/src/com/todoroo/astrid/service/UpdateMessageServiceTest.java diff --git a/astrid/src/com/todoroo/astrid/service/AddOnService.java b/astrid/src/com/todoroo/astrid/service/AddOnService.java index df0b88041..dda14048e 100644 --- a/astrid/src/com/todoroo/astrid/service/AddOnService.java +++ b/astrid/src/com/todoroo/astrid/service/AddOnService.java @@ -20,9 +20,9 @@ import android.widget.Button; import com.timsu.astrid.R; import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.data.AddOn; import com.todoroo.astrid.utility.Constants; -import com.todoroo.andlib.utility.Preferences; /** * Astrid Service for managing add-ons @@ -158,9 +158,19 @@ public class AddOnService { return isInstalled(addOn.getPackageName(), addOn.isInternal()); } + /** + * Check whether an external add-on is installed + * @param packageName + * @return + */ + public boolean isInstalled(String packageName) { + return isInstalled(packageName, false); + } + /** * Check whether a given add-on is installed * @param addOn + * @param internal whether to do api sig check * @return */ private boolean isInstalled(String packageName, boolean internal) { @@ -241,4 +251,5 @@ public class AddOnService { return list; } + } diff --git a/astrid/src/com/todoroo/astrid/service/StartupService.java b/astrid/src/com/todoroo/astrid/service/StartupService.java index 68a2aed35..b3719fc71 100644 --- a/astrid/src/com/todoroo/astrid/service/StartupService.java +++ b/astrid/src/com/todoroo/astrid/service/StartupService.java @@ -11,8 +11,8 @@ import android.app.AlertDialog; import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; import android.content.Intent; +import android.content.DialogInterface.OnClickListener; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.Log; @@ -114,7 +114,6 @@ public class StartupService { // perform startup activities in a background thread new Thread(new Runnable() { public void run() { - // start widget updating alarm AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, UpdateService.class); @@ -135,6 +134,9 @@ public class StartupService { ProducteevBackgroundService.scheduleService(); BackupService.scheduleService(context); + + // get and display update messages + new UpdateMessageService().processUpdates(); } }).start(); diff --git a/astrid/src/com/todoroo/astrid/service/UpdateMessageService.java b/astrid/src/com/todoroo/astrid/service/UpdateMessageService.java new file mode 100644 index 000000000..747589c39 --- /dev/null +++ b/astrid/src/com/todoroo/astrid/service/UpdateMessageService.java @@ -0,0 +1,163 @@ +package com.todoroo.astrid.service; + +import java.io.IOException; +import java.util.Locale; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.weloveastrid.rmilk.MilkUtilities; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.text.TextUtils; + +import com.timsu.astrid.R; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.ContextManager; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.service.RestClient; +import com.todoroo.andlib.utility.AndroidUtilities; +import com.todoroo.andlib.utility.DialogUtilities; +import com.todoroo.astrid.gtasks.GtasksPreferenceService; +import com.todoroo.astrid.producteev.ProducteevUtilities; +import com.todoroo.astrid.utility.AstridPreferences; +import com.todoroo.astrid.utility.Constants; + +/** + * Notifies users when there are server updates + * + * @author Tim Su + * + */ +@SuppressWarnings("nls") +public class UpdateMessageService { + + private static final String URL = "http://localhost/update.php"; + + private static final String PLUGIN_PDV = "pdv"; + private static final String PLUGIN_GTASKS = "gtasks"; + private static final String PLUGIN_RMILK = "rmilk"; + + @Autowired protected RestClient restClient; + @Autowired private GtasksPreferenceService gtasksPreferenceService; + @Autowired private AddOnService addOnService; + + public UpdateMessageService() { + DependencyInjectionService.getInstance().inject(this); + } + + public void processUpdates() { + if(shouldSkipUpdates()) + return; + + JSONArray updates = checkForUpdates(); + + if(updates == null || updates.length() == 0) + return; + + if(updatesHaveNotChanged(updates)) + return; + + StringBuilder builder = buildUpdateMessage(updates); + if(builder.length() == 0) + return; + + displayUpdateDialog(builder); + } + + protected boolean shouldSkipUpdates() { + Context context = ContextManager.getContext(); + return !(context instanceof Activity); + } + + private boolean updatesHaveNotChanged(JSONArray updates) { + String savedUpdates = AstridPreferences.getLatestUpdates(); + String latest = updates.toString(); + if(AndroidUtilities.equals(savedUpdates, latest)) + return true; + AstridPreferences.setLatestUpdates(latest); + return false; + } + + protected void displayUpdateDialog(final StringBuilder builder) { + final Activity activity = (Activity) ContextManager.getContext(); + if(activity == null) + return; + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + DialogUtilities.htmlDialog(activity, + builder.toString(), R.string.UpS_changelog_title); + } + }); + } + + protected StringBuilder buildUpdateMessage(JSONArray updates) { + StringBuilder builder = new StringBuilder(); + + for(int i = 0; i < updates.length(); i++) { + JSONObject update; + try { + update = updates.getJSONObject(i); + } catch (JSONException e) { + continue; + } + + String date = update.optString("date"); + String message = update.optString("message"); + String plugin = update.optString("plugin"); + + if(message == null) + continue; + if(plugin != null) { + // handle internal plugin specially + if(PLUGIN_PDV.equals(plugin)) { + if(!ProducteevUtilities.INSTANCE.isLoggedIn()) + continue; + } + else if(PLUGIN_GTASKS.equals(plugin)) { + if(!gtasksPreferenceService.isLoggedIn()) + continue; + } + else if(PLUGIN_RMILK.equals(plugin)) { + if(!MilkUtilities.INSTANCE.isLoggedIn()) + continue; + } + else if(!addOnService.isInstalled(plugin)) { + continue; + } + } + if(date != null) + builder.append("" + date + "
"); + builder.append(message); + builder.append("

"); + } + return builder; + } + + private JSONArray checkForUpdates() { + PackageManager pm = ContextManager.getContext().getPackageManager(); + try { + PackageInfo pi = pm.getPackageInfo(Constants.PACKAGE, PackageManager.GET_META_DATA); + int versionCode = pi.versionCode; + String result = restClient.get(URL + "?version=" + versionCode + "&" + + "language=" + Locale.getDefault().getISO3Language()); //$NON-NLS-1$ + if(TextUtils.isEmpty(result)) + return null; + + return new JSONArray(result); + } catch (IOException e) { + return null; + } catch (NameNotFoundException e) { + return null; + } catch (JSONException e) { + return null; + } + } + +} diff --git a/astrid/src/com/todoroo/astrid/service/UpgradeService.java b/astrid/src/com/todoroo/astrid/service/UpgradeService.java index 5645c6833..d293d9a73 100644 --- a/astrid/src/com/todoroo/astrid/service/UpgradeService.java +++ b/astrid/src/com/todoroo/astrid/service/UpgradeService.java @@ -6,7 +6,6 @@ import android.app.ProgressDialog; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; -import android.webkit.WebView; import com.timsu.astrid.R; import com.todoroo.andlib.service.Autowired; @@ -145,16 +144,8 @@ public final class UpgradeService { changeLog.append("Enjoy!"); String changeLogHtml = "" + changeLog; - WebView webView = new WebView(context); - webView.loadData(changeLogHtml, "text/html", "utf-8"); - webView.setBackgroundColor(0); - - new AlertDialog.Builder(context) - .setTitle(R.string.UpS_changelog_title) - .setView(webView) - .setIcon(android.R.drawable.ic_dialog_info) - .setPositiveButton(android.R.string.ok, null) - .show(); + DialogUtilities.htmlDialog(context, changeLogHtml, + R.string.UpS_changelog_title); } /** diff --git a/astrid/src/com/todoroo/astrid/utility/AstridPreferences.java b/astrid/src/com/todoroo/astrid/utility/AstridPreferences.java index 78699ad02..64af908db 100644 --- a/astrid/src/com/todoroo/astrid/utility/AstridPreferences.java +++ b/astrid/src/com/todoroo/astrid/utility/AstridPreferences.java @@ -12,6 +12,7 @@ import com.todoroo.andlib.utility.Preferences; public class AstridPreferences { private static final String P_CURRENT_VERSION = "cv"; //$NON-NLS-1$ + private static final String P_LATEST_UPDATES = "up"; //$NON-NLS-1$ /** Set preference defaults, if unset. called at startup */ public static void setPreferenceDefaults() { @@ -45,4 +46,14 @@ public class AstridPreferences { Preferences.setInt(P_CURRENT_VERSION, version); } + /** LatestUpdates: latest astrid updates from server */ + public static String getLatestUpdates() { + return Preferences.getStringValue(P_LATEST_UPDATES); + } + + /** LatestUpdates: latest astrid updates from server */ + public static void setLatestUpdates(String updates) { + Preferences.setString(P_LATEST_UPDATES, updates); + } + } diff --git a/tests/src/com/todoroo/astrid/service/UpdateMessageServiceTest.java b/tests/src/com/todoroo/astrid/service/UpdateMessageServiceTest.java new file mode 100644 index 000000000..fdb1478d1 --- /dev/null +++ b/tests/src/com/todoroo/astrid/service/UpdateMessageServiceTest.java @@ -0,0 +1,265 @@ +package com.todoroo.astrid.service; + +import java.io.IOException; + +import org.json.JSONArray; +import org.weloveastrid.rmilk.MilkUtilities; + +import com.todoroo.andlib.service.RestClient; +import com.todoroo.andlib.test.TodorooTestCase; +import com.todoroo.astrid.utility.AstridPreferences; +import com.todoroo.astrid.utility.Constants; + +public class UpdateMessageServiceTest extends TodorooTestCase { + + public void testNoUpdates() { + AstridPreferences.setLatestUpdates(null); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + fail("should not have displayed updates"); + } + + @Override + String getUpdates(String url) throws IOException { + assertTrue(url.contains("language=EN")); + assertTrue(url.contains("version=")); + return ""; + } + }.processUpdates(); + } + + public void testIOException() { + AstridPreferences.setLatestUpdates(null); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + fail("should not have displayed updates"); + } + + @Override + String getUpdates(String url) throws IOException { + throw new IOException("yayaya"); + } + }.processUpdates(); + } + + public void testNewUpdate() { + AstridPreferences.setLatestUpdates(null); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + assertTrue(message.contains("yo")); + } + + @Override + String getUpdates(String url) throws IOException { + return "[{message:'yo'}]"; + } + }.processUpdates(); + } + + public void testMultipleUpdates() { + AstridPreferences.setLatestUpdates(null); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + assertTrue(message.contains("yo")); + assertTrue(message.contains("cat")); + } + + @Override + String getUpdates(String url) throws IOException { + return "[{message:'yo'},{message:'cat'}]"; + } + }.processUpdates(); + } + + public void testExistingUpdate() { + AstridPreferences.setLatestUpdates(null); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + assertTrue(message.contains("yo")); + } + + @Override + String getUpdates(String url) throws IOException { + return "[{message:'yo'}]"; + } + }.processUpdates(); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + fail("should have not displayed again"); + } + + @Override + protected void onEmptyMessage() { + // expected + } + + @Override + String getUpdates(String url) throws IOException { + return "[{message:'yo'}]"; + } + }.processUpdates(); + } + + public void testUpdateWithDate() { + AstridPreferences.setLatestUpdates(null); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + assertTrue(message.contains("yo")); + assertTrue(message.contains("date")); + } + + @Override + String getUpdates(String url) throws IOException { + return "[{message:'yo',date:'date'}]"; + } + }.processUpdates(); + } + + public void testUpdateWithInternalPluginOn() { + AstridPreferences.setLatestUpdates(null); + MilkUtilities.INSTANCE.setToken("milk"); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + assertTrue(message.contains("rmilk man")); + } + + @Override + String getUpdates(String url) throws IOException { + return "[{message:'rmilk man',plugin:'rmilk'}]"; + } + }.processUpdates(); + } + + public void testUpdateWithInternalPluginOff() { + AstridPreferences.setLatestUpdates(null); + MilkUtilities.INSTANCE.setToken(null); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + fail("displayed update"); + } + + @Override + protected void onEmptyMessage() { + // expected + } + + @Override + String getUpdates(String url) throws IOException { + return "[{message:'rmilk man',plugin:'rmilk'}]"; + } + }.processUpdates(); + } + + public void testUpdateWithExternalPluginOn() { + AstridPreferences.setLatestUpdates(null); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + assertTrue(message.contains("astrid man")); + } + + @Override + String getUpdates(String url) throws IOException { + return "[{message:'astrid man',plugin:'" + Constants.PACKAGE + "'}]"; + } + }.processUpdates(); + } + + public void testUpdateWithExternalPluginOff() { + AstridPreferences.setLatestUpdates(null); + + new TestUpdateMessageService() { + + @Override + void verifyMessage(String message) { + fail("displayed update"); + } + + @Override + protected void onEmptyMessage() { + // expected + } + + @Override + String getUpdates(String url) throws IOException { + return "[{message:'astrid man',plugin:'com.bogus.package'}]"; + } + }.processUpdates(); + } + + // --- + + /** helper test class */ + abstract public class TestUpdateMessageService extends UpdateMessageService { + + public TestUpdateMessageService() { + super(); + restClient = new RestClient() { + + public String post(String url, String data) throws IOException { + return null; + } + + public String get(String url) throws IOException { + return getUpdates(url); + } + }; + } + + @Override + protected boolean shouldSkipUpdates() { + return false; + } + + abstract void verifyMessage(String message); + + abstract String getUpdates(String url) throws IOException; + + protected void onEmptyMessage() { + fail("empty update message"); + } + + @Override + protected StringBuilder buildUpdateMessage(JSONArray updates) { + StringBuilder builder = super.buildUpdateMessage(updates); + if(builder.length() == 0) + onEmptyMessage(); + return builder; + } + + @Override + protected void displayUpdateDialog(StringBuilder builder) { + verifyMessage(builder.toString()); + } + } + +}