/** * Copyright (c) 2012 Todoroo Inc * * See the file "LICENSE" for the full license governing this code. */ package com.todoroo.astrid.activity; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; import com.actionbarsherlock.app.SherlockListFragment; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuItem; 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.AndroidUtilities; import com.todoroo.astrid.actfm.TagViewFragment; import com.todoroo.astrid.adapter.FilterAdapter; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterWithUpdate; import com.todoroo.astrid.service.StatisticsService; import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagsPlugin; import com.todoroo.astrid.utility.AstridPreferences; import com.todoroo.astrid.welcome.HelpInfoPopover; /** * Activity that displays a user's task lists and allows users * to filter their task list. * * @author Tim Su <tim@todoroo.com> * */ public class FilterListFragment extends SherlockListFragment { public static final String TAG_FILTERLIST_FRAGMENT = "filterlist_fragment"; //$NON-NLS-1$ public static final String TOKEN_LAST_SELECTED = "lastSelected"; //$NON-NLS-1$ // -- extra codes //public static final String SHOW_BACK_BUTTON = "show_back"; //$NON-NLS-1$ // --- menu codes private static final int CONTEXT_MENU_SHORTCUT = R.string.FLA_context_shortcut; private static final int CONTEXT_MENU_INTENT = Menu.FIRST + 4; public static final int REQUEST_CUSTOM_INTENT = 10; static final int REQUEST_VIEW_TASKS = 2; public static final int REQUEST_NEW_BUTTON = 3; public static final int REQUEST_NEW_LIST = 4; // --- instance variables @Autowired ExceptionService exceptionService; protected FilterAdapter adapter = null; private final RefreshReceiver refreshReceiver = new RefreshReceiver(); private OnFilterItemClickedListener mListener; private View newListButton; /* ====================================================================== * ======================================================= initialization * ====================================================================== */ /** Container Activity must implement this interface and we ensure * that it does during the onAttach() callback */ public interface OnFilterItemClickedListener { public boolean onFilterItemClicked(FilterListItem item); } public FilterListFragment() { DependencyInjectionService.getInstance().inject(this); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // Check that the container activity has implemented the callback interface try { mListener = (OnFilterItemClickedListener) activity; adapter = instantiateAdapter(); } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnFilterItemClickedListener"); //$NON-NLS-1$ } } protected FilterAdapter instantiateAdapter() { return new FilterAdapter(getActivity(), null, R.layout.filter_adapter_row, false); } /* (non-Javadoc) * @see com.todoroo.astrid.fragment.ExpandableListFragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Activity activity = getActivity(); int layout = getLayout(activity); ViewGroup parent = (ViewGroup) activity.getLayoutInflater().inflate(layout, container, false); return parent; } protected int getLayout(Activity activity) { if (AstridPreferences.useTabletLayout(activity)) { adapter.filterStyle = R.style.TextAppearance_FLA_Filter_Tablet; return R.layout.filter_list_activity_3pane; } else return R.layout.filter_list_activity; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // We have a menu item to show in action bar. setHasOptionsMenu(true); getActivity().setDefaultKeyMode(Activity.DEFAULT_KEYS_SEARCH_LOCAL); //ImageView backButton = (ImageView) getView().findViewById(R.id.back); newListButton = getView().findViewById(R.id.new_list_button); if (newListButton != null) newListButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = TagsPlugin.newTagDialog(getActivity()); getActivity().startActivityForResult(intent, REQUEST_NEW_LIST); if (!AstridPreferences.useTabletLayout(getActivity())) AndroidUtilities.callOverridePendingTransition(getActivity(), R.anim.slide_left_in, R.anim.slide_left_out); } }); setUpList(); } /* ====================================================================== * ============================================================ lifecycle * ====================================================================== */ @Override public void onStart() { super.onStart(); } @Override public void onStop() { StatisticsService.sessionStop(getActivity()); super.onStop(); } @Override public void onResume() { super.onResume(); StatisticsService.sessionStart(getActivity()); if(adapter != null) adapter.registerRecevier(); // also load sync actions Activity activity = getActivity(); Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_SYNC_ACTIONS); activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); if (activity instanceof TaskListActivity) { ((TaskListActivity) activity).setupPopoverWithFilterList(this); } activity.registerReceiver(refreshReceiver, new IntentFilter(AstridApiConstants.BROADCAST_EVENT_REFRESH)); } @Override public void onPause() { StatisticsService.sessionPause(); super.onPause(); if(adapter != null) adapter.unregisterRecevier(); try { getActivity().unregisterReceiver(refreshReceiver); } catch (IllegalArgumentException e) { // Might not have fully initialized } } /* ====================================================================== * ===================================================== populating lists * ====================================================================== */ /** Sets up the coach list adapter */ protected void setUpList() { adapter.setListView(getListView()); setListAdapter(adapter); // Can't do context menus when list is in popup menu for some reason--workaround if (((AstridActivity) getActivity()).fragmentLayout == AstridActivity.LAYOUT_SINGLE) { getListView().setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { // Do stuff final Filter filter = adapter.getItem(position); final String[] labels = filter.contextMenuLabels; final Intent[] intents = filter.contextMenuIntents; ArrayAdapter<String> intentAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1); intentAdapter.add(getString(R.string.FLA_context_shortcut)); for (String l : labels) { intentAdapter.add(l); } AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(filter.title); builder.setAdapter(intentAdapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (which == 0) { showCreateShortcutDialog(getActivity(), ShortcutActivity.createIntent(filter), filter); } else { getActivity().startActivityForResult(intents[which - 1], REQUEST_CUSTOM_INTENT); } } }); Dialog d = builder.create(); d.setOwnerActivity(getActivity()); d.show(); return true; } }); } else { registerForContextMenu(getListView()); } } /* ====================================================================== * ============================================================== actions * ====================================================================== */ @Override public void onListItemClick(ListView parent, View v, int position, long id) { Filter item = adapter.getItem(position); mListener.onFilterItemClicked(item); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; Filter item = adapter.getItem(info.position); android.view.MenuItem menuItem; if(item instanceof Filter) { Filter filter = (Filter) item; menuItem = menu.add(0, CONTEXT_MENU_SHORTCUT, 0, R.string.FLA_context_shortcut); menuItem.setIntent(ShortcutActivity.createIntent(filter)); } for(int i = 0; i < item.contextMenuLabels.length; i++) { if(item.contextMenuIntents.length <= i) break; menuItem = menu.add(0, CONTEXT_MENU_INTENT, 0, item.contextMenuLabels[i]); menuItem.setIntent(item.contextMenuIntents[i]); } if(menu.size() > 0) menu.setHeaderTitle(item.listingTitle); } /** * Creates a shortcut on the user's home screen * * @param shortcutIntent * @param label */ private static void createShortcut(Activity activity, Filter filter, Intent shortcutIntent, String label) { if(label.length() == 0) return; String defaultImageId = filter.listingTitle; if (filter instanceof FilterWithUpdate) { FilterWithUpdate fwu = (FilterWithUpdate) filter; Bundle customExtras = fwu.customExtras; if (customExtras != null && customExtras.containsKey(TagViewFragment.EXTRA_TAG_UUID)) defaultImageId = customExtras.getString(TagViewFragment.EXTRA_TAG_UUID); } Bitmap bitmap = superImposeListIcon(activity, filter.listingIcon, defaultImageId); Intent createShortcutIntent = new Intent(); createShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); createShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, label); createShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap); createShortcutIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); //$NON-NLS-1$ activity.sendBroadcast(createShortcutIntent); Toast.makeText(activity, activity.getString(R.string.FLA_toast_onCreateShortcut, label), Toast.LENGTH_LONG).show(); } public static Bitmap superImposeListIcon(Activity activity, Bitmap listingIcon, String uuid) { Bitmap emblem = listingIcon; if(emblem == null) emblem = ((BitmapDrawable) activity.getResources().getDrawable( TagService.getDefaultImageIDForTag(uuid))).getBitmap(); // create icon by superimposing astrid w/ icon DisplayMetrics metrics = new DisplayMetrics(); activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); Bitmap bitmap = ((BitmapDrawable) activity.getResources().getDrawable( R.drawable.icon_blank)).getBitmap(); bitmap = bitmap.copy(bitmap.getConfig(), true); Canvas canvas = new Canvas(bitmap); int dimension = 22; canvas.drawBitmap(emblem, new Rect(0, 0, emblem.getWidth(), emblem.getHeight()), new Rect(bitmap.getWidth() - dimension, bitmap.getHeight() - dimension, bitmap.getWidth(), bitmap.getHeight()), null); return bitmap; } @Override public boolean onContextItemSelected(MenuItem item) { // called when context menu appears return onOptionsItemSelected(item); } @Override public boolean onOptionsItemSelected(final MenuItem item) { // handle my own menus switch (item.getItemId()) { case CONTEXT_MENU_SHORTCUT: { AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo(); final Intent shortcutIntent = item.getIntent(); FilterListItem filter = ((FilterAdapter.ViewHolder)info.targetView.getTag()).item; if(filter instanceof Filter) showCreateShortcutDialog(getActivity(), shortcutIntent, (Filter)filter); return true; } case CONTEXT_MENU_INTENT: { Intent intent = item.getIntent(); getActivity().startActivityForResult(intent, REQUEST_CUSTOM_INTENT); return true; } default: { TaskListFragment tasklist = (TaskListFragment) getActivity().getSupportFragmentManager().findFragmentByTag(TaskListFragment.TAG_TASKLIST_FRAGMENT); if (tasklist != null && tasklist.isInLayout()) return tasklist.onOptionsItemSelected(item); } } return false; } public static void showCreateShortcutDialog(final Activity activity, final Intent shortcutIntent, final Filter filter) { FrameLayout frameLayout = new FrameLayout(activity); frameLayout.setPadding(10, 0, 10, 0); final EditText editText = new EditText(activity); if(filter.listingTitle == null) filter.listingTitle = ""; //$NON-NLS-1$ editText.setText(filter.listingTitle. replaceAll("\\(\\d+\\)$", "").trim()); //$NON-NLS-1$ //$NON-NLS-2$ frameLayout.addView(editText, new FrameLayout.LayoutParams( FrameLayout.LayoutParams.FILL_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT)); final Runnable createShortcut = new Runnable() { @Override public void run() { String label = editText.getText().toString(); createShortcut(activity, filter, shortcutIntent, label); } }; editText.setOnEditorActionListener(new OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if(actionId == EditorInfo.IME_NULL) { createShortcut.run(); return true; } return false; } }); new AlertDialog.Builder(activity) .setTitle(R.string.FLA_shortcut_dialog_title) .setMessage(R.string.FLA_shortcut_dialog) .setView(frameLayout) .setIcon(android.R.drawable.ic_dialog_info) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { createShortcut.run(); } }) .setNegativeButton(android.R.string.cancel, null) .show().setOwnerActivity(activity); } public void clear() { adapter.clear(); } public void refresh() { adapter.clear(); adapter.getLists(); } public void showAddListPopover() { View anchor = getView().findViewById(R.id.new_list_button); HelpInfoPopover.showPopover(getActivity(), anchor, R.string.help_popover_add_lists, null); } /** * Receiver which receives refresh intents * * @author Tim Su <tim@todoroo.com> * */ protected class RefreshReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if(intent == null || !AstridApiConstants.BROADCAST_EVENT_REFRESH.equals(intent.getAction())) return; Activity activity = getActivity(); if (activity != null) { activity.runOnUiThread(new Runnable() { @Override public void run() { refresh(); } }); } } } }