diff --git a/astrid/common-src/com/todoroo/andlib/service/ContextManager.java b/astrid/common-src/com/todoroo/andlib/service/ContextManager.java
index de75b7bbc..fb8c0b6c9 100644
--- a/astrid/common-src/com/todoroo/andlib/service/ContextManager.java
+++ b/astrid/common-src/com/todoroo/andlib/service/ContextManager.java
@@ -4,6 +4,7 @@
package com.todoroo.andlib.service;
import android.content.Context;
+import android.content.res.Resources;
/**
* Singleton class to manage current application context
@@ -43,4 +44,14 @@ public final class ContextManager {
public static String getString(int resId, Object... formatArgs) {
return context.getString(resId, formatArgs);
}
+
+ /**
+ * Convenience method to read resources
+ *
+ * @return
+ */
+ public static Resources getResources() {
+ return context.getResources();
+ }
+
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java
index 42dc25d19..03dbb5c7f 100644
--- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java
@@ -14,7 +14,7 @@ import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task;
-import com.todoroo.astrid.service.AddonService;
+import com.todoroo.astrid.service.AddOnService;
/**
* Exposes {@link TaskDecoration} for timers
@@ -32,7 +32,7 @@ public class TimerActionExposer extends BroadcastReceiver {
if(taskId == -1)
return;
- if(!AddonService.isPowerPack())
+ if(!AddOnService.isPowerPack())
return;
Task task = PluginServices.getTaskService().fetchById(taskId, Task.ID, Task.TIMER_START,
diff --git a/astrid/res/drawable/icon_pp.png b/astrid/res/drawable/icon_pp.png
index df0637a7a..d6300277c 100644
Binary files a/astrid/res/drawable/icon_pp.png and b/astrid/res/drawable/icon_pp.png differ
diff --git a/astrid/res/layout/addon_activity.xml b/astrid/res/layout/addon_activity.xml
index 2b5c794c3..a93654032 100644
--- a/astrid/res/layout/addon_activity.xml
+++ b/astrid/res/layout/addon_activity.xml
@@ -7,32 +7,18 @@
-
-
-
-
-
-
+ android:layout_height="fill_parent" />
-
-
-
-
-
-
-
+ android:layout_height="fill_parent" />
+
\ No newline at end of file
diff --git a/astrid/res/layout/addon_adapter_row.xml b/astrid/res/layout/addon_adapter_row.xml
new file mode 100644
index 000000000..d5a9cebd5
--- /dev/null
+++ b/astrid/res/layout/addon_adapter_row.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/res/layout/task_edit_activity.xml b/astrid/res/layout/task_edit_activity.xml
index 91e5c5504..36b64d672 100644
--- a/astrid/res/layout/task_edit_activity.xml
+++ b/astrid/res/layout/task_edit_activity.xml
@@ -236,53 +236,75 @@
-
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
-
-
+ android:layout_height="fill_parent"
+ android:layout_weight="100">
+
-
-
-
+
+
+
+
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:src="@drawable/icon_pp" />
+
+
+
+
+
+
-
+
+
-
-
-
+ android:src="@drawable/tango_save" />
+
-
+
+
+
\ No newline at end of file
diff --git a/astrid/res/values/strings-core.xml b/astrid/res/values/strings-core.xml
index 20a30f974..8faca89a1 100644
--- a/astrid/res/values/strings-core.xml
+++ b/astrid/res/values/strings-core.xml
@@ -445,11 +445,15 @@ to the plugin creator for fastest service.
Available
-
-
- Support Astrid and get more productive with
- the Astrid Power Pack backup, widgets, no ads, and calendar integration.
- Power up today!
+
+
+ Free
+
+
+ Visit Website
+
+
+ Android Market
diff --git a/astrid/src/com/todoroo/astrid/activity/AddOnActivity.java b/astrid/src/com/todoroo/astrid/activity/AddOnActivity.java
index abcd77c27..095494d20 100644
--- a/astrid/src/com/todoroo/astrid/activity/AddOnActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/AddOnActivity.java
@@ -1,16 +1,40 @@
package com.todoroo.astrid.activity;
+import java.util.ArrayList;
+
import android.app.TabActivity;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.LayoutInflater;
+import android.widget.ListView;
import android.widget.TabHost;
+import android.widget.TextView;
import com.timsu.astrid.R;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.astrid.adapter.AddOnAdapter;
+import com.todoroo.astrid.model.AddOn;
+import com.todoroo.astrid.service.AddOnService;
+import com.todoroo.astrid.service.AstridDependencyInjector;
public class AddOnActivity extends TabActivity {
+ /** boolean: whether to start on available page */
+ public static final String TOKEN_START_WITH_AVAILABLE = "av"; //$NON-NLS-1$
+
+ @Autowired
+ AddOnService addOnService;
+
+ static {
+ AstridDependencyInjector.initialize();
+ }
+
+ public AddOnActivity() {
+ DependencyInjectionService.getInstance().inject(this);
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -24,13 +48,47 @@ public class AddOnActivity extends TabActivity {
tabHost.addTab(tabHost.newTabSpec(r.getString(R.string.AOA_tab_installed)).
setIndicator(r.getString(R.string.AOA_tab_installed),
r.getDrawable(R.drawable.tab_addons)).setContent(
- R.id.installed_tab));
+ R.id.installed));
tabHost.addTab(tabHost.newTabSpec(r.getString(R.string.AOA_tab_available)).
setIndicator(r.getString(R.string.AOA_tab_available),
r.getDrawable(R.drawable.tab_add)).setContent(
- R.id.availble_tab));
+ R.id.available));
setTitle(R.string.AOA_title);
+
+ populate();
}
+ private void populate() {
+ AddOn[] list = addOnService.getAddOns();
+ if(list == null)
+ return;
+
+ ArrayList installed = new ArrayList();
+ ArrayList available = new ArrayList();
+
+ for(AddOn addOn : list) {
+ if(addOnService.isInstalled(addOn))
+ installed.add(addOn);
+ else
+ available.add(addOn);
+ }
+ if(installed.size() == 0 || getIntent().getBooleanExtra(TOKEN_START_WITH_AVAILABLE, false))
+ getTabHost().setCurrentTab(1);
+
+ TextView noAddons = new TextView(this);
+ noAddons.setText(R.string.TEA_no_addons);
+ noAddons.setTextAppearance(this, R.style.TextAppearance_TLA_NoItems);
+
+ ListView installedList = (ListView) findViewById(R.id.installed);
+ installedList.setEmptyView(noAddons);
+ installedList.setAdapter(new AddOnAdapter(this, true, installed));
+
+ ListView availableList = (ListView) findViewById(R.id.available);
+ availableList.setEmptyView(noAddons);
+ availableList.setAdapter(new AddOnAdapter(this, false, available));
+ }
+
+
+
}
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java
index 385eb2847..2872f0209 100644
--- a/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java
@@ -37,7 +37,6 @@ import android.content.IntentFilter;
import android.content.DialogInterface.OnCancelListener;
import android.content.res.Resources;
import android.os.Bundle;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -51,17 +50,14 @@ import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.Spinner;
import android.widget.TabHost;
-import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ImageView.ScaleType;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;
@@ -76,7 +72,7 @@ import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.gcal.GCalControlSet;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.repeats.RepeatControlSet;
-import com.todoroo.astrid.service.AddonService;
+import com.todoroo.astrid.service.AddOnService;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagsControlSet;
@@ -215,30 +211,15 @@ public final class TaskEditActivity extends TabActivity {
controls.add(new RepeatControlSet(this, extrasAddons));
LinearLayout addonsAddons = (LinearLayout) findViewById(R.id.tab_addons_addons);
- if(AddonService.isPowerPack()) {
+ if(AddOnService.isPowerPack()) {
controls.add(new GCalControlSet(this, addonsAddons));
controls.add(new TimerControlSet(this, addonsAddons));
}
// show add-on help if necessary
if(addonsAddons.getChildCount() == 0) {
- ImageView ppIcon = new ImageView(this);
- ppIcon.setImageResource(R.drawable.icon_pp);
- ppIcon.setScaleType(ScaleType.CENTER);
- ppIcon.setPadding(5, 10, 5, 10);
- addonsAddons.addView(ppIcon);
-
- TextView addOnText = new TextView(this);
- addOnText.setText(R.string.TEA_no_addons);
- addOnText.setTextAppearance(this, R.style.TextAppearance_TLA_NoItems);
- addOnText.setGravity(Gravity.CENTER);
- addOnText.setPadding(5, 10, 5, 10);
- addonsAddons.addView(addOnText);
-
- Button addOnButton = new Button(this);
- addOnButton.setText(R.string.TEA_addons_button);
- addonsAddons.addView(addOnButton);
- addOnButton.setOnClickListener(new View.OnClickListener() {
+ findViewById(R.id.addons_empty).setVisibility(View.VISIBLE);
+ ((Button)findViewById(R.id.addons_button)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(TaskEditActivity.this, AddOnActivity.class));
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
index 1956f7988..252585aef 100644
--- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
@@ -64,7 +64,7 @@ import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.reminders.Notifications;
import com.todoroo.astrid.reminders.ReminderService;
import com.todoroo.astrid.reminders.ReminderService.AlarmScheduler;
-import com.todoroo.astrid.service.AddonService;
+import com.todoroo.astrid.service.AddOnService;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.TaskService;
@@ -158,7 +158,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
if(database == null)
return;
- AddonService.checkForUpgrades(this);
+ AddOnService.checkForUpgrades(this);
database.openForWriting();
setUpUiComponents();
diff --git a/astrid/src/com/todoroo/astrid/adapter/AddOnAdapter.java b/astrid/src/com/todoroo/astrid/adapter/AddOnAdapter.java
new file mode 100644
index 000000000..829417062
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/adapter/AddOnAdapter.java
@@ -0,0 +1,147 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.adapter;
+
+import java.util.List;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.timsu.astrid.R;
+import com.todoroo.astrid.model.AddOn;
+
+/**
+ * Adapter for {@link AddOn}s
+ *
+ * @author Tim Su
+ *
+ */
+public class AddOnAdapter extends ArrayAdapter {
+
+ // --- instance variables
+
+ private final Activity activity;
+ private final LayoutInflater inflater;
+ private final boolean installed;
+
+ public AddOnAdapter(Activity activity, boolean installed, List objects) {
+ super(activity, R.id.title, objects);
+ this.installed = installed;
+ this.activity = activity;
+ inflater = (LayoutInflater) activity.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ // --- view construction
+
+ View.OnClickListener intentClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = (Intent) v.getTag();
+ activity.startActivity(intent);
+ }
+ };
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if(convertView == null) {
+ convertView = inflater.inflate(R.layout.addon_adapter_row, parent, false);
+ ViewHolder viewHolder = new ViewHolder();
+ viewHolder.icon = (ImageView) convertView.findViewById(R.id.icon);
+ viewHolder.title = (TextView) convertView.findViewById(R.id.title);
+ viewHolder.description = (TextView) convertView.findViewById(R.id.description);
+ viewHolder.web = (ImageButton) convertView.findViewById(R.id.button_web);
+ viewHolder.market = (ImageButton) convertView.findViewById(R.id.button_market);
+ viewHolder.installedIcon = (ImageView) convertView.findViewById(R.id.check);
+ convertView.setTag(viewHolder);
+
+ viewHolder.web.setOnClickListener(intentClickListener);
+ viewHolder.market.setOnClickListener(intentClickListener);
+
+ }
+ ((ViewHolder)convertView.getTag()).item = getItem(position);
+ initializeView(convertView);
+
+ return convertView;
+ }
+
+ private class ViewHolder {
+ public AddOn item;
+ public ImageView icon;
+ public TextView title;
+ public TextView description;
+ public ImageButton web;
+ public ImageButton market;
+ public ImageView installedIcon;
+ }
+
+ private void initializeView(View convertView) {
+ ViewHolder viewHolder = (ViewHolder) convertView.getTag();
+ AddOn item = viewHolder.item;
+
+ viewHolder.icon.setImageBitmap(item.getIcon());
+ viewHolder.title.setText(item.getTitle());
+ viewHolder.description.setText(item.getDescription());
+
+ // populate buttons
+ if(item.getWebPage() != null) {
+ viewHolder.web.setVisibility(View.VISIBLE);
+ Intent webPageIntent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse(item.getWebPage()));
+ Drawable icon = getIntentIcon(webPageIntent);
+ if(icon == null)
+ viewHolder.web.setImageResource(
+ android.R.drawable.presence_online);
+ else
+ viewHolder.web.setImageDrawable(icon);
+ viewHolder.web.setTag(webPageIntent);
+ } else {
+ viewHolder.web.setVisibility(View.GONE);
+ }
+
+ if(installed) {
+ viewHolder.market.setVisibility(View.GONE);
+ viewHolder.installedIcon.setVisibility(View.VISIBLE);
+ } else {
+ viewHolder.market.setVisibility(View.VISIBLE);
+ viewHolder.installedIcon.setVisibility(View.GONE);
+ Intent marketIntent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("market://search?q=pname:" + //$NON-NLS-1$
+ item.getPackageName()));
+ marketIntent.setClassName("com.android.vending", //$NON-NLS-1$
+ "com.android.vending.SearchAssetListActivity"); //$NON-NLS-1$
+ Drawable icon = getIntentIcon(marketIntent);
+ if(icon == null)
+ viewHolder.web.setImageResource(
+ android.R.drawable.stat_sys_download);
+ else
+ viewHolder.web.setImageDrawable(icon);
+ }
+ }
+
+ public Drawable getIntentIcon(Intent intent) {
+ PackageManager pm = activity.getPackageManager();
+ List resolveInfoList = pm.queryIntentActivities(intent, 0);
+
+ // if options > 1, display open with...
+ if(resolveInfoList.size() > 0) {
+ return resolveInfoList.get(0).activityInfo.loadIcon(pm);
+ }
+
+ return null;
+ }
+
+}
diff --git a/astrid/src/com/todoroo/astrid/model/AddOn.java b/astrid/src/com/todoroo/astrid/model/AddOn.java
new file mode 100644
index 000000000..c23961a52
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/model/AddOn.java
@@ -0,0 +1,90 @@
+package com.todoroo.astrid.model;
+
+import android.graphics.Bitmap;
+
+/**
+ * An add-on installable by Astrid
+ *
+ * @author Tim Su
+ *
+ */
+public class AddOn {
+
+ private final boolean free;
+ private final boolean internal;
+ private final String title;
+ private final String author;
+ private final String description;
+ private final String packageName;
+ private final String webPage;
+ private final Bitmap icon;
+
+ public AddOn(boolean free, boolean internal, String title, String author, String description,
+ String packageName, String webPage, Bitmap icon) {
+ this.free = free;
+ this.internal = internal;
+ this.title = title;
+ this.author = author;
+ this.description = description;
+ this.packageName = packageName;
+ this.webPage = webPage;
+ this.icon = icon;
+ }
+
+ /**
+ * @return whether this add-on is available for free
+ */
+ public boolean isFree() {
+ return free;
+ }
+
+ /**
+ * @return whether this add-on is signed with the same key as Astrid
+ */
+ public boolean isInternal() {
+ return internal;
+ }
+
+ /**
+ * @return add-on title
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * @return add-on author
+ */
+ public String getAuthor() {
+ return author;
+ }
+
+ /**
+ * @return add-on description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @return add-on java package name
+ */
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * @return add-on web-page
+ */
+ public String getWebPage() {
+ return webPage;
+ }
+
+ /**
+ * @return add-on icon
+ */
+ public Bitmap getIcon() {
+ return icon;
+ }
+
+}
diff --git a/astrid/src/com/todoroo/astrid/service/AddonService.java b/astrid/src/com/todoroo/astrid/service/AddOnService.java
similarity index 70%
rename from astrid/src/com/todoroo/astrid/service/AddonService.java
rename to astrid/src/com/todoroo/astrid/service/AddOnService.java
index e337ee3df..2be038a07 100644
--- a/astrid/src/com/todoroo/astrid/service/AddonService.java
+++ b/astrid/src/com/todoroo/astrid/service/AddOnService.java
@@ -11,11 +11,14 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.astrid.model.AddOn;
import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Preferences;
@@ -26,13 +29,16 @@ import com.todoroo.astrid.utility.Preferences;
*
*/
@SuppressWarnings("nls")
-public class AddonService {
+public class AddOnService {
/** OEM preference key */
private static final String PREF_OEM = "poem";
/** Astrid Power Pack package */
- private static final String POWER_PACK_PACKAGE = "com.todoroo.astrid.ppack";
+ public static final String POWER_PACK_PACKAGE = "com.todoroo.astrid.ppack";
+
+ /** Astrid Locale package */
+ public static final String LOCALE_PACKAGE = "com.todoroo.astrid.locale";
/** Astrid Power Pack label */
public static final String POWER_PACK_LABEL = "Astrid Power Pack";
@@ -135,4 +141,48 @@ public class AddonService {
Preferences.setBoolean(PREF_OEM, true);
}
+ /**
+ * Check whether a given add-on is installed
+ * @param addOn
+ * @return
+ */
+ public boolean isInstalled(AddOn addOn) {
+ Context context = ContextManager.getContext();
+ ApplicationInfo applicationInfo;
+ try {
+ applicationInfo = context.getPackageManager().getApplicationInfo(
+ addOn.getPackageName(), 0);
+ } catch (Exception e) {
+ return false;
+ }
+
+ if(applicationInfo == null)
+ return false;
+ if(!addOn.isInternal())
+ return true;
+ return applicationInfo.uid == context.getApplicationInfo().uid;
+ }
+
+ /**
+ * Get a list of add-ons
+ *
+ * @return available add-ons
+ */
+ public AddOn[] getAddOns() {
+ Resources r = ContextManager.getContext().getResources();
+
+ // temporary temporary
+ AddOn[] list = new AddOn[2];
+ list[0] = new AddOn(false, true, "Astrid Power Pack", null,
+ "Support Astrid and get more productive with the Astrid Power Pack backup, widgets, no ads, and calendar integration. Power up today!",
+ POWER_PACK_PACKAGE, "http://www.weloveastrid.com/store",
+ ((BitmapDrawable)r.getDrawable(R.drawable.icon_pp)).getBitmap());
+
+ list[1] = new AddOn(false, true, "Astrid Locale Plugin", null,
+ "Allows Astrid to make use of the Locale application to send you notifications based on filter conditions",
+ LOCALE_PACKAGE, "http://www.weloveastrid.com/store",
+ ((BitmapDrawable)r.getDrawable(R.drawable.icon_pp)).getBitmap());
+
+ return list;
+ }
}
diff --git a/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java b/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
index 01bfb337c..0020f9216 100644
--- a/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
+++ b/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
@@ -102,6 +102,7 @@ public class AstridDependencyInjector implements AbstractDependencyInjector {
injectables.put("taskService", TaskService.class);
injectables.put("metadataService", MetadataService.class);
injectables.put("upgradeService", UpgradeService.class);
+ injectables.put("addonService", AddOnService.class);
// com.timsu.astrid.data
injectables.put("tasksTable", "tasks");
diff --git a/astrid/src/com/todoroo/astrid/service/UpgradeService.java b/astrid/src/com/todoroo/astrid/service/UpgradeService.java
index ea6859840..5b58578f3 100644
--- a/astrid/src/com/todoroo/astrid/service/UpgradeService.java
+++ b/astrid/src/com/todoroo/astrid/service/UpgradeService.java
@@ -23,7 +23,7 @@ public final class UpgradeService {
return;
if(from == 135)
- AddonService.recordOem();
+ AddOnService.recordOem();
if(from < 136)
new Astrid2To3UpgradeHelper().upgrade2To3();