diff --git a/api/src/com/todoroo/astrid/data/Update.java b/api/src/com/todoroo/astrid/data/Update.java index 7abd2aabd..cdd5e1e23 100644 --- a/api/src/com/todoroo/astrid/data/Update.java +++ b/api/src/com/todoroo/astrid/data/Update.java @@ -86,6 +86,10 @@ public class Update extends RemoteModel { /** List of all properties for this model */ public static final Property[] PROPERTIES = generateProperties(Update.class); + // --- constants + + public static final String PICTURE_LOADING = ""; + // --- defaults /** Default values container */ diff --git a/astrid/.classpath b/astrid/.classpath index e7b64bf23..c7efb1be2 100644 --- a/astrid/.classpath +++ b/astrid/.classpath @@ -22,7 +22,7 @@ - + diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index 174c7de37..2bd71740e 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -321,6 +321,12 @@ + + + @@ -365,7 +371,8 @@ - + diff --git a/astrid/plugin-src/com/timsu/astrid/C2DMReceiver.java b/astrid/plugin-src/com/timsu/astrid/C2DMReceiver.java index e1a971b43..bd034e572 100644 --- a/astrid/plugin-src/com/timsu/astrid/C2DMReceiver.java +++ b/astrid/plugin-src/com/timsu/astrid/C2DMReceiver.java @@ -296,7 +296,7 @@ public class C2DMReceiver extends BroadcastReceiver { } FilterWithCustomIntent filter = (FilterWithCustomIntent)TagFilterExposer.filterFromTagData(context, tagData); - filter.customExtras.putString(TagViewActivity.EXTRA_START_TAB, "updates"); + //filter.customExtras.putString(TagViewActivity.EXTRA_START_TAB, "updates"); if(intent.hasExtra("activity_id")) { try { Update update = new Update(); diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmCameraModule.java b/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmCameraModule.java new file mode 100644 index 000000000..957ae1d17 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmCameraModule.java @@ -0,0 +1,144 @@ +package com.todoroo.astrid.actfm; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.widget.ArrayAdapter; + +import com.timsu.astrid.R; +import com.todoroo.andlib.utility.DateUtilities; + +public class ActFmCameraModule { + + protected static final int REQUEST_CODE_CAMERA = 1; + protected static final int REQUEST_CODE_PICTURE = 2; + + private static File lastTempFile = null; + + public interface ClearImageCallback { + public void clearImage(); + } + + public static void showPictureLauncher(final Activity activity, final ClearImageCallback clearImageOption) { + ArrayList options = new ArrayList(); + options.add(activity.getString(R.string.actfm_picture_camera)); + options.add(activity.getString(R.string.actfm_picture_gallery)); + + if (clearImageOption != null) { + options.add(activity.getString(R.string.actfm_picture_clear)); + } + ArrayAdapter adapter = new ArrayAdapter(activity, + android.R.layout.simple_spinner_dropdown_item, options.toArray(new String[options.size()])); + + DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { + @SuppressWarnings("nls") + @Override + public void onClick(DialogInterface d, int which) { + if(which == 0) { + lastTempFile = getTempFile(activity); + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (lastTempFile != null) { + intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(lastTempFile)); + } + activity.startActivityForResult(intent, REQUEST_CODE_CAMERA); + } else if (which == 1) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + activity.startActivityForResult(Intent.createChooser(intent, + activity.getString(R.string.actfm_TVA_tag_picture)), REQUEST_CODE_PICTURE); + } else { + if (clearImageOption != null) + clearImageOption.clearImage(); + } + } + }; + + // show a menu of available options + new AlertDialog.Builder(activity) + .setAdapter(adapter, listener) + .show().setOwnerActivity(activity); + } + + @SuppressWarnings("nls") + private static File getTempFile(Activity activity) { + try { + String storageState = Environment.getExternalStorageState(); + if(storageState.equals(Environment.MEDIA_MOUNTED)) { + String path = Environment.getExternalStorageDirectory().getName() + File.separatorChar + "Android/data/" + activity.getPackageName() + "/files/"; + File photoFile = File.createTempFile("comment_pic_" + DateUtilities.now(), ".jpg", new File(path)); + return photoFile; + } + } catch (IOException e) { + return null; + } + return null; + } + + public interface CameraResultCallback { + public void handleCameraResult(Bitmap bitmap); + } + + private static Bitmap bitmapFromUri(Activity activity, Uri uri) { + String[] projection = { MediaStore.Images.Media.DATA }; + Cursor cursor = activity.managedQuery(uri, projection, null, null, null); + String path; + + if(cursor != null) { + try { + int column_index = cursor + .getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + cursor.moveToFirst(); + path = cursor.getString(column_index); + } finally { + cursor.close(); + } + } else { + path = uri.getPath(); + } + + return BitmapFactory.decodeFile(path); + } + + public static boolean activityResult(Activity activity, int requestCode, int resultCode, Intent data, + CameraResultCallback cameraResult) { + if(requestCode == ActFmCameraModule.REQUEST_CODE_CAMERA && resultCode == Activity.RESULT_OK) { + Bitmap bitmap; + if (data == null) { // large from camera + if (lastTempFile != null) { + bitmap = bitmapFromUri(activity, Uri.fromFile(lastTempFile)); + lastTempFile.deleteOnExit(); + lastTempFile = null; + } + else + bitmap = null; + } else + bitmap = data.getParcelableExtra("data"); //$NON-NLS-1$ + if(bitmap != null) { + activity.setResult(Activity.RESULT_OK); + cameraResult.handleCameraResult(bitmap); + } + return true; + } else if(requestCode == ActFmCameraModule.REQUEST_CODE_PICTURE && resultCode == Activity.RESULT_OK) { + Uri uri = data.getData(); + Bitmap bitmap = bitmapFromUri(activity, uri); + if(bitmap != null) { + activity.setResult(Activity.RESULT_OK); + cameraResult.handleCameraResult(bitmap); + } + return true; + } + return false; + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/TagSettingsActivity.java b/astrid/plugin-src/com/todoroo/astrid/actfm/TagSettingsActivity.java new file mode 100644 index 000000000..62a7dbbe7 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/TagSettingsActivity.java @@ -0,0 +1,295 @@ +package com.todoroo.astrid.actfm; + +import greendroid.widget.AsyncImageView; + +import java.io.IOException; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.Window; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import com.timsu.astrid.R; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.utility.AndroidUtilities; +import com.todoroo.andlib.utility.DialogUtilities; +import com.todoroo.andlib.utility.Preferences; +import com.todoroo.astrid.actfm.ActFmCameraModule.CameraResultCallback; +import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; +import com.todoroo.astrid.actfm.sync.ActFmSyncService; +import com.todoroo.astrid.data.TagData; +import com.todoroo.astrid.service.StatisticsConstants; +import com.todoroo.astrid.service.StatisticsService; +import com.todoroo.astrid.service.TagDataService; +import com.todoroo.astrid.service.ThemeService; +import com.todoroo.astrid.tags.TagFilterExposer; +import com.todoroo.astrid.tags.TagService; +import com.todoroo.astrid.ui.PeopleContainer; +import com.todoroo.astrid.utility.Flags; +import com.todoroo.astrid.welcome.HelpInfoPopover; + +public class TagSettingsActivity extends Activity { + + protected static final int REQUEST_ACTFM_LOGIN = 3; + + private static final String MEMBERS_IN_PROGRESS = "members"; //$NON-NLS-1$ + + private TagData tagData; + + @Autowired TagDataService tagDataService; + + @Autowired ActFmSyncService actFmSyncService; + + @Autowired ActFmPreferenceService actFmPreferenceService; + + private PeopleContainer tagMembers; + private AsyncImageView picture; + private EditText tagName; + private CheckBox isSilent; + + boolean isNewTag = false; + + public TagSettingsActivity() { + DependencyInjectionService.getInstance().inject(this); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + super.onCreate(savedInstanceState); + ThemeService.applyTheme(this); + setContentView(R.layout.tag_settings_activity); + tagData = getIntent().getParcelableExtra(TagViewActivity.EXTRA_TAG_DATA); + if (tagData == null) { + isNewTag = true; + tagData = new TagData(); + } + setUpSettingsPage(); + + if(savedInstanceState != null && savedInstanceState.containsKey(MEMBERS_IN_PROGRESS)) { + final String members = savedInstanceState.getString(MEMBERS_IN_PROGRESS); + new Thread(new Runnable() { + @Override + public void run() { + AndroidUtilities.sleepDeep(500); + runOnUiThread(new Runnable() { + @Override + public void run() { + updateMembers(members); + } + }); + } + }).start(); + } + showCollaboratorsPopover(); + } + + private void showCollaboratorsPopover() { + if (!Preferences.getBoolean(R.string.p_showed_collaborators_help, false)) { + View members = findViewById(R.id.members_container); + HelpInfoPopover.showPopover(this, members, R.string.help_popover_collaborators); + Preferences.setBoolean(R.string.p_showed_collaborators_help, true); + } + } + + protected void setUpSettingsPage() { + tagMembers = (PeopleContainer) findViewById(R.id.members_container); + tagName = (EditText) findViewById(R.id.tag_name); + picture = (AsyncImageView) findViewById(R.id.picture); + isSilent = (CheckBox) findViewById(R.id.tag_silenced); + + if(actFmPreferenceService.isLoggedIn()) { + picture.setVisibility(View.VISIBLE); + findViewById(R.id.listSettingsMore).setVisibility(View.VISIBLE); + } + + picture.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + ActFmCameraModule.showPictureLauncher(TagSettingsActivity.this, null); + } + }); + + findViewById(R.id.saveMembers).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + saveSettings(); + } + }); + + refreshSettingsPage(); + } + + private void saveSettings() { + setResult(RESULT_OK); + String oldName = tagData.getValue(TagData.NAME); + String newName = tagName.getText().toString(); + + if (TextUtils.isEmpty(newName)) { + return; + } + + boolean nameChanged = !oldName.equals(newName); + TagService service = TagService.getInstance(); + if (nameChanged) { + if (oldName.equalsIgnoreCase(newName)) { // Change the capitalization of a list manually + tagData.setValue(TagData.NAME, newName); + service.renameCaseSensitive(oldName, newName); + tagData.setFlag(TagData.FLAGS, TagData.FLAG_EMERGENT, false); + } else { // Rename list--check for existing name + newName = service.getTagWithCase(newName); + tagName.setText(newName); + if (!newName.equals(oldName)) { + tagData.setValue(TagData.NAME, newName); + service.rename(oldName, newName); + tagData.setFlag(TagData.FLAGS, TagData.FLAG_EMERGENT, false); + } else { + nameChanged = false; + } + } + } + + if(newName.length() > 0 && oldName.length() == 0) { + tagDataService.save(tagData); + //setUpNewTag(newName); + } + + JSONArray members = tagMembers.toJSONArray(); + if(members.length() > 0 && !actFmPreferenceService.isLoggedIn()) { + startActivityForResult(new Intent(this, ActFmLoginActivity.class), + REQUEST_ACTFM_LOGIN); + return; + } + + int oldMemberCount = tagData.getValue(TagData.MEMBER_COUNT); + if (members.length() > oldMemberCount) { + StatisticsService.reportEvent(StatisticsConstants.ACTFM_LIST_SHARED); + } + tagData.setValue(TagData.MEMBERS, members.toString()); + tagData.setValue(TagData.MEMBER_COUNT, members.length()); + tagData.setFlag(TagData.FLAGS, TagData.FLAG_SILENT, isSilent.isChecked()); + + if(actFmPreferenceService.isLoggedIn()) + Flags.set(Flags.TOAST_ON_SAVE); + else + Toast.makeText(this, R.string.tag_list_saved, Toast.LENGTH_LONG).show(); + + tagDataService.save(tagData); + + if (isNewTag) { + Intent intent = new Intent(this, TagViewActivity.class); + intent.putExtra(TagViewActivity.EXTRA_TAG_NAME, newName); + intent.putExtra(TagViewActivity.TOKEN_FILTER, TagFilterExposer.filterFromTagData(this, tagData)); + finish(); + startActivity(intent); + return; + } + + refreshSettingsPage(); + } + + @SuppressWarnings("nls") + private void refreshSettingsPage() { + tagName.setText(tagData.getValue(TagData.NAME)); + if (isNewTag) { + ((TextView)findViewById(R.id.listLabel)).setText(getString(R.string.tag_new_list)); + } else { + ((TextView) findViewById(R.id.listLabel)).setText(this.getString(R.string.tag_settings_title, tagData.getValue(TagData.NAME))); + } + picture.setUrl(tagData.getValue(TagData.PICTURE)); + setTitle(tagData.getValue(TagData.NAME)); + + TextView ownerLabel = (TextView) findViewById(R.id.tag_owner); + try { + if(tagData.getFlag(TagData.FLAGS, TagData.FLAG_EMERGENT)) { + ownerLabel.setText(String.format("<%s>", getString(R.string.actfm_TVA_tag_owner_none))); + } else if(tagData.getValue(TagData.USER_ID) == 0) { + ownerLabel.setText(Preferences.getStringValue(ActFmPreferenceService.PREF_NAME)); + } else { + JSONObject owner = new JSONObject(tagData.getValue(TagData.USER)); + ownerLabel.setText(owner.getString("name")); + } + } catch (JSONException e) { + Log.e("tag-view-activity", "json error refresh owner", e); + ownerLabel.setText(""); + System.err.println(tagData.getValue(TagData.USER)); + } + + String peopleJson = tagData.getValue(TagData.MEMBERS); + updateMembers(peopleJson); + } + + @SuppressWarnings("nls") + private void updateMembers(String peopleJson) { + tagMembers.removeAllViews(); + if(!TextUtils.isEmpty(peopleJson)) { + try { + JSONArray people = new JSONArray(peopleJson); + tagMembers.fromJSONArray(people); + } catch (JSONException e) { + System.err.println(peopleJson); + Log.e("tag-view-activity", "json error refresh members", e); + } + } + + tagMembers.addPerson(""); //$NON-NLS-1$ + } + + private void uploadTagPicture(final Bitmap bitmap) { + new Thread(new Runnable() { + @Override + public void run() { + try { + String url = actFmSyncService.setTagPicture(tagData.getValue(TagData.REMOTE_ID), bitmap); + tagData.setValue(TagData.PICTURE, url); + Flags.set(Flags.ACTFM_SUPPRESS_SYNC); + tagDataService.save(tagData); + } catch (IOException e) { + DialogUtilities.okDialog(TagSettingsActivity.this, e.toString(), null); + } + } + }).start(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if(tagMembers.getChildCount() > 1) { + JSONArray members = tagMembers.toJSONArray(); + outState.putString(MEMBERS_IN_PROGRESS, members.toString()); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + CameraResultCallback callback = new CameraResultCallback() { + @Override + public void handleCameraResult(Bitmap bitmap) { + picture.setImageBitmap(bitmap); + uploadTagPicture(bitmap); + } + }; + if (ActFmCameraModule.activityResult(this, requestCode, resultCode, data, callback)) { + // Handled + } else if(requestCode == REQUEST_ACTFM_LOGIN && resultCode == Activity.RESULT_OK) { + saveSettings(); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/TagUpdatesActivity.java b/astrid/plugin-src/com/todoroo/astrid/actfm/TagUpdatesActivity.java new file mode 100644 index 000000000..7deb356d7 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/TagUpdatesActivity.java @@ -0,0 +1,272 @@ +package com.todoroo.astrid.actfm; + +import android.app.ListActivity; +import android.app.ProgressDialog; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.view.Window; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +import com.timsu.astrid.R; +import com.todoroo.andlib.data.TodorooCursor; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.andlib.utility.DialogUtilities; +import com.todoroo.astrid.actfm.ActFmCameraModule.CameraResultCallback; +import com.todoroo.astrid.actfm.ActFmCameraModule.ClearImageCallback; +import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; +import com.todoroo.astrid.actfm.sync.ActFmSyncService; +import com.todoroo.astrid.adapter.UpdateAdapter; +import com.todoroo.astrid.dao.UpdateDao; +import com.todoroo.astrid.data.TagData; +import com.todoroo.astrid.data.Task; +import com.todoroo.astrid.data.Update; +import com.todoroo.astrid.service.StatisticsConstants; +import com.todoroo.astrid.service.StatisticsService; +import com.todoroo.astrid.service.TagDataService; +import com.todoroo.astrid.service.ThemeService; +import com.todoroo.astrid.utility.Flags; + +public class TagUpdatesActivity extends ListActivity { + + private TagData tagData; + private UpdateAdapter updateAdapter; + private EditText addCommentField; + + private ImageButton pictureButton; + + private Bitmap picture = null; + + private static final int MENU_REFRESH_ID = Menu.FIRST; + + @Autowired ActFmPreferenceService actFmPreferenceService; + @Autowired TagDataService tagDataService; + @Autowired UpdateDao updateDao; + @Autowired ActFmSyncService actFmSyncService; + + public TagUpdatesActivity() { + DependencyInjectionService.getInstance().inject(this); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + super.onCreate(savedInstanceState); + ThemeService.applyTheme(this); + setContentView(R.layout.tag_updates_activity); + tagData = getIntent().getParcelableExtra(TagViewActivity.EXTRA_TAG_DATA); + + OnTouchListener onTouch = new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + v.requestFocusFromTouch(); + return false; + } + }; + + addCommentField = (EditText) findViewById(R.id.commentField); + addCommentField.setOnTouchListener(onTouch); + + setUpUpdateList(); + } + + protected void setUpUpdateList() { + ((TextView) findViewById(R.id.listLabel)).setText(this.getString(R.string.tag_updates_title, tagData.getValue(TagData.NAME))); + final ImageButton commentButton = (ImageButton) findViewById(R.id.commentButton); + addCommentField = (EditText) findViewById(R.id.commentField); + addCommentField.setOnEditorActionListener(new OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView view, int actionId, KeyEvent event) { + if(actionId == EditorInfo.IME_NULL && addCommentField.getText().length() > 0) { + addComment(); + return true; + } + return false; + } + }); + addCommentField.addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable s) { + commentButton.setVisibility((s.length() > 0) ? View.VISIBLE : View.GONE); + } + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // + } + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // + } + }); + commentButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + addComment(); + } + }); + + final ClearImageCallback clearImage = new ClearImageCallback() { + @Override + public void clearImage() { + picture = null; + pictureButton.setImageResource(R.drawable.icn_camera); + } + }; + pictureButton = (ImageButton) findViewById(R.id.picture); + pictureButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (picture != null) + ActFmCameraModule.showPictureLauncher(TagUpdatesActivity.this, clearImage); + else + ActFmCameraModule.showPictureLauncher(TagUpdatesActivity.this, null); + } + }); + + refreshUpdatesList(); + } + + private void refreshUpdatesList() { + + if(!actFmPreferenceService.isLoggedIn() || tagData.getValue(Task.REMOTE_ID) <= 0) + return; + + if(updateAdapter == null) { + TodorooCursor currentCursor = tagDataService.getUpdates(tagData); + startManagingCursor(currentCursor); + + updateAdapter = new UpdateAdapter(this, R.layout.update_adapter_row, + currentCursor, false, null); + ((ListView)findViewById(android.R.id.list)).setAdapter(updateAdapter); + } else { + Cursor cursor = updateAdapter.getCursor(); + cursor.requery(); + startManagingCursor(cursor); + } + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + if(menu.size() > 0) + return true; + + MenuItem item; + if(actFmPreferenceService.isLoggedIn()) { + item = menu.add(Menu.NONE, MENU_REFRESH_ID, Menu.NONE, + R.string.ENA_refresh_comments); + item.setIcon(R.drawable.ic_menu_refresh); + } + + return true; + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + // handle my own menus + switch (item.getItemId()) { + + case MENU_REFRESH_ID: { + + final ProgressDialog progressDialog = DialogUtilities.progressDialog(this, getString(R.string.DLG_please_wait)); + actFmSyncService.fetchUpdatesForTag(tagData, true, new Runnable() { + @Override + public void run() { + runOnUiThread(new Runnable() { + @Override + public void run() { + refreshUpdatesList(); + DialogUtilities.dismissDialog(TagUpdatesActivity.this, progressDialog); + } + }); + } + }); + return true; + } + + default: return false; + } + } + + @SuppressWarnings("nls") + private void addComment() { + if(tagData.getValue(TagData.REMOTE_ID) == 0L) + return; + + Update update = new Update(); + update.setValue(Update.MESSAGE, addCommentField.getText().toString()); + update.setValue(Update.ACTION_CODE, "tag_comment"); + update.setValue(Update.USER_ID, 0L); + update.setValue(Update.TAGS, "," + tagData.getValue(TagData.REMOTE_ID) + ","); + update.setValue(Update.CREATION_DATE, DateUtilities.now()); + if (picture != null) { + update.setValue(Update.PICTURE, Update.PICTURE_LOADING); + } + Flags.set(Flags.ACTFM_SUPPRESS_SYNC); + updateDao.createNew(update); + + final long updateId = update.getId(); + new Thread() { + @Override + public void run() { + actFmSyncService.pushUpdate(updateId, picture); + } + }.start(); + addCommentField.setText(""); //$NON-NLS-1$ + pictureButton.setImageResource(R.drawable.icn_camera); + refreshUpdatesList(); + + StatisticsService.reportEvent(StatisticsConstants.ACTFM_TAG_COMMENT); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + CameraResultCallback callback = new CameraResultCallback() { + @Override + public void handleCameraResult(Bitmap bitmap) { + picture = bitmap; + pictureButton.setImageBitmap(picture); + } + }; + + if (ActFmCameraModule.activityResult(this, requestCode, resultCode, data, callback)) { + //Handled + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + + @Override + protected void onStop() { + StatisticsService.sessionStop(this); + super.onStop(); + } + + @Override + protected void onResume() { + super.onResume(); + StatisticsService.sessionStart(this); + } + + @Override + protected void onPause() { + super.onPause(); + StatisticsService.sessionPause(); + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewActivity.java b/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewActivity.java index c1619743b..067334f15 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewActivity.java @@ -8,25 +8,15 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import android.app.Activity; -import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; import android.os.Bundle; -import android.provider.MediaStore; -import android.text.Editable; import android.text.TextUtils; -import android.text.TextWatcher; +import android.util.DisplayMetrics; import android.util.Log; -import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -34,18 +24,10 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.widget.ArrayAdapter; -import android.widget.CheckBox; import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.ListView; -import android.widget.TabHost; -import android.widget.TabHost.OnTabChangeListener; -import android.widget.TabWidget; +import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; -import android.widget.Toast; import com.timsu.astrid.R; import com.todoroo.andlib.data.TodorooCursor; @@ -55,33 +37,25 @@ import com.todoroo.andlib.service.NotificationManager; import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Query; -import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.actfm.sync.ActFmSyncService; import com.todoroo.astrid.activity.TaskListActivity; -import com.todoroo.astrid.adapter.UpdateAdapter; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.core.SortHelper; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; -import com.todoroo.astrid.dao.UpdateDao; import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.Task; -import com.todoroo.astrid.data.Update; -import com.todoroo.astrid.service.StatisticsConstants; -import com.todoroo.astrid.service.StatisticsService; import com.todoroo.astrid.service.TagDataService; import com.todoroo.astrid.tags.TagFilterExposer; import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagService.Tag; -import com.todoroo.astrid.ui.PeopleContainer; -import com.todoroo.astrid.utility.Flags; import com.todoroo.astrid.welcome.HelpInfoPopover; -public class TagViewActivity extends TaskListActivity implements OnTabChangeListener { +public class TagViewActivity extends TaskListActivity { private static final String LAST_FETCH_KEY = "tag-fetch-"; //$NON-NLS-1$ @@ -89,17 +63,12 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList public static final String EXTRA_TAG_NAME = "tag"; //$NON-NLS-1$ public static final String EXTRA_TAG_REMOTE_ID = "remoteId"; //$NON-NLS-1$ - public static final String EXTRA_START_TAB = "tab"; //$NON-NLS-1$ - public static final String EXTRA_NEW_TAG = "new"; //$NON-NLS-1$ - protected static final int MENU_REFRESH_ID = MENU_SYNC_ID; + public static final String EXTRA_TAG_DATA = "tagData"; //$NON-NLS-1$ - protected static final int REQUEST_CODE_CAMERA = 1; - protected static final int REQUEST_CODE_PICTURE = 2; - protected static final int REQUEST_ACTFM_LOGIN = 3; + protected static final int MENU_REFRESH_ID = MENU_SYNC_ID; - private static final String MEMBERS_IN_PROGRESS = "members"; //$NON-NLS-1$ - private static final String TAB_IN_PROGRESS = "tab"; //$NON-NLS-1$ + private static final int REQUEST_CODE_SETTINGS = 0; private TagData tagData; @@ -109,24 +78,16 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList @Autowired ActFmPreferenceService actFmPreferenceService; - @Autowired UpdateDao updateDao; - - private UpdateAdapter updateAdapter; - private PeopleContainer tagMembers; - private EditText addCommentField; - private AsyncImageView picture; - private EditText tagName; private View taskListView; - private CheckBox isSilent; - - private TabHost tabHost; - private TabWidget tabWidget; - private String[] tabLabels; private boolean dataLoaded = false; - private boolean updatesTabAdded = false; + private long currentId; + private JSONObject currentMember; + private Filter originalFilter; + + //private ImageAdapter galleryAdapter; // --- UI initialization @@ -145,113 +106,69 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList } }; ((EditText) findViewById(R.id.quickAddText)).setOnTouchListener(onTouch); - ((EditText) findViewById(R.id.commentField)).setOnTouchListener(onTouch); - if(getIntent().hasExtra(EXTRA_START_TAB)) - tabHost.setCurrentTabByTag(getIntent().getStringExtra(EXTRA_START_TAB)); + View settingsContainer = findViewById(R.id.settingsContainer); + settingsContainer.setVisibility(View.VISIBLE); + + View settingsButton = findViewById(R.id.settings); + settingsButton.setOnClickListener(settingsListener); + + View membersEdit = findViewById(R.id.members_edit); + membersEdit.setOnClickListener(settingsListener); - if(savedInstanceState != null && savedInstanceState.containsKey(MEMBERS_IN_PROGRESS)) { - final String members = savedInstanceState.getString(MEMBERS_IN_PROGRESS); - new Thread(new Runnable() { + findViewById(R.id.listLabel).setPadding(0, 0, 0, 0); + + if (actFmPreferenceService.isLoggedIn()) { + View activityContainer = findViewById(R.id.activityContainer); + activityContainer.setVisibility(View.VISIBLE); + + ImageView activity = (ImageView) findViewById(R.id.activity); + activity.setOnClickListener(new View.OnClickListener() { @Override - public void run() { - AndroidUtilities.sleepDeep(500); - runOnUiThread(new Runnable() { - @Override - public void run() { - updateMembers(members); - } - }); + public void onClick(View v) { + Intent intent = new Intent(TagViewActivity.this, TagUpdatesActivity.class); + intent.putExtra(EXTRA_TAG_DATA, tagData); + startActivity(intent); } - }).start(); - } - if(savedInstanceState != null && savedInstanceState.containsKey(TAB_IN_PROGRESS)) { - tabHost.setCurrentTab(savedInstanceState.getInt(TAB_IN_PROGRESS)); + }); } - onNewIntent(getIntent()); + originalFilter = filter; + showListSettingsPopover(); } + private final OnClickListener settingsListener = new OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(TagViewActivity.this, TagSettingsActivity.class); + intent.putExtra(EXTRA_TAG_DATA, tagData); + startActivityForResult(intent, REQUEST_CODE_SETTINGS); + } + }; + /* (non-Javadoc) * @see com.todoroo.astrid.activity.TaskListActivity#getListBody(android.view.ViewGroup) */ - @SuppressWarnings("nls") @Override protected View getListBody(ViewGroup root) { - ViewGroup parent = (ViewGroup) getLayoutInflater().inflate(R.layout.task_list_body_tag, root, false); - ViewGroup tabContent = (ViewGroup) parent.findViewById(android.R.id.tabcontent); - - tabLabels = getResources().getStringArray(R.array.TVA_tabs); - tabHost = (TabHost) parent.findViewById(android.R.id.tabhost); - tabWidget = (TabWidget) parent.findViewById(android.R.id.tabs); - tabHost.setup(); + ViewGroup parent = (ViewGroup) getLayoutInflater().inflate(R.layout.task_list_body_tag_v2, root, false); taskListView = super.getListBody(parent); if(actFmPreferenceService.isLoggedIn()) ((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.DLG_loading); - tabContent.addView(taskListView); - - addTab(taskListView.getId(), "tasks", tabLabels[0]); - - tabHost.setOnTabChangedListener(this); + parent.addView(taskListView); return parent; } - private void addTab(int contentId, String id, String label) { - TabHost.TabSpec spec = tabHost.newTabSpec(id); - spec.setContent(contentId); - TextView textIndicator = (TextView) getLayoutInflater().inflate(R.layout.gd_tab_indicator, tabWidget, false); - textIndicator.setText(label); - spec.setIndicator(textIndicator); - tabHost.addTab(spec); - } - - private void showListTabPopover() { + private void showListSettingsPopover() { if (!Preferences.getBoolean(R.string.p_showed_list_settings_help, false)) { - View tabView = tabWidget.getChildTabViewAt(tabWidget.getTabCount() - 1); + View tabView = findViewById(R.id.settings); HelpInfoPopover.showPopover(this, tabView, R.string.help_popover_list_settings); Preferences.setBoolean(R.string.p_showed_list_settings_help, true); } } - private void showCollaboratorsPopover() { - if (!Preferences.getBoolean(R.string.p_showed_collaborators_help, false)) { - View members = findViewById(R.id.members_container); - HelpInfoPopover.showPopover(this, members, R.string.help_popover_collaborators); - Preferences.setBoolean(R.string.p_showed_collaborators_help, true); - } - } - - @SuppressWarnings("nls") - @Override - public void onTabChanged(String tabId) { - if(tabId.equals("tasks")) - findViewById(R.id.taskListFooter).setVisibility(View.VISIBLE); - else - findViewById(R.id.taskListFooter).setVisibility(View.GONE); - - if(tabId.equals("updates")) - findViewById(R.id.updatesFooter).setVisibility(View.VISIBLE); - else - findViewById(R.id.updatesFooter).setVisibility(View.GONE); - - if(tabId.equals("settings")) - findViewById(R.id.membersFooter).setVisibility(View.VISIBLE); - else - findViewById(R.id.membersFooter).setVisibility(View.GONE); - - showPopovers(); - } - - @SuppressWarnings("nls") - private void showPopovers() { - if(tabHost.getCurrentTabTag().equals("settings")) - showCollaboratorsPopover(); - else - showListTabPopover(); - } - /** * Create options menu (displayed when user presses menu key) * @@ -289,97 +206,6 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList return true; } - protected void setUpSettingsPage() { - addTab(R.id.tab_settings, "settings", tabLabels[2]); //$NON-NLS-1$ - tagMembers = (PeopleContainer) findViewById(R.id.members_container); - tagName = (EditText) findViewById(R.id.tag_name); - picture = (AsyncImageView) findViewById(R.id.picture); - isSilent = (CheckBox) findViewById(R.id.tag_silenced); - - if(actFmPreferenceService.isLoggedIn()) { - picture.setVisibility(View.VISIBLE); - findViewById(R.id.listSettingsMore).setVisibility(View.VISIBLE); - } - - picture.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View arg0) { - ArrayAdapter adapter = new ArrayAdapter(TagViewActivity.this, - android.R.layout.simple_spinner_dropdown_item, new String[] { - getString(R.string.actfm_picture_camera), - getString(R.string.actfm_picture_gallery), - }); - - DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { - @SuppressWarnings("nls") - @Override - public void onClick(DialogInterface d, int which) { - if(which == 0) { - Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - startActivityForResult(intent, REQUEST_CODE_CAMERA); - } else { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.setType("image/*"); - startActivityForResult(Intent.createChooser(intent, - getString(R.string.actfm_TVA_tag_picture)), REQUEST_CODE_PICTURE); - } - } - }; - - // show a menu of available options - new AlertDialog.Builder(TagViewActivity.this) - .setAdapter(adapter, listener) - .show().setOwnerActivity(TagViewActivity.this); - } - }); - - findViewById(R.id.saveMembers).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View arg0) { - saveSettings(); - } - }); - - refreshSettingsPage(); - } - - protected void setUpUpdateList() { - final ImageButton quickAddButton = (ImageButton) findViewById(R.id.commentButton); - addCommentField = (EditText) findViewById(R.id.commentField); - addCommentField.setOnEditorActionListener(new OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView view, int actionId, KeyEvent event) { - if(actionId == EditorInfo.IME_NULL && addCommentField.getText().length() > 0) { - addComment(); - return true; - } - return false; - } - }); - addCommentField.addTextChangedListener(new TextWatcher() { - @Override - public void afterTextChanged(Editable s) { - quickAddButton.setVisibility((s.length() > 0) ? View.VISIBLE : View.GONE); - } - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // - } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - // - } - }); - quickAddButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - addComment(); - } - }); - - refreshUpdatesList(); - } - // --- data loading @Override @@ -392,14 +218,10 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList String tag = getIntent().getStringExtra(EXTRA_TAG_NAME); long remoteId = getIntent().getLongExtra(EXTRA_TAG_REMOTE_ID, 0); - boolean newTag = getIntent().getBooleanExtra(EXTRA_NEW_TAG, false); - if(tag == null && remoteId == 0 && !newTag) + if(tag == null && remoteId == 0) return; - if(newTag) - getIntent().putExtra(TOKEN_FILTER, Filter.emptyFilter(getString(R.string.tag_new_list))); - TodorooCursor cursor = tagDataService.query(Query.select(TagData.PROPERTIES).where(Criterion.or(TagData.NAME.eqCaseInsensitive(tag), Criterion.and(TagData.REMOTE_ID.gt(0), TagData.REMOTE_ID.eq(remoteId))))); try { @@ -425,32 +247,9 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList } } - super.onNewIntent(intent); - setUpUpdateList(); - setUpSettingsPage(); - } - - private void refreshUpdatesList() { - if(!actFmPreferenceService.isLoggedIn() || tagData.getValue(Task.REMOTE_ID) <= 0) - return; + setUpMembersGallery(); - if(!updatesTabAdded ) { - updatesTabAdded = true; - addTab(R.id.tab_updates, "updates", tabLabels[1]); //$NON-NLS-1$ - } - - if(updateAdapter == null) { - TodorooCursor currentCursor = tagDataService.getUpdates(tagData); - startManagingCursor(currentCursor); - - updateAdapter = new UpdateAdapter(this, R.layout.update_adapter_row, - currentCursor, false, null); - ((ListView)findViewById(R.id.tab_updates)).setAdapter(updateAdapter); - } else { - Cursor cursor = updateAdapter.getCursor(); - cursor.requery(); - startManagingCursor(cursor); - } + super.onNewIntent(intent); } @Override @@ -465,48 +264,6 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList } } - @SuppressWarnings("nls") - private void refreshSettingsPage() { - tagName.setText(tagData.getValue(TagData.NAME)); - picture.setUrl(tagData.getValue(TagData.PICTURE)); - setTitle(getString(R.string.tag_FEx_name, tagData.getValue(TagData.NAME))); - - TextView ownerLabel = (TextView) findViewById(R.id.tag_owner); - try { - if(tagData.getFlag(TagData.FLAGS, TagData.FLAG_EMERGENT)) { - ownerLabel.setText(String.format("<%s>", getString(R.string.actfm_TVA_tag_owner_none))); - } else if(tagData.getValue(TagData.USER_ID) == 0) { - ownerLabel.setText(Preferences.getStringValue(ActFmPreferenceService.PREF_NAME)); - } else { - JSONObject owner = new JSONObject(tagData.getValue(TagData.USER)); - ownerLabel.setText(owner.getString("name")); - } - } catch (JSONException e) { - Log.e("tag-view-activity", "json error refresh owner", e); - ownerLabel.setText(""); - System.err.println(tagData.getValue(TagData.USER)); - } - - String peopleJson = tagData.getValue(TagData.MEMBERS); - updateMembers(peopleJson); - } - - @SuppressWarnings("nls") - private void updateMembers(String peopleJson) { - tagMembers.removeAllViews(); - if(!TextUtils.isEmpty(peopleJson)) { - try { - JSONArray people = new JSONArray(peopleJson); - tagMembers.fromJSONArray(people); - } catch (JSONException e) { - System.err.println(peopleJson); - Log.e("tag-view-activity", "json error refresh members", e); - } - } - - tagMembers.addPerson(""); //$NON-NLS-1$ - } - // --------------------------------------------------------- refresh data /** refresh the list with latest data from the web */ @@ -533,7 +290,6 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList public void run() { if(noRemoteId && tagData.getValue(TagData.REMOTE_ID) > 0) refreshData(manual, true); - refreshSettingsPage(); } }); @@ -555,6 +311,7 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList if(noRemoteId) return; + setUpMembersGallery(); actFmSyncService.fetchTasksForTag(tagData, manual, new Runnable() { @Override public void run() { @@ -575,12 +332,137 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList runOnUiThread(new Runnable() { @Override public void run() { - refreshUpdatesList(); + //refreshUpdatesList(); DialogUtilities.dismissDialog(TagViewActivity.this, progressDialog); } }); } }); + + } + + private void setUpMembersGallery() { + LinearLayout membersView = (LinearLayout)findViewById(R.id.shared_with); + membersView.setOnClickListener(settingsListener); + try { + String membersString = tagData.getValue(TagData.MEMBERS); + JSONArray members = new JSONArray(membersString); + if (members.length() > 0) { + membersView.setOnClickListener(null); + membersView.removeAllViews(); + for (int i = 0; i < members.length(); i++) { + JSONObject member = members.getJSONObject(i); + addImageForMember(membersView, member); + } + // Handle creator + if(tagData.getValue(TagData.USER_ID) != 0) { + JSONObject owner = new JSONObject(tagData.getValue(TagData.USER)); + addImageForMember(membersView, owner); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + findViewById(R.id.filter_assigned).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + resetAssignedFilter(); + } + }); + } + + @SuppressWarnings("nls") + private void addImageForMember(LinearLayout membersView, JSONObject member) { + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + AsyncImageView image = new AsyncImageView(this); + image.setLayoutParams(new LinearLayout.LayoutParams((int)(50 * displayMetrics.density), + (int)(50 * displayMetrics.density))); + image.setDefaultImageResource(R.drawable.ic_contact_picture_2); + image.setScaleType(ImageView.ScaleType.FIT_XY); + try { + final long id = member.getLong("id"); + if (id == ActFmPreferenceService.userId()) + member = ActFmPreferenceService.thisUser(); + final JSONObject memberToUse = member; + + final String memberName = displayName(memberToUse); + if (memberToUse.has("picture")) { + image.setUrl(memberToUse.getString("picture")); + } + image.setOnClickListener(listenerForImage(memberToUse, id, memberName)); + } catch (JSONException e) { + System.err.println("Unable to create listener"); + e.printStackTrace(); + } + membersView.addView(image); + } + + private OnClickListener listenerForImage(final JSONObject member, final long id, final String displayName) { + return new OnClickListener() { + @Override + public void onClick(View v) { + if (currentId == id) { + // Back to all + resetAssignedFilter(); + } else { + // New filter + currentId = id; + currentMember = member; + Criterion assigned = Criterion.and(TaskCriteria.activeAndVisible(), Task.USER_ID.eq(id)); + filter = TagFilterExposer.filterFromTag(TagViewActivity.this, new Tag(tagData), assigned); + TextView filterByAssigned = (TextView) findViewById(R.id.filter_assigned); + filterByAssigned.setVisibility(View.VISIBLE); + filterByAssigned.setText(getString(R.string.actfm_TVA_filtered_by_assign, displayName)); + setUpTaskList(); + } + } + }; + } + + @Override + protected Intent getOnClickQuickAddIntent(Task t) { + Intent intent = super.getOnClickQuickAddIntent(t); + // Customize extras + return intent; + } + + @Override + protected Intent getOnLongClickQuickAddIntent(Task t) { + Intent intent = super.getOnClickQuickAddIntent(t); + // Customize extras + return intent; + } + + private void resetAssignedFilter() { + currentId = -1; + currentMember = null; + filter = originalFilter; + findViewById(R.id.filter_assigned).setVisibility(View.GONE); + setUpTaskList(); + } + + @SuppressWarnings("nls") + private String displayName(JSONObject user) { + String name = user.optString("name"); + if (!TextUtils.isEmpty(name) && !"null".equals(name)) { + name = name.trim(); + int index = name.indexOf(' '); + if (index > 0) { + return name.substring(0, index); + } else { + return name; + } + } else { + String email = user.optString("email"); + email = email.trim(); + int index = email.indexOf('@'); + if (index > 0) { + return email.substring(0, index); + } else { + return email; + } + } } // --- receivers @@ -597,7 +479,7 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList runOnUiThread(new Runnable() { @Override public void run() { - refreshUpdatesList(); + //refreshUpdatesList(); } }); refreshData(false, true); @@ -622,79 +504,6 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList unregisterReceiver(notifyReceiver); } - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - if(tagMembers.getChildCount() > 1) { - JSONArray members = tagMembers.toJSONArray(); - outState.putString(MEMBERS_IN_PROGRESS, members.toString()); - } - outState.putInt(TAB_IN_PROGRESS, tabHost.getCurrentTab()); - } - - // --- events - - private void saveSettings() { - String oldName = tagData.getValue(TagData.NAME); - String newName = tagName.getText().toString(); - - boolean nameChanged = !oldName.equals(newName); - TagService service = TagService.getInstance(); - if (nameChanged) { - if (oldName.equalsIgnoreCase(newName)) { // Change the capitalization of a list manually - tagData.setValue(TagData.NAME, newName); - service.renameCaseSensitive(oldName, newName); - tagData.setFlag(TagData.FLAGS, TagData.FLAG_EMERGENT, false); - } else { // Rename list--check for existing name - newName = service.getTagWithCase(newName); - tagName.setText(newName); - if (!newName.equals(oldName)) { - tagData.setValue(TagData.NAME, newName); - service.rename(oldName, newName); - tagData.setFlag(TagData.FLAGS, TagData.FLAG_EMERGENT, false); - } else { - nameChanged = false; - } - } - } - - if(newName.length() > 0 && oldName.length() == 0) { - tagDataService.save(tagData); - setUpNewTag(newName); - } - - JSONArray members = tagMembers.toJSONArray(); - if(members.length() > 0 && !actFmPreferenceService.isLoggedIn()) { - startActivityForResult(new Intent(this, ActFmLoginActivity.class), - REQUEST_ACTFM_LOGIN); - return; - } - - int oldMemberCount = tagData.getValue(TagData.MEMBER_COUNT); - if (members.length() > oldMemberCount) { - StatisticsService.reportEvent(StatisticsConstants.ACTFM_LIST_SHARED); - } - tagData.setValue(TagData.MEMBERS, members.toString()); - tagData.setValue(TagData.MEMBER_COUNT, members.length()); - tagData.setFlag(TagData.FLAGS, TagData.FLAG_SILENT, isSilent.isChecked()); - - if(actFmPreferenceService.isLoggedIn()) - Flags.set(Flags.TOAST_ON_SAVE); - else - Toast.makeText(this, R.string.tag_list_saved, Toast.LENGTH_LONG).show(); - - tagDataService.save(tagData); - - if (nameChanged) { - filter = TagFilterExposer.filterFromTagData(this, tagData); - taskAdapter = null; - loadTaskListContent(true); - } - - refreshSettingsPage(); - } - @Override protected Task quickAddTask(String title, boolean selectNewTask) { if(tagData.getValue(TagData.NAME).length() == 0) { @@ -704,89 +513,18 @@ public class TagViewActivity extends TaskListActivity implements OnTabChangeList return super.quickAddTask(title, selectNewTask); } - private void setUpNewTag(String name) { - filter = TagFilterExposer.filterFromTag(this, new Tag(name, 0, 0), - TaskCriteria.activeAndVisible()); - getIntent().putExtra(TOKEN_FILTER, filter); - super.onNewIntent(getIntent()); - } - - @SuppressWarnings("nls") @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if(requestCode == REQUEST_CODE_CAMERA && resultCode == RESULT_OK) { - Bitmap bitmap = data.getParcelableExtra("data"); - if(bitmap != null) { - picture.setImageBitmap(bitmap); - uploadTagPicture(bitmap); - } - } else if(requestCode == REQUEST_CODE_PICTURE && resultCode == RESULT_OK) { - Uri uri = data.getData(); - String[] projection = { MediaStore.Images.Media.DATA }; - Cursor cursor = managedQuery(uri, projection, null, null, null); - String path; - - if(cursor != null) { - try { - int column_index = cursor - .getColumnIndexOrThrow(MediaStore.Images.Media.DATA); - cursor.moveToFirst(); - path = cursor.getString(column_index); - } finally { - cursor.close(); - } - } else { - path = uri.getPath(); - } - - Bitmap bitmap = BitmapFactory.decodeFile(path); - if(bitmap != null) { - picture.setImageBitmap(bitmap); - uploadTagPicture(bitmap); - } - } else if(requestCode == REQUEST_ACTFM_LOGIN && resultCode == Activity.RESULT_OK) { - saveSettings(); + if (requestCode == REQUEST_CODE_SETTINGS && resultCode == RESULT_OK) { + tagData = tagDataService.fetchById(tagData.getId(), TagData.PROPERTIES); + filter = TagFilterExposer.filterFromTagData(this, tagData); + taskAdapter = null; + loadTaskListContent(true); } else { super.onActivityResult(requestCode, resultCode, data); } } - private void uploadTagPicture(final Bitmap bitmap) { - new Thread(new Runnable() { - @Override - public void run() { - try { - String url = actFmSyncService.setTagPicture(tagData.getValue(TagData.REMOTE_ID), bitmap); - tagData.setValue(TagData.PICTURE, url); - Flags.set(Flags.ACTFM_SUPPRESS_SYNC); - tagDataService.save(tagData); - } catch (IOException e) { - DialogUtilities.okDialog(TagViewActivity.this, e.toString(), null); - } - } - }).start(); - } - - @SuppressWarnings("nls") - private void addComment() { - if(tagData.getValue(TagData.REMOTE_ID) == 0L) - return; - - Update update = new Update(); - update.setValue(Update.MESSAGE, addCommentField.getText().toString()); - update.setValue(Update.ACTION_CODE, "tag_comment"); - update.setValue(Update.USER_ID, 0L); - update.setValue(Update.TAGS, "," + tagData.getValue(TagData.REMOTE_ID) + ","); - update.setValue(Update.CREATION_DATE, DateUtilities.now()); - Flags.checkAndClear(Flags.ACTFM_SUPPRESS_SYNC); - updateDao.createNew(update); - - addCommentField.setText(""); //$NON-NLS-1$ - refreshUpdatesList(); - - StatisticsService.reportEvent(StatisticsConstants.ACTFM_TAG_COMMENT); - } - @Override public boolean onMenuItemSelected(int featureId, final MenuItem item) { // handle my own menus diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java index fe48fa7c3..06657d395 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java @@ -103,7 +103,7 @@ public class ActFmPreferenceService extends SyncProviderUtilities { } @SuppressWarnings("nls") - private synchronized static JSONObject thisUser() { + public synchronized static JSONObject thisUser() { if(user == null) { user = new JSONObject(); try { diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java index e257fde85..851954156 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java @@ -152,7 +152,7 @@ public final class ActFmSyncService { new Thread(new Runnable() { @Override public void run() { - pushUpdateOnSave(model, setValues); + pushUpdateOnSave(model, setValues, null); } }).start(); } @@ -227,7 +227,7 @@ public final class ActFmSyncService { /** * Synchronize with server when data changes */ - public void pushUpdateOnSave(Update update, ContentValues values) { + public void pushUpdateOnSave(Update update, ContentValues values, Bitmap imageData) { if(!values.containsKey(Update.MESSAGE.name)) return; @@ -243,13 +243,22 @@ public final class ActFmSyncService { if(update.getValue(Update.TASK) > 0) { params.add("task_id"); params.add(update.getValue(Update.TASK)); } + MultipartEntity picture = null; + if (imageData != null) { + picture = buildPictureData(imageData); + } if(!checkForToken()) return; try { params.add("token"); params.add(token); - JSONObject result = actFmInvoker.invoke("comment_add", params.toArray(new Object[params.size()])); + JSONObject result; + if (picture == null) + result = actFmInvoker.invoke("comment_add", params.toArray(new Object[params.size()])); + else + result = actFmInvoker.post("comment_add", picture, params.toArray(new Object[params.size()])); update.setValue(Update.REMOTE_ID, result.optLong("id")); + update.setValue(Update.PICTURE, result.optString("picture")); updateDao.saveExisting(update); } catch (IOException e) { if (notPermanentError(e)) @@ -396,7 +405,16 @@ public final class ActFmSyncService { */ public void pushUpdate(long updateId) { Update update = updateDao.fetch(updateId, Update.PROPERTIES); - pushUpdateOnSave(update, update.getMergedValues()); + pushUpdateOnSave(update, update.getMergedValues(), null); + } + + /** + * Push complete update with new image to server (used for new comments) + */ + + public void pushUpdate(long updateId, Bitmap imageData) { + Update update = updateDao.fetch(updateId, Update.PROPERTIES); + pushUpdateOnSave(update, update.getMergedValues(), imageData); } /** @@ -747,6 +765,12 @@ public final class ActFmSyncService { if(!checkForToken()) return null; + MultipartEntity data = buildPictureData(bitmap); + JSONObject result = actFmInvoker.post("tag_save", data, "id", tagId, "token", token); + return result.optString("picture"); + } + + private MultipartEntity buildPictureData(Bitmap bitmap) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); if(bitmap.getWidth() > 512 || bitmap.getHeight() > 512) { float scale = Math.min(512f / bitmap.getWidth(), 512f / bitmap.getHeight()); @@ -757,8 +781,7 @@ public final class ActFmSyncService { byte[] bytes = baos.toByteArray(); MultipartEntity data = new MultipartEntity(); data.addPart("picture", new ByteArrayBody(bytes, "image/jpg", "image.jpg")); - JSONObject result = actFmInvoker.post("tag_save", data, "id", tagId, "token", token); - return result.optString("picture"); + return data; } // --- generic invokation @@ -894,7 +917,7 @@ public final class ActFmSyncService { model.setValue(Update.MESSAGE, ""); else model.setValue(Update.MESSAGE, json.getString("message")); - model.setValue(Update.PICTURE, json.getString("picture")); + model.setValue(Update.PICTURE, json.optString("picture", "")); model.setValue(Update.CREATION_DATE, readDate(json, "created_at")); String tagIds = "," + json.optString("tag_ids", "") + ","; model.setValue(Update.TAGS, tagIds); diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/FilterByTagExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/FilterByTagExposer.java index 3ab5b1d83..977fbc49a 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/FilterByTagExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/FilterByTagExposer.java @@ -95,8 +95,7 @@ public class FilterByTagExposer extends BroadcastReceiver { Tag tag = new Tag(tags.get(which), 0, 0); String listTitle = tag.tag; - String title = ContextManager.getString( - R.string.tag_FEx_name, tag.tag); + String title = tag.tag; Criterion criterion = TaskCriteria.activeAndVisible(); QueryTemplate tagTemplate = tag.queryTemplate(criterion); ContentValues contentValues = new ContentValues(); diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java index ee6cf87f3..7c3c20682 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java @@ -63,7 +63,7 @@ public class TagFilterExposer extends BroadcastReceiver { /** Create filter from new tag object */ @SuppressWarnings("nls") public static FilterWithCustomIntent filterFromTag(Context context, Tag tag, Criterion criterion) { - String title = context.getString(R.string.tag_FEx_name, tag.tag); + String title = tag.tag; QueryTemplate tagTemplate = tag.queryTemplate(criterion); ContentValues contentValues = new ContentValues(); contentValues.put(Metadata.KEY.name, TagService.KEY); diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java index 8e61f8999..32d19a880 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java @@ -4,7 +4,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import com.todoroo.astrid.actfm.TagViewActivity; +import com.todoroo.astrid.actfm.TagSettingsActivity; import com.todoroo.astrid.api.Addon; import com.todoroo.astrid.api.AstridApiConstants; @@ -29,9 +29,7 @@ public class TagsPlugin extends BroadcastReceiver { * @param activity */ public static Intent newTagDialog(Context context) { - Intent intent = new Intent(context, TagViewActivity.class); - intent.putExtra(TagViewActivity.EXTRA_NEW_TAG, true); - intent.putExtra(TagViewActivity.EXTRA_START_TAB, "settings"); //$NON-NLS-1$ + Intent intent = new Intent(context, TagSettingsActivity.class); return intent; } diff --git a/astrid/res/drawable-hdpi/header_logo_new.png b/astrid/res/drawable-hdpi/header_logo_new.png new file mode 100644 index 000000000..b9b33f305 Binary files /dev/null and b/astrid/res/drawable-hdpi/header_logo_new.png differ diff --git a/astrid/res/drawable/header_background_pressed.png b/astrid/res/drawable/header_background_pressed.png new file mode 100644 index 000000000..010269fbd Binary files /dev/null and b/astrid/res/drawable/header_background_pressed.png differ diff --git a/astrid/res/drawable/header_button.xml b/astrid/res/drawable/header_button.xml new file mode 100644 index 000000000..4e05b00bb --- /dev/null +++ b/astrid/res/drawable/header_button.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/astrid/res/drawable/header_button_white.xml b/astrid/res/drawable/header_button_white.xml new file mode 100644 index 000000000..59f7bf0d3 --- /dev/null +++ b/astrid/res/drawable/header_button_white.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/astrid/res/drawable/header_logo_new.png b/astrid/res/drawable/header_logo_new.png new file mode 100644 index 000000000..8c89e7c4e Binary files /dev/null and b/astrid/res/drawable/header_logo_new.png differ diff --git a/astrid/res/drawable/header_settings_normal.png b/astrid/res/drawable/header_settings_normal.png new file mode 100644 index 000000000..e9556e2cf Binary files /dev/null and b/astrid/res/drawable/header_settings_normal.png differ diff --git a/astrid/res/drawable/header_settings_pressed.png b/astrid/res/drawable/header_settings_pressed.png new file mode 100644 index 000000000..ce47af1e1 Binary files /dev/null and b/astrid/res/drawable/header_settings_pressed.png differ diff --git a/astrid/res/drawable/icn_arrow.png b/astrid/res/drawable/icn_arrow.png new file mode 100644 index 000000000..b86b7bfd1 Binary files /dev/null and b/astrid/res/drawable/icn_arrow.png differ diff --git a/astrid/res/drawable/icn_arrow_light.png b/astrid/res/drawable/icn_arrow_light.png new file mode 100644 index 000000000..e4c4b4995 Binary files /dev/null and b/astrid/res/drawable/icn_arrow_light.png differ diff --git a/astrid/res/drawable/icn_arrow_over.png b/astrid/res/drawable/icn_arrow_over.png new file mode 100644 index 000000000..db06ba2e0 Binary files /dev/null and b/astrid/res/drawable/icn_arrow_over.png differ diff --git a/astrid/res/drawable/icn_camera.png b/astrid/res/drawable/icn_camera.png new file mode 100644 index 000000000..15134ddfe Binary files /dev/null and b/astrid/res/drawable/icn_camera.png differ diff --git a/astrid/res/drawable/icn_cmmt_off.png b/astrid/res/drawable/icn_cmmt_off.png new file mode 100644 index 000000000..3679334b5 Binary files /dev/null and b/astrid/res/drawable/icn_cmmt_off.png differ diff --git a/astrid/res/drawable/icn_lists.png b/astrid/res/drawable/icn_lists.png new file mode 100644 index 000000000..eeb34ba5a Binary files /dev/null and b/astrid/res/drawable/icn_lists.png differ diff --git a/astrid/res/drawable/icn_settings.png b/astrid/res/drawable/icn_settings.png new file mode 100644 index 000000000..453ca706a Binary files /dev/null and b/astrid/res/drawable/icn_settings.png differ diff --git a/astrid/res/drawable/ios_fabric_480.png b/astrid/res/drawable/ios_fabric_480.png new file mode 100644 index 000000000..baf66d279 Binary files /dev/null and b/astrid/res/drawable/ios_fabric_480.png differ diff --git a/astrid/res/drawable/ios_fabric_480_dark.png b/astrid/res/drawable/ios_fabric_480_dark.png new file mode 100644 index 000000000..7383e3fe6 Binary files /dev/null and b/astrid/res/drawable/ios_fabric_480_dark.png differ diff --git a/astrid/res/drawable/header_tags.xml b/astrid/res/drawable/members_arrow.xml similarity index 90% rename from astrid/res/drawable/header_tags.xml rename to astrid/res/drawable/members_arrow.xml index 9b9e71bcf..d17b8e624 100644 --- a/astrid/res/drawable/header_tags.xml +++ b/astrid/res/drawable/members_arrow.xml @@ -17,10 +17,10 @@ + android:state_focused="false" android:drawable="@drawable/icn_arrow_light" /> + android:drawable="@drawable/icn_arrow_over" /> + android:state_focused="true" android:drawable="@drawable/icn_arrow_over" /> diff --git a/astrid/res/drawable/members_arrow_white.xml b/astrid/res/drawable/members_arrow_white.xml new file mode 100644 index 000000000..254a95923 --- /dev/null +++ b/astrid/res/drawable/members_arrow_white.xml @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/astrid/res/layout/filter_list_activity.xml b/astrid/res/layout/filter_list_activity.xml index 98bd850ba..1bff57e37 100644 --- a/astrid/res/layout/filter_list_activity.xml +++ b/astrid/res/layout/filter_list_activity.xml @@ -29,7 +29,7 @@ android:layout_weight="100" android:gravity="center" android:paddingRight="50dip" - android:src="@drawable/header_logo" + android:src="@drawable/header_logo_new" android:scaleType="center"/> diff --git a/astrid/res/layout/tag_settings_activity.xml b/astrid/res/layout/tag_settings_activity.xml new file mode 100644 index 000000000..638eace6e --- /dev/null +++ b/astrid/res/layout/tag_settings_activity.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/astrid/res/layout/tag_updates_activity.xml b/astrid/res/layout/tag_updates_activity.xml new file mode 100644 index 000000000..d31e173f3 --- /dev/null +++ b/astrid/res/layout/tag_updates_activity.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/astrid/res/layout/task_list_activity.xml b/astrid/res/layout/task_list_activity.xml index adb17188b..420236637 100644 --- a/astrid/res/layout/task_list_activity.xml +++ b/astrid/res/layout/task_list_activity.xml @@ -7,44 +7,98 @@ style="@style/Content" android:orientation="vertical"> + - + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/astrid/res/layout/task_list_body_tag_v2.xml b/astrid/res/layout/task_list_body_tag_v2.xml new file mode 100644 index 000000000..99140c619 --- /dev/null +++ b/astrid/res/layout/task_list_body_tag_v2.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/astrid/res/layout/update_adapter_row.xml b/astrid/res/layout/update_adapter_row.xml index 202731a93..cc0953e4d 100644 --- a/astrid/res/layout/update_adapter_row.xml +++ b/astrid/res/layout/update_adapter_row.xml @@ -1,57 +1,77 @@ - - + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + - - - - - - - - - - + - + diff --git a/astrid/res/values/attrs.xml b/astrid/res/values/attrs.xml index b9890b0ed..1add6c5d6 100644 --- a/astrid/res/values/attrs.xml +++ b/astrid/res/values/attrs.xml @@ -4,6 +4,7 @@ + @@ -12,9 +13,12 @@ + + + \ No newline at end of file diff --git a/astrid/res/values/strings-actfm.xml b/astrid/res/values/strings-actfm.xml index facb009c9..fa581a001 100644 --- a/astrid/res/values/strings-actfm.xml +++ b/astrid/res/values/strings-actfm.xml @@ -27,6 +27,9 @@ Pick from Gallery + + + Clear Picture Refresh Lists @@ -48,6 +51,12 @@ List Settings + + + %s\'s tasks. Tap for all. + + + Private: tap to share this list Refresh diff --git a/astrid/res/values/strings-core.xml b/astrid/res/values/strings-core.xml index 4ad3f8e78..cc3aa2e67 100644 --- a/astrid/res/values/strings-core.xml +++ b/astrid/res/values/strings-core.xml @@ -59,6 +59,10 @@ Dismiss + OK + + Cancel + @@ -118,6 +122,9 @@ Add to this list... + + Tap to assign %s a task + Notifications are muted. You won\'t be able to hear Astrid! diff --git a/astrid/res/values/strings-tags.xml b/astrid/res/values/strings-tags.xml index 871365d31..a9188275c 100644 --- a/astrid/res/values/strings-tags.xml +++ b/astrid/res/values/strings-tags.xml @@ -85,5 +85,11 @@ We\'ve noticed that you have some lists that have the same name with different capitalizations. We think you may have intended them to be the same list, so we\'ve combined the duplicates. Don\'t worry though: the original lists are simply renamed with numbers (e.g. Shopping_1, Shopping_2). If you don\'t want this, you can simply delete the new combined list! + + + Settings: %s + + + Activity: %s diff --git a/astrid/res/values/styles.xml b/astrid/res/values/styles.xml index 1dfc16e5e..18f598d92 100644 --- a/astrid/res/values/styles.xml +++ b/astrid/res/values/styles.xml @@ -8,6 +8,7 @@ @null @drawable/background_gradient @drawable/header_background + @drawable/header_button #ffffffff #ff777777 #ff7777aa @@ -15,6 +16,8 @@ #ff777777 #ffcccccc @drawable/edit_titlebar + @drawable/ios_fabric_480_dark + @drawable/members_arrow