mirror of https://github.com/tasks/tasks
Removed all producteev source files
parent
8d298dda58
commit
3622ae6bc1
@ -1,47 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider;
|
|
||||||
import com.todoroo.astrid.service.StatisticsService;
|
|
||||||
import com.todoroo.astrid.sync.SyncBackgroundService;
|
|
||||||
import com.todoroo.astrid.sync.SyncProvider;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderUtilities;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SynchronizationService is the service that performs Astrid's background
|
|
||||||
* synchronization with online task managers. Starting this service
|
|
||||||
* schedules a repeating alarm which handles the synchronization
|
|
||||||
*
|
|
||||||
* @author Tim Su
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevBackgroundService extends SyncBackgroundService {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SyncProvider<?> getSyncProvider() {
|
|
||||||
return new ProducteevSyncProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SyncProviderUtilities getSyncUtilities() {
|
|
||||||
return ProducteevUtilities.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
StatisticsService.sessionStart(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
StatisticsService.sessionStop(this);
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,285 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.DialogInterface.OnClickListener;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.service.Autowired;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.andlib.service.ExceptionService;
|
|
||||||
import com.todoroo.andlib.utility.DateUtilities;
|
|
||||||
import com.todoroo.andlib.utility.DialogUtilities;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDataService;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevTask;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevUser;
|
|
||||||
import com.todoroo.astrid.service.MetadataService;
|
|
||||||
import com.todoroo.astrid.ui.PopupControlSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Control Set for managing task/dashboard assignments in Producteev
|
|
||||||
*
|
|
||||||
* @author Arne Jans <arne.jans@gmail.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevControlSet extends PopupControlSet {
|
|
||||||
|
|
||||||
private Spinner responsibleSelector;
|
|
||||||
private Spinner dashboardSelector;
|
|
||||||
|
|
||||||
private ArrayList<ProducteevUser> users = null;
|
|
||||||
private ArrayList<ProducteevDashboard> dashboards = null;
|
|
||||||
|
|
||||||
@Autowired MetadataService metadataService;
|
|
||||||
@Autowired ExceptionService exceptionService;
|
|
||||||
|
|
||||||
private int lastDashboardSelection = 0;
|
|
||||||
|
|
||||||
public ProducteevControlSet(final Activity activity, int layout, int displayViewLayout, int title) {
|
|
||||||
super(activity, layout, displayViewLayout, title);
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh the content of the responsibleSelector with the given userlist.
|
|
||||||
*
|
|
||||||
* @param newUsers the new userlist to show in the responsibleSelector
|
|
||||||
*/
|
|
||||||
private void refreshResponsibleSpinner(ArrayList<ProducteevUser> newUsers) {
|
|
||||||
Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(model.getId());
|
|
||||||
long responsibleId = -1;
|
|
||||||
if(metadata != null && metadata.containsNonNullValue(ProducteevTask.RESPONSIBLE_ID))
|
|
||||||
responsibleId = metadata.getValue(ProducteevTask.RESPONSIBLE_ID);
|
|
||||||
refreshResponsibleSpinner(newUsers, responsibleId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh the content of the responsibleSelector with the given userlist.
|
|
||||||
*
|
|
||||||
* @param newUsers the new userlist to show in the responsibleSelector
|
|
||||||
* @param responsibleId the id of the responsible user to set in the spinner
|
|
||||||
*/
|
|
||||||
private void refreshResponsibleSpinner(ArrayList<ProducteevUser> newUsers, long responsibleId) {
|
|
||||||
// Fill the responsible-spinner and set the current responsible
|
|
||||||
this.users = (newUsers == null ? new ArrayList<ProducteevUser>() : newUsers);
|
|
||||||
|
|
||||||
ArrayAdapter<ProducteevUser> usersAdapter = new ArrayAdapter<ProducteevUser>(activity,
|
|
||||||
android.R.layout.simple_spinner_item, this.users);
|
|
||||||
usersAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
responsibleSelector.setAdapter(usersAdapter);
|
|
||||||
|
|
||||||
int visibility = newUsers == null ? View.GONE : View.VISIBLE;
|
|
||||||
|
|
||||||
getView().findViewById(R.id.producteev_TEA_task_assign_label).setVisibility(visibility);
|
|
||||||
responsibleSelector.setVisibility(visibility);
|
|
||||||
|
|
||||||
int responsibleSpinnerIndex = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < this.users.size() ; i++) {
|
|
||||||
if (this.users.get(i).getId() == responsibleId ||
|
|
||||||
(responsibleId == -1 && this.users.get(i).getId() == Preferences.getLong(ProducteevUtilities.PREF_USER_ID, -1))) {
|
|
||||||
responsibleSpinnerIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
responsibleSelector.setSelection(responsibleSpinnerIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void readFromTaskOnInitialize() {
|
|
||||||
Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(model.getId());
|
|
||||||
if(metadata == null)
|
|
||||||
metadata = ProducteevTask.newMetadata();
|
|
||||||
|
|
||||||
// Fill the dashboard-spinner and set the current dashboard
|
|
||||||
long dashboardId = ProducteevUtilities.INSTANCE.getDefaultDashboard();
|
|
||||||
if(metadata.containsNonNullValue(ProducteevTask.DASHBOARD_ID))
|
|
||||||
dashboardId = metadata.getValue(ProducteevTask.DASHBOARD_ID);
|
|
||||||
|
|
||||||
StoreObject[] dashboardsData = ProducteevDataService.getInstance().getDashboards();
|
|
||||||
dashboards = new ArrayList<ProducteevDashboard>(dashboardsData.length);
|
|
||||||
ProducteevDashboard ownerDashboard = null;
|
|
||||||
int dashboardSpinnerIndex = -1;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (i=0;i<dashboardsData.length;i++) {
|
|
||||||
ProducteevDashboard dashboard = new ProducteevDashboard(dashboardsData[i]);
|
|
||||||
dashboards.add(dashboard);
|
|
||||||
if(dashboard.getId() == dashboardId) {
|
|
||||||
ownerDashboard = dashboard;
|
|
||||||
dashboardSpinnerIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//dashboard to not sync as first spinner-entry
|
|
||||||
dashboards.add(0, new ProducteevDashboard(ProducteevUtilities.DASHBOARD_NO_SYNC, activity.getString(R.string.producteev_no_dashboard),null));
|
|
||||||
// dummy entry for adding a new dashboard
|
|
||||||
dashboards.add(new ProducteevDashboard(ProducteevUtilities.DASHBOARD_CREATE, activity.getString(R.string.producteev_create_dashboard),null));
|
|
||||||
|
|
||||||
ArrayAdapter<ProducteevDashboard> dashAdapter = new ArrayAdapter<ProducteevDashboard>(activity,
|
|
||||||
android.R.layout.simple_spinner_item, dashboards);
|
|
||||||
dashAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
dashboardSelector.setAdapter(dashAdapter);
|
|
||||||
dashboardSelector.setSelection(dashboardSpinnerIndex+1);
|
|
||||||
|
|
||||||
if (ownerDashboard == null || ownerDashboard.getId() == ProducteevUtilities.DASHBOARD_NO_SYNC
|
|
||||||
|| ownerDashboard.getId() == ProducteevUtilities.DASHBOARD_CREATE) {
|
|
||||||
responsibleSelector.setEnabled(false);
|
|
||||||
responsibleSelector.setAdapter(null);
|
|
||||||
getView().findViewById(R.id.producteev_TEA_task_assign_label).setVisibility(View.GONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshResponsibleSpinner(ownerDashboard.getUsers());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void afterInflate() {
|
|
||||||
|
|
||||||
this.displayText.setText(activity.getString(R.string.producteev_TEA_control_set_display));
|
|
||||||
|
|
||||||
//view = LayoutInflater.from(activity).inflate(R.layout.producteev_control, parent, true);
|
|
||||||
|
|
||||||
this.responsibleSelector = (Spinner) getView().findViewById(R.id.producteev_TEA_task_assign);
|
|
||||||
TextView emptyView = new TextView(activity);
|
|
||||||
emptyView.setText(activity.getText(R.string.producteev_no_dashboard));
|
|
||||||
responsibleSelector.setEmptyView(emptyView);
|
|
||||||
|
|
||||||
this.dashboardSelector = (Spinner) getView().findViewById(R.id.producteev_TEA_dashboard_assign);
|
|
||||||
this.dashboardSelector.setOnItemSelectedListener(new OnItemSelectedListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> spinnerParent, View spinnerView,
|
|
||||||
int position, long id) {
|
|
||||||
final Spinner dashSelector = (Spinner) spinnerParent;
|
|
||||||
ProducteevDashboard dashboard = (ProducteevDashboard) dashSelector.getSelectedItem();
|
|
||||||
if (dashboard.getId() == ProducteevUtilities.DASHBOARD_CREATE) {
|
|
||||||
// let the user create a new dashboard
|
|
||||||
final EditText editor = new EditText(ProducteevControlSet.this.activity);
|
|
||||||
OnClickListener okListener = new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dlg, int which) {
|
|
||||||
Activity context = ProducteevControlSet.this.activity;
|
|
||||||
String newDashboardName = editor.getText().toString();
|
|
||||||
if (newDashboardName == null || newDashboardName.length() == 0) {
|
|
||||||
dlg.cancel();
|
|
||||||
} else {
|
|
||||||
// create the real dashboard, select it in the spinner and refresh responsiblespinner
|
|
||||||
ProgressDialog progressDialog = com.todoroo.andlib.utility.DialogUtilities.progressDialog(context,
|
|
||||||
context.getString(R.string.DLG_wait));
|
|
||||||
try {
|
|
||||||
progressDialog.show();
|
|
||||||
JSONObject newDashJSON = ProducteevSyncProvider.getInvoker().dashboardsCreate(
|
|
||||||
newDashboardName).getJSONObject("dashboard"); //$NON-NLS-1$
|
|
||||||
StoreObject local = ProducteevDataService.getInstance().updateDashboards(newDashJSON, true);
|
|
||||||
if (local != null) {
|
|
||||||
ProducteevDashboard newDashboard = new ProducteevDashboard(local);
|
|
||||||
ArrayAdapter<ProducteevDashboard> adapter = (ArrayAdapter<ProducteevDashboard>) dashSelector.getAdapter();
|
|
||||||
adapter.insert(newDashboard, adapter.getCount()-1);
|
|
||||||
dashSelector.setSelection(adapter.getCount()-2);
|
|
||||||
refreshResponsibleSpinner(newDashboard.getUsers());
|
|
||||||
DialogUtilities.dismissDialog(context, progressDialog);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
DialogUtilities.dismissDialog(context, progressDialog);
|
|
||||||
DialogUtilities.okDialog(context,
|
|
||||||
context.getString(R.string.DLG_error, e.getMessage()),
|
|
||||||
null);
|
|
||||||
exceptionService.reportError("pdv-create-dashboard", e); //$NON-NLS-1$
|
|
||||||
dashSelector.setSelection(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
OnClickListener cancelListener = new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dlg, int which) {
|
|
||||||
dlg.cancel();
|
|
||||||
dashboardSelector.setSelection(lastDashboardSelection);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
DialogUtilities.viewDialog(ProducteevControlSet.this.activity,
|
|
||||||
ProducteevControlSet.this.activity.getString(R.string.producteev_create_dashboard_name),
|
|
||||||
editor,
|
|
||||||
okListener,
|
|
||||||
cancelListener);
|
|
||||||
} else {
|
|
||||||
refreshResponsibleSpinner(dashboard.getUsers());
|
|
||||||
lastDashboardSelection = position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> spinnerParent) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String writeToModelAfterInitialized(Task task) {
|
|
||||||
Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(task.getId());
|
|
||||||
try {
|
|
||||||
if (metadata == null) {
|
|
||||||
metadata = new Metadata();
|
|
||||||
metadata.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY);
|
|
||||||
metadata.setValue(Metadata.TASK, task.getId());
|
|
||||||
metadata.setValue(ProducteevTask.ID, 0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
ProducteevDashboard dashboard = (ProducteevDashboard) dashboardSelector.getSelectedItem();
|
|
||||||
metadata.setValue(ProducteevTask.DASHBOARD_ID, dashboard.getId());
|
|
||||||
|
|
||||||
ProducteevUser responsibleUser = (ProducteevUser) responsibleSelector.getSelectedItem();
|
|
||||||
|
|
||||||
if(responsibleUser == null)
|
|
||||||
metadata.setValue(ProducteevTask.RESPONSIBLE_ID, 0L);
|
|
||||||
else
|
|
||||||
metadata.setValue(ProducteevTask.RESPONSIBLE_ID, responsibleUser.getId());
|
|
||||||
|
|
||||||
// Erase PDTV-repeating-info if task itself is repeating with Astrid-repeat
|
|
||||||
if (task.containsNonNullValue(Task.RECURRENCE) && task.getValue(Task.RECURRENCE).length()>0) {
|
|
||||||
metadata.setValue(ProducteevTask.REPEATING_SETTING, ""); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
if(metadata.getSetValues().size() > 0) {
|
|
||||||
metadataService.save(metadata);
|
|
||||||
task.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e("error-saving-pdv", "Error Saving Metadata", e); //$NON-NLS-1$ //$NON-NLS-2$
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void refreshDisplayView() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.sql.Criterion;
|
|
||||||
import com.todoroo.andlib.sql.Join;
|
|
||||||
import com.todoroo.andlib.sql.Query;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.api.CustomFilterCriterion;
|
|
||||||
import com.todoroo.astrid.api.MultipleSelectCriterion;
|
|
||||||
import com.todoroo.astrid.dao.MetadataDao;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDataService;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevTask;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevUser;
|
|
||||||
|
|
||||||
public class ProducteevCustomFilterCriteriaExposer extends BroadcastReceiver {
|
|
||||||
private static final String IDENTIFIER_PRODUCTEEV_WORKSPACE = "producteev_workspace"; //$NON-NLS-1$
|
|
||||||
private static final String IDENTIFIER_PRODUCTEEV_ASSIGNEE = "producteev_assignee"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
// if we aren't logged in, don't expose features
|
|
||||||
if(!ProducteevUtilities.INSTANCE.isLoggedIn())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Resources r = context.getResources();
|
|
||||||
|
|
||||||
StoreObject[] objects = ProducteevDataService.getInstance().getDashboards();
|
|
||||||
ProducteevDashboard[] dashboards = new ProducteevDashboard[objects.length];
|
|
||||||
for (int i = 0; i < objects.length; i++) {
|
|
||||||
dashboards[i] = new ProducteevDashboard(objects[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomFilterCriterion[] ret = new CustomFilterCriterion[2];
|
|
||||||
int j = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
String[] workspaceNames = new String[objects.length];
|
|
||||||
String[] workspaceIds = new String[objects.length];
|
|
||||||
for (int i = 0; i < dashboards.length; i++) {
|
|
||||||
workspaceNames[i] = dashboards[i].getName();
|
|
||||||
workspaceIds[i] = String.valueOf(dashboards[i].getId());
|
|
||||||
}
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(Metadata.KEY.name, ProducteevTask.METADATA_KEY);
|
|
||||||
values.put(ProducteevTask.DASHBOARD_ID.name, "?");
|
|
||||||
CustomFilterCriterion criterion = new MultipleSelectCriterion(
|
|
||||||
IDENTIFIER_PRODUCTEEV_WORKSPACE,
|
|
||||||
context.getString(R.string.CFC_producteev_in_workspace_text),
|
|
||||||
// Todo: abstract these metadata queries
|
|
||||||
Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner(
|
|
||||||
Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and(
|
|
||||||
TaskDao.TaskCriteria.activeAndVisible(),
|
|
||||||
MetadataDao.MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
ProducteevTask.DASHBOARD_ID.eq("?"))).toString(),
|
|
||||||
values,
|
|
||||||
workspaceNames,
|
|
||||||
workspaceIds,
|
|
||||||
((BitmapDrawable)r.getDrawable(R.drawable.silk_folder)).getBitmap(),
|
|
||||||
context.getString(R.string.CFC_producteev_in_workspace_name));
|
|
||||||
ret[j++] = criterion;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Set<ProducteevUser> users = new TreeSet<ProducteevUser>();
|
|
||||||
for (ProducteevDashboard dashboard : dashboards) {
|
|
||||||
users.addAll(dashboard.getUsers());
|
|
||||||
}
|
|
||||||
int numUsers = users.size();
|
|
||||||
String[] userNames = new String[numUsers];
|
|
||||||
String[] userIds = new String[numUsers];
|
|
||||||
int i = 0;
|
|
||||||
for (ProducteevUser user : users) {
|
|
||||||
userNames[i] = user.toString();
|
|
||||||
userIds[i] = String.valueOf(user.getId());
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
ContentValues values = new ContentValues(2);
|
|
||||||
values.put(Metadata.KEY.name, ProducteevTask.METADATA_KEY);
|
|
||||||
values.put(ProducteevTask.RESPONSIBLE_ID.name, "?");
|
|
||||||
CustomFilterCriterion criterion = new MultipleSelectCriterion(
|
|
||||||
IDENTIFIER_PRODUCTEEV_ASSIGNEE,
|
|
||||||
context.getString(R.string.CFC_producteev_assigned_to_text),
|
|
||||||
// Todo: abstract these metadata queries, and unify this code with the CustomFilterExposers.
|
|
||||||
Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner(
|
|
||||||
Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and(
|
|
||||||
TaskDao.TaskCriteria.activeAndVisible(),
|
|
||||||
MetadataDao.MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
ProducteevTask.RESPONSIBLE_ID.eq("?"))).toString(),
|
|
||||||
values,
|
|
||||||
userNames,
|
|
||||||
userIds,
|
|
||||||
((BitmapDrawable)r.getDrawable(R.drawable.silk_user_gray)).getBitmap(),
|
|
||||||
context.getString(R.string.CFC_producteev_assigned_to_name));
|
|
||||||
ret[j++] = criterion;
|
|
||||||
}
|
|
||||||
|
|
||||||
// transmit filter list
|
|
||||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_CUSTOM_FILTER_CRITERIA);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, ret);
|
|
||||||
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,180 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import java.text.DateFormatSymbols;
|
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
import com.todoroo.astrid.adapter.TaskAdapter;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDataService;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevTask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exposes Task Details for Producteev:
|
|
||||||
* - notes
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevDetailExposer extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
ContextManager.setContext(context);
|
|
||||||
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
|
|
||||||
if(taskId == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
String taskDetail;
|
|
||||||
try {
|
|
||||||
taskDetail = getTaskDetails(context, taskId);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(taskDetail == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail);
|
|
||||||
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public String getTaskDetails(Context context, long id) {
|
|
||||||
Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(id);
|
|
||||||
if(metadata == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
if(!ProducteevUtilities.INSTANCE.isLoggedIn())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
long dashboardId = -1;
|
|
||||||
if(metadata.containsNonNullValue(ProducteevTask.DASHBOARD_ID))
|
|
||||||
dashboardId = metadata.getValue(ProducteevTask.DASHBOARD_ID);
|
|
||||||
long responsibleId = -1;
|
|
||||||
if(metadata.containsNonNullValue(ProducteevTask.RESPONSIBLE_ID))
|
|
||||||
responsibleId = metadata.getValue(ProducteevTask.RESPONSIBLE_ID);
|
|
||||||
long creatorId = -1;
|
|
||||||
if(metadata.containsNonNullValue(ProducteevTask.CREATOR_ID))
|
|
||||||
creatorId = metadata.getValue(ProducteevTask.CREATOR_ID);
|
|
||||||
String repeatSetting = null;
|
|
||||||
if(metadata.containsNonNullValue(ProducteevTask.REPEATING_SETTING))
|
|
||||||
repeatSetting = metadata.getValue(ProducteevTask.REPEATING_SETTING);
|
|
||||||
|
|
||||||
// display dashboard if not "no sync" or "default"
|
|
||||||
StoreObject ownerDashboard = null;
|
|
||||||
for(StoreObject dashboard : ProducteevDataService.getInstance().getDashboards()) {
|
|
||||||
if(dashboard == null || !dashboard.containsNonNullValue(ProducteevDashboard.REMOTE_ID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(dashboard.getValue(ProducteevDashboard.REMOTE_ID) == dashboardId) {
|
|
||||||
ownerDashboard = dashboard;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(dashboardId != ProducteevUtilities.DASHBOARD_NO_SYNC && dashboardId
|
|
||||||
!= Preferences.getLong(ProducteevUtilities.PREF_DEFAULT_DASHBOARD, 0L) &&
|
|
||||||
ownerDashboard != null) {
|
|
||||||
String dashboardName = ownerDashboard.getValue(ProducteevDashboard.NAME);
|
|
||||||
builder.append("<img src='silk_folder'/> ").append(dashboardName).append(TaskAdapter.DETAIL_SEPARATOR); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
// display responsible user if not current one
|
|
||||||
if(responsibleId > 0 && ownerDashboard != null && responsibleId !=
|
|
||||||
Preferences.getLong(ProducteevUtilities.PREF_USER_ID, 0L)) {
|
|
||||||
String user = ProducteevDashboard.getUserFromDashboard(ownerDashboard, responsibleId);
|
|
||||||
if(user != null)
|
|
||||||
builder.append("<img src='silk_user_gray'/> ").append(user).append(TaskAdapter.DETAIL_SEPARATOR); //$NON-NLS-1$
|
|
||||||
} else {
|
|
||||||
// display creator user if not responsible user
|
|
||||||
if(creatorId > 0 && ownerDashboard != null && creatorId != responsibleId) {
|
|
||||||
String user = ProducteevDashboard.getUserFromDashboard(ownerDashboard, creatorId);
|
|
||||||
if(user != null)
|
|
||||||
builder.append("<img src='silk_user_orange'/> ").append( //$NON-NLS-1$
|
|
||||||
context.getString(R.string.producteev_PDE_task_from, user)).
|
|
||||||
append(TaskAdapter.DETAIL_SEPARATOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// display repeating task information
|
|
||||||
if (repeatSetting != null && repeatSetting.length() > 0) {
|
|
||||||
String interval = null;
|
|
||||||
String[] pdvRepeating = repeatSetting.split(",");
|
|
||||||
int pdvRepeatingValue = 0;
|
|
||||||
String pdvRepeatingDay = null;
|
|
||||||
try {
|
|
||||||
pdvRepeatingValue = Integer.parseInt(pdvRepeating[0]);
|
|
||||||
} catch (Exception e) {
|
|
||||||
pdvRepeatingDay = pdvRepeating[0];
|
|
||||||
pdvRepeatingValue = 1;
|
|
||||||
}
|
|
||||||
String pdvRepeatingInterval = pdvRepeating[1];
|
|
||||||
|
|
||||||
if (pdvRepeatingInterval.startsWith("day")) {
|
|
||||||
interval = context.getResources().getQuantityString(R.plurals.DUt_days, pdvRepeatingValue,
|
|
||||||
pdvRepeatingValue);
|
|
||||||
} else if (pdvRepeatingInterval.startsWith("weekday")) {
|
|
||||||
interval = context.getResources().getQuantityString(R.plurals.DUt_weekdays, pdvRepeatingValue,
|
|
||||||
pdvRepeatingValue);
|
|
||||||
} else if (pdvRepeatingInterval.startsWith("week")) {
|
|
||||||
interval = context.getResources().getQuantityString(R.plurals.DUt_weeks, pdvRepeatingValue,
|
|
||||||
pdvRepeatingValue);
|
|
||||||
} else if (pdvRepeatingInterval.startsWith("month")) {
|
|
||||||
interval = context.getResources().getQuantityString(R.plurals.DUt_months, pdvRepeatingValue,
|
|
||||||
pdvRepeatingValue);
|
|
||||||
} else if (pdvRepeatingInterval.startsWith("year")) {
|
|
||||||
interval = context.getResources().getQuantityString(R.plurals.DUt_years, pdvRepeatingValue,
|
|
||||||
pdvRepeatingValue);
|
|
||||||
}
|
|
||||||
interval = "<b>" + interval + "</b>"; //$NON-NLS-1$//$NON-NLS-2$
|
|
||||||
if (pdvRepeatingDay != null) {
|
|
||||||
DateFormatSymbols dfs = new DateFormatSymbols();
|
|
||||||
String[] weekdays = dfs.getShortWeekdays();
|
|
||||||
if (pdvRepeatingDay.equals("monday")) {
|
|
||||||
pdvRepeatingDay = weekdays[Calendar.MONDAY];
|
|
||||||
} else if (pdvRepeatingDay.equals("tuesday")) {
|
|
||||||
pdvRepeatingDay = weekdays[Calendar.TUESDAY];
|
|
||||||
} else if (pdvRepeatingDay.equals("wednesday")) {
|
|
||||||
pdvRepeatingDay = weekdays[Calendar.WEDNESDAY];
|
|
||||||
} else if (pdvRepeatingDay.equals("thursday")) {
|
|
||||||
pdvRepeatingDay = weekdays[Calendar.THURSDAY];
|
|
||||||
} else if (pdvRepeatingDay.equals("friday")) {
|
|
||||||
pdvRepeatingDay = weekdays[Calendar.FRIDAY];
|
|
||||||
} else if (pdvRepeatingDay.equals("saturday")) {
|
|
||||||
pdvRepeatingDay = weekdays[Calendar.SATURDAY];
|
|
||||||
} else if (pdvRepeatingDay.equals("sunday")) {
|
|
||||||
pdvRepeatingDay = weekdays[Calendar.SUNDAY];
|
|
||||||
}
|
|
||||||
interval = context.getResources().getString(R.string.repeat_detail_byday).replace("$I", //$NON-NLS-1$
|
|
||||||
interval).replace("$D", pdvRepeatingDay); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
String detail = context.getString(R.string.repeat_detail_duedate, interval);
|
|
||||||
builder.append("<img src='repeating_deadline'/> ").append(detail). //$NON-NLS-1$
|
|
||||||
append(TaskAdapter.DETAIL_SEPARATOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(builder.length() == 0)
|
|
||||||
return null;
|
|
||||||
String result = builder.toString();
|
|
||||||
return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,197 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.sql.Criterion;
|
|
||||||
import com.todoroo.andlib.sql.QueryTemplate;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.api.AstridFilterExposer;
|
|
||||||
import com.todoroo.astrid.api.Filter;
|
|
||||||
import com.todoroo.astrid.api.FilterCategory;
|
|
||||||
import com.todoroo.astrid.api.FilterListHeader;
|
|
||||||
import com.todoroo.astrid.api.FilterListItem;
|
|
||||||
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDataService;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevTask;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevUser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exposes filters based on Producteev Dashboards
|
|
||||||
*
|
|
||||||
* @author Arne Jans <arne.jans@gmail.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevFilterExposer extends BroadcastReceiver implements AstridFilterExposer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param context
|
|
||||||
*/
|
|
||||||
public static Filter filterFromList(Context context, ProducteevDashboard dashboard, long currentUserId) {
|
|
||||||
String dashboardTitle = dashboard.getName();
|
|
||||||
String title = dashboard.getName();
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(Metadata.KEY.name, ProducteevTask.METADATA_KEY);
|
|
||||||
values.put(ProducteevTask.DASHBOARD_ID.name, dashboard.getId());
|
|
||||||
values.put(ProducteevTask.ID.name, 0);
|
|
||||||
values.put(ProducteevTask.CREATOR_ID.name, currentUserId);
|
|
||||||
values.put(ProducteevTask.RESPONSIBLE_ID.name, currentUserId);
|
|
||||||
Filter filter;
|
|
||||||
if (currentUserId != -1)
|
|
||||||
filter = new Filter(dashboardTitle, title, new QueryTemplate().join(
|
|
||||||
ProducteevDataService.METADATA_JOIN).where(Criterion.and(
|
|
||||||
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
TaskCriteria.isActive(),
|
|
||||||
TaskCriteria.isVisible(),
|
|
||||||
Criterion.or(ProducteevTask.CREATOR_ID.eq(currentUserId),
|
|
||||||
ProducteevTask.RESPONSIBLE_ID.eq(currentUserId)),
|
|
||||||
ProducteevTask.DASHBOARD_ID.eq(dashboard.getId()))),
|
|
||||||
values);
|
|
||||||
else
|
|
||||||
filter = new Filter(dashboardTitle, title, new QueryTemplate().join(
|
|
||||||
ProducteevDataService.METADATA_JOIN).where(Criterion.and(
|
|
||||||
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
TaskCriteria.isActive(),
|
|
||||||
TaskCriteria.isVisible(),
|
|
||||||
ProducteevTask.DASHBOARD_ID.eq(dashboard.getId()))),
|
|
||||||
values);
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Filter filterUserAssignedByMe(Context context, ProducteevUser user, long currentUserId) {
|
|
||||||
String title = context.getString(R.string.producteev_FEx_responsible_title, user.toString());
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(Metadata.KEY.name, ProducteevTask.METADATA_KEY);
|
|
||||||
values.put(ProducteevTask.ID.name, 0);
|
|
||||||
values.put(ProducteevTask.CREATOR_ID.name, currentUserId);
|
|
||||||
values.put(ProducteevTask.RESPONSIBLE_ID.name, user.getId());
|
|
||||||
Filter filter = new Filter(user.toString(), title, new QueryTemplate().join(
|
|
||||||
ProducteevDataService.METADATA_JOIN).where(Criterion.and(
|
|
||||||
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
TaskCriteria.isActive(),
|
|
||||||
TaskCriteria.isVisible(),
|
|
||||||
ProducteevTask.CREATOR_ID.eq(currentUserId),
|
|
||||||
ProducteevTask.RESPONSIBLE_ID.eq(user.getId()))),
|
|
||||||
values);
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Filter filterUserAssignedByOthers(Context context, ProducteevUser user, long currentUserId) {
|
|
||||||
String title = context.getString(R.string.producteev_FEx_responsible_title, user.toString());
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(Metadata.KEY.name, ProducteevTask.METADATA_KEY);
|
|
||||||
values.put(ProducteevTask.ID.name, 0);
|
|
||||||
values.put(ProducteevTask.CREATOR_ID.name, 0);
|
|
||||||
values.put(ProducteevTask.RESPONSIBLE_ID.name, currentUserId);
|
|
||||||
Filter filter = new Filter(user.toString(), title, new QueryTemplate().join(
|
|
||||||
ProducteevDataService.METADATA_JOIN).where(Criterion.and(
|
|
||||||
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
TaskCriteria.isActive(),
|
|
||||||
TaskCriteria.isVisible(),
|
|
||||||
Criterion.not(ProducteevTask.CREATOR_ID.eq(currentUserId)),
|
|
||||||
ProducteevTask.RESPONSIBLE_ID.eq(user.getId()))),
|
|
||||||
values);
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
ContextManager.setContext(context);
|
|
||||||
FilterListItem[] list = prepareFilters(context);
|
|
||||||
|
|
||||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
|
|
||||||
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FilterListItem[] prepareFilters(Context context) {
|
|
||||||
// if we aren't logged in, don't expose features
|
|
||||||
if(!ProducteevUtilities.INSTANCE.isLoggedIn())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
StoreObject[] dashboards = ProducteevDataService.getInstance().getDashboards();
|
|
||||||
|
|
||||||
// If user does not have any dashboards, don't show this section at all
|
|
||||||
if(dashboards.length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
FilterListHeader producteevHeader = new FilterListHeader(context.getString(R.string.producteev_FEx_header));
|
|
||||||
|
|
||||||
long currentUserId = Preferences.getLong(ProducteevUtilities.PREF_USER_ID, -1);
|
|
||||||
|
|
||||||
// load dashboards
|
|
||||||
Filter[] dashboardFilters = new Filter[dashboards.length];
|
|
||||||
for(int i = 0; i < dashboards.length; i++)
|
|
||||||
dashboardFilters[i] = filterFromList(context, new ProducteevDashboard(dashboards[i]), currentUserId);
|
|
||||||
FilterCategory producteevDashboards = new FilterCategory(context.getString(R.string.producteev_FEx_dashboard),
|
|
||||||
dashboardFilters);
|
|
||||||
|
|
||||||
// load responsible people, assigned by me
|
|
||||||
TreeSet<ProducteevUser> people = loadResponsiblePeople(dashboards);
|
|
||||||
Filter[] peopleByMeFilters = new Filter[people.size()];
|
|
||||||
int index = 0;
|
|
||||||
for (ProducteevUser person : people)
|
|
||||||
peopleByMeFilters[index++] = filterUserAssignedByMe(context, person, currentUserId);
|
|
||||||
FilterCategory producteevUsersByMeCategory = new FilterCategory(context.getString(R.string.producteev_FEx_responsible_byme),
|
|
||||||
peopleByMeFilters);
|
|
||||||
|
|
||||||
// load responsible people, assigned by others
|
|
||||||
Filter[] peopleByOthersFilters = new Filter[people.size()];
|
|
||||||
index = 0;
|
|
||||||
for (ProducteevUser person : people)
|
|
||||||
peopleByOthersFilters[index++] = filterUserAssignedByOthers(context, person, currentUserId);
|
|
||||||
FilterCategory producteevUsersByOthersCategory = new FilterCategory(context.getString(R.string.producteev_FEx_responsible_byothers),
|
|
||||||
peopleByOthersFilters);
|
|
||||||
|
|
||||||
// transmit filter list
|
|
||||||
FilterListItem[] list = new FilterListItem[4];
|
|
||||||
list[0] = producteevHeader;
|
|
||||||
list[1] = producteevDashboards;
|
|
||||||
list[2] = producteevUsersByMeCategory;
|
|
||||||
list[3] = producteevUsersByOthersCategory;
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param dashboards
|
|
||||||
* @return people in a map of name => pair(dashboard id, user id)
|
|
||||||
*/
|
|
||||||
private TreeSet<ProducteevUser> loadResponsiblePeople(StoreObject[] dashboards) {
|
|
||||||
TreeSet<ProducteevUser> users = new TreeSet<ProducteevUser>();
|
|
||||||
for(StoreObject dashboard : dashboards) {
|
|
||||||
ProducteevDashboard elDashboard = new ProducteevDashboard(dashboard);
|
|
||||||
users.addAll(elDashboard.getUsers());
|
|
||||||
}
|
|
||||||
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FilterListItem[] getFilters() {
|
|
||||||
if (ContextManager.getContext() == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return prepareFilters(ContextManager.getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,240 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.Editable;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
|
||||||
import com.todoroo.andlib.utility.DialogUtilities;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
import com.todoroo.astrid.producteev.api.ApiAuthenticationException;
|
|
||||||
import com.todoroo.astrid.producteev.api.ProducteevInvoker;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider;
|
|
||||||
import com.todoroo.astrid.service.StatisticsConstants;
|
|
||||||
import com.todoroo.astrid.service.StatisticsService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This activity allows users to sign in or log in to Producteev
|
|
||||||
*
|
|
||||||
* @author arne.jans
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevLoginActivity extends Activity {
|
|
||||||
|
|
||||||
// --- ui initialization
|
|
||||||
|
|
||||||
public ProducteevLoginActivity() {
|
|
||||||
super();
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
ContextManager.setContext(this);
|
|
||||||
|
|
||||||
setContentView(R.layout.producteev_login_activity);
|
|
||||||
setTitle(R.string.producteev_PLA_title);
|
|
||||||
|
|
||||||
// terms clicking
|
|
||||||
findViewById(R.id.terms).setOnClickListener(new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View arg0) {
|
|
||||||
startActivity(new Intent(Intent.ACTION_VIEW,
|
|
||||||
Uri.parse("https://www.producteev.com/#terms"))); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final TextView errors = (TextView) findViewById(R.id.error);
|
|
||||||
final EditText emailEditText = (EditText) findViewById(R.id.email);
|
|
||||||
final EditText passwordEditText = (EditText) findViewById(R.id.password);
|
|
||||||
final View newUserLayout = findViewById(R.id.newUserLayout);
|
|
||||||
final Spinner timezoneList = (Spinner) findViewById(R.id.timezoneList);
|
|
||||||
|
|
||||||
String[] timezoneEntries = getResources().getStringArray(R.array.PLA_timezones_list);
|
|
||||||
String defaultTimeZone = TimeZone.getDefault().getID();
|
|
||||||
int selected = 0;
|
|
||||||
for(int i = 0; i < timezoneEntries.length; i++) {
|
|
||||||
if(timezoneEntries[i].equals(defaultTimeZone))
|
|
||||||
selected = i;
|
|
||||||
}
|
|
||||||
timezoneList.setSelection(selected);
|
|
||||||
|
|
||||||
Button signIn = (Button) findViewById(R.id.signIn);
|
|
||||||
signIn.setOnClickListener(new OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
errors.setVisibility(View.GONE);
|
|
||||||
if(newUserLayout.getVisibility() == View.VISIBLE)
|
|
||||||
newUserLayout.setVisibility(View.GONE);
|
|
||||||
else {
|
|
||||||
Editable email = emailEditText.getText();
|
|
||||||
Editable password = passwordEditText.getText();
|
|
||||||
if(email.length() == 0 || password.length() == 0) {
|
|
||||||
errors.setVisibility(View.VISIBLE);
|
|
||||||
errors.setText(R.string.producteev_PLA_errorEmpty);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
performLogin(email.toString(), password.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Button createNew = (Button) findViewById(R.id.createNew);
|
|
||||||
createNew.setOnClickListener(new OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
errors.setVisibility(View.GONE);
|
|
||||||
if(newUserLayout.getVisibility() != View.VISIBLE)
|
|
||||||
newUserLayout.setVisibility(View.VISIBLE);
|
|
||||||
else {
|
|
||||||
Editable email = emailEditText.getText();
|
|
||||||
Editable password = passwordEditText.getText();
|
|
||||||
Editable firstName = ((EditText)findViewById(R.id.firstName)).getText();
|
|
||||||
Editable lastName = ((EditText)findViewById(R.id.lastName)).getText();
|
|
||||||
String timezone = timezoneList.getSelectedItem().toString();
|
|
||||||
if(email.length() == 0 || password.length() == 0 ||
|
|
||||||
firstName.length() == 0 ||
|
|
||||||
lastName.length() == 0) {
|
|
||||||
errors.setVisibility(View.VISIBLE);
|
|
||||||
errors.setText(R.string.producteev_PLA_errorEmpty);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
performSignup(email.toString(), password.toString(),
|
|
||||||
firstName.toString(), lastName.toString(), timezone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void performLogin(final String email, final String password) {
|
|
||||||
final ProgressDialog dialog = DialogUtilities.progressDialog(this,
|
|
||||||
getString(R.string.DLG_wait));
|
|
||||||
final TextView errors = (TextView) findViewById(R.id.error);
|
|
||||||
dialog.show();
|
|
||||||
new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ProducteevInvoker invoker = ProducteevSyncProvider.getInvoker();
|
|
||||||
final StringBuilder errorMessage = new StringBuilder();
|
|
||||||
try {
|
|
||||||
invoker.authenticate(email, password);
|
|
||||||
|
|
||||||
Preferences.setString(R.string.producteev_PPr_email, email);
|
|
||||||
Preferences.setString(R.string.producteev_PPr_password, password);
|
|
||||||
ProducteevUtilities.INSTANCE.setToken(invoker.getToken());
|
|
||||||
|
|
||||||
StatisticsService.reportEvent(StatisticsConstants.PRODUCTEEV_LOGIN);
|
|
||||||
|
|
||||||
synchronize();
|
|
||||||
} catch (ApiAuthenticationException e) {
|
|
||||||
errorMessage.append(getString(R.string.producteev_PLA_errorAuth));
|
|
||||||
} catch (Exception e) {
|
|
||||||
errorMessage.append(e.getMessage());
|
|
||||||
} finally {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
DialogUtilities.dismissDialog(ProducteevLoginActivity.this, dialog);
|
|
||||||
if(errorMessage.length() > 0) {
|
|
||||||
errors.setVisibility(View.VISIBLE);
|
|
||||||
errors.setText(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performSignup(final String email, final String password,
|
|
||||||
final String firstName, final String lastName, final String timezone) {
|
|
||||||
final ProgressDialog dialog = DialogUtilities.progressDialog(this,
|
|
||||||
getString(R.string.DLG_wait));
|
|
||||||
final TextView errors = (TextView) findViewById(R.id.error);
|
|
||||||
dialog.show();
|
|
||||||
new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ProducteevInvoker invoker = ProducteevSyncProvider.getInvoker();
|
|
||||||
final StringBuilder errorMessage = new StringBuilder();
|
|
||||||
try {
|
|
||||||
invoker.usersSignUp(email, firstName, lastName, password, timezone, null);
|
|
||||||
invoker.authenticate(email, password);
|
|
||||||
|
|
||||||
Preferences.setString(R.string.producteev_PPr_email, email);
|
|
||||||
Preferences.setString(R.string.producteev_PPr_password, password);
|
|
||||||
ProducteevUtilities.INSTANCE.setToken(invoker.getToken());
|
|
||||||
|
|
||||||
StatisticsService.reportEvent(StatisticsConstants.PRODUCTEEV_SIGNUP);
|
|
||||||
|
|
||||||
synchronize();
|
|
||||||
} catch (Exception e) {
|
|
||||||
errorMessage.append(e.getMessage());
|
|
||||||
} finally {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
DialogUtilities.dismissDialog(ProducteevLoginActivity.this, dialog);
|
|
||||||
if(errorMessage.length() > 0) {
|
|
||||||
errors.setVisibility(View.VISIBLE);
|
|
||||||
errors.setText(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform synchronization
|
|
||||||
*/
|
|
||||||
protected void synchronize() {
|
|
||||||
startService(new Intent(null, null,
|
|
||||||
this, ProducteevBackgroundService.class));
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
StatisticsService.sessionStart(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
StatisticsService.sessionPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
StatisticsService.sessionStop(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.preference.ListPreference;
|
|
||||||
import android.preference.Preference;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDataService;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderPreferences;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderUtilities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays synchronization preferences and an action panel so users can
|
|
||||||
* initiate actions from the menu.
|
|
||||||
*
|
|
||||||
* @author timsu
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevPreferences extends SyncProviderPreferences {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPreferenceResource() {
|
|
||||||
return R.xml.preferences_producteev;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startSync() {
|
|
||||||
new ProducteevSyncProvider().synchronize(this);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logOut() {
|
|
||||||
new ProducteevSyncProvider().signOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SyncProviderUtilities getUtilities() {
|
|
||||||
return ProducteevUtilities.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
new ProducteevBackgroundService().scheduleService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
ListPreference defaultDash = (ListPreference)findPreference(getString(R.string.producteev_PPr_defaultdash_key));
|
|
||||||
String[] entries, entryValues;
|
|
||||||
StoreObject[] dashboards = ProducteevDataService.getInstance().getDashboards();
|
|
||||||
if(ProducteevUtilities.INSTANCE.isLoggedIn() && dashboards.length > 0) {
|
|
||||||
entries = new String[dashboards.length + 1];
|
|
||||||
entryValues = new String[dashboards.length + 1];
|
|
||||||
for(int i = 0; i < dashboards.length; i++) {
|
|
||||||
entries[i + 1] = dashboards[i].getValue(ProducteevDashboard.NAME);
|
|
||||||
entryValues[i + 1] = Long.toString(dashboards[i].getValue(ProducteevDashboard.REMOTE_ID));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
entries = new String[2];
|
|
||||||
entries[1] = getString(R.string.producteev_default_dashboard);
|
|
||||||
entryValues = new String[2];
|
|
||||||
entryValues[1] = Integer.toString(ProducteevUtilities.DASHBOARD_DEFAULT);
|
|
||||||
}
|
|
||||||
entries[0] = getString(R.string.producteev_no_dashboard);
|
|
||||||
entryValues[0] = Integer.toString(ProducteevUtilities.DASHBOARD_NO_SYNC);
|
|
||||||
defaultDash.setEntries(entries);
|
|
||||||
defaultDash.setEntryValues(entryValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updatePreferences(Preference preference, Object value) {
|
|
||||||
super.updatePreferences(preference, value);
|
|
||||||
final Resources r = getResources();
|
|
||||||
|
|
||||||
if (r.getString(R.string.producteev_PPr_defaultdash_key).equals(
|
|
||||||
preference.getKey())) {
|
|
||||||
int index = AndroidUtilities.indexOf(((ListPreference)preference).getEntryValues(), (String)value);
|
|
||||||
if(index == -1)
|
|
||||||
index = 1;
|
|
||||||
if(index == 0)
|
|
||||||
preference.setSummary(R.string.producteev_PPr_defaultdash_summary_none);
|
|
||||||
else
|
|
||||||
preference.setSummary(r.getString(
|
|
||||||
R.string.producteev_PPr_defaultdash_summary,
|
|
||||||
((ListPreference)preference).getEntries()[index]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
|
||||||
|
|
||||||
public class ProducteevStartupReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
static {
|
|
||||||
AstridDependencyInjector.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
/** Called when device is restarted */
|
|
||||||
public void onReceive(final Context context, Intent intent) {
|
|
||||||
ContextManager.setContext(context);
|
|
||||||
new ProducteevBackgroundService().scheduleService();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.api.SyncAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exposes sync action
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevSyncActionExposer extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
ContextManager.setContext(context);
|
|
||||||
// if we aren't logged in, don't expose sync action
|
|
||||||
if(!ProducteevUtilities.INSTANCE.isLoggedIn())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Intent syncIntent = new Intent(null, null,
|
|
||||||
context, ProducteevBackgroundService.class);
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getService(context, 0, syncIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
SyncAction syncAction = new SyncAction(context.getString(R.string.producteev_PPr_header),
|
|
||||||
pendingIntent);
|
|
||||||
|
|
||||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_SYNC_ACTIONS);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER);
|
|
||||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, syncAction);
|
|
||||||
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderUtilities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays synchronization preferences and an action panel so users can
|
|
||||||
* initiate actions from the menu.
|
|
||||||
*
|
|
||||||
* @author timsu
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevUtilities extends SyncProviderUtilities {
|
|
||||||
|
|
||||||
/** add-on identifier */
|
|
||||||
public static final String IDENTIFIER = "pdv"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
public static final ProducteevUtilities INSTANCE = new ProducteevUtilities();
|
|
||||||
|
|
||||||
/** setting for dashboard to getting created */
|
|
||||||
public static final int DASHBOARD_CREATE = -2;
|
|
||||||
|
|
||||||
/** setting for dashboard to not synchronize */
|
|
||||||
public static final int DASHBOARD_NO_SYNC = -1;
|
|
||||||
|
|
||||||
/** setting for dashboard to use default one */
|
|
||||||
public static final int DASHBOARD_DEFAULT = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getIdentifier() {
|
|
||||||
return IDENTIFIER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSyncIntervalKey() {
|
|
||||||
return R.string.producteev_PPr_interval_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- producteev-specific preferences
|
|
||||||
|
|
||||||
public static final String PREF_SERVER_LAST_SYNC = IDENTIFIER + "_last_server"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
public static final String PREF_SERVER_LAST_NOTIFICATION = IDENTIFIER + "_last_notification"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
public static final String PREF_SERVER_LAST_ACTIVITY = IDENTIFIER + "_last_activity"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
/** Producteev user's default dashboard. This is different from the
|
|
||||||
* preference key, which indicates where user wants to put new tasks */
|
|
||||||
public static final String PREF_DEFAULT_DASHBOARD = IDENTIFIER + "_defaultdash"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
/** Producteev user's id */
|
|
||||||
public static final String PREF_USER_ID = IDENTIFIER + "_userid"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets default dashboard from setting
|
|
||||||
* @return DASHBOARD_NO_SYNC if should not sync, otherwise remote id
|
|
||||||
*/
|
|
||||||
public long getDefaultDashboard() {
|
|
||||||
int defaultDashboard = Preferences.getIntegerFromString(R.string.producteev_PPr_defaultdash_key,
|
|
||||||
DASHBOARD_DEFAULT);
|
|
||||||
if(defaultDashboard == DASHBOARD_NO_SYNC)
|
|
||||||
return DASHBOARD_NO_SYNC;
|
|
||||||
else if(defaultDashboard == DASHBOARD_DEFAULT)
|
|
||||||
return Preferences.getLong(PREF_DEFAULT_DASHBOARD, 0);
|
|
||||||
else
|
|
||||||
return (long) defaultDashboard;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProducteevUtilities() {
|
|
||||||
// prevent instantiation
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLoggedInUserName() {
|
|
||||||
return Preferences.getStringValue(R.string.producteev_PPr_email);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.api;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception that is thrown when an authentication exception occurs and users
|
|
||||||
* need to sign in
|
|
||||||
*
|
|
||||||
* @author timsu
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ApiAuthenticationException extends ApiServiceException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1696103465107607150L;
|
|
||||||
|
|
||||||
public ApiAuthenticationException(String detailMessage) {
|
|
||||||
super(detailMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.api;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception that wraps an exception encountered during API invocation or
|
|
||||||
* processing.
|
|
||||||
*
|
|
||||||
* @author timsu
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ApiResponseParseException extends ApiServiceException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 5421855785088364483L;
|
|
||||||
|
|
||||||
public ApiResponseParseException(Throwable throwable) {
|
|
||||||
super("Exception reading API response: " + throwable.getMessage()); //$NON-NLS-1$
|
|
||||||
initCause(throwable);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.api;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception that wraps an exception encountered during API invocation or
|
|
||||||
* processing.
|
|
||||||
*
|
|
||||||
* @author timsu
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ApiServiceException extends IOException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 8805573304840404684L;
|
|
||||||
|
|
||||||
public ApiServiceException(String detailMessage) {
|
|
||||||
super(detailMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ApiServiceException(Throwable throwable) {
|
|
||||||
super(throwable.getMessage());
|
|
||||||
initCause(throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ApiServiceException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getSimpleName() + ": " + getMessage(); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.api;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception that wraps a 403 exception
|
|
||||||
*
|
|
||||||
* @author timsu
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ApiSignatureException extends ApiServiceException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 4320984373933L;
|
|
||||||
|
|
||||||
public ApiSignatureException(String detailMessage) {
|
|
||||||
super(detailMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.api;
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.service.ContextManager;
|
|
||||||
import com.todoroo.andlib.utility.DateUtilities;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.notes.NoteMetadata;
|
|
||||||
import com.todoroo.astrid.producteev.sync.ProducteevDataService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utilities for working with API responses and JSON objects
|
|
||||||
*
|
|
||||||
* @author timsu
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public final class ApiUtilities {
|
|
||||||
|
|
||||||
private static final SimpleDateFormat timeParser = new SimpleDateFormat(
|
|
||||||
"EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); //$NON-NLS-1$
|
|
||||||
|
|
||||||
private static final SimpleDateFormat timeWriter = new SimpleDateFormat(
|
|
||||||
"yyyy/MM/dd HH:mm:ss Z", Locale.US); //$NON-NLS-1$
|
|
||||||
|
|
||||||
private static final SimpleDateFormat dateWriter = new SimpleDateFormat(
|
|
||||||
"yyyy/MM/dd", Locale.US); //$NON-NLS-1$
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility method to convert PDV time to unix time
|
|
||||||
*
|
|
||||||
* @param date
|
|
||||||
* @param defaultValue
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static long producteevToUnixTime(String value, long defaultValue) {
|
|
||||||
synchronized(timeParser) {
|
|
||||||
try {
|
|
||||||
return DateUtilities.dateToUnixtime(timeParser.parse(value));
|
|
||||||
} catch (ParseException e) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility method to convert unix time to PDV time
|
|
||||||
* @param time
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String unixTimeToProducteev(long time) {
|
|
||||||
synchronized(timeWriter) {
|
|
||||||
return timeWriter.format(DateUtilities.unixtimeToDate(time));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility method to convert unix date to PDV date
|
|
||||||
* @param time
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String unixDateToProducteev(long date) {
|
|
||||||
synchronized(dateWriter) {
|
|
||||||
return dateWriter.format(DateUtilities.unixtimeToDate(date));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Un-escape a Producteev string
|
|
||||||
* @param string
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String decode(String string) {
|
|
||||||
return StringEscapeUtils.unescapeHtml(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create metadata from json object
|
|
||||||
* @param note JSON object with params id_note and message
|
|
||||||
* @param creatorName
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public static Metadata createNoteMetadata(JSONObject note, String creatorName) {
|
|
||||||
Metadata metadata = new Metadata();
|
|
||||||
metadata.setValue(Metadata.KEY, NoteMetadata.METADATA_KEY);
|
|
||||||
metadata.setValue(NoteMetadata.EXT_ID, note.optString("id_note"));
|
|
||||||
metadata.setValue(NoteMetadata.EXT_PROVIDER, ProducteevDataService.NOTE_PROVIDER);
|
|
||||||
metadata.setValue(NoteMetadata.BODY, ApiUtilities.decode(note.optString("message")));
|
|
||||||
|
|
||||||
long created = ApiUtilities.producteevToUnixTime(note.optString("time_create"), 0);
|
|
||||||
metadata.setValue(Metadata.CREATION_DATE, created);
|
|
||||||
|
|
||||||
if(creatorName != null)
|
|
||||||
metadata.setValue(NoteMetadata.TITLE, creatorName);
|
|
||||||
else
|
|
||||||
metadata.setValue(NoteMetadata.TITLE, DateUtilities.getDateStringWithWeekday(ContextManager.getContext(),
|
|
||||||
new Date(created)));
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,682 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.api;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.utility.Pair;
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public class ProducteevInvoker {
|
|
||||||
|
|
||||||
private final String URL = "https://api.producteev.com/";
|
|
||||||
|
|
||||||
private final String apiKey;
|
|
||||||
private final String apiSecret;
|
|
||||||
|
|
||||||
/** saved credentials in case we need to re-log in */
|
|
||||||
private String retryEmail;
|
|
||||||
private String retryPassword;
|
|
||||||
private String token = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create new producteev service
|
|
||||||
* @param apiKey
|
|
||||||
* @param apiSecret
|
|
||||||
*/
|
|
||||||
public ProducteevInvoker(String apiKey, String apiSecret) {
|
|
||||||
this.apiKey = apiKey;
|
|
||||||
this.apiSecret = apiSecret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- authentication and time
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authenticate the given user
|
|
||||||
*/
|
|
||||||
public void authenticate(String email, String password) throws IOException, ApiServiceException {
|
|
||||||
retryEmail = email;
|
|
||||||
retryPassword = password;
|
|
||||||
JSONObject response = invokeGet("users/login.json",
|
|
||||||
"email", email, "password", password);
|
|
||||||
try {
|
|
||||||
token = response.getJSONObject("login").getString("token");
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new ApiServiceException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasToken() {
|
|
||||||
return token != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCredentials(String token, String email, String password) {
|
|
||||||
this.token = token;
|
|
||||||
retryEmail = email;
|
|
||||||
retryPassword = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getToken() {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets Server time
|
|
||||||
*/
|
|
||||||
public String time() throws IOException, ApiServiceException {
|
|
||||||
JSONObject response = invokeGet("time.json");
|
|
||||||
try {
|
|
||||||
return response.getJSONObject("time").getString("value");
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new ApiServiceException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- users
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign up as the given user
|
|
||||||
*/
|
|
||||||
public JSONObject usersSignUp(String email, String firstName, String lastName, String
|
|
||||||
password, String timezone, Long fbUid) throws IOException, ApiServiceException {
|
|
||||||
return invokeGet("users/signup.json",
|
|
||||||
"email", email,
|
|
||||||
"firstname", firstName,
|
|
||||||
"lastname", lastName,
|
|
||||||
"password", password,
|
|
||||||
"timezone", timezone,
|
|
||||||
"fbuid", fbUid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- dashboards
|
|
||||||
|
|
||||||
/**
|
|
||||||
* show list
|
|
||||||
*
|
|
||||||
* @param idResponsible (optional) if null return every task for current user
|
|
||||||
* @param since (optional) if not null, the function only returns tasks modified or created since this date
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
*/
|
|
||||||
public JSONArray dashboardsShowList(String since) throws ApiServiceException, IOException {
|
|
||||||
return getResponse(callAuthenticated("dashboards/show_list.json",
|
|
||||||
"token", token,
|
|
||||||
"since", since), "dashboards");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a dasbhoard
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @return the new created dashboard as JSONObject
|
|
||||||
*/
|
|
||||||
public JSONObject dashboardsCreate(String name) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("dashboards/create.json",
|
|
||||||
"token", token,
|
|
||||||
"title", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return the list of users who can access a specific dashboard
|
|
||||||
*
|
|
||||||
* @param idDashboard
|
|
||||||
* @param dashboard array-information about the dashboard, if this ...
|
|
||||||
*/
|
|
||||||
public JSONArray dashboardsAccess(long idDashboard, String dashboard) throws ApiServiceException, IOException {
|
|
||||||
return getResponse(callAuthenticated("dashboards/access.json",
|
|
||||||
"token", token,
|
|
||||||
"id_dashboard", idDashboard,
|
|
||||||
"dashboard", dashboard),"dashboard");
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- tasks
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a task
|
|
||||||
*
|
|
||||||
* @param title
|
|
||||||
* @param idResponsible (optional)
|
|
||||||
* @param idDashboard (optional);
|
|
||||||
* @param deadline (optional)
|
|
||||||
* @param reminder (optional) 0 = "None", 1 = "5 minutes before", 2 = "15 minutes before", 3 = "30 minutes before", 4 = "1 hour before", 5 = "2 hours before", 6 = "1 day before", 7 = "2 days before", 8 = "on date of event"
|
|
||||||
* @param status (optional) (1 = UNDONE, 2 = DONE)
|
|
||||||
* @param star (optional) (0 to 5 stars)
|
|
||||||
*
|
|
||||||
* @return task
|
|
||||||
*/
|
|
||||||
public JSONObject tasksCreate(String title, Long idResponsible, Long idDashboard,
|
|
||||||
String deadline, Integer reminder, Integer status, Integer star) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/create.json",
|
|
||||||
"token", token,
|
|
||||||
"title", title,
|
|
||||||
"id_responsible", idResponsible,
|
|
||||||
"id_dashboard", idDashboard,
|
|
||||||
"deadline", deadline,
|
|
||||||
"reminder", reminder,
|
|
||||||
"status", status,
|
|
||||||
"star", star);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* show list
|
|
||||||
*
|
|
||||||
* @param idResponsible (optional) if null return every task for current user
|
|
||||||
* @param since (optional) if not null, the function only returns tasks modified or created since this date
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
*/
|
|
||||||
public JSONArray tasksShowList(Long idDashboard, String since) throws ApiServiceException, IOException {
|
|
||||||
return getResponse(callAuthenticated("tasks/show_list.json",
|
|
||||||
"token", token,
|
|
||||||
"id_dashboard", idDashboard,
|
|
||||||
"since", since), "tasks");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksView(Long idTask) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/view.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* change title of a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param title
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksSetTitle(long idTask, String title) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/set_title.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"title", title);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set status of a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param status (1 = UNDONE, 2 = DONE)
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksSetStatus(long idTask, int status) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/set_status.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"status", status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set star status of a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param star (0 to 5 stars)
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksSetStar(long idTask, int star) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/set_star.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"star", star);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set a deadline
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param deadline
|
|
||||||
* @param allDay (optional), 1: all day task (time specified in deadline will be ignored), 0: deadline with time
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksSetDeadline(long idTask, String deadline, Integer allDay) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/set_deadline.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"deadline", deadline,
|
|
||||||
"all_day", allDay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* unset a deadline
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksUnsetDeadline(long idTask) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/unset_deadline.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set repeating
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param repeatInterval
|
|
||||||
* @param repeatValue
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
* @throws IOException
|
|
||||||
* @throws ApiServiceException
|
|
||||||
*/
|
|
||||||
public JSONObject tasksSetRepeating(long idTask, String repeatInterval, Integer repeatValue) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/set_repeating.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"repeat_interval", repeatInterval,
|
|
||||||
"repeat_value", (repeatValue == null ? 1 : repeatValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* unset repeating
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
* @throws IOException
|
|
||||||
* @throws ApiServiceException
|
|
||||||
*/
|
|
||||||
public JSONObject tasksUnsetRepeating(long idTask) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/unset_repeating.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set a workspace
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param id_dashboard
|
|
||||||
*
|
|
||||||
* @return array tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksSetWorkspace(long idTask, long idDashboard) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/set_workspace.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"id_dashboard", idDashboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* delete a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
*
|
|
||||||
* @return array with the result = (Array("stats" => Array("result" => "TRUE|FALSE"))
|
|
||||||
*/
|
|
||||||
public JSONObject tasksDelete(long idTask) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/delete.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get labels assigned to a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
*
|
|
||||||
* @return array: list of labels/view
|
|
||||||
*/
|
|
||||||
public JSONArray tasksLabels(long idTask) throws ApiServiceException, IOException {
|
|
||||||
return getResponse(callAuthenticated("tasks/labels.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask), "labels");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* change labels for a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param idLabels
|
|
||||||
*
|
|
||||||
* @return array: tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksChangeLabel(long idTask, long... idLabels) throws ApiServiceException, IOException {
|
|
||||||
Object[] parameters = new Object[2 * (idLabels.length + 2)];
|
|
||||||
parameters[0] = "token"; parameters[1] = token;
|
|
||||||
parameters[2] = "id_task"; parameters[3] = idTask;
|
|
||||||
for(int i = 0; i < idLabels.length; i++) {
|
|
||||||
parameters[i * 2 + 4] = "id_label[]";
|
|
||||||
parameters[i * 2 + 5] = idLabels[i];
|
|
||||||
}
|
|
||||||
return callAuthenticated("tasks/change_labels.json", parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set a labels to a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param idLabels
|
|
||||||
*
|
|
||||||
* @return array: tasks/view
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public JSONObject tasksSetLabel(long idTask, long idLabel) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/set_label.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"id_label", idLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set a labels to a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param idLabel
|
|
||||||
*
|
|
||||||
* @return array: tasks/view
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public JSONObject tasksUnsetLabel(long idTask, long idLabel) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/unset_label.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"id_label", idLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* change responsible of a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param idResponsible
|
|
||||||
*
|
|
||||||
* @return array: tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksSetResponsible(long idTask, long idResponsible) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/set_responsible.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"id_responsible", idResponsible);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* change responsible of a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param idResponsible
|
|
||||||
*
|
|
||||||
* @return array: tasks/view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksUnsetResponsible(long idTask) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/unset_responsible.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a note attached to a task
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param message
|
|
||||||
*
|
|
||||||
* @return array tasks::note_view
|
|
||||||
*/
|
|
||||||
public JSONObject tasksNoteCreate(long idTask, String message) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("tasks/note_create.json",
|
|
||||||
"token", token,
|
|
||||||
"id_task", idTask,
|
|
||||||
"message", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- labels
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get every label for a given dashboard
|
|
||||||
*
|
|
||||||
* @param idTask
|
|
||||||
* @param idLabel
|
|
||||||
*
|
|
||||||
* @return array: labels/view
|
|
||||||
*/
|
|
||||||
public JSONArray labelsShowList(long idDashboard, String since) throws ApiServiceException, IOException {
|
|
||||||
return getResponse(callAuthenticated("labels/show_list.json",
|
|
||||||
"token", token,
|
|
||||||
"id_dashboard", idDashboard,
|
|
||||||
"since", since), "labels");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a label
|
|
||||||
*
|
|
||||||
* @param idDashboard
|
|
||||||
* @param title
|
|
||||||
*
|
|
||||||
* @return array: labels/view
|
|
||||||
*/
|
|
||||||
public JSONObject labelsCreate(long idDashboard, String title) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("labels/create.json",
|
|
||||||
"token", token,
|
|
||||||
"id_dashboard", idDashboard,
|
|
||||||
"title", title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JSONObject labelsDelete(long idLabel) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("labels/delete.json",
|
|
||||||
"token", token,
|
|
||||||
"id_label", idLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- notifications/activities
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get every activities
|
|
||||||
*
|
|
||||||
* @param dashboardId (optional) if not null, this function only returns notifications for this specific dashboard
|
|
||||||
* @param lastId (optional) this function returns only activities later than this id
|
|
||||||
*/
|
|
||||||
public JSONArray activitiesShowActivities(Long dashboardId, Long lastId) throws ApiResponseParseException, ApiServiceException, IOException {
|
|
||||||
return getResponse(callAuthenticated("activities/show_activities.json",
|
|
||||||
"token", token,
|
|
||||||
"id_dashboard", dashboardId,
|
|
||||||
"last_id", lastId), "activities");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get every notification for the current user
|
|
||||||
* @param dashboardId
|
|
||||||
* @param lastId
|
|
||||||
* @return
|
|
||||||
* @throws ApiResponseParseException
|
|
||||||
* @throws ApiServiceException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public JSONArray activitiesShowNotifications(Long dashboardId, Long lastId) throws ApiResponseParseException, ApiServiceException, IOException {
|
|
||||||
return getResponse(callAuthenticated("activities/show_notifications.json",
|
|
||||||
"token", token,
|
|
||||||
"id_dashboard", dashboardId,
|
|
||||||
"last_id", lastId), "activities");
|
|
||||||
}
|
|
||||||
// --- users
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get a user
|
|
||||||
*
|
|
||||||
* @param idColleague
|
|
||||||
*
|
|
||||||
* @return array information about the user
|
|
||||||
*/
|
|
||||||
public JSONObject usersView(Long idColleague) throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("users/view.json",
|
|
||||||
"token", token,
|
|
||||||
"id_colleague", idColleague);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JSONObject usersColleagues() throws ApiServiceException, IOException {
|
|
||||||
return callAuthenticated("users/colleagues.json",
|
|
||||||
"token", token);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- invocation
|
|
||||||
|
|
||||||
private final ProducteevRestClient restClient = new ProducteevRestClient();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invokes authenticated method using HTTP GET. Will retry after re-authenticating if service exception encountered
|
|
||||||
*
|
|
||||||
* @param method
|
|
||||||
* API method to invoke
|
|
||||||
* @param getParameters
|
|
||||||
* Name/Value pairs. Values will be URL encoded.
|
|
||||||
* @return response object
|
|
||||||
*/
|
|
||||||
private JSONObject callAuthenticated(String method, Object... getParameters)
|
|
||||||
throws IOException, ApiServiceException {
|
|
||||||
try {
|
|
||||||
String request = createFetchUrl(method, getParameters);
|
|
||||||
String response = null;
|
|
||||||
try {
|
|
||||||
response = restClient.get(request);
|
|
||||||
} catch (ApiSignatureException e) {
|
|
||||||
// clear cookies, get new token, retry
|
|
||||||
String oldToken = token;
|
|
||||||
restClient.reset();
|
|
||||||
authenticate(retryEmail, retryPassword);
|
|
||||||
for(int i = 0; i < getParameters.length; i++)
|
|
||||||
if(oldToken.equals(getParameters[i])) {
|
|
||||||
getParameters[i] = getToken();
|
|
||||||
}
|
|
||||||
request = createFetchUrl(method, getParameters);
|
|
||||||
try {
|
|
||||||
response = restClient.get(request);
|
|
||||||
} catch (ApiSignatureException newException) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
if(response == null)
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
if(response.startsWith("DEBUG MESSAGE")) {
|
|
||||||
System.err.println(response);
|
|
||||||
return new JSONObject();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if(TextUtils.isEmpty(response))
|
|
||||||
return new JSONObject();
|
|
||||||
|
|
||||||
return new JSONObject(response);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
System.err.println(response);
|
|
||||||
throw new ApiResponseParseException(e);
|
|
||||||
}
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invokes API method using HTTP GET
|
|
||||||
*
|
|
||||||
* @param method
|
|
||||||
* API method to invoke
|
|
||||||
* @param getParameters
|
|
||||||
* Name/Value pairs. Values will be URL encoded.
|
|
||||||
* @return response object
|
|
||||||
*/
|
|
||||||
JSONObject invokeGet(String method, Object... getParameters) throws IOException, ApiServiceException {
|
|
||||||
try {
|
|
||||||
String request = createFetchUrl(method, getParameters);
|
|
||||||
String response = restClient.get(request);
|
|
||||||
if(response.startsWith("DEBUG MESSAGE")) {
|
|
||||||
System.err.println(response);
|
|
||||||
return new JSONObject();
|
|
||||||
}
|
|
||||||
return new JSONObject(response);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new ApiResponseParseException(e);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a URL for invoking an HTTP GET/POST on the given method
|
|
||||||
* @param method
|
|
||||||
* @param getParameters
|
|
||||||
* @return
|
|
||||||
* @throws UnsupportedEncodingException
|
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
|
||||||
String createFetchUrl(String method, Object... getParameters) throws UnsupportedEncodingException, NoSuchAlgorithmException {
|
|
||||||
ArrayList<Pair<String, Object>> params = new ArrayList<Pair<String, Object>>();
|
|
||||||
for(int i = 0; i < getParameters.length; i += 2)
|
|
||||||
params.add(new Pair<String, Object>(getParameters[i].toString(), getParameters[i+1]));
|
|
||||||
params.add(new Pair<String, Object>("api_key", apiKey));
|
|
||||||
|
|
||||||
Collections.sort(params, new Comparator<Pair<String, Object>>() {
|
|
||||||
@Override
|
|
||||||
public int compare(Pair<String, Object> object1,
|
|
||||||
Pair<String, Object> object2) {
|
|
||||||
return object1.getLeft().compareTo(object2.getLeft());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
StringBuilder requestBuilder = new StringBuilder(URL).append(method).append('?');
|
|
||||||
StringBuilder sigBuilder = new StringBuilder();
|
|
||||||
for(Pair<String, Object> entry : params) {
|
|
||||||
if(entry.getRight() == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
String key = entry.getLeft();
|
|
||||||
String value = entry.getRight().toString();
|
|
||||||
String encoded = URLEncoder.encode(value, "UTF-8");
|
|
||||||
|
|
||||||
requestBuilder.append(key).append('=').append(encoded).append('&');
|
|
||||||
|
|
||||||
if(!key.endsWith("[]"))
|
|
||||||
sigBuilder.append(key).append(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
sigBuilder.append(apiSecret);
|
|
||||||
String signature = DigestUtils.md5Hex(sigBuilder.toString());
|
|
||||||
requestBuilder.append("api_sig").append('=').append(signature);
|
|
||||||
return requestBuilder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to get a field out or throw an api exception
|
|
||||||
* @param response
|
|
||||||
* @param field
|
|
||||||
* @return
|
|
||||||
* @throws ApiResponseParseException
|
|
||||||
*/
|
|
||||||
private JSONArray getResponse(JSONObject response, String field) throws ApiResponseParseException {
|
|
||||||
try {
|
|
||||||
if(!response.has(field))
|
|
||||||
return new JSONArray();
|
|
||||||
return response.getJSONArray(field);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new ApiResponseParseException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,188 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.api;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.HttpEntity;
|
|
||||||
import org.apache.http.HttpResponse;
|
|
||||||
import org.apache.http.client.HttpClient;
|
|
||||||
import org.apache.http.client.methods.HttpGet;
|
|
||||||
import org.apache.http.client.methods.HttpPost;
|
|
||||||
import org.apache.http.impl.client.DefaultHttpClient;
|
|
||||||
import org.apache.http.params.BasicHttpParams;
|
|
||||||
import org.apache.http.params.HttpConnectionParams;
|
|
||||||
import org.apache.http.params.HttpParams;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.service.RestClient;
|
|
||||||
import com.todoroo.astrid.utility.Constants;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RestClient allows Android to consume web requests.
|
|
||||||
* <p>
|
|
||||||
* Portions by Praeda:
|
|
||||||
* http://senior.ceng.metu.edu.tr/2009/praeda/2009/01/11/a-simple
|
|
||||||
* -restful-client-at-android/
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevRestClient implements RestClient {
|
|
||||||
|
|
||||||
private static final int HTTP_OK = 200;
|
|
||||||
|
|
||||||
private static final int TIMEOUT_MILLIS = 30000;
|
|
||||||
|
|
||||||
private static HttpClient httpClient = null;
|
|
||||||
|
|
||||||
private static String convertStreamToString(InputStream is) {
|
|
||||||
/*
|
|
||||||
* To convert the InputStream to String we use the
|
|
||||||
* BufferedReader.readLine() method. We iterate until the BufferedReader
|
|
||||||
* return null which means there's no more data to read. Each line will
|
|
||||||
* appended to a StringBuilder and returned as String.
|
|
||||||
*/
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is), 16384);
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
String line = null;
|
|
||||||
try {
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
sb.append(line + "\n"); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
is.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized static void initializeHttpClient() {
|
|
||||||
if (httpClient == null) {
|
|
||||||
HttpParams params = new BasicHttpParams();
|
|
||||||
HttpConnectionParams.setConnectionTimeout(params, TIMEOUT_MILLIS);
|
|
||||||
HttpConnectionParams.setSoTimeout(params, TIMEOUT_MILLIS);
|
|
||||||
httpClient = new DefaultHttpClient(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String processHttpResponse(HttpResponse response) throws IOException, ApiServiceException {
|
|
||||||
HttpEntity entity = response.getEntity();
|
|
||||||
String body = null;
|
|
||||||
if (entity != null) {
|
|
||||||
InputStream contentStream = entity.getContent();
|
|
||||||
try {
|
|
||||||
body = convertStreamToString(contentStream);
|
|
||||||
} finally {
|
|
||||||
contentStream.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(Constants.DEBUG)
|
|
||||||
System.err.println(body);
|
|
||||||
|
|
||||||
int statusCode = response.getStatusLine().getStatusCode();
|
|
||||||
if(statusCode != HTTP_OK || (body != null && body.startsWith("{\"error\":"))) { //$NON-NLS-1$
|
|
||||||
ApiServiceException error;
|
|
||||||
try {
|
|
||||||
JSONObject errorObject = new JSONObject(body).getJSONObject("error"); //$NON-NLS-1$
|
|
||||||
String errorMessage = errorObject.getString("message"); //$NON-NLS-1$
|
|
||||||
|
|
||||||
if(statusCode == 403)
|
|
||||||
error = new ApiSignatureException(errorMessage);
|
|
||||||
else if(statusCode == 401)
|
|
||||||
error = new ApiAuthenticationException(errorMessage);
|
|
||||||
else
|
|
||||||
error = new ApiServiceException(errorMessage);
|
|
||||||
} catch (Exception e) {
|
|
||||||
if(statusCode == 401)
|
|
||||||
error = new ApiAuthenticationException(response.getStatusLine().getReasonPhrase());
|
|
||||||
else
|
|
||||||
error = new ApiServiceException(response.getStatusLine() +
|
|
||||||
"\n" + body); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
public synchronized String get(String url) throws IOException {
|
|
||||||
initializeHttpClient();
|
|
||||||
|
|
||||||
if(Constants.DEBUG)
|
|
||||||
System.err.println("GET: " + url); //$NON-NLS-1$ // (debug)
|
|
||||||
|
|
||||||
try {
|
|
||||||
HttpGet httpGet = new HttpGet(url);
|
|
||||||
HttpResponse response = httpClient.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 url
|
|
||||||
* @param data
|
|
||||||
* url-encoded data
|
|
||||||
* @param headers
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public synchronized String post(String url, HttpEntity data, Header... headers) throws IOException {
|
|
||||||
initializeHttpClient();
|
|
||||||
|
|
||||||
if(Constants.DEBUG)
|
|
||||||
System.err.println("POST: " + url); //$NON-NLS-1$ // (debug)
|
|
||||||
|
|
||||||
try {
|
|
||||||
HttpPost httpPost = new HttpPost(url);
|
|
||||||
httpPost.setEntity(data);
|
|
||||||
HttpResponse response = httpClient.execute(httpPost);
|
|
||||||
|
|
||||||
return processHttpResponse(response);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
IOException ioException = new IOException(e.getMessage());
|
|
||||||
ioException.initCause(e);
|
|
||||||
throw ioException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy and re-create http client
|
|
||||||
*/
|
|
||||||
public void reset() {
|
|
||||||
httpClient = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,123 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.sync;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.data.Property.LongProperty;
|
|
||||||
import com.todoroo.andlib.data.Property.StringProperty;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link StoreObject} entries for a Producteev Dashboard
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevDashboard {
|
|
||||||
|
|
||||||
/** type*/
|
|
||||||
public static final String TYPE = "pdv-dash"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
/** dashboard id in producteev */
|
|
||||||
public static final LongProperty REMOTE_ID = new LongProperty(StoreObject.TABLE,
|
|
||||||
StoreObject.ITEM.name);
|
|
||||||
|
|
||||||
/** dashboard name */
|
|
||||||
public static final StringProperty NAME = new StringProperty(StoreObject.TABLE,
|
|
||||||
StoreObject.VALUE1.name);
|
|
||||||
|
|
||||||
/** users (list in the format "id_user,name;id_user,name;") */
|
|
||||||
public static final StringProperty USERS = new StringProperty(StoreObject.TABLE,
|
|
||||||
StoreObject.VALUE2.name);
|
|
||||||
|
|
||||||
// data class-part
|
|
||||||
private final long id;
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private ArrayList<ProducteevUser> users = null;
|
|
||||||
|
|
||||||
public ProducteevDashboard (StoreObject dashboardData) {
|
|
||||||
this(dashboardData.getValue(REMOTE_ID),dashboardData.getValue(NAME),(dashboardData.containsValue(USERS)?dashboardData.getValue(USERS):null));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for a dashboard.
|
|
||||||
*
|
|
||||||
* @param id id of the remote dashboard
|
|
||||||
* @param name name of the remote dashboard
|
|
||||||
* @param usercsv csv-userstring as returned by a StoreObject-dashboard with property ProducteevDashboard.USERS
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public ProducteevDashboard(long id, String name, String usercsv) {
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
|
|
||||||
if (usercsv == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
StringTokenizer tokenizer = new StringTokenizer(usercsv, ";");
|
|
||||||
int usercount = tokenizer.countTokens();
|
|
||||||
|
|
||||||
while (tokenizer.hasMoreTokens()) {
|
|
||||||
String userdata = tokenizer.nextToken();
|
|
||||||
int delim_index = userdata.indexOf(",");
|
|
||||||
String userid = userdata.substring(0, delim_index);
|
|
||||||
String username = userdata.substring(delim_index+1);
|
|
||||||
int name_gap = username.indexOf(" ");
|
|
||||||
String firstname = (name_gap == -1 ? username : username.substring(0,name_gap));
|
|
||||||
String lastname = (name_gap == -1 ? null : username.substring(name_gap+1));
|
|
||||||
if (users == null) {
|
|
||||||
users = new ArrayList<ProducteevUser>(usercount);
|
|
||||||
}
|
|
||||||
users.add(new ProducteevUser(Long.parseLong(userid),null,firstname,lastname));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the id
|
|
||||||
*/
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the name
|
|
||||||
*/
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return the name of this dashboard
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the users
|
|
||||||
*/
|
|
||||||
public ArrayList<ProducteevUser> getUsers() {
|
|
||||||
if (users == null) {
|
|
||||||
return new ArrayList<ProducteevUser>(); // Don't return null
|
|
||||||
}
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Try and find user in the dashboard. return null if un-findable */
|
|
||||||
public static String getUserFromDashboard(StoreObject dashboard, long userId) {
|
|
||||||
String users = ";" + dashboard.getValue(USERS); //$NON-NLS-1$
|
|
||||||
int index = users.indexOf(";" + userId + ","); //$NON-NLS-1$ //$NON-NLS-2$
|
|
||||||
if(index > -1)
|
|
||||||
return users.substring(users.indexOf(',', index) + 1,
|
|
||||||
users.indexOf(';', index + 1));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,310 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.sync;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.data.Property;
|
|
||||||
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.sql.Criterion;
|
|
||||||
import com.todoroo.andlib.sql.Join;
|
|
||||||
import com.todoroo.andlib.sql.Query;
|
|
||||||
import com.todoroo.astrid.core.PluginServices;
|
|
||||||
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
|
|
||||||
import com.todoroo.astrid.dao.StoreObjectDao;
|
|
||||||
import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.notes.NoteMetadata;
|
|
||||||
import com.todoroo.astrid.producteev.ProducteevUtilities;
|
|
||||||
import com.todoroo.astrid.producteev.api.ApiUtilities;
|
|
||||||
import com.todoroo.astrid.service.MetadataService;
|
|
||||||
import com.todoroo.astrid.tags.TaskToTagMetadata;
|
|
||||||
|
|
||||||
public final class ProducteevDataService {
|
|
||||||
|
|
||||||
// --- constants
|
|
||||||
|
|
||||||
/** Utility for joining tasks with metadata */
|
|
||||||
public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK));
|
|
||||||
|
|
||||||
/** NoteMetadata provider string */
|
|
||||||
public static final String NOTE_PROVIDER = "producteev"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
// --- singleton
|
|
||||||
|
|
||||||
private static ProducteevDataService instance = null;
|
|
||||||
|
|
||||||
public static synchronized ProducteevDataService getInstance() {
|
|
||||||
if(instance == null)
|
|
||||||
instance = new ProducteevDataService(ContextManager.getContext());
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- instance variables
|
|
||||||
|
|
||||||
protected final Context context;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private TaskDao taskDao;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MetadataService metadataService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private StoreObjectDao storeObjectDao;
|
|
||||||
|
|
||||||
private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE;
|
|
||||||
|
|
||||||
static final Random random = new Random();
|
|
||||||
|
|
||||||
private ProducteevDataService(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- task and metadata methods
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears metadata information. Used when user logs out of service
|
|
||||||
*/
|
|
||||||
public void clearMetadata() {
|
|
||||||
PluginServices.getTaskService().clearDetails(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).
|
|
||||||
where(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY))));
|
|
||||||
metadataService.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY));
|
|
||||||
storeObjectDao.deleteWhere(StoreObject.TYPE.eq(ProducteevDashboard.TYPE));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets tasks that were created since last sync
|
|
||||||
* @param properties
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public TodorooCursor<Task> getLocallyCreated(Property<?>[] properties) {
|
|
||||||
return
|
|
||||||
taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).where(
|
|
||||||
Criterion.and(
|
|
||||||
Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).
|
|
||||||
where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), ProducteevTask.ID.gt(0))))),
|
|
||||||
TaskCriteria.isActive())).
|
|
||||||
groupBy(Task.ID));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets tasks that were modified since last sync
|
|
||||||
* @param properties
|
|
||||||
* @return null if never sync'd
|
|
||||||
*/
|
|
||||||
public TodorooCursor<Task> getLocallyUpdated(Property<?>[] properties) {
|
|
||||||
long lastSyncDate = preferences.getLastSyncDate();
|
|
||||||
if(lastSyncDate == 0)
|
|
||||||
return taskDao.query(Query.select(Task.ID).where(Criterion.none));
|
|
||||||
return
|
|
||||||
taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).where(
|
|
||||||
Criterion.and(
|
|
||||||
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
ProducteevTask.ID.gt(0),
|
|
||||||
Task.MODIFICATION_DATE.gt(lastSyncDate))).
|
|
||||||
groupBy(Task.ID));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for a local task with same remote id, updates this task's id
|
|
||||||
* @param remoteTask
|
|
||||||
* @return true if found local match
|
|
||||||
*/
|
|
||||||
public boolean findLocalMatch(ProducteevTaskContainer remoteTask) {
|
|
||||||
if(remoteTask.task.getId() != Task.NO_ID)
|
|
||||||
return true;
|
|
||||||
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.ID).
|
|
||||||
join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
ProducteevTask.ID.eq(remoteTask.pdvTask.getValue(ProducteevTask.ID)))));
|
|
||||||
try {
|
|
||||||
if(cursor.getCount() == 0)
|
|
||||||
return false;
|
|
||||||
cursor.moveToFirst();
|
|
||||||
remoteTask.task.setId(cursor.get(Task.ID));
|
|
||||||
return true;
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves a task and its metadata
|
|
||||||
* @param task
|
|
||||||
*/
|
|
||||||
public void saveTaskAndMetadata(ProducteevTaskContainer task) {
|
|
||||||
taskDao.save(task.task);
|
|
||||||
|
|
||||||
task.metadata.add(task.pdvTask);
|
|
||||||
// note we don't include note metadata, since we only receive deltas
|
|
||||||
metadataService.synchronizeMetadata(task.task.getId(), task.metadata,
|
|
||||||
Criterion.or(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
MetadataCriteria.withKey(TaskToTagMetadata.KEY)), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a task and its metadata
|
|
||||||
* @param task
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public ProducteevTaskContainer readTaskAndMetadata(TodorooCursor<Task> taskCursor) {
|
|
||||||
Task task = new Task(taskCursor);
|
|
||||||
|
|
||||||
// read tags, notes, etc
|
|
||||||
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
|
|
||||||
TodorooCursor<Metadata> metadataCursor = metadataService.query(Query.select(Metadata.PROPERTIES).
|
|
||||||
where(Criterion.and(MetadataCriteria.byTask(task.getId()),
|
|
||||||
Criterion.or(MetadataCriteria.withKey(TaskToTagMetadata.KEY),
|
|
||||||
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
|
|
||||||
MetadataCriteria.withKey(NoteMetadata.METADATA_KEY)))));
|
|
||||||
try {
|
|
||||||
for(metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor.moveToNext()) {
|
|
||||||
metadata.add(new Metadata(metadataCursor));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
metadataCursor.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ProducteevTaskContainer(task, metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads metadata out of a task
|
|
||||||
* @return null if no metadata found
|
|
||||||
*/
|
|
||||||
public Metadata getTaskMetadata(long taskId) {
|
|
||||||
TodorooCursor<Metadata> cursor = metadataService.query(Query.select(
|
|
||||||
Metadata.PROPERTIES).where(
|
|
||||||
MetadataCriteria.byTaskAndwithKey(taskId, ProducteevTask.METADATA_KEY)));
|
|
||||||
try {
|
|
||||||
if(cursor.getCount() == 0)
|
|
||||||
return null;
|
|
||||||
cursor.moveToFirst();
|
|
||||||
return new Metadata(cursor);
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads task notes out of a task
|
|
||||||
*/
|
|
||||||
public TodorooCursor<Metadata> getTaskNotesCursor(long taskId) {
|
|
||||||
TodorooCursor<Metadata> cursor = metadataService.query(Query.select(Metadata.PROPERTIES).
|
|
||||||
where(MetadataCriteria.byTaskAndwithKey(taskId, NoteMetadata.METADATA_KEY)));
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readDashboards() {
|
|
||||||
if (dashboards == null) {
|
|
||||||
dashboards = readStoreObjects(ProducteevDashboard.TYPE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads store objects.
|
|
||||||
*/
|
|
||||||
public StoreObject[] readStoreObjects(String type) {
|
|
||||||
StoreObject[] ret;
|
|
||||||
TodorooCursor<StoreObject> cursor = storeObjectDao.query(Query.select(StoreObject.PROPERTIES).
|
|
||||||
where(StoreObjectCriteria.byType(type)));
|
|
||||||
try {
|
|
||||||
ret = new StoreObject[cursor.getCount()];
|
|
||||||
for(int i = 0; i < ret.length; i++) {
|
|
||||||
cursor.moveToNext();
|
|
||||||
StoreObject dashboard = new StoreObject(cursor);
|
|
||||||
ret[i] = dashboard;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- dashboard methods
|
|
||||||
|
|
||||||
private StoreObject[] dashboards = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a list of dashboards
|
|
||||||
*/
|
|
||||||
public StoreObject[] getDashboards() {
|
|
||||||
readDashboards();
|
|
||||||
return dashboards;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads dashboards
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public void updateDashboards(JSONArray changedDashboards) throws JSONException {
|
|
||||||
readDashboards();
|
|
||||||
for(int i = 0; i < changedDashboards.length(); i++) {
|
|
||||||
JSONObject remote = changedDashboards.getJSONObject(i).getJSONObject("dashboard");
|
|
||||||
updateDashboards(remote, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear dashboard cache
|
|
||||||
dashboards = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public StoreObject updateDashboards(JSONObject remote, boolean reinitCache) throws JSONException {
|
|
||||||
if (reinitCache)
|
|
||||||
readDashboards();
|
|
||||||
long id = remote.getLong("id_dashboard");
|
|
||||||
StoreObject local = null;
|
|
||||||
for(StoreObject dashboard : dashboards) {
|
|
||||||
if(dashboard.getValue(ProducteevDashboard.REMOTE_ID).equals(id)) {
|
|
||||||
local = dashboard;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(remote.getInt("deleted") != 0) {
|
|
||||||
if(local != null)
|
|
||||||
storeObjectDao.delete(local.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(local == null)
|
|
||||||
local = new StoreObject();
|
|
||||||
local.setValue(StoreObject.TYPE, ProducteevDashboard.TYPE);
|
|
||||||
local.setValue(ProducteevDashboard.REMOTE_ID, id);
|
|
||||||
local.setValue(ProducteevDashboard.NAME, ApiUtilities.decode(remote.getString("title")));
|
|
||||||
|
|
||||||
StringBuilder users = new StringBuilder();
|
|
||||||
JSONArray accessList = remote.getJSONArray("accesslist");
|
|
||||||
for(int j = 0; j < accessList.length(); j++) {
|
|
||||||
JSONObject user = accessList.getJSONObject(j).getJSONObject("user");
|
|
||||||
users.append(user.getLong("id_user")).append(',');
|
|
||||||
String name = ApiUtilities.decode(user.optString("firstname", "") + ' ' +
|
|
||||||
user.optString("lastname", ""));
|
|
||||||
users.append(name.trim()).append(';');
|
|
||||||
}
|
|
||||||
local.setValue(ProducteevDashboard.USERS, users.toString());
|
|
||||||
storeObjectDao.persist(local);
|
|
||||||
if (reinitCache)
|
|
||||||
dashboards = null;
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,811 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.sync;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Notification;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.timsu.astrid.R;
|
|
||||||
import com.todoroo.andlib.data.Property;
|
|
||||||
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.ExceptionService;
|
|
||||||
import com.todoroo.andlib.service.NotificationManager;
|
|
||||||
import com.todoroo.andlib.sql.Criterion;
|
|
||||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
||||||
import com.todoroo.andlib.utility.DateUtilities;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
import com.todoroo.astrid.activity.ShortcutActivity;
|
|
||||||
import com.todoroo.astrid.api.AstridApiConstants;
|
|
||||||
import com.todoroo.astrid.api.Filter;
|
|
||||||
import com.todoroo.astrid.core.PluginServices;
|
|
||||||
import com.todoroo.astrid.dao.TaskDao;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.StoreObject;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.notes.NoteMetadata;
|
|
||||||
import com.todoroo.astrid.producteev.ProducteevBackgroundService;
|
|
||||||
import com.todoroo.astrid.producteev.ProducteevFilterExposer;
|
|
||||||
import com.todoroo.astrid.producteev.ProducteevLoginActivity;
|
|
||||||
import com.todoroo.astrid.producteev.ProducteevPreferences;
|
|
||||||
import com.todoroo.astrid.producteev.ProducteevUtilities;
|
|
||||||
import com.todoroo.astrid.producteev.api.ApiResponseParseException;
|
|
||||||
import com.todoroo.astrid.producteev.api.ApiServiceException;
|
|
||||||
import com.todoroo.astrid.producteev.api.ApiUtilities;
|
|
||||||
import com.todoroo.astrid.producteev.api.ProducteevInvoker;
|
|
||||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
|
||||||
import com.todoroo.astrid.service.StatisticsConstants;
|
|
||||||
import com.todoroo.astrid.service.StatisticsService;
|
|
||||||
import com.todoroo.astrid.sync.SyncContainer;
|
|
||||||
import com.todoroo.astrid.sync.SyncProvider;
|
|
||||||
import com.todoroo.astrid.sync.SyncProviderUtilities;
|
|
||||||
import com.todoroo.astrid.tags.TaskToTagMetadata;
|
|
||||||
import com.todoroo.astrid.utility.Constants;
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer> {
|
|
||||||
|
|
||||||
private static final long TASK_ID_UNSYNCED = 1L;
|
|
||||||
private ProducteevDataService dataService = null;
|
|
||||||
private ProducteevInvoker invoker = null;
|
|
||||||
private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE;
|
|
||||||
|
|
||||||
/** producteev user id. set during sync */
|
|
||||||
private long userId;
|
|
||||||
|
|
||||||
/** map of producteev dashboard id + label name to id's */
|
|
||||||
private final HashMap<String, Long> labelMap = new HashMap<String, Long>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
AstridDependencyInjector.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected ExceptionService exceptionService;
|
|
||||||
|
|
||||||
public ProducteevSyncProvider() {
|
|
||||||
super();
|
|
||||||
DependencyInjectionService.getInstance().inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ------------------------------------------------------ utility methods
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign out of service, deleting all synchronization metadata
|
|
||||||
*/
|
|
||||||
public void signOut() {
|
|
||||||
preferences.setToken(null);
|
|
||||||
Preferences.setString(R.string.producteev_PPr_email, null);
|
|
||||||
Preferences.setString(R.string.producteev_PPr_password, null);
|
|
||||||
Preferences.setString(ProducteevUtilities.PREF_SERVER_LAST_SYNC, null);
|
|
||||||
Preferences.setStringFromInteger(R.string.producteev_PPr_defaultdash_key,
|
|
||||||
ProducteevUtilities.DASHBOARD_DEFAULT);
|
|
||||||
preferences.clearLastSyncDate();
|
|
||||||
|
|
||||||
dataService = ProducteevDataService.getInstance();
|
|
||||||
dataService.clearMetadata();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SyncProviderUtilities getUtilities() {
|
|
||||||
return ProducteevUtilities.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ------------------------------------------------------ initiating sync
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* initiate sync in background
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void initiateBackground() {
|
|
||||||
dataService = ProducteevDataService.getInstance();
|
|
||||||
|
|
||||||
try {
|
|
||||||
String authToken = preferences.getToken();
|
|
||||||
invoker = getInvoker();
|
|
||||||
|
|
||||||
String email = Preferences.getStringValue(R.string.producteev_PPr_email);
|
|
||||||
String password = Preferences.getStringValue(R.string.producteev_PPr_password);
|
|
||||||
|
|
||||||
// check if we have a token & it works
|
|
||||||
if(authToken != null) {
|
|
||||||
invoker.setCredentials(authToken, email, password);
|
|
||||||
performSync();
|
|
||||||
} else {
|
|
||||||
if (email == null && password == null) {
|
|
||||||
// we can't do anything, user is not logged in
|
|
||||||
} else {
|
|
||||||
invoker.authenticate(email, password);
|
|
||||||
preferences.setToken(invoker.getToken());
|
|
||||||
performSync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
// occurs when application was closed
|
|
||||||
} catch (Exception e) {
|
|
||||||
handleException("pdv-authenticate", e, true);
|
|
||||||
} finally {
|
|
||||||
preferences.stopOngoing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If user isn't already signed in, show sign in dialog. Else perform sync.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void initiateManual(Activity activity) {
|
|
||||||
String authToken = preferences.getToken();
|
|
||||||
ProducteevUtilities.INSTANCE.stopOngoing();
|
|
||||||
|
|
||||||
// check if we have a token & it works
|
|
||||||
if(authToken == null) {
|
|
||||||
// display login-activity
|
|
||||||
Intent intent = new Intent(activity, ProducteevLoginActivity.class);
|
|
||||||
activity.startActivityForResult(intent, 0);
|
|
||||||
} else {
|
|
||||||
activity.startService(new Intent(null, null,
|
|
||||||
activity, ProducteevBackgroundService.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ProducteevInvoker getInvoker() {
|
|
||||||
String z = stripslashes(0, "71o3346pr40o5o4nt4n7t6n287t4op28","2");
|
|
||||||
String v = stripslashes(2, "9641n76n9s1736q1578q1o1337q19233","4ae");
|
|
||||||
return new ProducteevInvoker(z, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ----------------------------------------------------- synchronization!
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
protected void performSync() {
|
|
||||||
preferences.recordSyncStart();
|
|
||||||
String syncSuccess = "failed";
|
|
||||||
|
|
||||||
try {
|
|
||||||
// load user information
|
|
||||||
JSONObject user = invoker.usersView(null).getJSONObject("user");
|
|
||||||
saveUserData(user);
|
|
||||||
|
|
||||||
String lastServerSync = Preferences.getStringValue(ProducteevUtilities.PREF_SERVER_LAST_SYNC);
|
|
||||||
|
|
||||||
// read dashboards
|
|
||||||
JSONArray dashboards = invoker.dashboardsShowList(lastServerSync);
|
|
||||||
dataService.updateDashboards(dashboards);
|
|
||||||
|
|
||||||
// read labels and tasks for each dashboard
|
|
||||||
ArrayList<ProducteevTaskContainer> remoteTasks = new ArrayList<ProducteevTaskContainer>();
|
|
||||||
for(StoreObject dashboard : dataService.getDashboards()) {
|
|
||||||
long dashboardId = dashboard.getValue(ProducteevDashboard.REMOTE_ID);
|
|
||||||
JSONArray labels = invoker.labelsShowList(dashboardId, null);
|
|
||||||
readLabels(labels);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// This invocation throws ApiServiceException for workspaces that need to be upgraded
|
|
||||||
JSONArray tasks = invoker.tasksShowList(dashboardId, lastServerSync);
|
|
||||||
for(int i = 0; i < tasks.length(); i++) {
|
|
||||||
ProducteevTaskContainer remote = parseRemoteTask(tasks.getJSONObject(i));
|
|
||||||
|
|
||||||
if(remote.pdvTask.getValue(ProducteevTask.CREATOR_ID) != userId &&
|
|
||||||
remote.pdvTask.getValue(ProducteevTask.RESPONSIBLE_ID) != userId)
|
|
||||||
remote.task.setValue(Task.IS_READONLY, 1);
|
|
||||||
else
|
|
||||||
remote.task.setValue(Task.IS_READONLY, 0);
|
|
||||||
|
|
||||||
// update reminder flags for incoming remote tasks to prevent annoying
|
|
||||||
if(remote.task.hasDueDate() && remote.task.getValue(Task.DUE_DATE) < DateUtilities.now())
|
|
||||||
remote.task.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
|
|
||||||
|
|
||||||
dataService.findLocalMatch(remote);
|
|
||||||
|
|
||||||
remoteTasks.add(remote);
|
|
||||||
}
|
|
||||||
} catch (ApiServiceException ase) {
|
|
||||||
// catch it here, so that other dashboards can still be synchronized
|
|
||||||
handleException("pdv-sync", ase, true); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncData<ProducteevTaskContainer> syncData = populateSyncData(remoteTasks);
|
|
||||||
try {
|
|
||||||
synchronizeTasks(syncData);
|
|
||||||
AndroidUtilities.sleepDeep(3000L);
|
|
||||||
checkForCreatedDuringSync();
|
|
||||||
} finally {
|
|
||||||
syncData.localCreated.close();
|
|
||||||
syncData.localUpdated.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
Preferences.setString(ProducteevUtilities.PREF_SERVER_LAST_SYNC, invoker.time());
|
|
||||||
preferences.recordSuccessfulSync();
|
|
||||||
|
|
||||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_REFRESH);
|
|
||||||
ContextManager.getContext().sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
|
||||||
|
|
||||||
// notification/activities stuff
|
|
||||||
processNotifications();
|
|
||||||
|
|
||||||
syncSuccess = getFinalSyncStatus();
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
// occurs when application was closed
|
|
||||||
} catch (Exception e) {
|
|
||||||
handleException("pdv-sync", e, true); //$NON-NLS-1$
|
|
||||||
} finally {
|
|
||||||
StatisticsService.reportEvent(StatisticsConstants.PDV_SYNC_FINISHED,
|
|
||||||
"success", syncSuccess); //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkForCreatedDuringSync() {
|
|
||||||
TodorooCursor<Task> localCreated = dataService.getLocallyCreated(PROPERTIES);
|
|
||||||
try {
|
|
||||||
SyncData<ProducteevTaskContainer> localCreatedData = new SyncData<ProducteevTaskContainer>(null, localCreated, null);
|
|
||||||
sendLocallyCreated(localCreatedData, new HashMap<String, Integer>());
|
|
||||||
} catch (IOException e) {
|
|
||||||
handleException("gtasks-sync", e, true);
|
|
||||||
} finally {
|
|
||||||
localCreated.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param activities
|
|
||||||
* @return
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private String[] parseActivities(JSONArray activities) throws JSONException {
|
|
||||||
int count = (activities == null ? 0 : activities.length());
|
|
||||||
String[] activitiesList = new String[count];
|
|
||||||
if(activities == null)
|
|
||||||
return activitiesList;
|
|
||||||
for(int i = 0; i < activities.length(); i++) {
|
|
||||||
String message = activities.getJSONObject(i).getJSONObject("activity").getString("message");
|
|
||||||
activitiesList[i] = ApiUtilities.decode(message);
|
|
||||||
}
|
|
||||||
return activitiesList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ------------------------------------------------------------ sync data
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
private void saveUserData(JSONObject user) throws JSONException {
|
|
||||||
long defaultDashboard = user.getLong("default_dashboard");
|
|
||||||
userId = user.getLong("id_user");
|
|
||||||
Preferences.setLong(ProducteevUtilities.PREF_DEFAULT_DASHBOARD, defaultDashboard);
|
|
||||||
Preferences.setLong(ProducteevUtilities.PREF_USER_ID, userId);
|
|
||||||
|
|
||||||
// save the default dashboard preference if unset
|
|
||||||
int defaultDashSetting = Preferences.getIntegerFromString(R.string.producteev_PPr_defaultdash_key,
|
|
||||||
ProducteevUtilities.DASHBOARD_DEFAULT);
|
|
||||||
if(defaultDashSetting == ProducteevUtilities.DASHBOARD_DEFAULT)
|
|
||||||
Preferences.setStringFromInteger(R.string.producteev_PPr_defaultdash_key, (int) defaultDashboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
// all synchronized properties
|
|
||||||
private static final Property<?>[] PROPERTIES = new Property<?>[] {
|
|
||||||
Task.ID,
|
|
||||||
Task.TITLE,
|
|
||||||
Task.IMPORTANCE,
|
|
||||||
Task.DUE_DATE,
|
|
||||||
Task.CREATION_DATE,
|
|
||||||
Task.COMPLETION_DATE,
|
|
||||||
Task.DELETION_DATE,
|
|
||||||
Task.REMINDER_FLAGS,
|
|
||||||
Task.NOTES,
|
|
||||||
Task.RECURRENCE
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Populate SyncData data structure
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private SyncData<ProducteevTaskContainer> populateSyncData(ArrayList<ProducteevTaskContainer> remoteTasks) throws JSONException {
|
|
||||||
// fetch locally created tasks
|
|
||||||
TodorooCursor<Task> localCreated = dataService.getLocallyCreated(PROPERTIES);
|
|
||||||
|
|
||||||
// fetch locally updated tasks
|
|
||||||
TodorooCursor<Task> localUpdated = dataService.getLocallyUpdated(PROPERTIES);
|
|
||||||
|
|
||||||
return new SyncData<ProducteevTaskContainer>(remoteTasks, localCreated, localUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ------------------------------------------------- create / push / pull
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ProducteevTaskContainer create(ProducteevTaskContainer local) throws IOException {
|
|
||||||
Task localTask = local.task;
|
|
||||||
long dashboard = ProducteevUtilities.INSTANCE.getDefaultDashboard();
|
|
||||||
if(local.pdvTask.containsNonNullValue(ProducteevTask.DASHBOARD_ID))
|
|
||||||
dashboard = local.pdvTask.getValue(ProducteevTask.DASHBOARD_ID);
|
|
||||||
long responsibleId = local.pdvTask.getValue(ProducteevTask.RESPONSIBLE_ID);
|
|
||||||
|
|
||||||
if(dashboard == ProducteevUtilities.DASHBOARD_NO_SYNC) {
|
|
||||||
// set a bogus task id, then return without creating
|
|
||||||
local.pdvTask.setValue(ProducteevTask.ID, TASK_ID_UNSYNCED);
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject response = invoker.tasksCreate(localTask.getValue(Task.TITLE),
|
|
||||||
responsibleId, dashboard, createDeadline(localTask), createReminder(localTask),
|
|
||||||
localTask.isCompleted() ? 2 : 1, createStars(localTask));
|
|
||||||
ProducteevTaskContainer newRemoteTask;
|
|
||||||
try {
|
|
||||||
newRemoteTask = parseRemoteTask(response);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new ApiResponseParseException(e);
|
|
||||||
}
|
|
||||||
transferIdentifiers(newRemoteTask, local);
|
|
||||||
push(local, newRemoteTask);
|
|
||||||
return newRemoteTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create a task container for the given ProducteevTask
|
|
||||||
* @throws JSONException */
|
|
||||||
private ProducteevTaskContainer parseRemoteTask(JSONObject remoteTask) throws JSONException {
|
|
||||||
Task task = new Task();
|
|
||||||
|
|
||||||
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
|
|
||||||
|
|
||||||
if(remoteTask.has("task"))
|
|
||||||
remoteTask = remoteTask.getJSONObject("task");
|
|
||||||
|
|
||||||
task.setValue(Task.TITLE, ApiUtilities.decode(remoteTask.getString("title")));
|
|
||||||
task.setValue(Task.CREATION_DATE, ApiUtilities.producteevToUnixTime(remoteTask.getString("time_created"), 0));
|
|
||||||
task.setValue(Task.COMPLETION_DATE, remoteTask.getInt("status") == 2 ? DateUtilities.now() : 0);
|
|
||||||
task.setValue(Task.DELETION_DATE, remoteTask.getInt("deleted") == 1 ? DateUtilities.now() : 0);
|
|
||||||
|
|
||||||
long dueDate = ApiUtilities.producteevToUnixTime(remoteTask.getString("deadline"), 0);
|
|
||||||
if(remoteTask.optInt("all_day", 0) == 1)
|
|
||||||
task.setValue(Task.DUE_DATE, Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, dueDate));
|
|
||||||
else
|
|
||||||
task.setValue(Task.DUE_DATE, Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, dueDate));
|
|
||||||
task.setValue(Task.IMPORTANCE, 5 - remoteTask.getInt("star"));
|
|
||||||
|
|
||||||
JSONArray labels = remoteTask.getJSONArray("labels");
|
|
||||||
for(int i = 0; i < labels.length(); i++) {
|
|
||||||
JSONObject label = labels.getJSONObject(i).getJSONObject("label");
|
|
||||||
if(label.getInt("deleted") != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Metadata tagData = new Metadata();
|
|
||||||
tagData.setValue(Metadata.KEY, TaskToTagMetadata.KEY);
|
|
||||||
tagData.setValue(TaskToTagMetadata.TAG_NAME, ApiUtilities.decode(label.getString("title")));
|
|
||||||
metadata.add(tagData);
|
|
||||||
}
|
|
||||||
|
|
||||||
ProducteevTaskContainer container = new ProducteevTaskContainer(task, metadata, remoteTask);
|
|
||||||
|
|
||||||
JSONArray notes = remoteTask.getJSONArray("notes");
|
|
||||||
for(int i = notes.length() - 1; i >= 0; i--) {
|
|
||||||
JSONObject note = notes.getJSONObject(i).getJSONObject("note");
|
|
||||||
PluginServices.getMetadataService().deleteWhere(Criterion.and(Metadata.KEY.eq(NoteMetadata.METADATA_KEY),
|
|
||||||
NoteMetadata.EXT_ID.eq(note.getString("id_note"))));
|
|
||||||
if(note.getLong("deleted") != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
long creator = note.getLong("id_creator");
|
|
||||||
metadata.add(ApiUtilities.createNoteMetadata(note, creatorName(container, creator)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String creatorName(ProducteevTaskContainer container, long creator) {
|
|
||||||
StoreObject[] dashboards = dataService.getDashboards();
|
|
||||||
for(int i = 0; i < dashboards.length; i++) {
|
|
||||||
Long dashboard = container.pdvTask.getValue(ProducteevTask.DASHBOARD_ID);
|
|
||||||
if(dashboard.equals(dashboards[i].getValue(ProducteevDashboard.REMOTE_ID))) {
|
|
||||||
return ProducteevDashboard.getUserFromDashboard(dashboards[i], creator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ProducteevTaskContainer pull(ProducteevTaskContainer task) throws IOException {
|
|
||||||
if(!task.pdvTask.containsNonNullValue(ProducteevTask.ID))
|
|
||||||
throw new ApiServiceException("Tried to read an invalid task"); //$NON-NLS-1$
|
|
||||||
|
|
||||||
JSONObject remote = invoker.tasksView(task.pdvTask.getValue(ProducteevTask.ID));
|
|
||||||
try {
|
|
||||||
return parseRemoteTask(remote);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new ApiResponseParseException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send changes for the given Task across the wire. If a remoteTask is
|
|
||||||
* supplied, we attempt to intelligently only transmit the values that
|
|
||||||
* have changed.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected ProducteevTaskContainer push(ProducteevTaskContainer local, ProducteevTaskContainer remote) throws IOException {
|
|
||||||
boolean remerge = false;
|
|
||||||
|
|
||||||
long idTask = local.pdvTask.getValue(ProducteevTask.ID);
|
|
||||||
long idDashboard = local.pdvTask.getValue(ProducteevTask.DASHBOARD_ID);
|
|
||||||
long idResponsible = local.pdvTask.getValue(ProducteevTask.RESPONSIBLE_ID);
|
|
||||||
|
|
||||||
// if local is marked do not sync, handle accordingly
|
|
||||||
if(idDashboard == ProducteevUtilities.DASHBOARD_NO_SYNC) {
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch remote task for comparison
|
|
||||||
if(remote == null)
|
|
||||||
remote = pull(local);
|
|
||||||
|
|
||||||
// either delete or re-create if necessary
|
|
||||||
if(shouldTransmit(local, Task.DELETION_DATE, remote)) {
|
|
||||||
if(local.task.getValue(Task.DELETION_DATE) > 0)
|
|
||||||
invoker.tasksDelete(idTask);
|
|
||||||
else {
|
|
||||||
// if we create, we transfer identifiers to old remote
|
|
||||||
// in case it is used by caller for other purposes
|
|
||||||
ProducteevTaskContainer newRemote = create(local);
|
|
||||||
transferIdentifiers(newRemote, remote);
|
|
||||||
remote = newRemote;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dashboard
|
|
||||||
if(remote != null && idDashboard != remote.pdvTask.getValue(ProducteevTask.DASHBOARD_ID)) {
|
|
||||||
invoker.tasksSetWorkspace(idTask, idDashboard);
|
|
||||||
remote = pull(local);
|
|
||||||
} else if(remote == null && idTask == TASK_ID_UNSYNCED) {
|
|
||||||
// was un-synced, create remote
|
|
||||||
remote = create(local);
|
|
||||||
}
|
|
||||||
|
|
||||||
// responsible
|
|
||||||
if(remote != null && idResponsible !=
|
|
||||||
remote.pdvTask.getValue(ProducteevTask.RESPONSIBLE_ID)) {
|
|
||||||
invoker.tasksSetResponsible(idTask, idResponsible);
|
|
||||||
}
|
|
||||||
|
|
||||||
// core properties
|
|
||||||
if(shouldTransmit(local, Task.TITLE, remote))
|
|
||||||
invoker.tasksSetTitle(idTask, local.task.getValue(Task.TITLE));
|
|
||||||
if(shouldTransmit(local, Task.IMPORTANCE, remote))
|
|
||||||
invoker.tasksSetStar(idTask, createStars(local.task));
|
|
||||||
if(shouldTransmit(local, Task.DUE_DATE, remote)) {
|
|
||||||
if(local.task.hasDueDate())
|
|
||||||
invoker.tasksSetDeadline(idTask, createDeadline(local.task), local.task.hasDueTime() ? 0 : 1);
|
|
||||||
else
|
|
||||||
invoker.tasksUnsetDeadline(idTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isPDVRepeating = ((local.pdvTask.containsNonNullValue(ProducteevTask.REPEATING_SETTING) &&
|
|
||||||
local.pdvTask.getValue(ProducteevTask.REPEATING_SETTING).length()>0) ||
|
|
||||||
(remote != null && remote.pdvTask.containsNonNullValue(ProducteevTask.REPEATING_SETTING) &&
|
|
||||||
remote.pdvTask.getValue(ProducteevTask.REPEATING_SETTING).length()>0));
|
|
||||||
|
|
||||||
boolean isAstridRepeating = local.task.containsNonNullValue(Task.RECURRENCE) &&
|
|
||||||
!TextUtils.isEmpty(local.task.getValue(Task.RECURRENCE));
|
|
||||||
|
|
||||||
if (isAstridRepeating && isPDVRepeating) {
|
|
||||||
// Astrid-repeat overrides PDV-repeat
|
|
||||||
invoker.tasksUnsetRepeating(idTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(shouldTransmit(local, Task.COMPLETION_DATE, remote)) {
|
|
||||||
invoker.tasksSetStatus(idTask, local.task.isCompleted() ? 2 : 1);
|
|
||||||
if (local.task.isCompleted() && !isAstridRepeating &&
|
|
||||||
isPDVRepeating) {
|
|
||||||
local.task.setValue(Task.COMPLETION_DATE, 0L);
|
|
||||||
remerge = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
// tags
|
|
||||||
transmitTags(local, remote, idTask, idDashboard);
|
|
||||||
|
|
||||||
// notes
|
|
||||||
if(!TextUtils.isEmpty(local.task.getValue(Task.NOTES))) {
|
|
||||||
String note = local.task.getValue(Task.NOTES);
|
|
||||||
JSONObject result = invoker.tasksNoteCreate(idTask, note);
|
|
||||||
local.metadata.add(ApiUtilities.createNoteMetadata(result.getJSONObject("note"), null));
|
|
||||||
local.task.setValue(Task.NOTES, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
remote = pull(local);
|
|
||||||
remote.task.setId(local.task.getId());
|
|
||||||
if(remerge) {
|
|
||||||
// transform local into remote
|
|
||||||
local.task = remote.task;
|
|
||||||
local.pdvTask.setValue(ProducteevTask.ID, remote.pdvTask.getValue(ProducteevTask.ID));
|
|
||||||
local.pdvTask.setValue(ProducteevTask.DASHBOARD_ID, remote.pdvTask.getValue(ProducteevTask.DASHBOARD_ID));
|
|
||||||
local.pdvTask.setValue(ProducteevTask.CREATOR_ID, remote.pdvTask.getValue(ProducteevTask.CREATOR_ID));
|
|
||||||
local.pdvTask.setValue(ProducteevTask.RESPONSIBLE_ID, remote.pdvTask.getValue(ProducteevTask.RESPONSIBLE_ID));
|
|
||||||
if(remote.pdvTask.containsNonNullValue(ProducteevTask.REPEATING_SETTING))
|
|
||||||
local.pdvTask.setValue(ProducteevTask.REPEATING_SETTING, remote.pdvTask.getValue(ProducteevTask.REPEATING_SETTING));
|
|
||||||
}
|
|
||||||
|
|
||||||
return remote;
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new ApiResponseParseException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transmit tags
|
|
||||||
*
|
|
||||||
* @param local
|
|
||||||
* @param remote
|
|
||||||
* @param idTask
|
|
||||||
* @param idDashboard
|
|
||||||
* @throws ApiServiceException
|
|
||||||
* @throws JSONException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
private void transmitTags(ProducteevTaskContainer local,
|
|
||||||
ProducteevTaskContainer remote, long idTask, long idDashboard) throws ApiServiceException, JSONException, IOException {
|
|
||||||
HashSet<String> localTags = new HashSet<String>();
|
|
||||||
HashSet<String> remoteTags = new HashSet<String>();
|
|
||||||
for(Metadata item : local.metadata)
|
|
||||||
if(TaskToTagMetadata.KEY.equals(item.getValue(Metadata.KEY)))
|
|
||||||
localTags.add(item.getValue(TaskToTagMetadata.TAG_NAME));
|
|
||||||
if(remote != null && remote.metadata != null) {
|
|
||||||
for(Metadata item : remote.metadata)
|
|
||||||
if(TaskToTagMetadata.KEY.equals(item.getValue(Metadata.KEY)))
|
|
||||||
remoteTags.add(item.getValue(TaskToTagMetadata.TAG_NAME));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!localTags.equals(remoteTags)) {
|
|
||||||
long[] labels = new long[localTags.size()];
|
|
||||||
int index = 0;
|
|
||||||
for(String label : localTags) {
|
|
||||||
String pdvLabel = idDashboard + label;
|
|
||||||
final long id;
|
|
||||||
if(!labelMap.containsKey(pdvLabel)) {
|
|
||||||
JSONObject result = invoker.labelsCreate(idDashboard, label).getJSONObject("label");
|
|
||||||
id = putLabelIntoCache(result);
|
|
||||||
} else
|
|
||||||
id = labelMap.get(pdvLabel);
|
|
||||||
labels[index++] = id;
|
|
||||||
}
|
|
||||||
invoker.tasksChangeLabel(idTask, labels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------- read / write
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ProducteevTaskContainer read(TodorooCursor<Task> cursor) throws IOException {
|
|
||||||
return dataService.readTaskAndMetadata(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void write(ProducteevTaskContainer task) throws IOException {
|
|
||||||
if(task.task.isSaved()) {
|
|
||||||
Task local = PluginServices.getTaskService().fetchById(task.task.getId(), Task.COMPLETION_DATE);
|
|
||||||
if(task.task.isCompleted() && !local.isCompleted())
|
|
||||||
StatisticsService.reportEvent(StatisticsConstants.PDV_TASK_COMPLETED);
|
|
||||||
} else { // Set default reminders for remotely created tasks
|
|
||||||
TaskDao.setDefaultReminders(task.task);
|
|
||||||
}
|
|
||||||
dataService.saveTaskAndMetadata(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------- misc helpers
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int matchTask(ArrayList<ProducteevTaskContainer> tasks, ProducteevTaskContainer target) {
|
|
||||||
int length = tasks.size();
|
|
||||||
for(int i = 0; i < length; i++) {
|
|
||||||
ProducteevTaskContainer task = tasks.get(i);
|
|
||||||
if (target.pdvTask.containsNonNullValue(ProducteevTask.ID) &&
|
|
||||||
task.pdvTask.getValue(ProducteevTask.ID).equals(target.pdvTask.getValue(ProducteevTask.ID)))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get stars in producteev format
|
|
||||||
* @param local
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private Integer createStars(Task local) {
|
|
||||||
return 5 - local.getValue(Task.IMPORTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get reminder in producteev format
|
|
||||||
* @param local
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private Integer createReminder(Task local) {
|
|
||||||
if(local.getFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AT_DEADLINE))
|
|
||||||
return 8;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get deadline in producteev format
|
|
||||||
* @param task
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private String createDeadline(Task task) {
|
|
||||||
if(!task.hasDueDate())
|
|
||||||
return "";
|
|
||||||
return ApiUtilities.unixTimeToProducteev(task.getValue(Task.DUE_DATE));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether this task's property should be transmitted
|
|
||||||
* @param task task to consider
|
|
||||||
* @param property property to consider
|
|
||||||
* @param remoteTask remote task proxy
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private boolean shouldTransmit(SyncContainer task, Property<?> property, SyncContainer remoteTask) {
|
|
||||||
if(!task.task.containsValue(property))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(remoteTask == null)
|
|
||||||
return true;
|
|
||||||
if(!remoteTask.task.containsValue(property))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// special cases - match if they're zero or nonzero
|
|
||||||
if(property == Task.COMPLETION_DATE ||
|
|
||||||
property == Task.DELETION_DATE)
|
|
||||||
return !AndroidUtilities.equals((Long)task.task.getValue(property) == 0,
|
|
||||||
(Long)remoteTask.task.getValue(property) == 0);
|
|
||||||
|
|
||||||
return !AndroidUtilities.equals(task.task.getValue(property),
|
|
||||||
remoteTask.task.getValue(property));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int updateNotification(Context context, Notification notification) {
|
|
||||||
String notificationTitle = context.getString(R.string.producteev_notification_title);
|
|
||||||
Intent intent = new Intent(context, ProducteevPreferences.class);
|
|
||||||
PendingIntent notificationIntent = PendingIntent.getActivity(context, 0,
|
|
||||||
intent, 0);
|
|
||||||
notification.setLatestEventInfo(context,
|
|
||||||
notificationTitle, context.getString(R.string.SyP_progress),
|
|
||||||
notificationIntent);
|
|
||||||
return Constants.NOTIFICATION_SYNC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void transferIdentifiers(ProducteevTaskContainer source,
|
|
||||||
ProducteevTaskContainer destination) {
|
|
||||||
destination.pdvTask = source.pdvTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read labels into label map
|
|
||||||
* @param dashboardId
|
|
||||||
* @throws JSONException
|
|
||||||
* @throws ApiServiceException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
private void readLabels(JSONArray labels) throws JSONException, ApiServiceException, IOException {
|
|
||||||
for(int i = 0; i < labels.length(); i++) {
|
|
||||||
JSONObject label = labels.getJSONObject(i).getJSONObject("label");
|
|
||||||
putLabelIntoCache(label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Puts a single label into the cache
|
|
||||||
* @param dashboardId
|
|
||||||
* @param label
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private long putLabelIntoCache(JSONObject label)
|
|
||||||
throws JSONException {
|
|
||||||
String name = ApiUtilities.decode(label.getString("title"));
|
|
||||||
long dashboard = label.getLong("id_dashboard");
|
|
||||||
labelMap.put(dashboard + name, label.getLong("id_label"));
|
|
||||||
return label.getLong("id_label");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show workspace notifications
|
|
||||||
*
|
|
||||||
* @throws ApiResponseParseException
|
|
||||||
* @throws ApiServiceException
|
|
||||||
* @throws IOException
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
private void processNotifications() throws ApiResponseParseException,
|
|
||||||
ApiServiceException, IOException, JSONException {
|
|
||||||
String lastNotificationId = Preferences.getStringValue(ProducteevUtilities.PREF_SERVER_LAST_NOTIFICATION);
|
|
||||||
String lastActivityId = Preferences.getStringValue(ProducteevUtilities.PREF_SERVER_LAST_ACTIVITY);
|
|
||||||
JSONArray notifications = invoker.activitiesShowNotifications(null, (lastNotificationId == null ? null : new Long(lastNotificationId)));
|
|
||||||
String[] notificationsList = parseActivities(notifications);
|
|
||||||
// update lastIds
|
|
||||||
if (notifications.length() > 0) {
|
|
||||||
lastNotificationId = ""+notifications.getJSONObject(0).getJSONObject("activity").getLong("id_activity");
|
|
||||||
}
|
|
||||||
|
|
||||||
// display notifications from producteev-log
|
|
||||||
Context context = ContextManager.getContext();
|
|
||||||
final NotificationManager nm = new NotificationManager.AndroidNotificationManager(context);
|
|
||||||
for (int i = 0; i< notificationsList.length; i++) {
|
|
||||||
long id_dashboard = notifications.getJSONObject(i).getJSONObject("activity").getLong("id_dashboard");
|
|
||||||
String dashboardName = null;
|
|
||||||
StoreObject[] dashboardsData = ProducteevDataService.getInstance().getDashboards();
|
|
||||||
ProducteevDashboard dashboard = null;
|
|
||||||
if (dashboardsData != null) {
|
|
||||||
for (int j=0; i<dashboardsData.length;i++) {
|
|
||||||
long id = dashboardsData[j].getValue(ProducteevDashboard.REMOTE_ID);
|
|
||||||
if (id == id_dashboard) {
|
|
||||||
dashboardName = dashboardsData[j].getValue(ProducteevDashboard.NAME);
|
|
||||||
dashboard = new ProducteevDashboard(id, dashboardName, null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// it seems dashboard is null if we get a notification about an unknown dashboard, just filter it.
|
|
||||||
if (dashboard != null) {
|
|
||||||
// initialize notification
|
|
||||||
int icon = R.drawable.ic_producteev_notification;
|
|
||||||
long when = System.currentTimeMillis();
|
|
||||||
Notification notification = new Notification(icon, null, when);
|
|
||||||
CharSequence contentTitle = context.getString(R.string.producteev_notification_title)+": "+dashboard.getName();
|
|
||||||
|
|
||||||
Filter filter = ProducteevFilterExposer.filterFromList(context, dashboard, userId);
|
|
||||||
Intent notificationIntent = ShortcutActivity.createIntent(filter);
|
|
||||||
|
|
||||||
// filter the tags from the message
|
|
||||||
String message = notificationsList[i].replaceAll("<[^>]+>", "");
|
|
||||||
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
|
|
||||||
notification.setLatestEventInfo(context, contentTitle, message, contentIntent);
|
|
||||||
|
|
||||||
nm.notify(Constants.NOTIFICATION_PRODUCTEEV_NOTIFICATIONS-i, notification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// store lastIds in Preferences
|
|
||||||
Preferences.setString(ProducteevUtilities.PREF_SERVER_LAST_NOTIFICATION, lastNotificationId);
|
|
||||||
Preferences.setString(ProducteevUtilities.PREF_SERVER_LAST_ACTIVITY, lastActivityId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
// ------------------------------------------------------- helper methods
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
private static final String stripslashes(int ____,String __,String ___) {
|
|
||||||
int _=__.charAt(____/92);_=_==116?_-1:_;_=((_>=97)&&(_<=123)?
|
|
||||||
((_-83)%27+97):_);return TextUtils.htmlEncode(____==31?___:
|
|
||||||
stripslashes(____+1,__.substring(1),___+((char)_)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.sync;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.data.Property.LongProperty;
|
|
||||||
import com.todoroo.andlib.data.Property.StringProperty;
|
|
||||||
import com.todoroo.andlib.utility.Preferences;
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.producteev.ProducteevUtilities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Metadata entries for a Producteev Task
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevTask {
|
|
||||||
|
|
||||||
/** metadata key */
|
|
||||||
public static final String METADATA_KEY = "producteev"; //$NON-NLS-1$
|
|
||||||
|
|
||||||
/** task id in producteev */
|
|
||||||
public static final LongProperty ID = new LongProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE1.name);
|
|
||||||
|
|
||||||
/** dashboard id */
|
|
||||||
public static final LongProperty DASHBOARD_ID = new LongProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE2.name);
|
|
||||||
|
|
||||||
/** creator id */
|
|
||||||
public static final LongProperty CREATOR_ID = new LongProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE3.name);
|
|
||||||
|
|
||||||
/** responsible id */
|
|
||||||
public static final LongProperty RESPONSIBLE_ID = new LongProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE4.name);
|
|
||||||
|
|
||||||
/** repeating settings */
|
|
||||||
public static final StringProperty REPEATING_SETTING = new StringProperty(Metadata.TABLE,
|
|
||||||
Metadata.VALUE5.name);
|
|
||||||
|
|
||||||
public static Metadata newMetadata() {
|
|
||||||
Metadata metadata = new Metadata();
|
|
||||||
metadata.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY);
|
|
||||||
metadata.setValue(ID, 0L);
|
|
||||||
metadata.setValue(DASHBOARD_ID, ProducteevUtilities.INSTANCE.getDefaultDashboard());
|
|
||||||
metadata.setValue(CREATOR_ID, Preferences.getLong(ProducteevUtilities.PREF_USER_ID, 0L));
|
|
||||||
metadata.setValue(RESPONSIBLE_ID, Preferences.getLong(ProducteevUtilities.PREF_USER_ID, 0L));
|
|
||||||
metadata.setValue(REPEATING_SETTING, ""); //$NON-NLS-1$
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.sync;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import com.todoroo.astrid.data.Metadata;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.sync.SyncContainer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RTM Task Container
|
|
||||||
*
|
|
||||||
* @author Tim Su <tim@todoroo.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProducteevTaskContainer extends SyncContainer {
|
|
||||||
|
|
||||||
public Metadata pdvTask;
|
|
||||||
|
|
||||||
public ProducteevTaskContainer(Task task, ArrayList<Metadata> metadata, Metadata pdvTask) {
|
|
||||||
this.task = task;
|
|
||||||
this.metadata = metadata;
|
|
||||||
this.pdvTask = pdvTask;
|
|
||||||
if(this.pdvTask == null) {
|
|
||||||
this.pdvTask = ProducteevTask.newMetadata();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public ProducteevTaskContainer(Task task, ArrayList<Metadata> metadata, JSONObject remoteTask) {
|
|
||||||
this(task, metadata, new Metadata());
|
|
||||||
pdvTask.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY);
|
|
||||||
pdvTask.setValue(ProducteevTask.ID, remoteTask.optLong("id_task"));
|
|
||||||
pdvTask.setValue(ProducteevTask.DASHBOARD_ID, remoteTask.optLong("id_dashboard"));
|
|
||||||
pdvTask.setValue(ProducteevTask.RESPONSIBLE_ID, remoteTask.optLong("id_responsible"));
|
|
||||||
pdvTask.setValue(ProducteevTask.CREATOR_ID, remoteTask.optLong("id_creator"));
|
|
||||||
String repeatingValue = remoteTask.optString("repeating_value");
|
|
||||||
String repeatingInterval = remoteTask.optString("repeating_interval");
|
|
||||||
if (!"0".equals(repeatingValue) && repeatingValue.length() > 0 &&
|
|
||||||
repeatingInterval != null && repeatingInterval.length() > 0) {
|
|
||||||
pdvTask.setValue(ProducteevTask.REPEATING_SETTING, repeatingValue+","+repeatingInterval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProducteevTaskContainer(Task task, ArrayList<Metadata> metadata) {
|
|
||||||
this.task = task;
|
|
||||||
this.metadata = metadata;
|
|
||||||
|
|
||||||
for(Iterator<Metadata> iterator = metadata.iterator(); iterator.hasNext(); ) {
|
|
||||||
Metadata item = iterator.next();
|
|
||||||
if(ProducteevTask.METADATA_KEY.equals(item.getValue(Metadata.KEY))) {
|
|
||||||
pdvTask = item;
|
|
||||||
iterator.remove();
|
|
||||||
// don't break, could be multiple
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(this.pdvTask == null) {
|
|
||||||
this.pdvTask = ProducteevTask.newMetadata();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.producteev.sync;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Arne Jans <arne.jans@gmail.com>
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public class ProducteevUser implements Comparable<ProducteevUser> {
|
|
||||||
|
|
||||||
private final long id;
|
|
||||||
|
|
||||||
private final String email;
|
|
||||||
|
|
||||||
private final String firstname;
|
|
||||||
|
|
||||||
private final String lastname;
|
|
||||||
|
|
||||||
public ProducteevUser(long id, String email, String firstname,
|
|
||||||
String lastname) {
|
|
||||||
this.id = id;
|
|
||||||
this.email = email;
|
|
||||||
this.firstname = firstname;
|
|
||||||
this.lastname = lastname;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProducteevUser(JSONObject elt) throws JSONException {
|
|
||||||
this.id = elt.getLong("id");
|
|
||||||
this.email = elt.getString("email");
|
|
||||||
this.firstname = elt.getString("firstname");
|
|
||||||
this.lastname = elt.getString("lastname");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the email
|
|
||||||
*/
|
|
||||||
public String getEmail() {
|
|
||||||
return email;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the firstname
|
|
||||||
*/
|
|
||||||
public String getFirstname() {
|
|
||||||
return firstname;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the lastname
|
|
||||||
*/
|
|
||||||
public String getLastname() {
|
|
||||||
return lastname;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the id
|
|
||||||
*/
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
String displayString = "";
|
|
||||||
boolean hasFirstname = false;
|
|
||||||
boolean hasLastname = false;
|
|
||||||
if (firstname != null && firstname.length() > 0) {
|
|
||||||
displayString += firstname;
|
|
||||||
hasFirstname = true;
|
|
||||||
}
|
|
||||||
if (lastname != null && lastname.length() > 0)
|
|
||||||
hasLastname = true;
|
|
||||||
if (hasFirstname && hasLastname)
|
|
||||||
displayString += " ";
|
|
||||||
if (hasLastname)
|
|
||||||
displayString += lastname;
|
|
||||||
|
|
||||||
if (!hasFirstname && !hasLastname && email != null
|
|
||||||
&& email.length() > 0)
|
|
||||||
displayString += email;
|
|
||||||
return displayString;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
|
|
||||||
ProducteevUser that = (ProducteevUser) o;
|
|
||||||
|
|
||||||
if (id != that.id) return false;
|
|
||||||
if (email != null ? !email.equals(that.email) : that.email != null) return false;
|
|
||||||
if (firstname != null ? !firstname.equals(that.firstname) : that.firstname != null) return false;
|
|
||||||
if (lastname != null ? !lastname.equals(that.lastname) : that.lastname != null) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = (int) (id ^ (id >>> 32));
|
|
||||||
result = 31 * result + (email != null ? email.hashCode() : 0);
|
|
||||||
result = 31 * result + (firstname != null ? firstname.hashCode() : 0);
|
|
||||||
result = 31 * result + (lastname != null ? lastname.hashCode() : 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(ProducteevUser o) {
|
|
||||||
int ret = toString().compareTo(o.toString());
|
|
||||||
return ret == 0 ? (new Long(id).compareTo(o.id)) : ret;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +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:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingLeft="5dip"
|
|
||||||
android:paddingRight="5dip">
|
|
||||||
|
|
||||||
<!-- producteev task assignment controlset -->
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="100">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/producteev_TEA_dashboard_assign_label"
|
|
||||||
style="@style/TextAppearance.GEN_EditLabel.DLG_EditLabel" />
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/producteev_TEA_dashboard_assign"
|
|
||||||
android:prompt="@string/producteev_TEA_dashboard_assign_label"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/producteev_TEA_task_assign_label"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/producteev_TEA_task_assign_label"
|
|
||||||
style="@style/TextAppearance.GEN_EditLabel.DLG_EditLabel" />
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/producteev_TEA_task_assign"
|
|
||||||
android:prompt="@string/producteev_TEA_task_assign_label"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="1px"
|
|
||||||
android:padding="5dip"
|
|
||||||
android:background="@android:drawable/divider_horizontal_dark" />
|
|
||||||
<include layout="@layout/control_dialog_ok"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
@ -1,112 +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.
|
|
||||||
-->
|
|
||||||
<ScrollView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:background="@drawable/pdv_body">
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingTop="5dip"
|
|
||||||
android:paddingBottom="5dip"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
android:src="@drawable/pdv_logo" />
|
|
||||||
<TextView
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingBottom="10dip"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textColor="#ffffff"
|
|
||||||
android:text="@string/producteev_PLA_body" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/error"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:paddingBottom="20dip"
|
|
||||||
android:textColor="#ff0000"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:visibility="gone" />
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/email"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:hint="@string/producteev_PLA_email"
|
|
||||||
android:contentDescription="E-Mail with which you registered to Producteev-service"
|
|
||||||
android:inputType="textEmailAddress" />
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/password"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:contentDescription="Password for your Producteev account"
|
|
||||||
android:hint="@string/producteev_PLA_password"
|
|
||||||
android:inputType="textPassword" />
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/newUserLayout"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingTop="20dip"
|
|
||||||
android:visibility="gone">
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/firstName"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:hint="@string/producteev_PLA_firstName"
|
|
||||||
android:inputType="textPersonName" />
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/lastName"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:hint="@string/producteev_PLA_lastName"
|
|
||||||
android:inputType="textPersonName" />
|
|
||||||
<Spinner
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:id="@+id/timezoneList"
|
|
||||||
android:entries="@array/PLA_timezones_list"
|
|
||||||
android:contentDescription="@string/producteev_PLA_timezone"
|
|
||||||
android:prompt="@string/producteev_PLA_timezone" />
|
|
||||||
</LinearLayout>
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingTop="5dip"
|
|
||||||
android:baselineAligned="false">
|
|
||||||
<Button
|
|
||||||
android:id="@+id/signIn"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/producteev_PLA_signIn" />
|
|
||||||
<Button
|
|
||||||
android:id="@+id/createNew"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/producteev_PLA_createNew" />
|
|
||||||
</LinearLayout>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/terms"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingTop="10dip"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textColor="#0000ff"
|
|
||||||
android:linksClickable="true"
|
|
||||||
android:text="@string/producteev_PLA_terms" />
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
@ -1,137 +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.
|
|
||||||
-->
|
|
||||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<!-- ====================== Plugin Boilerplate ========================= -->
|
|
||||||
|
|
||||||
<!-- filters header: Producteev -->
|
|
||||||
<string name="producteev_FEx_header">Producteev</string>
|
|
||||||
|
|
||||||
<!-- filter category for Producteev dashboards -->
|
|
||||||
<string name="producteev_FEx_dashboard">Workspaces</string>
|
|
||||||
|
|
||||||
<!-- filter category for Producteev responsible person -->
|
|
||||||
<string name="producteev_FEx_responsible_byme">Assigned by me to</string>
|
|
||||||
|
|
||||||
<!-- filter category for Producteev responsible person -->
|
|
||||||
<string name="producteev_FEx_responsible_byothers">Assigned by others to</string>
|
|
||||||
|
|
||||||
<!-- Producteev responsible filter title (%s => responsiblename) -->
|
|
||||||
<string name="producteev_FEx_responsible_title">Assigned To \'%s\'</string>
|
|
||||||
|
|
||||||
<!-- detail for showing tasks created by someone else (%s => person name) -->
|
|
||||||
<string name="producteev_PDE_task_from">from %s</string>
|
|
||||||
|
|
||||||
<!-- replacement string for task edit "Notes" when using Producteev -->
|
|
||||||
<string name="producteev_TEA_notes">Add a Comment</string>
|
|
||||||
|
|
||||||
<!-- ==================================================== Preferences == -->
|
|
||||||
|
|
||||||
<!-- Preferences Title: Producteev -->
|
|
||||||
<string name="producteev_PPr_header">Producteev</string>
|
|
||||||
|
|
||||||
<!-- dashboard title for producteev default dashboard -->
|
|
||||||
<string name="producteev_default_dashboard">Default Workspace</string>
|
|
||||||
|
|
||||||
<!-- dashboard title for tasks that are not synchronized -->
|
|
||||||
<string name="producteev_no_dashboard">(Do Not Synchronize)</string>
|
|
||||||
|
|
||||||
<!-- dashboard spinner entry on TEA for adding a new dashboard -->
|
|
||||||
<string name="producteev_create_dashboard">Add new Workspace...</string>
|
|
||||||
|
|
||||||
<!-- dashboard spinner entry on TEA for adding a new dashboard -->
|
|
||||||
<string name="producteev_create_dashboard_name">Name for new Workspace</string>
|
|
||||||
|
|
||||||
<!-- preference title for default dashboard -->
|
|
||||||
<string name="producteev_PPr_defaultdash_title">Default Workspace</string>
|
|
||||||
|
|
||||||
<!-- preference description for default dashboard (%s -> setting) -->
|
|
||||||
<string name="producteev_PPr_defaultdash_summary">New tasks will be added to: %s</string>
|
|
||||||
|
|
||||||
<!-- preference description for default dashboard (when set to 'not synchronized') -->
|
|
||||||
<string name="producteev_PPr_defaultdash_summary_none">New tasks will not be synchronized by default</string>
|
|
||||||
|
|
||||||
<!-- ================================================= Login Activity == -->
|
|
||||||
|
|
||||||
<!-- Activity Title: Producteev Login -->
|
|
||||||
<string name="producteev_PLA_title">Log In to Producteev</string>
|
|
||||||
|
|
||||||
<!-- Instructions: Producteev login -->
|
|
||||||
<string name="producteev_PLA_body">Sign in with your existing
|
|
||||||
Producteev account, or create a new account!</string>
|
|
||||||
|
|
||||||
<!-- Producteev Terms Link -->
|
|
||||||
<string name="producteev_PLA_terms">Terms & Conditions</string>
|
|
||||||
|
|
||||||
<!-- Sign In Button -->
|
|
||||||
<string name="producteev_PLA_signIn">Sign In</string>
|
|
||||||
|
|
||||||
<!-- Create New User Button -->
|
|
||||||
<string name="producteev_PLA_createNew">Create New User</string>
|
|
||||||
|
|
||||||
<!-- E-mail Address Label -->
|
|
||||||
<string name="producteev_PLA_email">E-mail</string>
|
|
||||||
|
|
||||||
<!-- Password Label -->
|
|
||||||
<string name="producteev_PLA_password">Password</string>
|
|
||||||
|
|
||||||
<!-- Timezone Spinner -->
|
|
||||||
<string name="producteev_PLA_timezone">Timezone</string>
|
|
||||||
|
|
||||||
<!-- Confirm Password Label -->
|
|
||||||
|
|
||||||
<!-- First Name Label -->
|
|
||||||
<string name="producteev_PLA_firstName">First Name</string>
|
|
||||||
|
|
||||||
<!-- Last Name Label -->
|
|
||||||
<string name="producteev_PLA_lastName">Last Name</string>
|
|
||||||
|
|
||||||
<!-- Error Message when fields aren't filled out -->
|
|
||||||
<string name="producteev_PLA_errorEmpty">Error: fill out all fields!</string>
|
|
||||||
|
|
||||||
<!-- Error Message when passwords don't match -->
|
|
||||||
|
|
||||||
<!-- Error Message when we receive a HTTP 401 Unauthorized -->
|
|
||||||
<string name="producteev_PLA_errorAuth">Error: e-mail or password incorrect!</string>
|
|
||||||
|
|
||||||
<!-- ================================================ Synchronization == -->
|
|
||||||
|
|
||||||
<!-- title for notification tray after synchronizing -->
|
|
||||||
<string name="producteev_notification_title">Producteev</string>
|
|
||||||
|
|
||||||
<!-- text for notification tray when synchronizing -->
|
|
||||||
|
|
||||||
<!-- Error msg when io exception -->
|
|
||||||
|
|
||||||
<!-- Prod Login email not specified-->
|
|
||||||
|
|
||||||
<!-- Prod Login password not specified-->
|
|
||||||
|
|
||||||
<!-- ================================================ labels for layout-elements == -->
|
|
||||||
|
|
||||||
<!-- Label for Producteev control set row -->
|
|
||||||
<string name="producteev_TEA_control_set_display">Producteev Assignment</string>
|
|
||||||
|
|
||||||
<!-- label for task-assignment spinner on taskeditactivity -->
|
|
||||||
<string name="producteev_TEA_task_assign_label">Assign this task to this person:</string>
|
|
||||||
|
|
||||||
<!-- Spinner-item for unassigned tasks on taskeditactivity -->
|
|
||||||
|
|
||||||
<!-- label for dashboard-assignment spinner on taskeditactivity -->
|
|
||||||
<string name="producteev_TEA_dashboard_assign_label">Assign this task to this workspace:</string>
|
|
||||||
|
|
||||||
<!-- Spinner-item for default dashboard on taskeditactivity -->
|
|
||||||
|
|
||||||
<string name="CFC_producteev_in_workspace_text">In workspace: ?</string>
|
|
||||||
|
|
||||||
<string name="CFC_producteev_in_workspace_name">In workspace...</string>
|
|
||||||
|
|
||||||
<string name="CFC_producteev_assigned_to_text">Assigned to: ?</string>
|
|
||||||
|
|
||||||
<string name="CFC_producteev_assigned_to_name">Assigned to...</string>
|
|
||||||
|
|
||||||
</resources>
|
|
@ -1,321 +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.
|
|
||||||
-->
|
|
||||||
<resources>
|
|
||||||
<!-- list of allowed timezones for user-signup in producteev -->
|
|
||||||
<string-array name="PLA_timezones_list">
|
|
||||||
<item>Africa/Abidjan</item>
|
|
||||||
<item>Africa/Accra</item>
|
|
||||||
<item>Africa/Addis_Ababa</item>
|
|
||||||
<item>Africa/Algiers</item>
|
|
||||||
<item>Africa/Asmara</item>
|
|
||||||
<item>Africa/Bamako</item>
|
|
||||||
<item>Africa/Bangui</item>
|
|
||||||
<item>Africa/Banjul</item>
|
|
||||||
<item>Africa/Bissau</item>
|
|
||||||
<item>Africa/Blantyre</item>
|
|
||||||
<item>Africa/Brazzaville</item>
|
|
||||||
<item>Africa/Bujumbura</item>
|
|
||||||
<item>Africa/Cairo</item>
|
|
||||||
<item>Africa/Casablanca</item>
|
|
||||||
<item>Africa/Ceuta</item>
|
|
||||||
<item>Africa/Conakry</item>
|
|
||||||
<item>Africa/Dakar</item>
|
|
||||||
<item>Africa/Dar_es_Salaam</item>
|
|
||||||
<item>Africa/Djibouti</item>
|
|
||||||
<item>Africa/Douala</item>
|
|
||||||
<item>Africa/El_Aaiun</item>
|
|
||||||
<item>Africa/Freetown</item>
|
|
||||||
<item>Africa/Gaborone</item>
|
|
||||||
<item>Africa/Harare</item>
|
|
||||||
<item>Africa/Johannesburg</item>
|
|
||||||
<item>Africa/Kampala</item>
|
|
||||||
<item>Africa/Khartoum</item>
|
|
||||||
<item>Africa/Kigali</item>
|
|
||||||
<item>Africa/Kinshasa</item>
|
|
||||||
<item>Africa/Lagos</item>
|
|
||||||
<item>Africa/Libreville</item>
|
|
||||||
<item>Africa/Lome</item>
|
|
||||||
<item>Africa/Luanda</item>
|
|
||||||
<item>Africa/Lubumbashi</item>
|
|
||||||
<item>Africa/Lusaka</item>
|
|
||||||
<item>Africa/Malabo</item>
|
|
||||||
<item>Africa/Maputo</item>
|
|
||||||
<item>Africa/Maseru</item>
|
|
||||||
<item>Africa/Mbabane</item>
|
|
||||||
<item>Africa/Mogadishu</item>
|
|
||||||
<item>Africa/Monrovia</item>
|
|
||||||
<item>Africa/Nairobi</item>
|
|
||||||
<item>Africa/Ndjamena</item>
|
|
||||||
<item>Africa/Niamey</item>
|
|
||||||
<item>Africa/Nouakchott</item>
|
|
||||||
<item>Africa/Ouagadougou</item>
|
|
||||||
<item>Africa/Porto-Novo</item>
|
|
||||||
<item>Africa/Sao_Tome</item>
|
|
||||||
<item>Africa/Tripoli</item>
|
|
||||||
<item>Africa/Tunis</item>
|
|
||||||
<item>Africa/Windhoek</item>
|
|
||||||
<item>America/Anchorage</item>
|
|
||||||
<item>America/Anguilla</item>
|
|
||||||
<item>America/Antigua</item>
|
|
||||||
<item>America/Araguaina</item>
|
|
||||||
<item>America/Argentina/Buenos_Aires</item>
|
|
||||||
<item>America/Aruba</item>
|
|
||||||
<item>America/Asuncion</item>
|
|
||||||
<item>America/Bahia</item>
|
|
||||||
<item>America/Barbados</item>
|
|
||||||
<item>America/Belem</item>
|
|
||||||
<item>America/Belize</item>
|
|
||||||
<item>America/Boa_Vista</item>
|
|
||||||
<item>America/Bogota</item>
|
|
||||||
<item>America/Campo_Grande</item>
|
|
||||||
<item>America/Caracas</item>
|
|
||||||
<item>America/Cayenne</item>
|
|
||||||
<item>America/Cayman</item>
|
|
||||||
<item>America/Chicago</item>
|
|
||||||
<item>America/Costa_Rica</item>
|
|
||||||
<item>America/Cuiaba</item>
|
|
||||||
<item>America/Curacao</item>
|
|
||||||
<item>America/Danmarkshavn</item>
|
|
||||||
<item>America/Dawson_Creek</item>
|
|
||||||
<item>America/Denver</item>
|
|
||||||
<item>America/Dominica</item>
|
|
||||||
<item>America/Edmonton</item>
|
|
||||||
<item>America/El_Salvador</item>
|
|
||||||
<item>America/Fortaleza</item>
|
|
||||||
<item>America/Godthab</item>
|
|
||||||
<item>America/Grand_Turk</item>
|
|
||||||
<item>America/Grenada</item>
|
|
||||||
<item>America/Guadeloupe</item>
|
|
||||||
<item>America/Guatemala</item>
|
|
||||||
<item>America/Guayaquil</item>
|
|
||||||
<item>America/Guyana</item>
|
|
||||||
<item>America/Halifax</item>
|
|
||||||
<item>America/Havana</item>
|
|
||||||
<item>America/Hermosillo</item>
|
|
||||||
<item>America/Iqaluit</item>
|
|
||||||
<item>America/Jamaica</item>
|
|
||||||
<item>America/La_Paz</item>
|
|
||||||
<item>America/Lima</item>
|
|
||||||
<item>America/Los_Angeles</item>
|
|
||||||
<item>America/Maceio</item>
|
|
||||||
<item>America/Managua</item>
|
|
||||||
<item>America/Manaus</item>
|
|
||||||
<item>America/Martinique</item>
|
|
||||||
<item>America/Mazatlan</item>
|
|
||||||
<item>America/Mexico_City</item>
|
|
||||||
<item>America/Miquelon</item>
|
|
||||||
<item>America/Montevideo</item>
|
|
||||||
<item>America/Montreal</item>
|
|
||||||
<item>America/Montserrat</item>
|
|
||||||
<item>America/Nassau</item>
|
|
||||||
<item>America/New_York</item>
|
|
||||||
<item>America/Noronha</item>
|
|
||||||
<item>America/Panama</item>
|
|
||||||
<item>America/Paramaribo</item>
|
|
||||||
<item>America/Phoenix</item>
|
|
||||||
<item>America/Port_of_Spain</item>
|
|
||||||
<item>America/Port-au-Prince</item>
|
|
||||||
<item>America/Porto_Velho</item>
|
|
||||||
<item>America/Puerto_Rico</item>
|
|
||||||
<item>America/Recife</item>
|
|
||||||
<item>America/Regina</item>
|
|
||||||
<item>America/Rio_Branco</item>
|
|
||||||
<item>America/Santiago</item>
|
|
||||||
<item>America/Santo_Domingo</item>
|
|
||||||
<item>America/Sao_Paulo</item>
|
|
||||||
<item>America/Scoresbysund</item>
|
|
||||||
<item>America/St_Johns</item>
|
|
||||||
<item>America/St_Kitts</item>
|
|
||||||
<item>America/St_Lucia</item>
|
|
||||||
<item>America/St_Thomas</item>
|
|
||||||
<item>America/St_Vincent</item>
|
|
||||||
<item>America/Tegucigalpa</item>
|
|
||||||
<item>America/Thule</item>
|
|
||||||
<item>America/Tijuana</item>
|
|
||||||
<item>America/Toronto</item>
|
|
||||||
<item>America/Tortola</item>
|
|
||||||
<item>America/Vancouver</item>
|
|
||||||
<item>America/Whitehorse</item>
|
|
||||||
<item>America/Winnipeg</item>
|
|
||||||
<item>America/Yellowknife</item>
|
|
||||||
<item>Antarctica/Casey</item>
|
|
||||||
<item>Antarctica/Davis</item>
|
|
||||||
<item>Antarctica/DumontDUrville</item>
|
|
||||||
<item>Antarctica/Mawson</item>
|
|
||||||
<item>Antarctica/Palmer</item>
|
|
||||||
<item>Antarctica/Rothera</item>
|
|
||||||
<item>Antarctica/Syowa</item>
|
|
||||||
<item>Antarctica/Vostok</item>
|
|
||||||
<item>Asia/Aden</item>
|
|
||||||
<item>Asia/Almaty</item>
|
|
||||||
<item>Asia/Amman</item>
|
|
||||||
<item>Asia/Aqtau</item>
|
|
||||||
<item>Asia/Aqtobe</item>
|
|
||||||
<item>Asia/Ashgabat</item>
|
|
||||||
<item>Asia/Baghdad</item>
|
|
||||||
<item>Asia/Bahrain</item>
|
|
||||||
<item>Asia/Baku</item>
|
|
||||||
<item>Asia/Bangkok</item>
|
|
||||||
<item>Asia/Beirut</item>
|
|
||||||
<item>Asia/Bishkek</item>
|
|
||||||
<item>Asia/Brunei</item>
|
|
||||||
<item>Asia/Calcutta</item>
|
|
||||||
<item>Asia/Choibalsan</item>
|
|
||||||
<item>Asia/Colombo</item>
|
|
||||||
<item>Asia/Damascus</item>
|
|
||||||
<item>Asia/Dhaka</item>
|
|
||||||
<item>Asia/Dili</item>
|
|
||||||
<item>Asia/Dubai</item>
|
|
||||||
<item>Asia/Dushanbe</item>
|
|
||||||
<item>Asia/Gaza</item>
|
|
||||||
<item>Asia/Hong_Kong</item>
|
|
||||||
<item>Asia/Hovd</item>
|
|
||||||
<item>Asia/Irkutsk</item>
|
|
||||||
<item>Asia/Jakarta</item>
|
|
||||||
<item>Asia/Jayapura</item>
|
|
||||||
<item>Asia/Jerusalem</item>
|
|
||||||
<item>Asia/Kabul</item>
|
|
||||||
<item>Asia/Kamchatka</item>
|
|
||||||
<item>Asia/Karachi</item>
|
|
||||||
<item>Asia/Katmandu</item>
|
|
||||||
<item>Asia/Krasnoyarsk</item>
|
|
||||||
<item>Asia/Kuala_Lumpur</item>
|
|
||||||
<item>Asia/Kuwait</item>
|
|
||||||
<item>Asia/Macau</item>
|
|
||||||
<item>Asia/Magadan</item>
|
|
||||||
<item>Asia/Makassar</item>
|
|
||||||
<item>Asia/Manila</item>
|
|
||||||
<item>Asia/Muscat</item>
|
|
||||||
<item>Asia/Nicosia</item>
|
|
||||||
<item>Asia/Omsk</item>
|
|
||||||
<item>Asia/Phnom_Penh</item>
|
|
||||||
<item>Asia/Pyongyang</item>
|
|
||||||
<item>Asia/Qatar</item>
|
|
||||||
<item>Asia/Rangoon</item>
|
|
||||||
<item>Asia/Riyadh</item>
|
|
||||||
<item>Asia/Saigon</item>
|
|
||||||
<item>Asia/Seoul</item>
|
|
||||||
<item>Asia/Shanghai</item>
|
|
||||||
<item>Asia/Singapore</item>
|
|
||||||
<item>Asia/Taipei</item>
|
|
||||||
<item>Asia/Tashkent</item>
|
|
||||||
<item>Asia/Tbilisi</item>
|
|
||||||
<item>Asia/Tehran</item>
|
|
||||||
<item>Asia/Thimphu</item>
|
|
||||||
<item>Asia/Tokyo</item>
|
|
||||||
<item>Asia/Ulaanbaatar</item>
|
|
||||||
<item>Asia/Vientiane</item>
|
|
||||||
<item>Asia/Vladivostok</item>
|
|
||||||
<item>Asia/Yakutsk</item>
|
|
||||||
<item>Asia/Yekaterinburg</item>
|
|
||||||
<item>Asia/Yerevan</item>
|
|
||||||
<item>Atlantic/Azores</item>
|
|
||||||
<item>Atlantic/Bermuda</item>
|
|
||||||
<item>Atlantic/Canary</item>
|
|
||||||
<item>Atlantic/Cape_Verde</item>
|
|
||||||
<item>Atlantic/Faroe</item>
|
|
||||||
<item>Atlantic/Reykjavik</item>
|
|
||||||
<item>Atlantic/South_Georgia</item>
|
|
||||||
<item>Atlantic/St_Helena</item>
|
|
||||||
<item>Atlantic/Stanley</item>
|
|
||||||
<item>Australia/Adelaide</item>
|
|
||||||
<item>Australia/Brisbane</item>
|
|
||||||
<item>Australia/Darwin</item>
|
|
||||||
<item>Australia/Hobart</item>
|
|
||||||
<item>Australia/Perth</item>
|
|
||||||
<item>Australia/Sydney</item>
|
|
||||||
<item>Etc/GMT</item>
|
|
||||||
<item>Europe/Amsterdam</item>
|
|
||||||
<item>Europe/Andorra</item>
|
|
||||||
<item>Europe/Athens</item>
|
|
||||||
<item>Europe/Belgrade</item>
|
|
||||||
<item>Europe/Berlin</item>
|
|
||||||
<item>Europe/Brussels</item>
|
|
||||||
<item>Europe/Bucharest</item>
|
|
||||||
<item>Europe/Budapest</item>
|
|
||||||
<item>Europe/Chisinau</item>
|
|
||||||
<item>Europe/Copenhagen</item>
|
|
||||||
<item>Europe/Dublin</item>
|
|
||||||
<item>Europe/Gibraltar</item>
|
|
||||||
<item>Europe/Helsinki</item>
|
|
||||||
<item>Europe/Istanbul</item>
|
|
||||||
<item>Europe/Kaliningrad</item>
|
|
||||||
<item>Europe/Kiev</item>
|
|
||||||
<item>Europe/Lisbon</item>
|
|
||||||
<item>Europe/London</item>
|
|
||||||
<item>Europe/Luxembourg</item>
|
|
||||||
<item>Europe/Madrid</item>
|
|
||||||
<item>Europe/Malta</item>
|
|
||||||
<item>Europe/Minsk</item>
|
|
||||||
<item>Europe/Monaco</item>
|
|
||||||
<item>Europe/Moscow</item>
|
|
||||||
<item>Europe/Oslo</item>
|
|
||||||
<item>Europe/Paris</item>
|
|
||||||
<item>Europe/Prague</item>
|
|
||||||
<item>Europe/Riga</item>
|
|
||||||
<item>Europe/Rome</item>
|
|
||||||
<item>Europe/Samara</item>
|
|
||||||
<item>Europe/Sofia</item>
|
|
||||||
<item>Europe/Stockholm</item>
|
|
||||||
<item>Europe/Tallinn</item>
|
|
||||||
<item>Europe/Tirane</item>
|
|
||||||
<item>Europe/Vaduz</item>
|
|
||||||
<item>Europe/Vienna</item>
|
|
||||||
<item>Europe/Vilnius</item>
|
|
||||||
<item>Europe/Warsaw</item>
|
|
||||||
<item>Europe/Zurich</item>
|
|
||||||
<item>Indian/Antananarivo</item>
|
|
||||||
<item>Indian/Chagos</item>
|
|
||||||
<item>Indian/Christmas</item>
|
|
||||||
<item>Indian/Cocos</item>
|
|
||||||
<item>Indian/Comoro</item>
|
|
||||||
<item>Indian/Kerguelen</item>
|
|
||||||
<item>Indian/Mahe</item>
|
|
||||||
<item>Indian/Maldives</item>
|
|
||||||
<item>Indian/Mauritius</item>
|
|
||||||
<item>Indian/Mayotte</item>
|
|
||||||
<item>Indian/Reunion</item>
|
|
||||||
<item>Pacific/Apia</item>
|
|
||||||
<item>Pacific/Auckland</item>
|
|
||||||
<item>Pacific/Easter</item>
|
|
||||||
<item>Pacific/Efate</item>
|
|
||||||
<item>Pacific/Enderbury</item>
|
|
||||||
<item>Pacific/Fakaofo</item>
|
|
||||||
<item>Pacific/Fiji</item>
|
|
||||||
<item>Pacific/Funafuti</item>
|
|
||||||
<item>Pacific/Galapagos</item>
|
|
||||||
<item>Pacific/Gambier</item>
|
|
||||||
<item>Pacific/Guadalcanal</item>
|
|
||||||
<item>Pacific/Guam</item>
|
|
||||||
<item>Pacific/Honolulu</item>
|
|
||||||
<item>Pacific/Johnston</item>
|
|
||||||
<item>Pacific/Kiritimati</item>
|
|
||||||
<item>Pacific/Kosrae</item>
|
|
||||||
<item>Pacific/Kwajalein</item>
|
|
||||||
<item>Pacific/Majuro</item>
|
|
||||||
<item>Pacific/Marquesas</item>
|
|
||||||
<item>Pacific/Midway</item>
|
|
||||||
<item>Pacific/Nauru</item>
|
|
||||||
<item>Pacific/Niue</item>
|
|
||||||
<item>Pacific/Norfolk</item>
|
|
||||||
<item>Pacific/Noumea</item>
|
|
||||||
<item>Pacific/Pago_Pago</item>
|
|
||||||
<item>Pacific/Palau</item>
|
|
||||||
<item>Pacific/Pitcairn</item>
|
|
||||||
<item>Pacific/Ponape</item>
|
|
||||||
<item>Pacific/Port_Moresby</item>
|
|
||||||
<item>Pacific/Rarotonga</item>
|
|
||||||
<item>Pacific/Saipan</item>
|
|
||||||
<item>Pacific/Tahiti</item>
|
|
||||||
<item>Pacific/Tarawa</item>
|
|
||||||
<item>Pacific/Tongatapu</item>
|
|
||||||
<item>Pacific/Truk</item>
|
|
||||||
<item>Pacific/Wake</item>
|
|
||||||
<item>Pacific/Wallis</item>
|
|
||||||
<item>US/Eastern</item>
|
|
||||||
</string-array>
|
|
||||||
</resources>
|
|
@ -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.
|
|
||||||
-->
|
|
||||||
<PreferenceScreen
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<PreferenceCategory
|
|
||||||
android:key="@string/sync_SPr_group_status"
|
|
||||||
android:title="@string/sync_SPr_group_status">
|
|
||||||
|
|
||||||
<com.todoroo.astrid.ui.MultilinePreference
|
|
||||||
android:layout="@layout/status_preference"
|
|
||||||
android:key="@string/sync_SPr_status_key"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:gravity="center"/>
|
|
||||||
|
|
||||||
<com.todoroo.astrid.ui.MultilinePreference
|
|
||||||
android:key="@string/sync_SPr_key_last_error"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:gravity="center"/>
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory
|
|
||||||
android:key="@string/sync_SPr_key_options"
|
|
||||||
android:title="@string/sync_SPr_group_options">
|
|
||||||
|
|
||||||
<com.todoroo.astrid.ui.MultilineListPreference
|
|
||||||
android:key="@string/producteev_PPr_interval_key"
|
|
||||||
android:entries="@array/sync_SPr_interval_entries"
|
|
||||||
android:entryValues="@array/sync_SPr_interval_values"
|
|
||||||
android:title="@string/sync_SPr_interval_title" />
|
|
||||||
|
|
||||||
<com.todoroo.astrid.ui.MultilineListPreference
|
|
||||||
android:key="@string/producteev_PPr_defaultdash_key"
|
|
||||||
android:title="@string/producteev_PPr_defaultdash_title" />
|
|
||||||
|
|
||||||
<com.todoroo.astrid.ui.MultilinePreference
|
|
||||||
android:key="@string/sync_SPr_forget_key"
|
|
||||||
android:title="@string/sync_SPr_forget"
|
|
||||||
android:summary="@string/sync_SPr_forget_description" />
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
|
@ -1,54 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.todoroo.astrid.service;
|
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
|
|
||||||
import android.database.sqlite.SQLiteException;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.service.ExceptionService.ErrorReporter;
|
|
||||||
import com.todoroo.astrid.producteev.api.ProducteevRestClient;
|
|
||||||
|
|
||||||
public class FlurryReporter implements ErrorReporter {
|
|
||||||
|
|
||||||
@SuppressWarnings("nls")
|
|
||||||
public void handleError(String name, Throwable error) {
|
|
||||||
if(error == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
String message = error.toString();
|
|
||||||
|
|
||||||
StringWriter writer = new StringWriter();
|
|
||||||
PrintWriter writerPrinter = new PrintWriter(writer);
|
|
||||||
error.printStackTrace(writerPrinter);
|
|
||||||
writerPrinter.flush();
|
|
||||||
writerPrinter.close();
|
|
||||||
|
|
||||||
String trace = writer.toString();
|
|
||||||
|
|
||||||
// shorten the string
|
|
||||||
trace = trace.substring(message.length());
|
|
||||||
trace = trace.replaceAll(ProducteevRestClient.class.getName(), "!pdvRest");
|
|
||||||
trace = trace.replaceAll("com.todoroo.bente", "!ctb");
|
|
||||||
trace = trace.replaceAll("com.todoroo.astrid", "!cta");
|
|
||||||
trace = trace.replaceAll("com.todoroo.android", "!ctc");
|
|
||||||
trace = trace.replaceAll("com.mdt.rtm", "!rtm");
|
|
||||||
trace = trace.replaceAll("android.database.sqlite", "!sqlite");
|
|
||||||
trace = trace.replaceAll("android", "!A");
|
|
||||||
trace = trace.replaceAll("database", "!db");
|
|
||||||
trace = trace.replaceAll("org.apache.harmony.xml.parsers", "!xmlp");
|
|
||||||
trace = trace.replaceAll(IllegalStateException.class.getName(), "IlStEx");
|
|
||||||
trace = trace.replaceAll(ClassCastException.class.getName(), "ClCaEx");
|
|
||||||
trace = trace.replaceAll(NullPointerException.class.getName(), "NPEx");
|
|
||||||
trace = trace.replaceAll(SQLiteException.class.getName(), "SqLiEx");
|
|
||||||
trace = trace.replaceAll(".java:", ":");
|
|
||||||
|
|
||||||
StatisticsService.reportError(name, message, trace);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue