Remove update message service

pull/46/head
Alex Baker 11 years ago
parent d1d684d348
commit 69829e3f68

@ -1,18 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.service;
import java.io.IOException;
public class HttpErrorException extends IOException {
private static final long serialVersionUID = 5373340422464657279L;
public HttpErrorException(int code, String message) {
super(String.format("%d %s", code, message)); //$NON-NLS-1$
}
}

@ -1,246 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.service;
import android.util.Log;
import com.todoroo.andlib.utility.AndroidUtilities;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnManagerPNames;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.HttpEntityWrapper;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HttpContext;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.zip.GZIPInputStream;
/**
* RestClient allows Android to consume web requests.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class HttpRestClient implements RestClient {
private static final int HTTP_UNAVAILABLE_END = 599;
private static final int HTTP_UNAVAILABLE_START = 500;
private static final int HTTP_OK = 200;
private static final int TIMEOUT_MILLIS = 60000;
private WeakReference<HttpClient> httpClient = null;
protected boolean debug = false;
private int timeout = TIMEOUT_MILLIS;
public HttpRestClient() {
DependencyInjectionService.getInstance().inject(this);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, timeout);
HttpConnectionParams.setSoTimeout(params, timeout);
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
cm = new ThreadSafeClientConnManager(params, schemeRegistry);
}
public HttpRestClient(int timeout) {
super();
this.timeout = timeout;
HttpConnectionParams.setConnectionTimeout(params, timeout);
HttpConnectionParams.setSoTimeout(params, timeout);
}
private HttpParams params;
private ThreadSafeClientConnManager cm;
private synchronized HttpClient getClient() {
if (httpClient == null || httpClient.get() == null) {
DefaultHttpClient client = new DefaultHttpClient(cm, params);
httpClient = new WeakReference<HttpClient>(client);
actsAsGzippable(client);
return client;
}
return httpClient.get();
}
protected void actsAsGzippable(DefaultHttpClient client) {
client.addRequestInterceptor(new HttpRequestInterceptor() {
@Override
public void process(
final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
if (!request.containsHeader("Accept-Encoding")) {
request.addHeader("Accept-Encoding", "gzip");
}
}
});
client.addResponseInterceptor(new HttpResponseInterceptor() {
@Override
public void process(
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
HttpEntity entity = response.getEntity();
Header ceheader = entity.getContentEncoding();
if (ceheader != null) {
HeaderElement[] codecs = ceheader.getElements();
for (int i = 0; i < codecs.length; i++) {
if (codecs[i].getName().equalsIgnoreCase("gzip")) {
response.setEntity(
new GzipDecompressingEntity(response.getEntity()));
return;
}
}
}
}
});
}
private static class GzipDecompressingEntity extends HttpEntityWrapper {
public GzipDecompressingEntity(final HttpEntity entity) {
super(entity);
}
@Override
public InputStream getContent()
throws IOException, IllegalStateException {
// the wrapped entity's getContent() decides about repeatability
InputStream wrappedin = wrappedEntity.getContent();
return new GZIPInputStream(wrappedin);
}
@Override
public long getContentLength() {
// length of ungzipped content is not known
return -1;
}
}
private String processHttpResponse(HttpResponse response) throws IOException {
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode >= HTTP_UNAVAILABLE_START && statusCode <= HTTP_UNAVAILABLE_END) {
throw new HttpUnavailableException();
}
HttpEntity entity = response.getEntity();
String body = null;
if (entity != null) {
InputStream contentStream = entity.getContent();
try {
body = AndroidUtilities.readInputStream(contentStream);
} finally {
contentStream.close();
}
}
if(statusCode != HTTP_OK) {
System.out.println(body);
throw new HttpErrorException(response.getStatusLine().getStatusCode(),
response.getStatusLine().getReasonPhrase());
}
return body;
}
/**
* Issue an HTTP GET for the given URL, return the response
*
* @param url url with url-encoded params
* @return response, or null if there was no response
* @throws IOException
*/
@Override
public synchronized String get(String url) throws IOException {
if(debug) {
Log.d("http-rest-client-get", url); //$NON-NLS-1$
}
try {
HttpGet httpGet = new HttpGet(url);
HttpResponse response = getClient().execute(httpGet);
return processHttpResponse(response);
} catch (IOException e) {
throw e;
} catch (Exception e) {
IOException ioException = new IOException(e.getMessage());
ioException.initCause(e);
throw ioException;
}
}
/**
* Issue an HTTP POST for the given URL, return the response
*
* @param data
* url-encoded data
* @throws IOException
*/
@Override
public synchronized String post(String url, HttpEntity data, Header... headers) throws IOException {
if(debug) {
Log.d("http-rest-client-post", url + " | " + data); //$NON-NLS-1$ //$NON-NLS-2$
}
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(data);
for(Header header : headers) {
httpPost.addHeader(header);
}
HttpResponse response = getClient().execute(httpPost);
return processHttpResponse(response);
} catch (IOException e) {
throw e;
} catch (Exception e) {
IOException ioException = new IOException(e.getMessage());
ioException.initCause(e);
throw ioException;
}
}
}

@ -1,30 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.service;
import java.io.IOException;
/**
* Exception displayed when a 500 error is received on an HTTP invocation
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class HttpUnavailableException extends IOException {
private static final long serialVersionUID = 5373340422464657279L;
public HttpUnavailableException() {
super();
DependencyInjectionService.getInstance().inject(this);
}
@Override
public String getMessage() {
return "Sorry, our servers are experiencing some issues. Please try again later!"; //$NON-NLS-1$ // FIXME
}
}

@ -1,22 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.service;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import java.io.IOException;
/**
* RestClient stub invokes the HTML requests as desired
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface RestClient {
public String get(String url) throws IOException;
public String post(String url, HttpEntity data, Header... headers) throws IOException;
}

@ -256,7 +256,6 @@ abstract public class SyncProviderPreferences extends TodorooPreferenceActivity
exceptionsToDisplayMessages = new HashMap<String, Integer>();
exceptionsToDisplayMessages.put("java.net.ConnectionException", R.string.sync_error_offline);
exceptionsToDisplayMessages.put("java.net.UnknownHostException", R.string.sync_error_offline);
exceptionsToDisplayMessages.put("org.apache.http.conn.HttpHostConnectionException", R.string.sync_error_offline);
}
return exceptionsToDisplayMessages;
}

@ -71,7 +71,6 @@ dependencies {
compile group: 'com.google.guava', name: 'guava', version: '11.0.1', transitive: false
compile group: 'com.google.code.gson', name: 'gson', version: '1.7.1', transitive: false
compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.1.1', transitive: false
compile group: 'com.google.api-client', name: 'google-api-client', version: '1.6.0-beta', transitive: false
compile group: 'com.google.api-client', name: 'google-api-client-extensions', version: '1.6.0-beta', transitive: false
compile group: 'com.google.api-client', name: 'google-api-client-extensions-android2', version: '1.6.0-beta', transitive: false

@ -1,330 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.service;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.RestClient;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.test.DatabaseTestCase;
import com.todoroo.astrid.utility.Constants;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.json.JSONArray;
import java.io.IOException;
public class UpdateMessageServiceTest extends DatabaseTestCase {
@Autowired private StoreObjectDao storeObjectDao;
@Autowired private GtasksPreferenceService gtasksPreferenceService;
public void testNoUpdates() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
fail("should not have displayed updates");
}
@Override
String getUpdates(String url) throws IOException {
assertTrue(url, url.contains("language=eng"));
assertTrue(url.contains("version="));
return "";
}
}.processUpdates();
}
public void testIOException() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
fail("should not have displayed updates");
}
@Override
String getUpdates(String url) throws IOException {
throw new IOException("yayaya");
}
}.processUpdates();
}
public void testNewUpdate() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
assertTrue(message.message.toString().contains("yo"));
}
@Override
String getUpdates(String url) throws IOException {
return "[{message:'yo'}]";
}
}.processUpdates();
}
public void testMultipleUpdates() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
assertTrue(message.message.toString().contains("yo"));
assertFalse(message.message.toString().contains("cat")); // We only process the first update now
}
@Override
String getUpdates(String url) throws IOException {
return "[{message:'yo'},{message:'cat'}]";
}
}.processUpdates();
}
public void testExistingUpdate() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
assertTrue(message.message.toString().contains("yo"));
}
@Override
String getUpdates(String url) throws IOException {
return "[{message:'yo'}]";
}
}.processUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple 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() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
assertTrue(message.message.toString().contains("yo"));
}
@Override
String getUpdates(String url) throws IOException {
return "[{message:'yo',date:'date'}]";
}
}.processUpdates();
}
public void testUpdateWithInternalPluginOn() {
clearLatestUpdates();
gtasksPreferenceService.setToken("gtasks");
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
assertTrue(message.message.toString().contains("gtasks man"));
}
@Override
String getUpdates(String url) throws IOException {
return "[{message:'gtasks man',plugin:'gtasks'}]";
}
}.processUpdates();
gtasksPreferenceService.setToken(null);
}
public void testUpdateWithInternalPluginOff() {
clearLatestUpdates();
gtasksPreferenceService.setToken(null);
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
fail("displayed update");
}
@Override
protected void onEmptyMessage() {
// expected
}
@Override
String getUpdates(String url) throws IOException {
return "[{message:'gtasks man',plugin:'gtasks'}]";
}
}.processUpdates();
}
public void testUpdateWithExternalPluginOn() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
assertTrue(message.message.toString().contains("astrid man"));
}
@Override
String getUpdates(String url) throws IOException {
return "[{message:'astrid man',plugin:'" + Constants.PACKAGE + "'}]";
}
}.processUpdates();
}
public void testUpdateWithExternalPluginOff() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple 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();
}
public void testUpdateWithScreenFlow() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
assertTrue(message.linkText.size() > 0);
assertTrue(message.click.size() > 0);
}
@Override
String getUpdates(String url) throws IOException {
return "[{type:'screen',screens:['com.todoroo.astrid.activity.TaskListActivity'],message:'Screens'}]";
}
};
}
public void testUpdateWithPrefs() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
assertTrue(message.linkText.size() > 0);
assertTrue(message.click.size() > 0);
}
@Override
String getUpdates(String url) throws IOException {
return "[{type:'pref',prefs:[{key:'key', type:'bool', title:'my pref'}],message:'Prefs'}]";
}
};
}
public void testUpdateWithLinks() {
clearLatestUpdates();
new TestUpdateMessageService() {
@Override
void verifyMessage(MessageTuple message) {
assertEquals("Message", message.message);
assertEquals("link", message.linkText.get(0));
assertNotNull(message.click.get(0));
}
@Override
String getUpdates(String url) throws IOException {
return "[{message:'Message', links:[{title:'link',url:'http://astrid.com'}]]";
}
};
}
// ---
private void clearLatestUpdates() {
storeObjectDao.deleteWhere(StoreObjectCriteria.byType(UpdateMessageService.UpdateMessage.TYPE));
}
/** helper test class */
abstract public class TestUpdateMessageService extends UpdateMessageService {
public TestUpdateMessageService() {
super(null);
restClient = new RestClient() {
public String post(String url, HttpEntity data, Header... headers) throws IOException {
return null;
}
public String get(String url) throws IOException {
return getUpdates(url);
}
};
}
abstract void verifyMessage(MessageTuple message);
abstract String getUpdates(String url) throws IOException;
protected void onEmptyMessage() {
fail("empty update message");
}
@Override
protected MessageTuple buildUpdateMessage(JSONArray updates) {
MessageTuple message = super.buildUpdateMessage(updates);
if(message == null || message.message.length() == 0)
onEmptyMessage();
return message;
}
@Override
protected void displayUpdateDialog(MessageTuple tuple) {
verifyMessage(tuple);
}
}
}

@ -9,7 +9,6 @@ import com.todoroo.andlib.service.AbstractDependencyInjector;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService.AndroidLogReporter;
import com.todoroo.andlib.service.ExceptionService.ErrorReporter;
import com.todoroo.andlib.service.HttpRestClient;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.StoreObjectDao;
@ -56,7 +55,6 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
// com.todoroo.android.service
injectables.put("applicationName", "astrid");
injectables.put("restClient", HttpRestClient.class);
// com.todoroo.astrid.dao
injectables.put("database", Database.class);

@ -1,383 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.service;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Color;
import android.net.Uri;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager.BadTokenException;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.TodorooCursor;
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.sql.Query;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.utility.Constants;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.tasks.R;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Notifies users when there are server updates
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class UpdateMessageService {
private static final String URL = "http://blog.astrid.com/updates";
private static final String PLUGIN_GTASKS = "gtasks";
@Autowired protected RestClient restClient;
@Autowired private GtasksPreferenceService gtasksPreferenceService;
@Autowired private AddOnService addOnService;
@Autowired private StoreObjectDao storeObjectDao;
private final Activity activity;
public UpdateMessageService(Activity activity) {
this.activity = activity;
DependencyInjectionService.getInstance().inject(this);
}
public void processUpdates() {
JSONArray updates = checkForUpdates();
if(updates == null || updates.length() == 0) {
return;
}
MessageTuple message = buildUpdateMessage(updates);
if(message == null || message.message.length() == 0) {
return;
}
displayUpdateDialog(message);
}
public static class MessageTuple {
public String message = null;
public List<String> linkText = new ArrayList<String>();
public List<OnClickListener> click = new ArrayList<OnClickListener>();
}
private static interface DialogShower {
void showDialog(Activity activity);
}
private void tryShowDialog(DialogShower ds) {
try {
ds.showDialog(activity);
} catch (BadTokenException bt) {
try {
Activity current = (Activity) ContextManager.getContext();
ds.showDialog(current);
} catch (ClassCastException c) {
// Oh well, context wasn't an activity
} catch (BadTokenException bt2) {
// Oh well, activity isn't running
}
}
}
protected void displayUpdateDialog(final MessageTuple message) {
if(activity == null) {
return;
}
if (message.linkText.size() > 0) {
final DialogShower ds = new DialogShower() {
@Override
public void showDialog(Activity a) {
try {
final Dialog d = new Dialog(activity, R.style.ReminderDialog);
d.setContentView(R.layout.update_message_view);
// TODO: Make HTML message
WebView messageView = (WebView) d.findViewById(R.id.reminder_message);
String html = "<html><body>" + message.message + "</body></html>";
messageView.loadDataWithBaseURL("file:///android_asset/", html, "text/html", "utf-8", null);
messageView.setBackgroundColor(0);
d.findViewById(R.id.dismiss).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
d.dismiss();
}
});
LinearLayout root = (LinearLayout) d.findViewById(R.id.reminder_root);
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
TypedValue themeColor = new TypedValue();
activity.getTheme().resolveAttribute(R.attr.asThemeTextColor, themeColor, false);
int color = activity.getResources().getColor(themeColor.data);
for (int i = 0; i < message.linkText.size(); i++) {
Button linkButton = new Button(activity);
linkButton.setText(message.linkText.get(i));
final OnClickListener click = message.click.get(i);
linkButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
click.onClick(v);
d.dismiss();
}
});
LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, (int) (metrics.density * 35));
params.leftMargin = params.rightMargin = (int) (metrics.density * 5);
params.bottomMargin = (int) (metrics.density * 10);
linkButton.setTextColor(Color.WHITE);
linkButton.setTextSize(20);
linkButton.setBackgroundColor(color);
linkButton.setLayoutParams(params);
linkButton.setPadding(0, 0, 0, 0);
root.addView(linkButton);
}
d.show();
} catch (Exception e) {
// This should never ever crash
}
}
};
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
tryShowDialog(ds);
}
});
}
}
protected MessageTuple buildUpdateMessage(JSONArray updates) {
for(int i = 0; i < updates.length(); i++) {
JSONObject update;
try {
update = updates.getJSONObject(i);
} catch (JSONException e) {
continue;
}
String date = update.optString("date", null);
String message = update.optString("message", null);
String plugin = update.optString("plugin", null);
String notPlugin = update.optString("notplugin", null);
if(message == null) {
continue;
}
if(plugin != null) {
if(!pluginConditionMatches(plugin)) {
continue;
}
}
if(notPlugin != null) {
if(pluginConditionMatches(notPlugin)) {
continue;
}
}
MessageTuple toReturn = new MessageTuple();
toReturn.message = message;
String type = update.optString("type", null);
if ("screen".equals(type) || "pref".equals(type)) {
String linkText = update.optString("link");
OnClickListener click = getClickListenerForUpdate(update, type);
if (click == null) {
continue;
}
toReturn.linkText.add(linkText);
toReturn.click.add(click);
} else {
JSONArray links = update.optJSONArray("links");
if (links != null) {
for (int j = 0; j < links.length(); j++) {
JSONObject link = links.optJSONObject(j);
if (link == null) {
continue;
}
String linkText = link.optString("title");
if (TextUtils.isEmpty(linkText)) {
continue;
}
final String url = link.optString("url");
OnClickListener click = new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
activity.startActivity(intent);
}
};
toReturn.linkText.add(linkText);
toReturn.click.add(click);
}
}
}
if(messageAlreadySeen(date, message)) {
continue;
}
return toReturn;
}
return null;
}
private OnClickListener getClickListenerForUpdate(JSONObject update, String type) {
if ("pref".equals(type)) {
try {
if (!update.has("action_list")) {
return null;
}
JSONArray prefSpec = update.getJSONArray("action_list");
if (prefSpec.length() == 0) {
return null;
}
final String prefArray = prefSpec.toString();
return new View.OnClickListener() {
@Override
public void onClick(View b) {
Intent prefScreen = new Intent(activity, UpdateMessagePreference.class);
prefScreen.putExtra(UpdateMessagePreference.TOKEN_PREFS_ARRAY, prefArray);
activity.startActivityForResult(prefScreen, 0);
}
};
} catch (JSONException e) {
return null;
}
} else if ("screen".equals(type)) {
try {
if (!update.has("action_list")) {
return null;
}
JSONArray screens = update.getJSONArray("action_list");
if (screens.length() == 0) {
return null;
}
final ArrayList<String> screenList = new ArrayList<String>();
for (int i = 0; i < screens.length(); i++) {
String screen = screens.getString(i).trim();
if (!TextUtils.isEmpty(screen)) {
screenList.add(screen);
}
}
return new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent screenFlow = new Intent(activity, UpdateScreenFlow.class);
screenFlow.putStringArrayListExtra(UpdateScreenFlow.TOKEN_SCREENS, screenList);
activity.startActivity(screenFlow);
}
};
} catch (JSONException e) {
return null;
}
}
return null;
}
private boolean pluginConditionMatches(String plugin) {
// handle internal plugin specially
if(PLUGIN_GTASKS.equals(plugin)) {
return gtasksPreferenceService.isLoggedIn();
} else {
return addOnService.isInstalled(plugin);
}
}
private boolean messageAlreadySeen(String date, String message) {
if(date != null) {
message = date + message;
}
String hash = AndroidUtilities.md5(message);
TodorooCursor<StoreObject> cursor = storeObjectDao.query(Query.select(StoreObject.ID).
where(StoreObjectCriteria.byTypeAndItem(UpdateMessage.TYPE, hash)));
try {
if(cursor.getCount() > 0) {
return true;
}
} finally {
cursor.close();
}
StoreObject newUpdateMessage = new StoreObject();
newUpdateMessage.setValue(StoreObject.TYPE, UpdateMessage.TYPE);
newUpdateMessage.setValue(UpdateMessage.HASH, hash);
storeObjectDao.persist(newUpdateMessage);
return false;
}
private JSONArray checkForUpdates() {
PackageManager pm = ContextManager.getContext().getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(Constants.PACKAGE, PackageManager.GET_META_DATA);
int versionCode = pi.versionCode;
String url = URL + "?version=" + versionCode + "&" +
"language=" + Locale.getDefault().getISO3Language() + "&" +
"market=" + Constants.MARKET_STRATEGY.strategyId() + "&" +
"actfm=0" + "&" +
"premium=1";
String result = restClient.get(url); //$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;
}
}
/** store object for messages a user has seen */
static class UpdateMessage {
/** type*/
public static final String TYPE = "update-message"; //$NON-NLS-1$
/** message contents */
public static final StringProperty HASH = new StringProperty(StoreObject.TABLE,
StoreObject.ITEM.name);
}
}

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<merge>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/speech_bubble_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="2dip"
android:layout_marginRight="5dip"
android:layout_marginBottom="10dip"
android:gravity="bottom">
<ImageView
android:id="@+id/astridIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dip"
android:src="@drawable/icon"
android:scaleType="fitCenter"
android:layout_alignParentLeft="true"/>
<LinearLayout
android:id="@+id/speech_bubble_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginRight="5dip"
android:layout_marginBottom="5dip"
android:minHeight="60dip"
android:layout_alignParentBottom="true"
android:gravity="center_vertical"
android:background="@drawable/speech_bubble_reminder"
android:layout_toRightOf="@id/astridIcon">
<WebView
android:id="@+id/reminder_message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.TLA_Reminder"
android:background="@android:color/transparent"
android:gravity="center_vertical"/>
</LinearLayout>
</LinearLayout>
</merge>

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/reminder_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="5dip"
android:layout_marginRight="5dip"
android:layout_marginBottom="20dip"
android:layout_marginLeft="5dip">
<TextView
android:id="@+id/update_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="@android:color/white"
android:text="@string/UpS_updates_title"
android:layout_weight="1"/>
<ImageView
android:id="@+id/dismiss"
android:layout_width="25dip"
android:layout_height="25dip"
android:scaleType="fitCenter"
android:src="@drawable/ic_menu_close"/>
</LinearLayout>
<include layout="@layout/update_message_speech_bubble"/>
</LinearLayout>

@ -168,7 +168,6 @@
<string name="MCA_task_title_no_name">Retorna la trucada a %s</string>
<string name="MCA_schedule_dialog_title">Retorna la trucada a %s en...</string>
<string name="UpS_changelog_title">Què hi ha de nou en Tasks?</string>
<string name="UpS_updates_title">Novetats de l\'Tasks</string>
<string name="EPr_title">Tasks: Paràmetres</string>
<string name="EPr_deactivated">desactivat</string>
<string name="EPr_appearance_header">Aparença</string>

@ -162,7 +162,6 @@
<string name="MCA_task_title_no_name">Zavolat %s zpět</string>
<string name="MCA_schedule_dialog_title">Zavolat %s zpět...</string>
<string name="UpS_changelog_title">Co je nového v Tasks?</string>
<string name="UpS_updates_title">Poslední \"Tasks\" novinky</string>
<string name="EPr_title">Tasks: Vlastnosti</string>
<string name="EPr_appearance_header">Vzhled</string>
<string name="EPr_fontSize_title">Velikost seznamu úkolů</string>

@ -98,7 +98,6 @@
<string name="TEA_control_notes">Noter</string>
<string name="MCA_title">%1$s\ncalled at %2$s</string>
<string name="UpS_changelog_title">Hvad er nyt i Tasks</string>
<string name="UpS_updates_title">Seneste Tasks-nyheder</string>
<string name="EPr_title">Tasks: Opsætning</string>
<string name="EPr_appearance_header">Udseende</string>
<string name="EPr_fontSize_title">Opgavelistestørrelse</string>

@ -197,7 +197,6 @@
<string name="CRA_list_created_title">Liste erstellt</string>
<string name="CRA_default_list_name">Einzelaufgaben: %s</string>
<string name="UpS_changelog_title">Was ist neu bei Tasks?</string>
<string name="UpS_updates_title">Tasks Neuigkeiten</string>
<string name="EPr_title">Tasks: Einstellungen</string>
<string name="EPr_deactivated">deaktiviert</string>
<string name="EPr_appearance_header">Erscheinungsbild</string>

@ -205,7 +205,6 @@
<string name="CRA_list_created_title">Lista creada</string>
<string name="CRA_default_list_name">Acciones: %s</string>
<string name="UpS_changelog_title">¿Que hay de nuevo en Tasks?</string>
<string name="UpS_updates_title">Últimas Noticias de Tasks</string>
<string name="EPr_title">Tasks: Preferencias</string>
<string name="EPr_deactivated">desactivada</string>
<string name="EPr_appearance_header">Apariencia</string>

@ -202,7 +202,6 @@
<string name="CRA_list_created_title">Liste créée !</string>
<string name="CRA_default_list_name">Choix d\'actions: %s</string>
<string name="UpS_changelog_title">Quoi de neuf dans Tasks ?</string>
<string name="UpS_updates_title">Dernières nouvelles d\'Tasks</string>
<string name="EPr_title">Tasks : Paramètres</string>
<string name="EPr_deactivated">désactivé</string>
<string name="EPr_appearance_header">Apparence</string>

@ -190,7 +190,6 @@
<string name="CRA_list_created_title">Lista creata!</string>
<string name="CRA_default_list_name">Azioni: %s</string>
<string name="UpS_changelog_title">Novità in Tasks?</string>
<string name="UpS_updates_title">Ultime Novità di Tasks</string>
<string name="EPr_title">Tasks: Preferenze</string>
<string name="EPr_deactivated">disattivato</string>
<string name="EPr_appearance_header">Aspetto</string>

@ -199,7 +199,6 @@
<string name="CRA_list_created_title">רשימה נוצרה!</string>
<string name="CRA_default_list_name">פריטי פעולה: %s</string>
<string name="UpS_changelog_title">מה חדש אצל אסטריד?</string>
<string name="UpS_updates_title">חדשות Tasks אחרונות</string>
<string name="EPr_title">הגדרות אסטריד</string>
<string name="EPr_deactivated">מופסק</string>
<string name="EPr_appearance_header">חזות</string>

@ -130,7 +130,6 @@
<string name="MCA_ignore">無視する</string>
<string name="CRA_ignore">無視する</string>
<string name="UpS_changelog_title">Tasks の変更点</string>
<string name="UpS_updates_title">最新のTasksニュース</string>
<string name="EPr_title">Tasks: 設定</string>
<string name="EPr_appearance_header">外観</string>
<string name="EPr_fontSize_title">リストの文字サイズ</string>

@ -208,7 +208,6 @@
<string name="CRA_list_created_title">목록을 만들었습니다!</string>
<string name="CRA_default_list_name">활동 아이템: %s</string>
<string name="UpS_changelog_title">Tasks 의 새로운 기능</string>
<string name="UpS_updates_title">최신 아스트리드 뉴스</string>
<string name="EPr_title">설정</string>
<string name="EPr_deactivated">비활성화됨</string>
<string name="EPr_appearance_header">보기 설정</string>

@ -91,7 +91,6 @@
<string name="TEA_control_notes">Notater</string>
<string name="MCA_title">%1$s\ncalled at %2$s</string>
<string name="UpS_changelog_title">Hva er nytt i Tasks?</string>
<string name="UpS_updates_title">Siste nytt om Tasks</string>
<string name="EPr_title">Tasks: Innstillinger</string>
<string name="EPr_appearance_header">Utseende</string>
<string name="EPr_fontSize_title">Tekststørrelse for oppgavelista</string>

@ -203,7 +203,6 @@
<string name="CRA_list_created_title">Lijst gemaakt!</string>
<string name="CRA_default_list_name">Actie-items: %s</string>
<string name="UpS_changelog_title">Nieuw in Tasks</string>
<string name="UpS_updates_title">Laatste Tasks nieuws</string>
<string name="EPr_title">Tasks: Instellingen</string>
<string name="EPr_deactivated">uitgeschakeld</string>
<string name="EPr_appearance_header">Uiterlijk</string>

@ -197,7 +197,6 @@
<string name="CRA_list_created_title">Lista utworzona!</string>
<string name="CRA_default_list_name">Wydarzenia aktywne: %s</string>
<string name="UpS_changelog_title">Co nowego w Tasks?</string>
<string name="UpS_updates_title">Aktualności Tasks</string>
<string name="EPr_title">Tasks: Właściwości</string>
<string name="EPr_deactivated">niekatywny</string>
<string name="EPr_appearance_header">Wygląd</string>

@ -201,7 +201,6 @@
<string name="CRA_list_created_title">Lista criada!</string>
<string name="CRA_default_list_name">Itens de ação: %s</string>
<string name="UpS_changelog_title">O que há de novo no Tasks?</string>
<string name="UpS_updates_title">Ultimas novidades no Tasks</string>
<string name="EPr_title">Tasks: Configurações</string>
<string name="EPr_deactivated">desativado</string>
<string name="EPr_appearance_header">Aparência</string>

@ -200,7 +200,6 @@
<string name="CRA_list_created_title">Список создан!</string>
<string name="CRA_default_list_name">Пункты события: %s</string>
<string name="UpS_changelog_title">Что нового в Tasks?</string>
<string name="UpS_updates_title">Последние новости Tasks</string>
<string name="EPr_title">Tasks: Настройки</string>
<string name="EPr_deactivated">выключен</string>
<string name="EPr_appearance_header">Интерфейс</string>

@ -193,7 +193,6 @@
<string name="CRA_list_created_title">Listan skapades!</string>
<string name="CRA_default_list_name">Saker att göra: %s</string>
<string name="UpS_changelog_title">Vad är nytt i Tasks?</string>
<string name="UpS_updates_title">Senaste Tasks nyheter</string>
<string name="EPr_title">Inställningar</string>
<string name="EPr_deactivated">inaktiverad</string>
<string name="EPr_appearance_header">Utseende</string>

@ -72,7 +72,6 @@
<string name="TEA_control_notes">บันทึกย่อ</string>
<string name="MCA_title">%1$s\ncalled at %2$s</string>
<string name="UpS_changelog_title">มีอะไรใหม่ใน Tasks?</string>
<string name="UpS_updates_title">ข่าวล่าสุดของ Tasks</string>
<string name="EPr_appearance_header">รูปโฉม</string>
<string name="EPr_fontSize_desc">ขนาดฟอนต์บนหน้ารายการหลัก</string>
<string name="EPr_showNotes_desc_enabled">บันทึกจะแสดงให้เห็นตลอดเวลา</string>

@ -187,7 +187,6 @@
<string name="CRA_dont_invite">Hayır teşekkürler</string>
<string name="CRA_list_created_title">Liste oluşturuldu!</string>
<string name="UpS_changelog_title">Tasks\'te Yenilikler Neler?</string>
<string name="UpS_updates_title">En Son Tasks Haberleri</string>
<string name="EPr_title">Tasks: Ayarlar</string>
<string name="EPr_deactivated">devre dışı</string>
<string name="EPr_appearance_header">Görünüm</string>

@ -208,7 +208,6 @@
<string name="CRA_list_created_title">Список створений!</string>
<string name="CRA_default_list_name">Список дій: %s</string>
<string name="UpS_changelog_title">Що нового?</string>
<string name="UpS_updates_title">Останні новини Tasks</string>
<string name="EPr_title">Налаштування</string>
<string name="EPr_deactivated">деактивовані</string>
<string name="EPr_appearance_header">Інтерфейс</string>

@ -172,7 +172,6 @@
<string name="CRA_postpone">也许以后</string>
<string name="CRA_list_exists_title">列表已存在</string>
<string name="UpS_changelog_title">清单小助理有哪些最新功能?</string>
<string name="UpS_updates_title">清单小助理最新消息</string>
<string name="EPr_title">清单小助理:设置</string>
<string name="EPr_deactivated">禁用</string>
<string name="EPr_appearance_header">外观</string>

@ -191,7 +191,6 @@
<string name="CRA_list_created_title">列表建立了!</string>
<string name="CRA_default_list_name">行動事項:%s</string>
<string name="UpS_changelog_title">Tasks 有哪些最新消息?</string>
<string name="UpS_updates_title">Tasks 最新消息</string>
<string name="EPr_title">Tasks: 偏好</string>
<string name="EPr_deactivated">禁用</string>
<string name="EPr_appearance_header">外觀</string>

@ -459,9 +459,6 @@
<!-- Changelog Window Title -->
<string name="UpS_changelog_title">What\'s new?</string>
<!-- Updates Window Title -->
<string name="UpS_updates_title">Latest Tasks News</string>
<!-- ================================================== EditPreferences == -->
<!-- slide 31g: Preference Window Title -->

Loading…
Cancel
Save