Java tutorial
/** * @author Todo.txt contributors <todotxt@yahoogroups.com> * @author Mark Janssen * @license http://www.gnu.org/licenses/gpl.html * @copyright 2009-2012 Todo.txt contributors (http://todotxt.com) * @copyright 2013- Mark Janssen */ package nl.mpcjanssen.simpletask; import nl.mpcjanssen.simpletask.adapters.DrawerAdapter; import nl.mpcjanssen.simpletask.remote.RemoteClient; import nl.mpcjanssen.simpletask.sort.MultiComparator; import nl.mpcjanssen.simpletask.task.Priority; import nl.mpcjanssen.simpletask.task.Task; import nl.mpcjanssen.simpletask.task.TaskBag; import nl.mpcjanssen.simpletask.util.Strings; import nl.mpcjanssen.simpletask.util.Util; import nl.mpcjanssen.simpletask.util.Util.OnMultiChoiceDialogListener; import android.app.AlertDialog; import android.app.DatePickerDialog; import android.app.Dialog; import android.app.ListActivity; import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.content.res.Resources; import android.database.DataSetObserver; import android.graphics.Color; import android.graphics.Paint; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.provider.CalendarContract; import android.provider.CalendarContract.Events; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.widget.DrawerLayout; import android.text.SpannableString; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.view.ActionMode; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.CheckBox; import android.widget.DatePicker; import android.widget.EditText; import android.widget.Filter; import android.widget.Filterable; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.PopupMenu; import android.widget.SearchView; import android.widget.TextView; import org.joda.time.DateTime; import org.joda.time.format.ISODateTimeFormat; import java.io.File; import java.net.URL; import java.util.*; import static java.lang.Thread.sleep; public class Simpletask extends ListActivity implements AdapterView.OnItemLongClickListener { final static String TAG = Simpletask.class.getSimpleName(); private final static int REQUEST_FILTER = 1; private final static int REQUEST_PREFERENCES = 2; private final static int DRAWER_CONTEXT = 1; private final static int DRAWER_PROJECT = 2; private static final int SYNC_CHOICE_DIALOG = 100; private static final int SYNC_CONFLICT_DIALOG = 101; Menu options_menu; TodoApplication m_app; ActiveFilter mFilter; TaskAdapter m_adapter; private BroadcastReceiver m_broadcastReceiver; private ActionMode actionMode; // Drawer vars private ListView m_leftDrawerList; private ListView m_rightDrawerList; private DrawerLayout m_drawerLayout; private ActionBarDrawerToggle m_drawerToggle; private void showHelp() { Intent i = new Intent(this, HelpScreen.class); startActivity(i); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. if (m_drawerToggle != null) { m_drawerToggle.syncState(); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (m_drawerToggle != null) { m_drawerToggle.onConfigurationChanged(newConfig); } } @Override public boolean onOptionsItemSelected(MenuItem item) { // Pass the event to ActionBarDrawerToggle, if it returns // true, then it has handled the app icon touch event if (m_drawerToggle != null && m_drawerToggle.onOptionsItemSelected(item)) { return true; } // Handle your other action bar items... return super.onOptionsItemSelected(item); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_FILTER) { if (resultCode == RESULT_OK) { setIntent(data); handleIntent(null); } } } @Override public void onCreate(Bundle savedInstanceState) { Log.v(TAG, "onCreate"); m_app = (TodoApplication) getApplication(); m_app.setActionBarStyle(getWindow()); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); super.onCreate(savedInstanceState); final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(getPackageName() + Constants.BROADCAST_ACTION_ARCHIVE); intentFilter.addAction(getPackageName() + Constants.BROADCAST_SYNC_CONFLICT); intentFilter.addAction(getPackageName() + Constants.BROADCAST_ACTION_LOGOUT); intentFilter.addAction(getPackageName() + Constants.BROADCAST_UPDATE_UI); intentFilter.addAction(getPackageName() + Constants.BROADCAST_SYNC_START); intentFilter.addAction(getPackageName() + Constants.BROADCAST_SYNC_DONE); m_broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().endsWith(Constants.BROADCAST_ACTION_ARCHIVE)) { // archive // refresh screen to remove completed tasks // push to remote archiveTasks(null); } else if (intent.getAction().endsWith(Constants.BROADCAST_ACTION_LOGOUT)) { Log.v(TAG, "Logging out from Dropbox"); m_app.getRemoteClientManager().getRemoteClient().deauthenticate(); m_app.setManualMode(false); Intent i = new Intent(context, LoginScreen.class); startActivity(i); finish(); } else if (intent.getAction().endsWith(Constants.BROADCAST_UPDATE_UI)) { handleIntent(null); } else if (intent.getAction().endsWith(Constants.BROADCAST_SYNC_CONFLICT)) { handleSyncConflict(); } else if (intent.getAction().endsWith(Constants.BROADCAST_SYNC_START) && !m_app.isCloudLess()) { setProgressBarIndeterminateVisibility(true); } else if (intent.getAction().endsWith(Constants.BROADCAST_SYNC_DONE) && !m_app.isCloudLess()) { setProgressBarIndeterminateVisibility(false); } } }; registerReceiver(m_broadcastReceiver, intentFilter); // Set the proper theme setTheme(m_app.getActiveTheme()); if (m_app.hasLandscapeDrawers()) { setContentView(R.layout.main_landscape); } else { setContentView(R.layout.main); } // Replace drawables if the theme is dark if (m_app.isDarkTheme()) { ImageView actionBarIcon = (ImageView) findViewById(R.id.actionbar_icon); if (actionBarIcon != null) { actionBarIcon.setImageResource(R.drawable.labels); } ImageView actionBarClear = (ImageView) findViewById(R.id.actionbar_clear); if (actionBarClear != null) { actionBarClear.setImageResource(R.drawable.cancel); } } setProgressBarIndeterminateVisibility(false); getTaskBag().reload(); if (m_app.hasSyncOnResume()) { syncClient(false); } handleIntent(savedInstanceState); } private TaskBag getTaskBag() { return m_app.getTaskBag(); } private void handleIntent(Bundle savedInstanceState) { if (!m_app.isCloudLess()) { RemoteClient remoteClient = m_app.getRemoteClientManager().getRemoteClient(); // Keep allowing use of the app in manual unauthenticated mode // This will probably be removed in the future. if (!remoteClient.isAuthenticated() && !m_app.isManualMode()) { startLogin(); return; } } mFilter = new ActiveFilter(getResources()); m_leftDrawerList = (ListView) findViewById(R.id.left_drawer); m_rightDrawerList = (ListView) findViewById(R.id.right_drawer_list); m_drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (m_drawerLayout == null) { // In tablet landscape mode ViewGroup m_container = (ViewGroup) findViewById(R.id.tablet_drawer_layout); } else { // Not in tablet landscape mode ViewGroup m_container = m_drawerLayout; } // Set the list's click listener m_leftDrawerList.setOnItemClickListener(new DrawerItemClickListener()); if (m_drawerLayout != null) { m_drawerToggle = new ActionBarDrawerToggle(this, /* host Activity */ m_drawerLayout, /* DrawerLayout object */ R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */ R.string.changelist, /* "open drawer" description */ R.string.app_label /* "close drawer" description */ ) { /** * Called when a drawer has settled in a completely closed * state. */ public void onDrawerClosed(View view) { // setTitle(R.string.app_label); } /** Called when a drawer has settled in a completely open state. */ public void onDrawerOpened(View drawerView) { // setTitle(R.string.changelist); } }; // Set the drawer toggle as the DrawerListener m_drawerLayout.setDrawerListener(m_drawerToggle); getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); m_drawerToggle.syncState(); } // Show search or filter results Intent intent = getIntent(); if (savedInstanceState != null) { Log.v(TAG, "handleIntent: savedInstance state"); mFilter.initFromBundle(savedInstanceState); } else if (intent.getExtras() != null) { Log.v(TAG, "handleIntent launched with filter:" + intent.getExtras().keySet()); mFilter.initFromIntent(intent); } else { // Set previous filters and sort Log.v(TAG, "handleIntent: from m_prefs state"); mFilter.initFromPrefs(TodoApplication.getPrefs()); } // Initialize Adapter if (m_adapter == null) { m_adapter = new TaskAdapter(this, R.layout.list_item, getLayoutInflater(), getListView()); } m_adapter.setFilteredTasks(true); setListAdapter(this.m_adapter); ListView lv = getListView(); lv.setTextFilterEnabled(true); lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); lv.setMultiChoiceModeListener(new ActionBarListener()); // If we were started with a selected task, // select it now and clear it from the intent String selectedTask = intent.getStringExtra(Constants.INTENT_SELECTED_TASK); if (selectedTask != null) { String[] parts = selectedTask.split(":", 2); setSelectedTask(Integer.valueOf(parts[0]), parts[1]); intent.removeExtra(Constants.INTENT_SELECTED_TASK); setIntent(intent); } else { // Set the adapter for the list view updateDrawers(); } } private void setSelectedTask(int index, String selectedTask) { Log.v(TAG, "Selected task: " + selectedTask); Task task = new Task(index, selectedTask); int position = m_adapter.getPosition(task); if (position != -1) { ListView lv = getListView(); lv.setItemChecked(position, true); lv.setSelection(position); } } private void updateFilterBar() { ListView lv = getListView(); int index = lv.getFirstVisiblePosition(); View v = lv.getChildAt(0); int top = (v == null) ? 0 : v.getTop(); lv.setSelectionFromTop(index, top); final ImageButton actionbar_clear = (ImageButton) findViewById(R.id.actionbar_clear); final TextView filterText = (TextView) findViewById(R.id.filter_text); if (mFilter.hasFilter()) { actionbar_clear.setVisibility(View.VISIBLE); } else { actionbar_clear.setVisibility(View.GONE); } filterText.setText(mFilter.getTitle()); } private void startLogin() { Intent intent = new Intent(this, LoginScreen.class); startActivity(intent); finish(); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(m_broadcastReceiver); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mFilter.saveInBundle(outState); } @Override protected void onRestart() { super.onRestart(); Log.v(TAG, "onRestart: " + getIntent().getExtras()); if (m_app.hasSyncOnResume()) { syncClient(false); } handleIntent(null); } @Override protected void onStop() { super.onStop(); finishActionmode(); mFilter.saveInPrefs(TodoApplication.getPrefs()); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView(); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setIconifiedByDefault(false); // Do not iconify the widget; // expand it by default this.options_menu = menu; if (m_app.isCloudLess()) { menu.findItem(R.id.sync).setVisible(false); } return super.onCreateOptionsMenu(menu); } @Override public void onListItemClick(ListView l, View v, int position, long id) { onItemLongClick(l, v, position, id); } @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { ListView l = (ListView) parent; l.setItemChecked(position, !l.isItemChecked(position)); return true; } private Task getTaskAt(final int pos) { return m_adapter.getItem(pos); } private void shareTodoList() { String text = ""; for (int i = 0; i < m_adapter.getCount(); i++) { Task task = m_adapter.getItem(i); if (task != null) { text = text + (task.inFileFormat()) + "\n"; } } Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND); shareIntent.setType("text/plain"); shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Simpletask list"); shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, text); startActivity(Intent.createChooser(shareIntent, "Share")); } private void removeTaskTags(final List<Task> tasks) { LinkedHashSet<String> contexts = new LinkedHashSet<String>(); LinkedHashSet<String> projects = new LinkedHashSet<String>(); for (Task task : tasks) { for (String s : task.getLists()) { contexts.add("@" + s); } for (String s : task.getTags()) { projects.add("+" + s); } } final ArrayList<String> items = new ArrayList<String>(); items.addAll(contexts); items.addAll(projects); if (items.size() == 0) { showToast(R.string.not_tagged); return; } String[] values = items.toArray(new String[items.size()]); Dialog tagChooser = Util.createMultiChoiceDialog(this, values, null, R.string.remove_list_or_tag, null, new OnMultiChoiceDialogListener() { @Override public void onClick(boolean[] selected) { for (int i = 0; i < selected.length; i++) { if (selected[i]) { for (Task t : tasks) { t.removeTag(items.get(i)); } } } getTaskBag().store(); m_app.updateWidgets(); m_app.setNeedToPush(true); // We have change the data, views should refresh m_adapter.setFilteredTasks(false); sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE)); } }); tagChooser.show(); } private void tagTasks(final List<Task> tasks) { TaskBag taskBag = getTaskBag(); List<String> strings = new ArrayList<String>(); for (String s : taskBag.getContexts(false)) { strings.add("@" + s); } for (String s : taskBag.getProjects(false)) { strings.add("+" + s); } final String[] items = strings.toArray(new String[strings.size()]); Dialog tagChooser = Util.createMultiChoiceDialog(this, items, null, R.string.add_list_or_tag, null, new OnMultiChoiceDialogListener() { @Override public void onClick(boolean[] selected) { for (int i = 0; i < selected.length; i++) { if (selected[i]) { for (Task t : tasks) { t.append(items[i]); } } } getTaskBag().store(); m_app.updateWidgets(); m_app.setNeedToPush(true); // We have change the data, views should refresh m_adapter.setFilteredTasks(false); sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE)); } }); tagChooser.show(); } private void prioritizeTasks(final List<Task> tasks) { List<String> strings = Priority.rangeInCode(Priority.NONE, Priority.Z); final String[] prioArr = strings.toArray(new String[strings.size()]); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.select_priority); builder.setSingleChoiceItems(prioArr, 0, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, final int which) { dialog.dismiss(); for (Task task : tasks) { if (task != null) { task.setPriority(Priority.toPriority(prioArr[which])); } } finishActionmode(); getTaskBag().store(); m_app.updateWidgets(); m_app.setNeedToPush(true); // We have change the data, views should refresh m_adapter.setFilteredTasks(false); sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE)); } }); builder.show(); } private void completeTasks(List<Task> tasks) { TaskBag taskBag = getTaskBag(); for (Task t : tasks) { if (t != null && !t.isCompleted()) { if (t.getRecurrencePattern() != null) { Task newTask = taskBag.addAsTask(t.inFileFormat()); boolean fromOriginalDate = m_app.hasRecurOriginalDates(); if (newTask.getDueDate() == null && newTask.getThresholdDate() == null) { newTask.deferDueDate(t.getRecurrencePattern(), fromOriginalDate); } else { if (newTask.getDueDate() != null) { newTask.deferDueDate(t.getRecurrencePattern(), fromOriginalDate); } if (newTask.getThresholdDate() != null) { newTask.deferThresholdDate(t.getRecurrencePattern(), fromOriginalDate); } } } t.markComplete(new DateTime()); } } taskBag.store(); if (m_app.isAutoArchive()) { taskBag.archive(null); } m_app.updateWidgets(); m_app.setNeedToPush(true); // We have change the data, views should refresh m_adapter.setFilteredTasks(true); sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE)); } private void undoCompleteTasks(List<Task> tasks) { for (Task t : tasks) { if (t != null && t.isCompleted()) { t.markIncomplete(); } } getTaskBag().store(); m_app.updateWidgets(); m_app.setNeedToPush(true); // We have change the data, views should refresh m_adapter.setFilteredTasks(true); sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE)); } private void deferTasks(List<Task> tasks, final int dateType) { final List<Task> tasksToDefer = tasks; Dialog d = Util.createDeferDialog(this, dateType, true, new Util.InputDialogListener() { @Override public void onClick(String selected) { if (selected != null && selected.equals("pick")) { final DateTime today = new DateTime(); DatePickerDialog dialog = new DatePickerDialog(Simpletask.this, new DatePickerDialog.OnDateSetListener() { @Override public void onDateSet(DatePicker datePicker, int year, int month, int day) { month++; DateTime date = ISODateTimeFormat.date() .parseDateTime(year + "-" + month + "-" + day); deferTasks(date, tasksToDefer, dateType); } }, today.getYear(), today.getMonthOfYear() - 1, today.getDayOfMonth()); dialog.show(); } else { deferTasks(selected, tasksToDefer, dateType); } } }); d.show(); } private void deferTasks(DateTime selected, List<Task> tasksToDefer, int type) { for (Task t : tasksToDefer) { if (t != null) { if (type == Task.DUE_DATE) { t.setDueDate(selected); } else { t.setThresholdDate(selected); } } } m_adapter.setFilteredTasks(false); getTaskBag().store(); m_app.updateWidgets(); m_app.setNeedToPush(true); // We have change the data, views should refresh sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE)); } private void deferTasks(String selected, List<Task> tasksToDefer, int type) { for (Task t : tasksToDefer) { if (t != null) { if (type == Task.DUE_DATE) { t.deferDueDate(selected, false); } else { t.deferThresholdDate(selected, false); } } } m_adapter.setFilteredTasks(false); getTaskBag().store(); m_app.updateWidgets(); m_app.setNeedToPush(true); // We have change the data, views should refresh sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE)); } private void deleteTasks(final List<Task> tasks) { m_app.showConfirmationDialog(this, R.string.delete_task_message, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { for (Task t : tasks) { if (t != null) { getTaskBag().delete(t); } } m_adapter.setFilteredTasks(false); getTaskBag().store(); m_app.updateWidgets(); m_app.setNeedToPush(true); updateDrawers(); // We have change the data, views should refresh sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE)); } }, R.string.delete_task_title); } private void archiveTasks(final List<Task> tasksToArchive) { new AsyncTask<Void, Void, Boolean>() { @Override protected Boolean doInBackground(Void... params) { try { getTaskBag().archive(tasksToArchive); return true; } catch (Exception e) { Log.e(TAG, e.getMessage(), e); return false; } } @Override protected void onPostExecute(Boolean result) { if (result) { Util.showToastLong(Simpletask.this, "Archived tasks"); sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE)); sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_UPDATE_UI)); } else { Util.showToastLong(Simpletask.this, "Could not archive tasks"); } } }.execute(); } @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { Log.v(TAG, "onMenuItemSelected: " + item.getItemId()); switch (item.getItemId()) { case R.id.add_new: startAddTaskActivity(null); break; case R.id.search: break; case R.id.preferences: startPreferencesActivity(); break; case R.id.filter: startFilterActivity(); break; case R.id.share: shareTodoList(); break; case R.id.sync: syncClient(false); break; case R.id.help: showHelp(); break; case R.id.archive: m_app.showConfirmationDialog(this, R.string.delete_task_message, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { archiveTasks(null); } }, R.string.archive_task_title); break; case R.id.open_file: if (m_app.isCloudLess()) { m_app.openCloudlessFile(this); } else { m_app.showConfirmationDialog(this, R.string.dropbox_open, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { m_app.openDropboxFile(Simpletask.this); } }, R.string.dropbox); } break; default: return super.onMenuItemSelected(featureId, item); } return true; } private void startAddTaskActivity(List<Task> tasks) { Log.v(TAG, "Starting addTask activity"); ArrayList<String> payload = new ArrayList<String>(); if (tasks != null) { for (Task t : tasks) { payload.add("" + t.getId() + ":" + t.inFileFormat()); } } Intent intent = new Intent(this, AddTask.class); intent.putExtra(Constants.EXTRA_TASK, Util.join(payload, "\n")); mFilter.saveInIntent(intent); startActivity(intent); } private void startPreferencesActivity() { Intent settingsActivity = new Intent(getBaseContext(), Preferences.class); startActivityForResult(settingsActivity, REQUEST_PREFERENCES); } /** * Called when we can't sync due to a merge conflict. Prompts the user to * force an upload or download. */ private void handleSyncConflict() { m_app.m_pushing = false; m_app.m_pulling = false; showDialog(SYNC_CONFLICT_DIALOG); } /** * Sync with remote client. * <p/> * <ul> * <li>Will Pull in auto mode. * <li>Will ask "push or pull" in manual mode. * </ul> * * @param force * true to force pull */ private void syncClient(boolean force) { if (isManualMode()) { Log.v(TAG, "Manual mode, choice forced; prompt user to ask which way to sync"); showDialog(SYNC_CHOICE_DIALOG); } else { Log.i(TAG, "auto sync mode; should automatically sync; force = " + force); Intent i = new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_WITH_REMOTE); if (force) { i.putExtra(Constants.EXTRA_FORCE_SYNC, true); } sendBroadcast(i); } } private boolean isManualMode() { return m_app.isManualMode(); } @Override protected Dialog onCreateDialog(int id) { final Dialog d; if (id == SYNC_CHOICE_DIALOG) { Log.v(TAG, "Time to show the sync choice dialog"); AlertDialog.Builder upDownChoice = new AlertDialog.Builder(this); upDownChoice.setTitle(R.string.sync_dialog_title); upDownChoice.setMessage(R.string.sync_dialog_msg); upDownChoice.setPositiveButton(R.string.sync_dialog_upload, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE) .putExtra(Constants.EXTRA_FORCE_SYNC, true)); // backgroundPushToRemote(); showToast(getString(R.string.sync_upload_message)); removeDialog(SYNC_CHOICE_DIALOG); } }); upDownChoice.setNegativeButton(R.string.sync_dialog_download, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_FROM_REMOTE) .putExtra(Constants.EXTRA_FORCE_SYNC, true)); // backgroundPullFromRemote(); showToast(getString(R.string.sync_download_message)); removeDialog(SYNC_CHOICE_DIALOG); } }); return upDownChoice.show(); } else if (id == SYNC_CONFLICT_DIALOG) { Log.v(TAG, "Time to show the sync conflict dialog"); AlertDialog.Builder upDownChoice = new AlertDialog.Builder(this); upDownChoice.setTitle(R.string.sync_conflict_dialog_title); upDownChoice.setMessage(R.string.sync_conflict_dialog_msg); upDownChoice.setPositiveButton(R.string.sync_dialog_upload, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { Log.v(TAG, "User selected PUSH"); sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_TO_REMOTE) .putExtra(Constants.EXTRA_OVERWRITE, true).putExtra(Constants.EXTRA_FORCE_SYNC, true)); // backgroundPushToRemote(); showToast(getString(R.string.sync_upload_message)); removeDialog(SYNC_CONFLICT_DIALOG); } }); upDownChoice.setNegativeButton(R.string.sync_dialog_download, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { Log.v(TAG, "User selected PULL"); sendBroadcast(new Intent(getPackageName() + Constants.BROADCAST_START_SYNC_FROM_REMOTE) .putExtra(Constants.EXTRA_FORCE_SYNC, true)); // backgroundPullFromRemote(); showToast(getString(R.string.sync_download_message)); removeDialog(SYNC_CONFLICT_DIALOG); } }); return upDownChoice.show(); } else { return null; } } /** * Handle clear filter click * */ public void onClearClick(View v) { // Collapse the actionview if we are searching Intent intent = getIntent(); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { options_menu.findItem(R.id.search).collapseActionView(); } clearFilter(); m_adapter.setFilteredTasks(false); } public ArrayList<ActiveFilter> getSavedFilter() { ArrayList<ActiveFilter> saved_filters = new ArrayList<ActiveFilter>(); SharedPreferences saved_filter_ids = getSharedPreferences("filters", MODE_PRIVATE); Set<String> filterIds = saved_filter_ids.getStringSet("ids", new HashSet<String>()); for (String id : filterIds) { SharedPreferences filter_pref = getSharedPreferences(id, MODE_PRIVATE); ActiveFilter filter = new ActiveFilter(getResources()); filter.initFromPrefs(filter_pref); filter.setPrefName(id); saved_filters.add(filter); } return saved_filters; } /** * Handle add filter click * */ public void onAddFilterClick(View v) { AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle(R.string.save_filter); alert.setMessage(R.string.save_filter_message); // Set an EditText view to get user input final EditText input = new EditText(this); alert.setView(input); input.setText(mFilter.getProposedName()); alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String value = input.getText().toString(); if (value.equals("")) { Util.showToastShort(getApplicationContext(), R.string.filter_name_empty); } else { SharedPreferences saved_filters = getSharedPreferences("filters", MODE_PRIVATE); int newId = saved_filters.getInt("max_id", 1) + 1; Set<String> filters = saved_filters.getStringSet("ids", new HashSet<String>()); filters.add("filter_" + newId); saved_filters.edit().putStringSet("ids", filters).putInt("max_id", newId).commit(); SharedPreferences test_filter_prefs = getSharedPreferences("filter_" + newId, MODE_PRIVATE); mFilter.setName(value); mFilter.saveInPrefs(test_filter_prefs); updateRightDrawer(); } } }); alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Canceled. } }); alert.show(); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { Intent currentIntent = getIntent(); currentIntent.putExtra(SearchManager.QUERY, intent.getStringExtra(SearchManager.QUERY)); setIntent(currentIntent); handleIntent(null); options_menu.findItem(R.id.search).collapseActionView(); } else if (intent.getExtras() != null) { // Only change intent if it actually contains a filter setIntent(intent); } Log.v(TAG, "onNewIntent: " + intent); } void clearFilter() { // Also clear the intent so we wont get the old filter after // switching back to app later fixes [1c5271ee2e] Intent intent = new Intent(); mFilter.clear(); mFilter.saveInIntent(intent); setIntent(intent); finishActionmode(); updateDrawers(); } private void updateDrawers() { updateLeftDrawer(); updateRightDrawer(); } private void updateRightDrawer() { ArrayList<String> names = new ArrayList<String>(); final ArrayList<ActiveFilter> filters = getSavedFilter(); Collections.sort(filters, new Comparator<ActiveFilter>() { public int compare(ActiveFilter f1, ActiveFilter f2) { return f1.getName().compareToIgnoreCase(f2.getName()); } }); for (ActiveFilter f : filters) { names.add(f.getName()); } m_rightDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, names)); m_rightDrawerList.setChoiceMode(AbsListView.CHOICE_MODE_NONE); m_leftDrawerList.setLongClickable(true); m_rightDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { mFilter = filters.get(position); m_adapter.setFilteredTasks(false); if (m_drawerLayout != null) { m_drawerLayout.closeDrawer(Gravity.RIGHT); } finishActionmode(); updateDrawers(); } }); m_rightDrawerList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { final ActiveFilter filter = filters.get(position); final String prefsName = filter.getPrefName(); PopupMenu popupMenu = new PopupMenu(Simpletask.this, view); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int menuid = item.getItemId(); switch (menuid) { case R.id.menu_saved_filter_delete: deleteSavedFilter(prefsName); break; case R.id.menu_saved_filter_shortcut: createFilterShortcut(filter); break; case R.id.menu_saved_filter_rename: renameSavedFilter(prefsName); break; case R.id.menu_saved_filter_update: updateSavedFilter(prefsName); break; default: break; } return true; } }); MenuInflater inflater = popupMenu.getMenuInflater(); inflater.inflate(R.menu.saved_filter, popupMenu.getMenu()); popupMenu.show(); return true; } }); } public void createFilterShortcut(ActiveFilter filter) { final Intent shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT"); Intent target = new Intent(Constants.INTENT_START_FILTER); filter.saveInIntent(target); target.putExtra("name", filter.getName()); // Setup target intent for shortcut shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, target); // Set shortcut icon Intent.ShortcutIconResource iconRes = Intent.ShortcutIconResource.fromContext(this, R.drawable.icon); shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes); shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, filter.getName()); sendBroadcast(shortcut); } private void deleteSavedFilter(String prefsName) { SharedPreferences saved_filters = getSharedPreferences("filters", MODE_PRIVATE); HashSet<String> ids = new HashSet<String>(); ids.addAll(saved_filters.getStringSet("ids", new HashSet<String>())); ids.remove(prefsName); saved_filters.edit().putStringSet("ids", ids).commit(); SharedPreferences filter_prefs = getSharedPreferences(prefsName, MODE_PRIVATE); filter_prefs.edit().clear().commit(); File prefs_path = new File(this.getFilesDir(), "../shared_prefs"); File prefs_xml = new File(prefs_path, prefsName + ".xml"); prefs_xml.delete(); updateRightDrawer(); } private void updateSavedFilter(String prefsName) { SharedPreferences filter_pref = getSharedPreferences(prefsName, MODE_PRIVATE); ActiveFilter old_filter = new ActiveFilter(getResources()); old_filter.initFromPrefs(filter_pref); String filterName = old_filter.getName(); mFilter.setName(filterName); mFilter.saveInPrefs(filter_pref); updateRightDrawer(); } private void renameSavedFilter(String prefsName) { final SharedPreferences filter_pref = getSharedPreferences(prefsName, MODE_PRIVATE); ActiveFilter old_filter = new ActiveFilter(getResources()); old_filter.initFromPrefs(filter_pref); String filterName = old_filter.getName(); AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle(R.string.rename_filter); alert.setMessage(R.string.rename_filter_message); // Set an EditText view to get user input final EditText input = new EditText(this); alert.setView(input); input.setText(filterName); alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String value = input.getText().toString(); if (value.equals("")) { Util.showToastShort(getApplicationContext(), R.string.filter_name_empty); } else { mFilter.setName(value); mFilter.saveInPrefs(filter_pref); updateRightDrawer(); } } }); alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Canceled. } }); alert.show(); } private void updateLeftDrawer() { TaskBag taskBag = getTaskBag(); DrawerAdapter drawerAdapter = new DrawerAdapter(getLayoutInflater(), taskBag.getDecoratedContexts(true), taskBag.getDecoratedProjects(true)); m_leftDrawerList.setAdapter(drawerAdapter); m_leftDrawerList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE); m_leftDrawerList.setOnItemClickListener(new DrawerItemClickListener()); for (String context : mFilter.getContexts()) { int position = drawerAdapter.getIndexOf("@" + context); if (position != -1) { m_leftDrawerList.setItemChecked(position, true); } } for (String project : mFilter.getProjects()) { int position = drawerAdapter.getIndexOf("+" + project); if (position != -1) { m_leftDrawerList.setItemChecked(position, true); } } m_leftDrawerList.setItemChecked(drawerAdapter.getContextHeaderPosition(), mFilter.getContextsNot()); m_leftDrawerList.setItemChecked(drawerAdapter.getProjectsHeaderPosition(), mFilter.getProjectsNot()); } public void storeKeys(String accessTokenKey, String accessTokenSecret) { Editor editor = m_app.getPrefs().edit(); editor.putString(Constants.PREF_ACCESSTOKEN_KEY, accessTokenKey); editor.putString(Constants.PREF_ACCESSTOKEN_SECRET, accessTokenSecret); editor.commit(); } public void showToast(String string) { Util.showToastLong(this, string); } public void showToast(int id) { Util.showToastLong(this, getString(id)); } public void startFilterActivity() { Intent i = new Intent(this, FilterActivity.class); mFilter.saveInIntent(i); i.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); startActivityForResult(i, REQUEST_FILTER); } private static class ViewHolder { private TextView tasktext; private TextView taskage; private TextView taskdue; private TextView taskthreshold; private CheckBox cbCompleted; } public class TaskAdapter extends BaseAdapter implements ListAdapter, Filterable { public class VisibleLine { private Task task = null; private String title = null; private boolean header = false; public VisibleLine(String title) { this.title = title; this.header = true; } public VisibleLine(Task task) { this.task = task; this.header = false; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; VisibleLine other = (VisibleLine) obj; if (other.header != this.header) return false; if (other.header) { return title.equals(other.title); } else { return this.task.equals(other.task); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((title == null) ? 0 : title.hashCode()); result = prime * result + ((task == null) ? 0 : task.hashCode()); return result; } } ArrayList<VisibleLine> visibleLines = new ArrayList<VisibleLine>(); Set<DataSetObserver> obs = new HashSet<DataSetObserver>(); private LayoutInflater m_inflater; public TaskAdapter(Context context, int textViewResourceId, LayoutInflater inflater, ListView view) { this.m_inflater = inflater; } void setFilteredTasks(boolean reload) { ArrayList<Task> visibleTasks = new ArrayList<Task>(); Log.v(TAG, "setFilteredTasks called, reload: " + reload); if (reload) { getTaskBag().reload(); // Update lists in side drawer // Set the adapter for the list view updateDrawers(); } visibleTasks.clear(); visibleLines.clear(); visibleTasks.addAll(mFilter.apply(getTaskBag().getTasks())); ArrayList<String> sorts = mFilter.getSort(); Collections.sort(visibleTasks, MultiComparator.create(sorts)); String header = ""; String newHeader = ""; int index = 0; int position = 0; int firstGroupSortIndex = 0; if (sorts.size() > 1 && sorts.get(0).contains("completed") || sorts.get(0).contains("future")) { firstGroupSortIndex++; if (sorts.size() > 2 && sorts.get(1).contains("completed") || sorts.get(1).contains("future")) { firstGroupSortIndex++; } } String firstSort = sorts.get(firstGroupSortIndex); for (Task t : visibleTasks) { newHeader = t.getHeader(firstSort, getString(R.string.no_header)); if (!header.equals(newHeader)) { VisibleLine headerLine = new VisibleLine(newHeader); int last = visibleLines.size() - 1; if (last != -1 && visibleLines.get(last).header && !m_app.showEmptyLists()) { visibleLines.set(last, headerLine); } else { visibleLines.add(headerLine); } header = newHeader; } if (!t.isHidden() || m_app.showHidden()) { // enduring tasks should not be displayed VisibleLine taskLine = new VisibleLine(t); visibleLines.add(taskLine); } } for (DataSetObserver ob : obs) { ob.onChanged(); } updateFilterBar(); } @Override public void registerDataSetObserver(DataSetObserver observer) { obs.add(observer); } /* ** Get the adapter position for task */ public int getPosition(Task task) { VisibleLine line = new VisibleLine(task); int index = visibleLines.indexOf(line); return index; } @Override public void unregisterDataSetObserver(DataSetObserver observer) { obs.remove(observer); } @Override public int getCount() { return visibleLines.size(); } @Override public Task getItem(int position) { VisibleLine line = visibleLines.get(position); if (line.header) { return null; } return line.task; } @Override public long getItemId(int position) { return position; } @Override public boolean hasStableIds() { return true; // To change body of implemented methods use File | // Settings | File Templates. } @Override public View getView(int position, View convertView, ViewGroup parent) { VisibleLine line = visibleLines.get(position); if (line.header) { convertView = m_inflater.inflate(R.layout.list_header, null); TextView t = (TextView) convertView.findViewById(R.id.list_header_title); t.setText(line.title); } else { final ViewHolder holder; if (convertView == null) { convertView = m_inflater.inflate(R.layout.list_item, null); holder = new ViewHolder(); holder.tasktext = (TextView) convertView.findViewById(R.id.tasktext); holder.taskage = (TextView) convertView.findViewById(R.id.taskage); holder.taskdue = (TextView) convertView.findViewById(R.id.taskdue); holder.taskthreshold = (TextView) convertView.findViewById(R.id.taskthreshold); holder.cbCompleted = (CheckBox) convertView.findViewById(R.id.checkBox); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } Task task; task = line.task; if (task != null) { SpannableString ss = new SpannableString(task.inScreenFormat(mFilter)); ArrayList<String> colorizeStrings = new ArrayList<String>(); for (String context : task.getLists()) { colorizeStrings.add("@" + context); } Util.setColor(ss, Color.GRAY, colorizeStrings); colorizeStrings.clear(); for (String project : task.getTags()) { colorizeStrings.add("+" + project); } Util.setColor(ss, Color.GRAY, colorizeStrings); Resources res = getResources(); int prioColor; switch (task.getPriority()) { case A: prioColor = res.getColor(android.R.color.holo_red_dark); break; case B: prioColor = res.getColor(android.R.color.holo_orange_dark); break; case C: prioColor = res.getColor(android.R.color.holo_green_dark); break; case D: prioColor = res.getColor(android.R.color.holo_blue_dark); break; default: prioColor = res.getColor(android.R.color.darker_gray); } Util.setColor(ss, prioColor, task.getPriority().inFileFormat()); holder.tasktext.setText(ss); final ArrayList<Task> tasks = new ArrayList<Task>(); tasks.add(task); if (task.isCompleted()) { // Log.v(TAG, "Striking through " + task.getText()); holder.tasktext .setPaintFlags(holder.tasktext.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); holder.taskage.setPaintFlags(holder.taskage.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); holder.cbCompleted.setChecked(true); holder.cbCompleted.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { undoCompleteTasks(tasks); finishActionmode(); } }); } else { holder.tasktext .setPaintFlags(holder.tasktext.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); holder.taskage.setPaintFlags(holder.taskage.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); holder.cbCompleted.setChecked(false); holder.cbCompleted.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { completeTasks(tasks); finishActionmode(); } }); } String relAge = task.getRelativeAge(); SpannableString relDue = task.getRelativeDueDate(res, m_app.hasColorDueDates()); String relThres = task.getRelativeThresholdDate(); boolean anyDateShown = false; if (!Strings.isEmptyOrNull(relAge)) { holder.taskage.setText(relAge); anyDateShown = true; } else { holder.taskage.setText(""); } if (relDue != null) { anyDateShown = true; holder.taskdue.setText(relDue); } else { holder.taskdue.setText(""); } if (!Strings.isEmptyOrNull(relThres)) { anyDateShown = true; holder.taskthreshold.setText(relThres); } else { holder.taskthreshold.setText(""); } LinearLayout datesBar = (LinearLayout) convertView.findViewById(R.id.datebar); if (!anyDateShown || task.isCompleted()) { datesBar.setVisibility(View.GONE); holder.tasktext.setPadding(holder.tasktext.getPaddingLeft(), holder.tasktext.getPaddingTop(), holder.tasktext.getPaddingRight(), 4); } else { datesBar.setVisibility(View.VISIBLE); holder.tasktext.setPadding(holder.tasktext.getPaddingLeft(), holder.tasktext.getPaddingTop(), holder.tasktext.getPaddingRight(), 0); } } } return convertView; } @Override public int getItemViewType(int position) { VisibleLine line = visibleLines.get(position); if (line.header) { return 0; } else { return 1; } } @Override public int getViewTypeCount() { return 2; } @Override public boolean isEmpty() { return visibleLines.size() == 0; } @Override public boolean areAllItemsEnabled() { return false; } @Override public boolean isEnabled(int position) { VisibleLine line = visibleLines.get(position); return !line.header; } @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence charSequence) { mFilter.setSearch(charSequence.toString()); //Log.v(TAG, "performFiltering: " + charSequence.toString()); return null; } @Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { setFilteredTasks(false); } }; } } class ActionBarListener implements AbsListView.MultiChoiceModeListener { @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { getListView().invalidateViews(); mode.invalidate(); } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.task_context, menu); actionMode = mode; return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { List<Task> checkedTasks = getCheckedTasks(); int numSelected = checkedTasks.size(); String title = "" + numSelected; mode.setTitle(title); menu.removeGroup(Menu.CATEGORY_SECONDARY); for (Task t : getCheckedTasks()) { for (String s : t.getPhoneNumbers()) { menu.add(Menu.CATEGORY_SECONDARY, R.id.phone_number, Menu.NONE, s); } for (String s : t.getMailAddresses()) { menu.add(Menu.CATEGORY_SECONDARY, R.id.mail, Menu.NONE, s); } for (URL u : t.getLinks()) { menu.add(Menu.CATEGORY_SECONDARY, R.id.url, Menu.NONE, u.toString()); } } return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { List<Task> checkedTasks = getCheckedTasks(); int menuid = item.getItemId(); Intent intent; switch (menuid) { case R.id.update: startAddTaskActivity(checkedTasks); break; case R.id.delete: deleteTasks(checkedTasks); break; case R.id.archive: archiveTasks(checkedTasks); break; case R.id.defer_due: deferTasks(checkedTasks, Task.DUE_DATE); break; case R.id.defer_threshold: deferTasks(checkedTasks, Task.THRESHOLD_DATE); break; case R.id.priority: prioritizeTasks(checkedTasks); return true; case R.id.share: String shareText = selectedTasksAsString(); intent = new Intent(android.content.Intent.ACTION_SEND).setType("text/plain") .putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.share_title)) .putExtra(android.content.Intent.EXTRA_TEXT, shareText); startActivity(Intent.createChooser(intent, "Share")); break; case R.id.calendar: String calendarTitle = getString(R.string.calendar_title); String calendarDescription = ""; if (checkedTasks.size() == 1) { // Set the task as title calendarTitle = checkedTasks.get(0).getText(); } else { // Set the tasks as description calendarDescription = selectedTasksAsString(); } intent = new Intent(android.content.Intent.ACTION_EDIT).setType(Constants.ANDROID_EVENT) .putExtra(Events.TITLE, calendarTitle).putExtra(Events.DESCRIPTION, calendarDescription); // Explicitly set start and end date/time. // Some calendar providers need this. GregorianCalendar calDate = new GregorianCalendar(); intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, calDate.getTimeInMillis()); intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, calDate.getTimeInMillis() + 60 * 60 * 1000); startActivity(intent); break; case R.id.url: Log.v(TAG, "url: " + item.getTitle().toString()); intent = new Intent(Intent.ACTION_VIEW, Uri.parse(item.getTitle().toString())); startActivity(intent); break; case R.id.mail: Log.v(TAG, "mail: " + item.getTitle().toString()); intent = new Intent(Intent.ACTION_SEND, Uri.parse(item.getTitle().toString())); intent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { item.getTitle().toString() }); intent.setType("text/plain"); startActivity(intent); break; case R.id.phone_number: Log.v(TAG, "phone_number"); String encodedNumber = Uri.encode(item.getTitle().toString()); intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + encodedNumber)); startActivity(intent); break; case R.id.update_lists: updateLists(checkedTasks); return true; case R.id.update_tags: updateTags(checkedTasks); return true; } mode.finish(); return true; } private String selectedTasksAsString() { List<String> result = new ArrayList<String>(); for (Task t : getCheckedTasks()) { result.add(t.inFileFormat()); } return Util.join(result, "\n"); } private List<Task> getCheckedTasks() { ArrayList<Task> checkedTasks = new ArrayList<Task>(); SparseBooleanArray checkedItems = getListView().getCheckedItemPositions(); for (int i = 0; i < checkedItems.size(); i++) { if (checkedItems.valueAt(i) == true) { checkedTasks.add(getTaskAt(checkedItems.keyAt(i))); } } return checkedTasks; } @Override public void onDestroyActionMode(ActionMode mode) { actionMode = null; if (m_drawerLayout != null) { m_drawerLayout.closeDrawers(); } updateDrawers(); } } private void updateLists(final List<Task> checkedTasks) { final ArrayList<String> contexts = new ArrayList<String>(); Set<String> selectedContexts = new HashSet<String>(); TaskBag taskbag = getTaskBag(); contexts.addAll(taskbag.getContexts(false)); for (Task t : checkedTasks) { selectedContexts.addAll(t.getLists()); } View view = getLayoutInflater().inflate(R.layout.tag_dialog, null); final ListView lv = (ListView) view.findViewById(R.id.listView); lv.setAdapter(new ArrayAdapter<String>(this, R.layout.simple_list_item_multiple_choice, contexts.toArray(new String[contexts.size()]))); lv.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE); for (String context : selectedContexts) { int position = contexts.indexOf(context); if (position != -1) { lv.setItemChecked(position, true); } } lv.setLongClickable(true); lv.setOnItemLongClickListener(this); final EditText ed = (EditText) view.findViewById(R.id.editText); m_app.setEditTextHint(ed, R.string.new_list_name); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setView(view); builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ArrayList<String> items = new ArrayList<String>(); ArrayList<String> uncheckedItesm = new ArrayList<String>(); uncheckedItesm.addAll(Util.getCheckedItems(lv, false)); items.addAll(Util.getCheckedItems(lv, true)); String newText = ed.getText().toString(); if (!newText.equals("")) { items.add(ed.getText().toString()); } for (String item : items) { for (Task t : checkedTasks) { t.addList(item); } } for (String item : uncheckedItesm) { for (Task t : checkedTasks) { t.removeTag("@" + item); } } finishActionmode(); m_adapter.setFilteredTasks(false); getTaskBag().store(); m_app.updateWidgets(); m_app.setNeedToPush(true); updateDrawers(); } }); builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // User cancelled the dialog } }); // Create the AlertDialog AlertDialog dialog = builder.create(); dialog.setTitle(R.string.update_lists); dialog.show(); } private void updateTags(final List<Task> checkedTasks) { final ArrayList<String> projects = new ArrayList<String>(); Set<String> selectedProjects = new HashSet<String>(); TaskBag taskbag = getTaskBag(); projects.addAll(taskbag.getProjects(false)); for (Task t : checkedTasks) { selectedProjects.addAll(t.getTags()); } View view = getLayoutInflater().inflate(R.layout.tag_dialog, null); final ListView lv = (ListView) view.findViewById(R.id.listView); lv.setAdapter(new ArrayAdapter<String>(this, R.layout.simple_list_item_multiple_choice, projects.toArray(new String[projects.size()]))); lv.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE); for (String context : selectedProjects) { int position = projects.indexOf(context); if (position != -1) { lv.setItemChecked(position, true); } } final EditText ed = (EditText) view.findViewById(R.id.editText); m_app.setEditTextHint(ed, R.string.new_list_name); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setView(view); builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ArrayList<String> items = new ArrayList<String>(); ArrayList<String> uncheckedItesm = new ArrayList<String>(); uncheckedItesm.addAll(Util.getCheckedItems(lv, false)); items.addAll(Util.getCheckedItems(lv, true)); String newText = ed.getText().toString(); if (!newText.equals("")) { items.add(ed.getText().toString()); } for (String item : items) { for (Task t : checkedTasks) { t.addTag(item); } } for (String item : uncheckedItesm) { for (Task t : checkedTasks) { t.removeTag("+" + item); } } finishActionmode(); m_adapter.setFilteredTasks(false); getTaskBag().store(); m_app.updateWidgets(); m_app.setNeedToPush(true); updateDrawers(); } }); builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // User cancelled the dialog } }); // Create the AlertDialog AlertDialog dialog = builder.create(); dialog.setTitle(R.string.update_tags); dialog.show(); } private class DrawerItemClickListener implements AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //view.setSelected(true); ArrayList<String> tags; ListView lv = (ListView) parent; DrawerAdapter adapter = (DrawerAdapter) lv.getAdapter(); if (adapter.getProjectsHeaderPosition() == position) { mFilter.setProjectsNot(!mFilter.getProjectsNot()); } if (adapter.getContextHeaderPosition() == position) { mFilter.setContextsNot(!mFilter.getContextsNot()); } else { tags = Util.getCheckedItems(lv, true); ArrayList<String> filteredContexts = new ArrayList<String>(); ArrayList<String> filteredProjects = new ArrayList<String>(); for (String tag : tags) { if (tag.startsWith("+")) { filteredProjects.add(tag.substring(1)); } else if (tag.startsWith("@")) { filteredContexts.add(tag.substring(1)); } } mFilter.setContexts(filteredContexts); mFilter.setProjects(filteredProjects); } Intent intent = getIntent(); mFilter.saveInIntent(intent); setIntent(intent); adapter.notifyDataSetChanged(); finishActionmode(); m_adapter.setFilteredTasks(false); //m_drawerLayout.closeDrawer(Gravity.LEFT); } } private void finishActionmode() { if (actionMode != null) { actionMode.finish(); } } }