/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.tags;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.EditText;
import android.widget.Toast;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.TagViewFragment;
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.FilterListItem;
import com.todoroo.astrid.api.FilterWithCustomIntent;
import com.todoroo.astrid.api.FilterWithUpdate;
import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TagMetadataDao;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.service.ThemeService;
import com.todoroo.astrid.tags.TagService.Tag;
/**
* Exposes filters based on tags
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TagFilterExposer extends BroadcastReceiver implements AstridFilterExposer {
private static final String TAG = "tag"; //$NON-NLS-1$
@Autowired protected TagDataService tagDataService;
@Autowired GtasksPreferenceService gtasksPreferenceService;
protected boolean addUntaggedFilter = true;
/** Create filter from new tag object */
@SuppressWarnings("nls")
public static FilterWithCustomIntent filterFromTag(Context context, Tag tag, Criterion criterion) {
String title = tag.tag;
if (TextUtils.isEmpty(title))
return null;
QueryTemplate tagTemplate = tag.queryTemplate(criterion);
ContentValues contentValues = new ContentValues();
contentValues.put(Metadata.KEY.name, TaskToTagMetadata.KEY);
contentValues.put(TaskToTagMetadata.TAG_NAME.name, tag.tag);
contentValues.put(TaskToTagMetadata.TAG_UUID.name, tag.uuid);
FilterWithUpdate filter = new FilterWithUpdate(tag.tag,
title, tagTemplate,
contentValues);
if(!RemoteModel.NO_UUID.equals(tag.uuid)) {
filter.listingTitle += " (" + tag.count + ")";
}
int deleteIntentLabel;
if (tag.memberCount > 0 && !Task.USER_ID_SELF.equals(tag.userId))
deleteIntentLabel = R.string.tag_cm_leave;
else
deleteIntentLabel = R.string.tag_cm_delete;
filter.contextMenuLabels = new String[] {
context.getString(R.string.tag_cm_rename),
context.getString(deleteIntentLabel)
};
filter.contextMenuIntents = new Intent[] {
newTagIntent(context, RenameTagActivity.class, tag, tag.uuid),
newTagIntent(context, DeleteTagActivity.class, tag, tag.uuid)
};
filter.customTaskList = new ComponentName(ContextManager.getContext(), TagViewFragment.class);
if(tag.image != null)
filter.imageUrl = tag.image;
Bundle extras = new Bundle();
extras.putString(TagViewFragment.EXTRA_TAG_NAME, tag.tag);
extras.putString(TagViewFragment.EXTRA_TAG_UUID, tag.uuid.toString());
filter.customExtras = extras;
return filter;
}
/** Create a filter from tag data object */
public static FilterWithCustomIntent filterFromTagData(Context context, TagData tagData) {
Tag tag = new Tag(tagData);
return filterFromTag(context, tag, TaskCriteria.activeAndVisible());
}
private static Intent newTagIntent(Context context, Class<? extends Activity> activity, Tag tag, String uuid) {
Intent ret = new Intent(context, activity);
ret.putExtra(TAG, tag.tag);
ret.putExtra(TagViewFragment.EXTRA_TAG_UUID, uuid);
return ret;
}
@Override
public void onReceive(Context context, Intent intent) {
FilterListItem[] listAsArray = prepareFilters(context);
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, listAsArray);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, TagsPlugin.IDENTIFIER);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
protected FilterListItem[] prepareFilters(Context context) {
DependencyInjectionService.getInstance().inject(this);
ContextManager.setContext(context);
ArrayList<FilterListItem> list = new ArrayList<FilterListItem>();
addTags(list);
// transmit filter list
FilterListItem[] listAsArray = list.toArray(new FilterListItem[list.size()]);
return listAsArray;
}
private void addTags(ArrayList<FilterListItem> list) {
List<Tag> tagList = getTagList();
list.add(filterFromTags(tagList.toArray(new Tag[tagList.size()]),
R.string.tag_FEx_header));
}
protected List<Tag> getTagList() {
return TagService.getInstance().getTagList();
}
private FilterCategory filterFromTags(Tag[] tags, int name) {
boolean shouldAddUntagged = addUntaggedFilter &&
Preferences.getBoolean(R.string.p_show_not_in_list_filter, true);
ArrayList<Filter> filters = new ArrayList<Filter>(tags.length);
Context context = ContextManager.getContext();
Resources r = context.getResources();
int themeFlags = ThemeService.getFilterThemeFlags();
// --- untagged
if (shouldAddUntagged) {
Filter untagged = new Filter(r.getString(R.string.tag_FEx_untagged),
r.getString(R.string.tag_FEx_untagged),
TagService.getInstance().untaggedTemplate(),
null);
untagged.listingIcon = ((BitmapDrawable)r.getDrawable(
ThemeService.getDrawable(R.drawable.gl_lists, themeFlags))).getBitmap();
filters.add(untagged);
}
for(int i = 0; i < tags.length; i++) {
Filter f = constructFilter(context, tags[i]);
if (f != null)
filters.add(f);
}
FilterCategory filter = new FilterCategory(context.getString(name), filters.toArray(new Filter[filters.size()]));
return filter;
}
protected Filter constructFilter(Context context, Tag tag) {
return filterFromTag(context, tag, TaskCriteria.activeAndVisible());
}
// --- tag manipulation activities
public abstract static class TagActivity extends Activity {
protected String tag;
protected String uuid;
@Autowired public TagService tagService;
@Autowired public TagDataDao tagDataDao;
@Autowired public TagMetadataDao tagMetadataDao;
static {
AstridDependencyInjector.initialize();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tag = getIntent().getStringExtra(TAG);
uuid = getIntent().getStringExtra(TagViewFragment.EXTRA_TAG_UUID);
if(tag == null || RemoteModel.isUuidEmpty(uuid)) {
finish();
return;
}
DependencyInjectionService.getInstance().inject(this);
TagData tagData = tagDataDao.fetch(uuid, TagData.MEMBER_COUNT, TagData.USER_ID);
showDialog(tagData);
}
protected DialogInterface.OnClickListener getOkListener() {
return new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
Intent result = ok();
if (result != null) {
setResult(RESULT_OK, result);
} else {
toastNoChanges();
setResult(RESULT_CANCELED);
}
} finally {
finish();
}
}
};
}
protected DialogInterface.OnClickListener getCancelListener() {
return new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
toastNoChanges();
} finally {
setResult(RESULT_CANCELED);
finish();
}
}
};
}
private void toastNoChanges() {
Toast.makeText(this, R.string.TEA_no_tags_modified,
Toast.LENGTH_SHORT).show();
}
protected abstract void showDialog(TagData tagData);
protected abstract Intent ok();
}
public static class DeleteTagActivity extends TagActivity {
@Override
protected void showDialog(TagData tagData) {
int string;
if (tagData != null && (tagMetadataDao.tagHasMembers(uuid) || tagData.getValue(TagData.MEMBER_COUNT) > 0)) {
if (Task.USER_ID_SELF.equals(tagData.getValue(TagData.USER_ID)))
string = R.string.actfm_tag_operation_owner_delete;
else
string = R.string.DLG_leave_this_shared_tag_question;
}
else
string = R.string.DLG_delete_this_tag_question;
DialogUtilities.okCancelDialog(this, getString(string, tag), getOkListener(), getCancelListener());
}
@Override
protected Intent ok() {
return tagService.deleteOrLeaveTag(this, tag, uuid);
}
}
public static class RenameTagActivity extends TagActivity {
private EditText editor;
@Override
protected void showDialog(TagData tagData) {
editor = new EditText(this);
DialogUtilities.viewDialog(this, getString(R.string.DLG_rename_this_tag_header, tag), editor, getOkListener(), getCancelListener());
}
@Override
protected Intent ok() {
if(editor == null)
return null;
String text = editor.getText().toString();
if (text == null || text.length() == 0) {
return null;
} else {
int renamed = tagService.rename(uuid, text);
Toast.makeText(this, getString(R.string.TEA_tags_renamed, tag, text, renamed),
Toast.LENGTH_SHORT).show();
if (renamed > 0) {
Intent intent = new Intent(AstridApiConstants.BROADCAST_EVENT_TAG_RENAMED);
intent.putExtra(TagViewFragment.EXTRA_TAG_UUID, uuid);
ContextManager.getContext().sendBroadcast(intent);
return intent;
}
return null;
}
}
}
@Override
public FilterListItem[] getFilters() {
if (ContextManager.getContext() == null)
return null;
return prepareFilters(ContextManager.getContext());
}
}