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 * @copyright 2015 Vojtech Kral */ package nl.mpcjanssen.simpletask; import android.annotation.SuppressLint; import android.app.*; 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.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.Bundle; import android.provider.CalendarContract; import android.provider.CalendarContract.Events; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.MenuItemCompat; import android.support.v4.widget.DrawerLayout; import android.text.Editable; import android.text.SpannableString; import android.util.Log; import android.util.SparseBooleanArray; 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.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.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.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.GregorianCalendar; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TimeZone; import hirondelle.date4j.DateTime; import nl.mpcjanssen.simpletask.adapters.DrawerAdapter; import nl.mpcjanssen.simpletask.task.Priority; import nl.mpcjanssen.simpletask.task.Task; import nl.mpcjanssen.simpletask.task.TaskCache; import nl.mpcjanssen.simpletask.task.token.Token; import nl.mpcjanssen.simpletask.util.DateStrings; import nl.mpcjanssen.simpletask.util.Strings; import nl.mpcjanssen.simpletask.util.Util; public class Simpletask extends ThemedActivity implements SharedPreferences.OnSharedPreferenceChangeListener, AdapterView.OnItemClickListener { final static String TAG = Simpletask.class.getSimpleName(); private final static int REQUEST_SHARE_PARTS = 1; private final static int REQUEST_PREFERENCES = 2; private final static String ACTION_LINK = "link"; private final static String ACTION_PHONE = "phone"; private final static String ACTION_MAIL = "mail"; public final static Uri URI_BASE = Uri.fromParts("simpletask", "", null); public final static Uri URI_SEARCH = Uri.withAppendedPath(URI_BASE, "search"); Menu options_menu; TodoApplication m_app; ActiveFilter mFilter; TaskAdapter m_adapter; private BroadcastReceiver m_broadcastReceiver; private LocalBroadcastManager localBroadcastManager; @Nullable private ActionMode actionMode; // Drawer vars private ListView m_leftDrawerList; private ListView m_rightDrawerList; private DrawerLayout m_drawerLayout; private ActionBarDrawerToggle m_drawerToggle; private Bundle m_savedInstanceState; private ProgressDialog m_sync_dialog; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQUEST_SHARE_PARTS: if (resultCode != Activity.RESULT_CANCELED) { int flags = resultCode - Activity.RESULT_FIRST_USER; shareTodoList(flags); } break; } } private void showHelp() { Intent i = new Intent(this, HelpScreen.class); startActivity(i); } @Override public boolean onSearchRequested() { MenuItem searchMenuItem = options_menu.findItem(R.id.search); searchMenuItem.expandActionView(); return true; } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. if (m_drawerToggle != null) { m_drawerToggle.syncState(); } } @NotNull private List<Task> getCheckedTasks() { ArrayList<Task> checkedTasks = new ArrayList<Task>(); SparseBooleanArray checkedItems = getListView().getCheckedItemPositions(); if (checkedItems == null) { return checkedTasks; } for (int i = 0; i < checkedItems.size(); i++) { if (checkedItems.valueAt(i)) { Task t = getTaskAt(checkedItems.keyAt(i)); // Ignore headers if (t != null) { checkedTasks.add(getTaskAt(checkedItems.keyAt(i))); } } } return checkedTasks; } @NotNull private String selectedTasksAsString() { List<String> result = new ArrayList<String>(); for (Task t : getCheckedTasks()) { result.add(t.inFileFormat()); } return Util.join(result, "\n"); } private ListView getListView() { return (ListView) this.findViewById(R.id.list); } private void selectAllTasks() { ListView lv = getListView(); int itemCount = lv.getCount(); for (int i = 0; i < itemCount; i++) { // Only check tasks that are not checked yet // and skip headers // This prevents double counting in the CAB title Task t = getTaskAt(i); if (t != null && !lv.isItemChecked(i)) { lv.setItemChecked(i, true); } } } @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; } return super.onOptionsItemSelected(item); } @Override public void onCreate(Bundle savedInstanceState) { Log.v(TAG, "onCreate"); m_app = (TodoApplication) getApplication(); m_app.setActionBarStyle(getWindow()); m_savedInstanceState = savedInstanceState; super.onCreate(savedInstanceState); m_app.prefsChangeListener(this); final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Constants.BROADCAST_ACTION_ARCHIVE); intentFilter.addAction(Constants.BROADCAST_ACTION_LOGOUT); intentFilter.addAction(Constants.BROADCAST_UPDATE_UI); intentFilter.addAction(Constants.BROADCAST_SYNC_START); intentFilter.addAction(Constants.BROADCAST_SYNC_DONE); localBroadcastManager = m_app.getLocalBroadCastManager(); m_broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, @NotNull Intent intent) { if (intent.getAction().equals(Constants.BROADCAST_ACTION_ARCHIVE)) { // archive // refresh screen to remove completed tasks // push to remote archiveTasks(null); } else if (intent.getAction().equals(Constants.BROADCAST_ACTION_LOGOUT)) { Log.v(TAG, "Logging out from Dropbox"); m_app.deauthenticate(); Intent i = new Intent(context, LoginScreen.class); startActivity(i); finish(); } else if (intent.getAction().equals(Constants.BROADCAST_UPDATE_UI)) { int position = getListView().getFirstVisiblePosition(); Log.v(TAG, "Updating UI because of broadcast"); handleIntent(); getListView().setSelectionFromTop(position, 0); } else if (intent.getAction().equals(Constants.BROADCAST_SYNC_START)) { setProgressBarIndeterminateVisibility(true); } else if (intent.getAction().equals(Constants.BROADCAST_SYNC_DONE)) { setProgressBarIndeterminateVisibility(false); } } }; localBroadcastManager.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 actionBarClear = (ImageView) findViewById(R.id.actionbar_clear); if (actionBarClear != null) { actionBarClear.setImageResource(R.drawable.cancel); } } setProgressBarIndeterminateVisibility(false); } private void handleIntent() { if (!m_app.isAuthenticated()) { Log.v(TAG, "handleIntent: not authenticated"); startLogin(); return; } if (!m_app.initialSyncDone()) { m_sync_dialog = new ProgressDialog(this, m_app.getActiveTheme()); m_sync_dialog.setIndeterminate(true); m_sync_dialog.setMessage("Initial Dropbox sync in progress, please wait...."); m_sync_dialog.setCancelable(false); m_sync_dialog.show(); } else if (m_sync_dialog != null) { m_sync_dialog.cancel(); } mFilter = new ActiveFilter(); 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); // 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); ActionBar actionBar = getActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); } m_drawerToggle.syncState(); } // Show search or filter results Intent intent = getIntent(); if (Constants.INTENT_START_FILTER.equals(intent.getAction())) { mFilter.initFromIntent(intent); Log.v(TAG, "handleIntent: launched with filter" + mFilter); Log.v(TAG, "handleIntent: saving filter in prefs"); mFilter.saveInPrefs(TodoApplication.getPrefs()); } 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(getLayoutInflater()); } m_adapter.setFilteredTasks(); getListView().setAdapter(this.m_adapter); ListView lv = getListView(); lv.setTextFilterEnabled(true); lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); lv.setMultiChoiceModeListener(new ActionBarListener()); lv.setClickable(true); lv.setOnItemClickListener(this); // 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 (!Strings.isEmptyOrNull(selectedTask)) { 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(); } if (m_savedInstanceState != null) { ArrayList<String> selection = m_savedInstanceState.getStringArrayList("selection"); int position = m_savedInstanceState.getInt("position"); if (selection != null) { for (String selected : selection) { String[] parts = selected.split(":", 2); setSelectedTask(Integer.valueOf(parts[0]), parts[1]); } } lv.setSelectionFromTop(position, 0); } } private void setSelectedTask(int index, @NotNull 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 LinearLayout actionbar = (LinearLayout) findViewById(R.id.actionbar); final TextView filterText = (TextView) findViewById(R.id.filter_text); if (mFilter.hasFilter()) { actionbar.setVisibility(View.VISIBLE); } else { actionbar.setVisibility(View.GONE); } int count = m_adapter != null ? m_adapter.getCountVisbleTasks() : 0; int total = getTaskBag() != null ? getTaskBag().size() : 0; filterText.setText(mFilter.getTitle(count, total, getText(R.string.priority_prompt), getText(R.string.project_prompt), getText(R.string.context_prompt), getText(R.string.search), getText(R.string.script), getText(R.string.title_filter_applied), getText(R.string.no_filter))); } private void startLogin() { Intent intent = new Intent(this, LoginScreen.class); startActivity(intent); finish(); } @Override protected void onDestroy() { super.onDestroy(); localBroadcastManager.unregisterReceiver(m_broadcastReceiver); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { // just update all if ("theme".equals(key) || "fontsize".equals(key)) { this.recreate(); } } @Override protected void onSaveInstanceState(@NotNull Bundle outState) { super.onSaveInstanceState(outState); ArrayList<String> selection = new ArrayList<String>(); for (Task t : getCheckedTasks()) { selection.add("" + t.getId() + ":" + t.inFileFormat()); } outState.putStringArrayList("selection", selection); outState.putInt("position", getListView().getFirstVisiblePosition()); } @Override protected void onResume() { super.onResume(); handleIntent(); } @Override protected void onPause() { super.onPause(); finishActionmode(); } @Override public boolean onCreateOptionsMenu(@NotNull final Menu menu) { MenuInflater inflater = getMenuInflater(); if (m_app.isDarkActionbar()) { inflater.inflate(R.menu.main, menu); } else { inflater.inflate(R.menu.main_light, menu); } if (!m_app.fileStoreCanSync()) { MenuItem mItem = menu.findItem(R.id.sync); mItem.setVisible(false); } SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView(); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setIconifiedByDefault(false); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { public boolean m_ignoreSearchChangeCallback; @Override public boolean onQueryTextSubmit(String query) { // Stupid searchview code will call onQueryTextChange callback // When the actionView collapse and the textview is reset // ugly global hack around this m_ignoreSearchChangeCallback = true; menu.findItem(R.id.search).collapseActionView(); m_ignoreSearchChangeCallback = false; return true; } @Override public boolean onQueryTextChange(String newText) { if (!m_ignoreSearchChangeCallback) { if (mFilter == null) { mFilter = new ActiveFilter(); } mFilter.setSearch(newText); mFilter.saveInPrefs(TodoApplication.getPrefs()); if (m_adapter != null) { m_adapter.setFilteredTasks(); } } return true; } }); this.options_menu = menu; return super.onCreateOptionsMenu(menu); } @Nullable private Task getTaskAt(final int pos) { return m_adapter.getItem(pos); } private void shareTodoList(int format) { StringBuilder text = new StringBuilder(); for (int i = 0; i < m_adapter.getCount(); i++) { Task task = m_adapter.getItem(i); if (task != null) { text.append(task.showParts(format)).append("\n"); } } shareText(text.toString()); } private void shareText(String text) { Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND); shareIntent.setType("text/plain"); shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Simpletask list"); // If text is small enough SEND it directly if (text.length() < 50000) { shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, text); } else { // Create a cache file to pass in EXTRA_STREAM try { Util.createCachedFile(this, Constants.SHARE_FILE_NAME, text); Uri fileUri = Uri .parse("content://" + CachedFileProvider.AUTHORITY + "/" + Constants.SHARE_FILE_NAME); shareIntent.putExtra(android.content.Intent.EXTRA_STREAM, fileUri); } catch (Exception e) { Log.w(TAG, "Failed to create file for sharing"); } } startActivity(Intent.createChooser(shareIntent, "Share")); } private void prioritizeTasks(@NotNull final List<Task> tasks) { List<String> strings = Priority.rangeInCode(Priority.NONE, Priority.Z); final String[] prioArr = strings.toArray(new String[strings.size()]); int prioIdx = 0; if (tasks.size() == 1) { prioIdx = strings.indexOf(tasks.get(0).getPriority().getCode()); } AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.select_priority); builder.setSingleChoiceItems(prioArr, prioIdx, new DialogInterface.OnClickListener() { @Override public void onClick(@NotNull DialogInterface dialog, final int which) { dialog.dismiss(); Priority prio = Priority.toPriority(prioArr[which]); ArrayList<String> originalTasks = Util.tasksToString(tasks); for (Task t : tasks) { t.setPriority(prio); } getTaskBag().modify(originalTasks, tasks, null, null); finishActionmode(); } }); builder.show(); } private void completeTasks(@NotNull List<Task> tasks) { getTaskBag().complete(tasks, m_app.hasRecurOriginalDates(), m_app.hasKeepPrio()); if (m_app.isAutoArchive()) { archiveTasks(null); } } private void undoCompleteTasks(@NotNull List<Task> tasks) { getTaskBag().undoComplete(tasks); } 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(@Nullable String selected) { if (selected == null) { Log.w(TAG, "Can't defer, selected is null. This should not happen"); return; } if (selected.equals("pick")) { final DateTime today = DateTime.today(TimeZone.getDefault()); DatePickerDialog dialog = new DatePickerDialog(Simpletask.this, new DatePickerDialog.OnDateSetListener() { @Override public void onDateSet(DatePicker datePicker, int year, int month, int day) { month++; DateTime date = DateTime.forDateOnly(year, month, day); getTaskBag().defer(date.format(Constants.DATE_FORMAT), tasksToDefer, dateType); } }, today.getYear(), today.getMonth() - 1, today.getDay()); boolean showCalendar = m_app.showCalendar(); dialog.getDatePicker().setCalendarViewShown(showCalendar); dialog.getDatePicker().setSpinnersShown(!showCalendar); dialog.show(); } else { getTaskBag().defer(selected, tasksToDefer, dateType); } } }); d.show(); } 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) { m_app.getTaskCache(null).modify(null, null, null, tasks); // We have change the data, views should refresh } }, R.string.delete_task_title); } private void archiveTasks(List<Task> tasksToArchive) { if (m_app.getTodoFileName().equals(m_app.getDoneFileName())) { Util.showToastShort(this, "You have the done.txt file opened."); return; } ArrayList<Task> tasksToDelete = new ArrayList<Task>(); if (tasksToArchive == null) { tasksToArchive = getTaskBag().getTasks(); } for (Task t : tasksToArchive) { if (t.isCompleted()) { tasksToDelete.add(t); } } try { m_app.getFileStore().appendTaskToFile(m_app.getDoneFileName(), tasksToDelete); getTaskBag().modify(null, null, null, tasksToDelete); } catch (IOException e) { Util.showToastShort(this, "Archive failed"); e.printStackTrace(); } } @Override public boolean onMenuItemSelected(int featureId, @NotNull 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: startActivityForResult(new Intent(getBaseContext(), TaskDisplayActivity.class), REQUEST_SHARE_PARTS); break; case R.id.help: showHelp(); break; case R.id.sync: m_app.sync(); 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: m_app.browseForNewFile(this); break; default: return super.onMenuItemSelected(featureId, item); } return true; } private void startAddTaskActivity(List<Task> tasks) { Log.v(TAG, "Starting addTask activity"); getTaskBag().setTasksToUpdate(tasks); Intent intent = new Intent(this, AddTask.class); mFilter.saveInIntent(intent); startActivity(intent); } private void startPreferencesActivity() { Intent settingsActivity = new Intent(getBaseContext(), Preferences.class); startActivityForResult(settingsActivity, REQUEST_PREFERENCES); } /** * Handle clear filter click * */ public void onClearClick(View v) { // Collapse the actionview if we are searching Intent intent = getIntent(); clearFilter(); } @NotNull 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(); 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) { Editable text = input.getText(); String value; if (text == null) { value = ""; } else { value = text.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).apply(); 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 public void onBackPressed() { if (m_drawerLayout != null) { if (m_drawerLayout.isDrawerOpen(Gravity.LEFT)) { m_drawerLayout.closeDrawer(Gravity.LEFT); return; } if (m_drawerLayout.isDrawerOpen(Gravity.RIGHT)) { m_drawerLayout.closeDrawer(Gravity.RIGHT); return; } } if (m_app.backClearsFilter() && mFilter != null && mFilter.hasFilter()) { clearFilter(); onNewIntent(getIntent()); return; } if (!m_app.switchPreviousTodoFile()) { super.onBackPressed(); } } @Override protected void onNewIntent(@NotNull 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); options_menu.findItem(R.id.search).collapseActionView(); } else if (CalendarContract.ACTION_HANDLE_CUSTOM_EVENT.equals(intent.getAction())) { Uri uri = Uri.parse(intent.getStringExtra(CalendarContract.EXTRA_CUSTOM_APP_URI)); String search = uri.getLastPathSegment(); // TODO: activate search } 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); mFilter.saveInPrefs(TodoApplication.getPrefs()); setIntent(intent); finishActionmode(); updateDrawers(); m_adapter.setFilteredTasks(); } 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(@NotNull ActiveFilter f1, @NotNull 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_rightDrawerList.setLongClickable(true); m_rightDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { mFilter = filters.get(position); Intent intent = getIntent(); mFilter.saveInIntent(intent); setIntent(intent); mFilter.saveInPrefs(TodoApplication.getPrefs()); m_adapter.setFilteredTasks(); 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(@NotNull 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(@NotNull 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.ic_launcher); 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).apply(); SharedPreferences filter_prefs = getSharedPreferences(prefsName, MODE_PRIVATE); ActiveFilter deleted_filter = new ActiveFilter(); deleted_filter.initFromPrefs(filter_prefs); filter_prefs.edit().clear().apply(); File prefs_path = new File(this.getFilesDir(), "../shared_prefs"); File prefs_xml = new File(prefs_path, prefsName + ".xml"); final boolean deleted = prefs_xml.delete(); if (!deleted) { Log.w(TAG, "Failed to delete saved filter: " + deleted_filter.getName()); } updateRightDrawer(); } private void updateSavedFilter(String prefsName) { SharedPreferences filter_pref = getSharedPreferences(prefsName, MODE_PRIVATE); ActiveFilter old_filter = new ActiveFilter(); 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(); 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) { Editable text = input.getText(); String value; if (text == null) { value = ""; } else { value = text.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() { TaskCache taskBag = getTaskBag(); ArrayList<String> decoratedContexts = Util.sortWithPrefix(taskBag.getDecoratedContexts(), m_app.sortCaseSensitive(), "@-"); ArrayList<String> decoratedProjects = Util.sortWithPrefix(taskBag.getDecoratedProjects(), m_app.sortCaseSensitive(), "+-"); DrawerAdapter drawerAdapter = new DrawerAdapter(getLayoutInflater(), getString(R.string.context_prompt), decoratedContexts, getString(R.string.project_prompt), decoratedProjects); 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()); } private TaskCache getTaskBag() { return m_app.getTaskCache(null); } public void startFilterActivity() { Intent i = new Intent(this, FilterActivity.class); mFilter.saveInIntent(i); startActivity(i); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Task t = getTaskAt(position); final ArrayList<String> actions = new ArrayList<>(); final ArrayList<String> links = new ArrayList<>(); for (String link : t.getLinks()) { actions.add(ACTION_LINK); links.add(link); } for (String number : t.getPhoneNumbers()) { actions.add(ACTION_PHONE); links.add(number); } for (String mail : t.getMailAddresses()) { actions.add(ACTION_MAIL); links.add(mail); } final String[] linksArray = links.toArray(new String[0]); if (linksArray.length == 0) { getListView().setItemChecked(position, !getListView().isItemChecked(position)); } else { AlertDialog.Builder build = new AlertDialog.Builder(this); build.setItems(linksArray, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent; String url = links.get(which); Log.v(TAG, "" + actions.get(which) + ": " + url); switch (actions.get(which)) { case ACTION_LINK: if (url.startsWith("todo://")) { File todoFolder = m_app.getTodoFile().getParentFile(); File newName = new File(todoFolder, url.substring(7)); m_app.switchTodoFile(newName); } else { intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); } break; case ACTION_PHONE: String encodedNumber = Uri.encode(url); intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + encodedNumber)); startActivity(intent); break; case ACTION_MAIL: intent = new Intent(Intent.ACTION_SEND, Uri.parse(url)); intent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { url }); intent.setType("text/plain"); startActivity(intent); } } }); build.create().show(); } return; } 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 { public class VisibleLine { @NotNull private Task task; private boolean header = false; public VisibleLine(@NotNull String title) { this.task = new Task(0, title); this.header = true; } public VisibleLine(@NotNull Task task) { this.task = task; this.header = false; } @Override public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; VisibleLine other = (VisibleLine) obj; return other.header == this.header && this.task.equals(other.task); } @Override public int hashCode() { final int prime = 31; int result = 1; int headerHash = header ? 1231 : 1237; result = prime * result + headerHash; result = prime * result + task.hashCode(); return result; } } @NotNull ArrayList<VisibleLine> visibleLines = new ArrayList<VisibleLine>(); @NotNull Set<DataSetObserver> obs = new HashSet<DataSetObserver>(); private LayoutInflater m_inflater; private int countVisbleTasks; public TaskAdapter(LayoutInflater inflater) { this.m_inflater = inflater; } void setFilteredTasks() { setTitle(m_app.getTodoFileName().replaceAll("([^/])[^/]*/", "$1/")); ArrayList<Task> visibleTasks; countVisbleTasks = 0; Log.v(TAG, "setFilteredTasks called: " + getTaskBag()); ArrayList<String> sorts = mFilter.getSort(m_app.getDefaultSorts()); visibleTasks = getTaskBag().getTasks(mFilter, sorts, m_app.sortCaseSensitive()); visibleLines.clear(); String header = ""; String newHeader; 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.isVisible() || m_app.showHidden()) { // enduring tasks should not be displayed VisibleLine taskLine = new VisibleLine(t); visibleLines.add(taskLine); countVisbleTasks++; } } for (DataSetObserver ob : obs) { ob.onChanged(); } updateFilterBar(); } public int getCountVisbleTasks() { return countVisbleTasks; } @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); return visibleLines.indexOf(line); } @Override public void unregisterDataSetObserver(DataSetObserver observer) { obs.remove(observer); } @Override public int getCount() { return visibleLines.size(); } @Nullable @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. } @Nullable @Override public View getView(int position, @Nullable View convertView, ViewGroup parent) { VisibleLine line = visibleLines.get(position); if (line.header) { if (convertView == null) { convertView = m_inflater.inflate(R.layout.list_header, parent, false); } TextView t = (TextView) convertView.findViewById(R.id.list_header_title); t.setText(line.task.inFileFormat()); } else { final ViewHolder holder; if (convertView == null) { convertView = m_inflater.inflate(R.layout.list_item, parent, false); 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 (m_app.showCompleteCheckbox()) { holder.cbCompleted.setVisibility(View.VISIBLE); } else { holder.cbCompleted.setVisibility(View.GONE); } int tokensToShow = Token.SHOW_ALL; tokensToShow = tokensToShow & ~Token.CREATION_DATE; tokensToShow = tokensToShow & ~Token.COMPLETED; tokensToShow = tokensToShow & ~Token.COMPLETED_DATE; tokensToShow = tokensToShow & ~Token.THRESHOLD_DATE; tokensToShow = tokensToShow & ~Token.DUE_DATE; if (mFilter.getHideLists()) { tokensToShow = tokensToShow & ~Token.LIST; } if (mFilter.getHideTags()) { tokensToShow = tokensToShow & ~Token.TTAG; } SpannableString ss = new SpannableString(task.showParts(tokensToShow).trim()); 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(); } }); } Context mContext = TodoApplication.getAppContext(); String relAge = task.getRelativeAge(mContext); SpannableString relDue = task.getRelativeDueDate(mContext, res.getColor(android.R.color.holo_green_light), res.getColor(android.R.color.holo_red_light), m_app.hasColorDueDates()); String relThres = task.getRelativeThresholdDate(mContext); if (!Strings.isEmptyOrNull(relAge)) { holder.taskage.setText(relAge); holder.taskage.setVisibility(View.VISIBLE); } else { holder.taskage.setText(""); holder.taskage.setVisibility(View.GONE); } if (relDue != null) { holder.taskdue.setText(relDue); holder.taskdue.setVisibility(View.VISIBLE); } else { holder.taskdue.setText(""); holder.taskdue.setVisibility(View.GONE); } if (!Strings.isEmptyOrNull(relThres)) { holder.taskthreshold.setText(relThres); holder.taskthreshold.setVisibility(View.VISIBLE); } else { holder.taskthreshold.setText(""); holder.taskthreshold.setVisibility(View.GONE); } } 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; } } class ActionBarListener implements AbsListView.MultiChoiceModeListener { int numSelected; Menu menu; @Override public void onItemCheckedStateChanged(@NotNull ActionMode mode, int position, long id, boolean checked) { Task t = getTaskAt(position); if (checked) { numSelected++; } else { numSelected--; } if (numSelected == 0) { return; } String title = "" + numSelected; mode.setTitle(title); } @Override public boolean onCreateActionMode(ActionMode mode, @NotNull Menu menu) { MenuInflater inflater = getMenuInflater(); if (m_app.isDarkActionbar()) { inflater.inflate(R.menu.task_context, menu); } else { inflater.inflate(R.menu.task_context_light, menu); } actionMode = mode; if (!m_app.showCompleteCheckbox()) { menu.findItem(R.id.complete).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); menu.findItem(R.id.uncomplete).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); } numSelected = 0; return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { this.menu = menu; return true; } @Override public boolean onActionItemClicked(@NotNull ActionMode mode, @NotNull MenuItem item) { List<Task> checkedTasks = getCheckedTasks(); int menuid = item.getItemId(); Intent intent; switch (menuid) { case R.id.complete: completeTasks(checkedTasks); break; case R.id.select_all: selectAllTasks(); return true; case R.id.uncomplete: undoCompleteTasks(checkedTasks); break; 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(); shareText(shareText); 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.update_lists: updateLists(checkedTasks); return true; case R.id.update_tags: updateTags(checkedTasks); return true; } mode.finish(); return true; } @Override public void onDestroyActionMode(ActionMode mode) { actionMode = null; if (m_drawerLayout != null) { m_drawerLayout.closeDrawers(); } updateDrawers(); } } private void updateLists(@NotNull final List<Task> checkedTasks) { final ArrayList<String> contexts = new ArrayList<String>(); Set<String> selectedContexts = new HashSet<String>(); final TaskCache taskbag = getTaskBag(); contexts.addAll(Util.sortWithPrefix(taskbag.getContexts(), m_app.sortCaseSensitive(), null)); for (Task t : checkedTasks) { selectedContexts.addAll(t.getLists()); } @SuppressLint("InflateParams") View view = getLayoutInflater().inflate(R.layout.tag_dialog, null, false); 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); } } 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> originalLines = new ArrayList<String>(); originalLines.addAll(Util.tasksToString(getCheckedTasks())); 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_app.getTaskCache(null).modify(originalLines, checkedTasks, null, null); } }); 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(@NotNull final List<Task> checkedTasks) { final ArrayList<String> projects = new ArrayList<String>(); Set<String> selectedProjects = new HashSet<String>(); final TaskCache taskbag = getTaskBag(); projects.addAll(Util.sortWithPrefix(taskbag.getProjects(), m_app.sortCaseSensitive(), null)); for (Task t : checkedTasks) { selectedProjects.addAll(t.getTags()); } @SuppressLint("InflateParams") View view = getLayoutInflater().inflate(R.layout.tag_dialog, null, false); 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_tag_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> originalLines = new ArrayList<String>(); originalLines.addAll(Util.tasksToString(checkedTasks)); ArrayList<String> uncheckedItems = new ArrayList<String>(); uncheckedItems.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 : uncheckedItems) { for (Task t : checkedTasks) { t.removeTag("+" + item); } } finishActionmode(); m_app.getTaskCache(null).modify(originalLines, checkedTasks, null, null); } }); 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) { 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); mFilter.saveInPrefs(TodoApplication.getPrefs()); setIntent(intent); finishActionmode(); m_adapter.setFilteredTasks(); adapter.notifyDataSetChanged(); } } private void finishActionmode() { if (actionMode != null) { actionMode.finish(); } } }