update service + test

pull/14/head
Tim Su 15 years ago
parent 957b7661d1
commit bbec509caa

@ -20,9 +20,9 @@ import android.widget.Button;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.data.AddOn; import com.todoroo.astrid.data.AddOn;
import com.todoroo.astrid.utility.Constants; import com.todoroo.astrid.utility.Constants;
import com.todoroo.andlib.utility.Preferences;
/** /**
* Astrid Service for managing add-ons * Astrid Service for managing add-ons
@ -158,9 +158,19 @@ public class AddOnService {
return isInstalled(addOn.getPackageName(), addOn.isInternal()); 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 * Check whether a given add-on is installed
* @param addOn * @param addOn
* @param internal whether to do api sig check
* @return * @return
*/ */
private boolean isInstalled(String packageName, boolean internal) { private boolean isInstalled(String packageName, boolean internal) {
@ -241,4 +251,5 @@ public class AddOnService {
return list; return list;
} }
} }

@ -11,8 +11,8 @@ import android.app.AlertDialog;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent; import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.util.Log; import android.util.Log;
@ -114,7 +114,6 @@ public class StartupService {
// perform startup activities in a background thread // perform startup activities in a background thread
new Thread(new Runnable() { new Thread(new Runnable() {
public void run() { public void run() {
// start widget updating alarm // start widget updating alarm
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, UpdateService.class); Intent intent = new Intent(context, UpdateService.class);
@ -135,6 +134,9 @@ public class StartupService {
ProducteevBackgroundService.scheduleService(); ProducteevBackgroundService.scheduleService();
BackupService.scheduleService(context); BackupService.scheduleService(context);
// get and display update messages
new UpdateMessageService().processUpdates();
} }
}).start(); }).start();

@ -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 <tim@todoroo.com>
*
*/
@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("<b>" + date + "</b><br />");
builder.append(message);
builder.append("<br /><br />");
}
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;
}
}
}

@ -6,7 +6,6 @@ import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.webkit.WebView;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
@ -145,16 +144,8 @@ public final class UpgradeService {
changeLog.append("Enjoy!</body></html>"); changeLog.append("Enjoy!</body></html>");
String changeLogHtml = "<html><body style='color: white'>" + changeLog; String changeLogHtml = "<html><body style='color: white'>" + changeLog;
WebView webView = new WebView(context); DialogUtilities.htmlDialog(context, changeLogHtml,
webView.loadData(changeLogHtml, "text/html", "utf-8"); R.string.UpS_changelog_title);
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();
} }
/** /**

@ -12,6 +12,7 @@ import com.todoroo.andlib.utility.Preferences;
public class AstridPreferences { public class AstridPreferences {
private static final String P_CURRENT_VERSION = "cv"; //$NON-NLS-1$ 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 */ /** Set preference defaults, if unset. called at startup */
public static void setPreferenceDefaults() { public static void setPreferenceDefaults() {
@ -45,4 +46,14 @@ public class AstridPreferences {
Preferences.setInt(P_CURRENT_VERSION, version); 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);
}
} }

@ -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());
}
}
}
Loading…
Cancel
Save