Convert TaskEditControlSets to fragments

pull/384/head
Alex Baker 9 years ago
parent 5f352a4966
commit 52f7743a46

@ -19,7 +19,7 @@ public class NotifyAtDeadlineTest extends AndroidTestCase {
@Override
public void setUp() {
Preferences preferences = new Preferences(getContext(), null, null);
Preferences preferences = new Preferences(getContext(), null);
reminderService = new ReminderService(getContext(), preferences, mock(AlarmManager.class));
freezeAt(new DateTime(2014, 1, 24, 17, 23, 37));
}

@ -233,14 +233,14 @@ public class TitleParserTest extends DatabaseTestCase {
String title = "Jog " + acceptedString;
task.setTitle(title); //test at end of task. should set importance.
taskService.createWithValues(task, null, title);
assertEquals((int)task.getImportance(), Task.IMPORTANCE_LEAST);
assertEquals((int)task.getImportance(), Task.IMPORTANCE_NONE);
}
for (String acceptedString:acceptedStrings){
task = new Task();
String title = acceptedString + " jog";
task.setTitle(title); //test at beginning of task. should not set importance.
taskService.createWithValues(task, null, title);
assertNotSame(task.getImportance(),Task.IMPORTANCE_LEAST);
assertNotSame(task.getImportance(),Task.IMPORTANCE_NONE);
}
}

@ -1,6 +1,7 @@
package org.tasks.location;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import org.tasks.preferences.Preferences;
@ -10,7 +11,7 @@ public class PlacePicker {
return null;
}
public static Geofence getPlace(Activity activity, Intent data, Preferences preferences) {
public static Geofence getPlace(Context context, Intent data, Preferences preferences) {
return null;
}
}

@ -97,6 +97,10 @@
<!-- ====================================================== Activities = -->
<activity
android:name=".activities.DatePickerActivity"
android:theme="@style/Theme.AppCompat.Dialog" />
<activity
android:name=".activities.TimePickerActivity"
android:theme="@style/Theme.AppCompat.Dialog" />
@ -377,7 +381,7 @@
<activity
android:name=".activities.CalendarSelectionActivity"
android:theme="@style/TranslucentDialog" />
android:theme="@style/Theme.AppCompat.Dialog" />
<activity
android:name="com.todoroo.astrid.gcal.CalendarReminderActivity"

@ -14,13 +14,17 @@ import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterWithCustomIntent;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.repeats.RepeatControlSet;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.UpgradeActivity;
import com.todoroo.astrid.subtasks.SubtasksHelper;
import com.todoroo.astrid.timers.TimerControlSet;
import org.tasks.R;
import org.tasks.injection.InjectingAppCompatActivity;
import org.tasks.ui.NavigationDrawerFragment;
import org.tasks.ui.PriorityControlSet;
import javax.inject.Inject;
@ -37,8 +41,11 @@ import timber.log.Timber;
* @author Arne
*
*/
public abstract class AstridActivity extends InjectingAppCompatActivity
implements TaskListFragment.OnTaskListItemClickedListener {
public abstract class AstridActivity extends InjectingAppCompatActivity implements
TaskListFragment.OnTaskListItemClickedListener,
PriorityControlSet.OnPriorityChanged,
TimerControlSet.TimerControlSetCallback,
RepeatControlSet.RepeatChangedListener {
public static final int LAYOUT_SINGLE = 0;
public static final int LAYOUT_DOUBLE = 1;
@ -150,14 +157,36 @@ public abstract class AstridActivity extends InjectingAppCompatActivity
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_UPGRADE && resultCode == RESULT_OK) {
if (data != null && data.getBooleanExtra(UpgradeActivity.EXTRA_RESTART, false)) {
Timber.w("Upgrade requires restart");
finish();
startActivity(getIntent());
if (requestCode == REQUEST_UPGRADE) {
if (resultCode == RESULT_OK) {
if (data != null && data.getBooleanExtra(UpgradeActivity.EXTRA_RESTART, false)) {
Timber.w("Upgrade requires restart");
finish();
startActivity(getIntent());
}
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void onPriorityChange(int priority) {
getTaskEditFragment().onPriorityChange(priority);
}
@Override
public void repeatChanged(boolean repeat) {
getTaskEditFragment().onRepeatChanged(repeat);
}
@Override
public Task stopTimer() {
return getTaskEditFragment().stopTimer();
}
@Override
public Task startTimer() {
return getTaskEditFragment().startTimer();
}
}

@ -145,8 +145,7 @@ public class BeastModePreferences extends InjectingListActivity {
}
for (String s : itemsArray) {
if (!s.equals(context.getString(R.string.TEA_ctrl_title_pref)) &&
!s.equals(context.getString(R.string.TEA_ctrl_share_pref)) &&
if (!s.equals(context.getString(R.string.TEA_ctrl_share_pref)) &&
!s.equals(context.getString(R.string.TEA_ctrl_more_pref))) {
list.add(s);
}

@ -15,8 +15,12 @@ import android.view.KeyEvent;
import com.todoroo.andlib.utility.AndroidUtilities;
import org.tasks.ui.CalendarControlSet;
import org.tasks.ui.DescriptionControlSet;
import org.tasks.R;
import org.tasks.preferences.ActivityPreferences;
import org.tasks.ui.PriorityControlSet;
import javax.inject.Inject;

@ -6,8 +6,10 @@
package com.todoroo.astrid.activity;
import android.app.Activity;
import android.app.Dialog;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
@ -25,69 +27,53 @@ import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.actfm.ActFmCameraModule;
import com.todoroo.astrid.alarms.AlarmService;
import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.UserActivityDao;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.files.AACRecordingActivity;
import com.todoroo.astrid.files.FilesControlSet;
import com.todoroo.astrid.gcal.GCalControlSet;
import com.todoroo.astrid.gcal.GCalHelper;
import com.todoroo.astrid.helper.TaskEditControlSet;
import com.todoroo.astrid.notes.EditNoteActivity;
import com.todoroo.astrid.repeats.RepeatControlSet;
import com.todoroo.astrid.service.TaskDeleter;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.tags.TagsControlSet;
import com.todoroo.astrid.timers.TimerActionControlSet;
import com.todoroo.astrid.timers.TimerControlSet;
import com.todoroo.astrid.timers.TimerPlugin;
import com.todoroo.astrid.ui.CheckableImageView;
import com.todoroo.astrid.ui.DescriptionControlSet;
import com.todoroo.astrid.ui.EditTitleControlSet;
import com.todoroo.astrid.ui.HideUntilControlSet;
import com.todoroo.astrid.ui.PopupControlSet;
import com.todoroo.astrid.ui.ReminderControlSet;
import com.todoroo.astrid.utility.Flags;
import org.tasks.R;
import org.tasks.activities.AddAttachmentActivity;
import org.tasks.activities.CameraActivity;
import org.tasks.activities.TimePickerActivity;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ForActivity;
import org.tasks.injection.InjectingFragment;
import org.tasks.location.Geofence;
import org.tasks.location.GeofenceService;
import org.tasks.location.PlacePicker;
import org.tasks.notifications.NotificationManager;
import org.tasks.preferences.ActivityPreferences;
import org.tasks.preferences.Device;
import org.tasks.preferences.PermissionRequestor;
import org.tasks.ui.CalendarControlSet;
import org.tasks.ui.DeadlineControlSet;
import org.tasks.ui.DescriptionControlSet;
import org.tasks.ui.MenuColorizer;
import org.tasks.ui.PriorityControlSet;
import org.tasks.ui.TaskEditControlFragment;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
@ -95,6 +81,8 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import timber.log.Timber;
import static android.app.Activity.RESULT_OK;
/**
* This activity is responsible for creating new tasks and editing existing
* ones. It saves a task when it is paused (screen rotated, back button pressed)
@ -120,8 +108,6 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
*/
public static final String TOKEN_VALUES = "v"; //$NON-NLS-1$
public static final String TOKEN_OPEN_CONTROL = "open_control"; //$NON-NLS-1$
/**
* Task in progress (during orientation change)
*/
@ -139,8 +125,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
// --- request codes
public static final int REQUEST_CODE_RECORD = 30;
public static final int REQUEST_ADD_ATTACHMENT = 50;
public static final int REQUEST_CODE_RECORD = 30; // TODO: move this to file control set
public static final int REQUEST_CODE_CAMERA = 60;
// --- result codes
@ -154,42 +139,27 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
public static final int TAB_VIEW_UPDATES = 0;
@Inject TaskService taskService;
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject TagService tagService;
@Inject MetadataDao metadataDao;
@Inject UserActivityDao userActivityDao;
@Inject TaskDeleter taskDeleter;
@Inject NotificationManager notificationManager;
@Inject AlarmService alarmService;
@Inject GCalHelper gcalHelper;
@Inject ActivityPreferences preferences;
@Inject TagDataDao tagDataDao;
@Inject ActFmCameraModule actFmCameraModule;
@Inject GeofenceService geofenceService;
@Inject DialogBuilder dialogBuilder;
@Inject PermissionRequestor permissionRequestor;
@Inject Device device;
@Inject @ForActivity Context context;
// --- UI components
private final HashMap<String, TaskEditControlSet> controlSetMap = new HashMap<>();
private FilesControlSet filesControlSet;
private TimerActionControlSet timerAction;
private final Map<String, Integer> controlSetFragments = new HashMap<>();
private final List<Integer> displayedFragments = new ArrayList<>();
private EditNoteActivity editNotes;
private HideUntilControlSet hideUntilControls;
private ReminderControlSet reminderControlSet;
@Bind(R.id.title) EditText title;
@Bind(R.id.pager) ViewPager mPager;
@Bind(R.id.updatesFooter) View commentsBar;
@Bind(R.id.completeBox) CheckableImageView checkbox;
@Bind(R.id.timer_container) LinearLayout timerShortcut;
@Bind(R.id.basic_controls) LinearLayout basicControls;
@Bind(R.id.edit_scroll) ScrollView scrollView;
@Bind(R.id.commentField) EditText commentField;
private final List<TaskEditControlSet> controls = Collections.synchronizedList(new ArrayList<TaskEditControlSet>());
// --- other instance variables
/** true if editing started with a new task */
@ -206,7 +176,6 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
private String uuid = RemoteModel.NO_UUID;
private boolean showEditComments;
private boolean showTimerShortcut;
/*
* ======================================================================
@ -231,9 +200,8 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
}
showEditComments = preferences.getBoolean(R.string.p_show_task_edit_comments, true);
showTimerShortcut = preferences.getBoolean(R.string.p_show_timer_shortcut, false);
getActivity().setResult(Activity.RESULT_OK);
getActivity().setResult(RESULT_OK);
}
/*
@ -247,9 +215,90 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.task_edit_activity, container, false);
ButterKnife.bind(this, view);
loadItem(getActivity().getIntent());
registerFragment(R.string.TEA_ctrl_title_pref);
registerFragment(R.string.TEA_ctrl_when_pref);
registerFragment(R.string.TEA_ctrl_gcal);
registerFragment(R.string.TEA_ctrl_importance_pref);
registerFragment(R.string.TEA_ctrl_notes_pref);
registerFragment(R.string.TEA_ctrl_hide_until_pref);
registerFragment(R.string.TEA_ctrl_reminders_pref);
registerFragment(R.string.TEA_ctrl_files_pref);
registerFragment(R.string.TEA_ctrl_timer_pref);
registerFragment(R.string.TEA_ctrl_lists_pref);
registerFragment(R.string.TEA_ctrl_repeat_pref);
ArrayList<String> controlOrder = BeastModePreferences.constructOrderedControlList(preferences, getActivity());
controlOrder.add(0, getString(R.string.TEA_ctrl_title_pref));
String hideAlwaysTrigger = getString(R.string.TEA_ctrl_hide_section_pref);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (String item : controlOrder) {
if (item.equals(hideAlwaysTrigger)) {
break;
}
Integer fragmentId = controlSetFragments.get(item);
if (fragmentId == null) {
Timber.e("Unknown task edit control %s", item);
continue;
}
displayedFragments.add(fragmentId);
if (fragmentManager.findFragmentByTag(item) == null) {
TaskEditControlFragment fragment = createFragment(controlSetFragments.get(item));
if (fragment != null) {
fragment.initialize(isNewTask, model);
fragmentTransaction.add(basicControls.getId(), fragment, item);
}
}
}
fragmentTransaction.commit();
if (!showEditComments) {
commentsBar.setVisibility(View.GONE);
}
return view;
}
private void registerFragment(int resId) {
controlSetFragments.put(getString(resId), resId);
}
private TaskEditControlFragment createFragment(int fragmentId) {
switch (fragmentId) {
case R.string.TEA_ctrl_title_pref:
return new EditTitleControlSet();
case R.string.TEA_ctrl_when_pref:
return new DeadlineControlSet();
case R.string.TEA_ctrl_importance_pref:
return new PriorityControlSet();
case R.string.TEA_ctrl_notes_pref:
return new DescriptionControlSet();
case R.string.TEA_ctrl_gcal:
return new CalendarControlSet();
case R.string.TEA_ctrl_hide_until_pref:
return new HideUntilControlSet();
case R.string.TEA_ctrl_reminders_pref:
return new ReminderControlSet();
case R.string.TEA_ctrl_files_pref:
return new FilesControlSet();
case R.string.TEA_ctrl_timer_pref:
return new TimerControlSet();
case R.string.TEA_ctrl_lists_pref:
return new TagsControlSet();
case R.string.TEA_ctrl_repeat_pref:
return new RepeatControlSet();
default:
throw new RuntimeException("Unsupported fragment");
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@ -267,81 +316,6 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
}
}
// populate control set
EditTitleControlSet editTitle = new EditTitleControlSet(
taskService,
getActivity(),
title,
checkbox);
controls.add(editTitle);
timerAction = new TimerActionControlSet(notificationManager, taskService, getActivity(), getView());
controls.add(timerAction);
TagsControlSet tagsControlSet = new TagsControlSet(metadataDao, tagDataDao, preferences, tagService, getActivity(), dialogBuilder);
controls.add(tagsControlSet);
controlSetMap.put(getString(R.string.TEA_ctrl_lists_pref), tagsControlSet);
RepeatControlSet repeatControls = new RepeatControlSet(preferences, getActivity(), dialogBuilder);
controlSetMap.put(getString(R.string.TEA_ctrl_repeat_pref), repeatControls);
GCalControlSet gcalControl = new GCalControlSet(gcalHelper, preferences, this, permissionRequestor);
controlSetMap.put(getString(R.string.TEA_ctrl_gcal), gcalControl);
// The deadline control set contains the repeat controls and the
// calendar controls.
// NOTE: we add the gcalControl AFTER the
// deadline control, because
// otherwise the correct date may not be written to the calendar event.
// Order matters!
DeadlineControlSet deadlineControl = new DeadlineControlSet(getActivity(), preferences);
controlSetMap.put(getString(R.string.TEA_ctrl_when_pref), deadlineControl);
controls.add(repeatControls);
repeatControls.addListener(editTitle);
controls.add(deadlineControl);
controls.add(gcalControl);
PriorityControlSet importanceControl = new PriorityControlSet(getActivity());
controls.add(importanceControl);
importanceControl.addListener(editTitle);
controlSetMap.put(getString(R.string.TEA_ctrl_importance_pref),
importanceControl);
DescriptionControlSet notesControlSet = new DescriptionControlSet(getActivity());
controls.add(notesControlSet);
controlSetMap.put(getString(R.string.TEA_ctrl_notes_pref),
notesControlSet);
reminderControlSet = new ReminderControlSet(alarmService, geofenceService, this, permissionRequestor, device);
controls.add(reminderControlSet);
controlSetMap.put(getString(R.string.TEA_ctrl_reminders_pref), reminderControlSet);
hideUntilControls = new HideUntilControlSet(this);
controls.add(hideUntilControls);
controlSetMap.put(getString(R.string.TEA_ctrl_hide_until_pref), hideUntilControls);
// TODO: Fix the fact that hideUntil doesn't update accordingly with date changes when lazy loaded. Until then, don't lazy load.
hideUntilControls.getView();
TimerControlSet timerControl = new TimerControlSet(preferences, getActivity(), dialogBuilder);
timerAction.addListener(timerControl);
controls.add(timerControl);
controlSetMap.put(getString(R.string.TEA_ctrl_timer_pref), timerControl);
filesControlSet = new FilesControlSet(preferences, taskAttachmentDao, this);
controls.add(filesControlSet);
controlSetMap.put(getString(R.string.TEA_ctrl_files_pref), filesControlSet);
loadEditPageOrder();
if (!showEditComments) {
commentsBar.setVisibility(View.GONE);
}
if (!showTimerShortcut) {
timerShortcut.setVisibility(View.GONE);
}
// Load task data in background
new TaskEditBackgroundLoader().start();
}
@ -349,7 +323,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
private void instantiateEditNotes() {
if (showEditComments) {
long idParam = getActivity().getIntent().getLongExtra(TOKEN_ID, -1L);
editNotes = new EditNoteActivity(actFmCameraModule, preferences, metadataDao, userActivityDao,
editNotes = new EditNoteActivity(actFmCameraModule, metadataDao, userActivityDao,
taskService, this, getView(), idParam);
editNotes.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT));
@ -373,12 +347,11 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
editNotes.loadViewForTaskID(idParam);
}
if (timerAction != null && editNotes != null) {
timerAction.removeListener(editNotes);
timerAction.addListener(editNotes);
}
if (editNotes != null) {
TimerControlSet timerControl = getTimerControl();
if (timerControl != null) {
timerControl.setEditNotes(editNotes);
}
editNotes.addListener(this);
}
@ -406,40 +379,14 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
mPager.setCurrentItem(position);
}
private void loadEditPageOrder() {
ArrayList<String> controlOrder = BeastModePreferences.constructOrderedControlList(preferences, getActivity());
String[] itemOrder = controlOrder.toArray(new String[controlOrder.size()]);
String hideAlwaysTrigger = getString(R.string.TEA_ctrl_hide_section_pref);
Class<?> openControl = (Class<?>) getActivity().getIntent().getSerializableExtra(TOKEN_OPEN_CONTROL);
for (String item : itemOrder) {
if (item.equals(hideAlwaysTrigger)) {
break; // As soon as we hit the hide section, we're done
} else {
View controlSet = null;
TaskEditControlSet curr = controlSetMap.get(item);
if (curr != null) {
controlSet = curr.getView();
}
if (controlSet != null) {
ImageView icon = (ImageView) controlSet.findViewById(R.id.icon);
if (icon != null) {
icon.setImageResource(curr.getIcon());
}
basicControls.addView(controlSet);
}
if (curr != null && curr.getClass().equals(openControl) && curr instanceof PopupControlSet) {
curr.getView().performClick();
}
}
}
public Task stopTimer() {
TimerPlugin.stopTimer(notificationManager, taskService, context, model);
return model;
}
getActivity().getIntent().removeExtra(TOKEN_OPEN_CONTROL);
public Task startTimer() {
TimerPlugin.startTimer(notificationManager, taskService, context, model);
return model;
}
/**
@ -538,61 +485,44 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
public void repopulateFromScratch(Intent intent) {
model = null;
uuid = RemoteModel.NO_UUID;
populateFields(intent);
loadMoreContainer();
}
/** Populate UI component values from the model */
public void populateFields(Intent intent) {
loadItem(intent);
synchronized (controls) {
for (TaskEditControlSet controlSet : controls) {
controlSet.readFromTask(model);
}
}
private String getTitle() {
return getEditTitleControlSet().getTitle();
}
/** Save task model from values in UI components */
public void save(boolean onPause) {
String title = getTitle();
if (title == null) {
return;
}
if (title.getText().length() > 0) {
if (title.length() > 0) {
model.setDeletionDate(0L);
}
if (title.getText().length() == 0) {
if (title.length() == 0) {
return;
}
synchronized (controls) {
for (TaskEditControlSet controlSet : controls) {
if (controlSet instanceof PopupControlSet) { // Save open control set
PopupControlSet popup = (PopupControlSet) controlSet;
Dialog d = popup.getDialog();
if (d != null && d.isShowing()) {
getActivity().getIntent().putExtra(TOKEN_OPEN_CONTROL, popup.getClass());
}
}
controlSet.writeToModel(model);
if (!onPause) {
for (Integer fragmentId : displayedFragments) {
getFragment(fragmentId).apply(model);
}
}
boolean tagsChanged = Flags.check(Flags.TAGS_CHANGED);
model.putTransitory(TaskService.TRANS_EDIT_SAVE, true); // TODO: not used?
taskService.save(model);
taskService.save(model);
if (!onPause) {
boolean taskEditActivity = (getActivity() instanceof TaskEditActivity);
boolean tagsChanged = Flags.check(Flags.TAGS_CHANGED);
if (taskEditActivity) {
Intent data = new Intent();
data.putExtra(TOKEN_TAGS_CHANGED, tagsChanged);
data.putExtra(TOKEN_ID, model.getId());
data.putExtra(TOKEN_UUID, model.getUuid());
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().setResult(RESULT_OK, data);
} else {
// Notify task list fragment in multi-column case
@ -611,13 +541,29 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
removeExtrasFromIntent(getActivity().getIntent());
shouldSaveState = false;
getActivity().onBackPressed();
}
}
private EditTitleControlSet getEditTitleControlSet() {
return getFragment(R.string.TEA_ctrl_title_pref);
}
private FilesControlSet getFilesControlSet() {
return getFragment(R.string.TEA_ctrl_files_pref);
}
private TimerControlSet getTimerControl() {
return getFragment(R.string.TEA_ctrl_timer_pref);
}
@SuppressWarnings("unchecked")
private <T extends TaskEditControlFragment> T getFragment(int tag) {
return (T) getFragmentManager().findFragmentByTag(getString(tag));
}
public boolean onKeyDown(int keyCode) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if(title.getText().length() == 0) {
if(getTitle().length() == 0) {
discardButtonClick();
} else {
saveButtonClick();
@ -640,11 +586,11 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
}
if (activity instanceof TaskListActivity) {
if (title.getText().length() == 0 && isNewTask && model != null && model.isSaved()) {
if (getTitle().length() == 0 && isNewTask && model != null && model.isSaved()) {
taskDeleter.delete(model);
}
} else if (activity instanceof TaskEditActivity) {
if (title.getText().length() == 0 && isNewTask && model != null && model.isSaved()) {
if (getTitle().length() == 0 && isNewTask && model != null && model.isSaved()) {
taskDeleter.delete(model);
}
}
@ -674,7 +620,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
shouldSaveState = false;
// abandon editing in this case
if (title.getText().toString().trim().length() == 0 || TextUtils.isEmpty(model.getTitle())) {
if (getTitle().trim().length() == 0 || TextUtils.isEmpty(model.getTitle())) {
if (isNewTask) {
TimerPlugin.stopTimer(notificationManager, taskService, getActivity(), model);
taskDeleter.delete(model);
@ -700,7 +646,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
Activity a = getActivity();
if (a instanceof TaskEditActivity) {
getActivity().setResult(Activity.RESULT_OK);
getActivity().setResult(RESULT_OK);
getActivity().onBackPressed();
} else if (a instanceof TaskListActivity) {
discardButtonClick();
@ -738,7 +684,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
deleteButtonClick();
return true;
case android.R.id.home:
if (title.getText().toString().trim().length() == 0) {
if (getTitle().trim().length() == 0) {
discardButtonClick();
} else {
saveButtonClick();
@ -768,59 +714,18 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
}
}
@Override
public void onStart() {
super.onStart();
populateFields(getActivity().getIntent());
if (isNewTask) {
title.requestFocus();
title.setCursorVisible(true);
getActivity().getWindow()
.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (editNotes == null) {
instantiateEditNotes();
}
if (requestCode == HideUntilControlSet.REQUEST_HIDE_UNTIL && resultCode == Activity.RESULT_OK) {
long timestamp = data.getLongExtra(TimePickerActivity.EXTRA_TIMESTAMP, 0L);
if (timestamp > 0) {
hideUntilControls.setCustomDate(timestamp);
} else {
Timber.e("Invalid timestamp");
}
} else if (requestCode == ReminderControlSet.REQUEST_NEW_ALARM && resultCode == Activity.RESULT_OK) {
long timestamp = data.getLongExtra(TimePickerActivity.EXTRA_TIMESTAMP, 0L);
if (timestamp > 0) {
reminderControlSet.addAlarmRow(timestamp);
} else {
Timber.e("Invalid timestamp");
}
} else if (requestCode == ReminderControlSet.REQUEST_LOCATION_REMINDER) {
if (resultCode == Activity.RESULT_OK) {
Geofence geofence = PlacePicker.getPlace(getActivity(), data, preferences);
if (geofence != null) {
reminderControlSet.addGeolocationReminder(geofence);
} else {
Timber.e("Invalid geofence");
}
}
} else if (requestCode == REQUEST_CODE_RECORD && resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_RECORD && resultCode == RESULT_OK) {
String recordedAudioPath = data.getStringExtra(AACRecordingActivity.RESULT_OUTFILE);
String recordedAudioName = data.getStringExtra(AACRecordingActivity.RESULT_FILENAME);
filesControlSet.createNewFileAttachment(recordedAudioPath, recordedAudioName, TaskAttachment.FILE_TYPE_AUDIO + "m4a"); //$NON-NLS-1$
} else if (requestCode == REQUEST_ADD_ATTACHMENT && resultCode == Activity.RESULT_OK) {
String path = data.getStringExtra(AddAttachmentActivity.EXTRA_PATH);
File file = new File(path);
String extension = path.substring(path.lastIndexOf('.') + 1);
filesControlSet.createNewFileAttachment(path, file.getName(), TaskAttachment.FILE_TYPE_IMAGE + extension);
getFilesControlSet().createNewFileAttachment(recordedAudioPath, recordedAudioName, TaskAttachment.FILE_TYPE_AUDIO + "m4a"); //$NON-NLS-1$
} else if (requestCode == REQUEST_CODE_CAMERA) {
if (editNotes != null && resultCode == Activity.RESULT_OK) {
if (editNotes != null && resultCode == RESULT_OK) {
Uri uri = data.getParcelableExtra(CameraActivity.EXTRA_URI);
editNotes.setPictureUri(uri);
}
@ -901,8 +806,16 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
}
private void hideKeyboard() {
AndroidUtilities.hideSoftInputForViews(getActivity(), title, commentField);
title.setCursorVisible(false);
getEditTitleControlSet().hideKeyboard();
AndroidUtilities.hideSoftInputForViews(getActivity(), commentField);
commentField.setCursorVisible(false);
}
public void onPriorityChange(int priority) {
getEditTitleControlSet().setPriority(priority);
}
public void onRepeatChanged(boolean repeat) {
getEditTitleControlSet().repeatChanged(repeat);
}
}

@ -391,11 +391,11 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
}
private void showFilesDialog(Task task) {
FilesControlSet filesControlSet = new FilesControlSet(
preferences, taskAttachmentDao, fragment);
filesControlSet.hideAddAttachmentButton();
filesControlSet.readFromTask(task);
filesControlSet.getView().performClick();
// TODO: reimplement this
// FilesControlSet filesControlSet = new FilesControlSet();
// filesControlSet.hideAddAttachmentButton();
// filesControlSet.readFromTask(task);
// filesControlSet.getView().performClick();
}
/* ======================================================================

@ -73,7 +73,9 @@ public class DefaultsPreferences extends InjectingPreferenceActivity {
}
private void startCalendarSelectionActivity() {
startActivityForResult(new Intent(DefaultsPreferences.this, CalendarSelectionActivity.class), REQUEST_CALENDAR_SELECTION);
startActivityForResult(new Intent(DefaultsPreferences.this, CalendarSelectionActivity.class) {{
putExtra(CalendarSelectionActivity.EXTRA_SHOW_NONE, true);
}}, REQUEST_CALENDAR_SELECTION);
}
@Override

@ -55,6 +55,10 @@ public class TagDataDao {
.orderBy(Order.asc(TagData.ID)));
}
public TagData getByUuid(String uuid) {
return getByUuid(uuid, TagData.PROPERTIES);
}
public TagData getByUuid(String uuid, Property<?>... properties) {
return dao.getFirst(Query.select(properties).where(TagData.UUID.eq(uuid)));
}

@ -110,6 +110,7 @@ public final class TagData extends RemoteModel {
setValue(LAST_AUTOSYNC, lastAutosync);
}
// TODO: remove?
public String getUUID() {
return getValue(UUID);
}

@ -150,17 +150,6 @@ public class Task extends RemoteModel {
/**
* @return colors that correspond to importance values
*/
public static int[] getImportanceColors(Resources r) {
return new int[] {
r.getColor(R.color.importance_1),
r.getColor(R.color.importance_2),
r.getColor(R.color.importance_3),
r.getColor(R.color.importance_4)
};
}
public static int IMPORTANCE_MOST = IMPORTANCE_DO_OR_DIE;
public static int IMPORTANCE_LEAST = IMPORTANCE_NONE;
// --- defaults
@ -544,10 +533,14 @@ public class Task extends RemoteModel {
return getValue(ESTIMATED_SECONDS);
}
public void setELAPSED_SECONDS(Integer elapsedSeconds) {
public void setElapsedSeconds(Integer elapsedSeconds) {
setValue(ELAPSED_SECONDS, elapsedSeconds);
}
public void setEstimatedSeconds(Integer estimatedSeconds) {
setValue(ESTIMATED_SECONDS, estimatedSeconds);
}
public void setCalendarUri(String calendarUri) {
setValue(CALENDAR_URI, calendarUri);
}

@ -6,17 +6,20 @@
package com.todoroo.astrid.files;
import android.app.Activity;
import android.app.Fragment;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.LinearLayout;
import android.widget.TextView;
@ -24,46 +27,112 @@ import android.widget.Toast;
import com.todoroo.andlib.data.Callback;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R;
import org.tasks.activities.AddAttachmentActivity;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.preferences.ActivityPreferences;
import org.tasks.injection.ForActivity;
import org.tasks.ui.TaskEditControlFragment;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import timber.log.Timber;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop;
public class FilesControlSet extends TaskEditControlSetBase {
private final ArrayList<TaskAttachment> files = new ArrayList<>();
private final LayoutInflater inflater;
private final TaskAttachmentDao taskAttachmentDao;
private final Fragment fragment;
private final DialogBuilder dialogBuilder;
private LinearLayout attachmentContainer;
private TextView addAttachment;
public FilesControlSet(ActivityPreferences preferences, TaskAttachmentDao taskAttachmentDao,
Fragment fragment) {
super(fragment.getActivity(), R.layout.control_set_files);
this.taskAttachmentDao = taskAttachmentDao;
this.fragment = fragment;
this.dialogBuilder = new DialogBuilder(activity, preferences);
inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
public class FilesControlSet extends TaskEditControlFragment {
private static final int REQUEST_ADD_ATTACHMENT = 50;
private static final String EXTRA_UUID = "extra_uuid";
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject DialogBuilder dialogBuilder;
@Inject @ForActivity Context context;
@Bind(R.id.attachment_container) LinearLayout attachmentContainer;
@Bind(R.id.add_attachment) TextView addAttachment;
private String taskUuid;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
taskUuid = savedInstanceState.getString(EXTRA_UUID);
}
final List<TaskAttachment> files = new ArrayList<>();
taskAttachmentDao.getAttachments(taskUuid, new Callback<TaskAttachment>() {
@Override
public void apply(TaskAttachment attachment) {
files.add(attachment);
addAttachment(attachment);
}
});
validateFiles(files);
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_UUID, taskUuid);
}
@OnClick(R.id.add_attachment)
void addAttachment(View view) {
startActivityForResult(new Intent(context, AddAttachmentActivity.class), REQUEST_ADD_ATTACHMENT);
}
@Override
protected int getLayout() {
return R.layout.control_set_files;
}
@Override
public int getIcon() {
return R.drawable.ic_attachment_24dp;
}
@Override
public void initialize(boolean isNewTask, Task task) {
taskUuid = task.getUuid();
}
@Override
public void apply(Task task) {
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ADD_ATTACHMENT) {
if (resultCode == Activity.RESULT_OK) {
String path = data.getStringExtra(AddAttachmentActivity.EXTRA_PATH);
File file = new File(path);
String extension = path.substring(path.lastIndexOf('.') + 1);
createNewFileAttachment(path, file.getName(), TaskAttachment.FILE_TYPE_IMAGE + extension);
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void addAttachment(TaskAttachment taskAttachment) {
View fileRow = inflater.inflate(R.layout.file_row, null);
View fileRow = getActivity().getLayoutInflater().inflate(R.layout.file_row, null);
fileRow.setTag(taskAttachment);
attachmentContainer.addView(fileRow);
addAttachment(taskAttachment, fileRow);
@ -71,7 +140,6 @@ public class FilesControlSet extends TaskEditControlSetBase {
private void addAttachment(final TaskAttachment taskAttachment, final View fileRow) {
TextView nameView = (TextView) fileRow.findViewById(R.id.file_text);
nameView.setTextColor(themeColor);
String name = taskAttachment.getName();
nameView.setText(name);
nameView.setOnClickListener(new OnClickListener() {
@ -93,7 +161,6 @@ public class FilesControlSet extends TaskEditControlSetBase {
File f = new File(taskAttachment.getFilePath());
f.delete();
}
files.remove(taskAttachment);
attachmentContainer.removeView(fileRow);
}
})
@ -103,28 +170,7 @@ public class FilesControlSet extends TaskEditControlSetBase {
});
}
@Override
public int getIcon() {
return R.drawable.ic_attachment_24dp;
}
public void refreshMetadata() {
if (model != null) {
files.clear();
taskAttachmentDao.getAttachments(model.getUuid(), new Callback<TaskAttachment>() {
@Override
public void apply(TaskAttachment attachment) {
files.add(attachment);
}
});
validateFiles();
if (initialized) {
afterInflate();
}
}
}
private void validateFiles() {
private void validateFiles(List<TaskAttachment> files) {
for (int i = 0; i < files.size(); i++) {
TaskAttachment m = files.get(i);
if (m.containsNonNullValue(TaskAttachment.FILE_PATH)) {
@ -140,34 +186,6 @@ public class FilesControlSet extends TaskEditControlSetBase {
}
}
@Override
protected void readFromTaskOnInitialize() {
attachmentContainer.removeAllViews();
taskAttachmentDao.getAttachments(model.getUuid(), new Callback<TaskAttachment>() {
@Override
public void apply(TaskAttachment entry) {
addAttachment(entry);
}
});
}
@Override
protected void writeToModelAfterInitialized(Task task) {
// Nothing to write
}
@Override
protected void afterInflate() {
attachmentContainer = (LinearLayout) getView().findViewById(R.id.attachment_container);
addAttachment = (TextView) getView().findViewById(R.id.add_attachment);
addAttachment.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
fragment.startActivityForResult(new Intent(activity, AddAttachmentActivity.class), TaskEditFragment.REQUEST_ADD_ATTACHMENT);
}
});
}
public void hideAddAttachmentButton() {
addAttachment.setVisibility(View.GONE);
}
@ -210,10 +228,10 @@ public class FilesControlSet extends TaskEditControlSetBase {
if (atLeastLollipop()) {
intent.setClipData(ClipData.newRawUri(null, uri));
}
activity.startActivity(intent);
getActivity().startActivity(intent);
} catch(ActivityNotFoundException e) {
Timber.e(e, e.getMessage());
Toast.makeText(activity, R.string.no_application_found, Toast.LENGTH_SHORT).show();
Toast.makeText(context, R.string.no_application_found, Toast.LENGTH_SHORT).show();
}
} else {
String useType = fileType;
@ -240,17 +258,16 @@ public class FilesControlSet extends TaskEditControlSetBase {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(file)), type);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(intent);
getActivity().startActivity(intent);
} catch (ActivityNotFoundException e) {
Timber.e(e, e.getMessage());
Toast.makeText(activity, R.string.file_type_unhandled, Toast.LENGTH_LONG).show();
Toast.makeText(context, R.string.file_type_unhandled, Toast.LENGTH_LONG).show();
}
}
public void createNewFileAttachment(String path, String fileName, String fileType) {
TaskAttachment attachment = TaskAttachment.createNewAttachment(model.getUuid(), path, fileName, fileType);
TaskAttachment attachment = TaskAttachment.createNewAttachment(taskUuid, path, fileName, fileType);
taskAttachmentDao.createNew(attachment);
refreshMetadata();
addAttachment(attachment);
}
}

@ -19,7 +19,6 @@ public class Calendars {
public static final String ID_COLUMN_NAME = "_id";
public static final String CALENDARS_DISPLAY_COL = CalendarContract.Calendars.CALENDAR_DISPLAY_NAME;
public static final String CALENDARS_ACCESS_LEVEL_COL = CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL;
public static final String EVENTS_DTSTART_COL = CalendarContract.Events.DTSTART;
public static final String EVENTS_DTEND_COL = CalendarContract.Events.DTEND;
public static final String EVENTS_NAME_COL = CalendarContract.Events.TITLE;
@ -34,7 +33,7 @@ public class Calendars {
// Only show calendars that the user can modify. Access level 500
// corresponds to Calendars.CONTRIBUTOR_ACCESS
public static final String CALENDARS_WHERE = CALENDARS_ACCESS_LEVEL_COL + ">= 500";
public static final String CALENDARS_WHERE = CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL + ">= 500";
public static final String CALENDARS_SORT = CALENDARS_DISPLAY_COL + " ASC";

@ -1,266 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gcal;
import android.app.FragmentManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R;
import org.tasks.activities.CalendarSelectionDialog;
import org.tasks.preferences.PermissionRequestor;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
/**
* Control Set for managing repeats
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class GCalControlSet extends TaskEditControlSetBase implements CalendarSelectionDialog.CalendarSelectionHandler {
private static final String FRAG_TAG_CALENDAR_SELECTION = "frag_tag_calendar_selection";
// --- instance variables
private final GCalHelper gcal;
private Preferences preferences;
private final TaskEditFragment taskEditFragment;
private PermissionRequestor permissionRequestor;
private Uri calendarUri = null;
private boolean hasEvent = false;
private TextView calendar;
private ImageView cancelButton;
private String calendarId;
private String calendarName;
public GCalControlSet(GCalHelper gcal, Preferences preferences,
TaskEditFragment taskEditFragment, PermissionRequestor permissionRequestor) {
super(taskEditFragment.getActivity(), R.layout.control_set_gcal_display);
this.gcal = gcal;
this.preferences = preferences;
this.taskEditFragment = taskEditFragment;
this.permissionRequestor = permissionRequestor;
}
@Override
protected void afterInflate() {
View view = getView();
calendar = (TextView) view.findViewById(R.id.calendar_display_which);
cancelButton = (ImageView) view.findViewById(R.id.clear);
calendar.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (hasEvent) {
viewCalendarEvent();
} else {
// TODO: show calendar selection if permission has just been granted
// can't do this now because the app saves state when TEA is paused,
// which triggers calendar creation if there is a default add to calendar.
if (permissionRequestor.requestCalendarPermissions()) {
showCalendarSelectionDialog();
}
}
}
});
cancelButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
clearEvent();
refreshDisplayView();
}
});
}
public void showCalendarSelectionDialog() {
FragmentManager fragmentManager = taskEditFragment.getFragmentManager();
CalendarSelectionDialog fragmentByTag = (CalendarSelectionDialog) fragmentManager.findFragmentByTag(FRAG_TAG_CALENDAR_SELECTION);
if (fragmentByTag == null) {
fragmentByTag = new CalendarSelectionDialog();
fragmentByTag.show(fragmentManager, FRAG_TAG_CALENDAR_SELECTION);
}
fragmentByTag.setCalendarSelectionHandler(GCalControlSet.this);
}
@Override
protected void readFromTaskOnInitialize() {
String uri = gcal.getTaskEventUri(model);
if(!TextUtils.isEmpty(uri)) {
try {
calendarUri = Uri.parse(uri);
// try to load calendar
ContentResolver cr = activity.getContentResolver();
Cursor cursor = cr.query(calendarUri, new String[] { "dtstart" }, null, null, null); //$NON-NLS-1$
try {
boolean deleted = cursor.getCount() == 0;
if(deleted) {
clearEvent();
hasEvent = false;
} else {
hasEvent = true;
}
} finally {
cursor.close();
}
} catch (Exception e) {
Timber.e(e, "unable-to-parse-calendar: %s", model.getCalendarURI());
}
} else {
hasEvent = false;
clearEvent();
}
refreshDisplayView();
}
private void clearEvent() {
calendarId = null;
calendarUri = null;
calendarName = null;
}
@Override
protected void writeToModelAfterInitialized(Task task) {
if (!task.hasDueDate()) {
return;
}
if ((preferences.isDefaultCalendarSet() || calendarId != null) && calendarUri == null) {
try{
ContentResolver cr = activity.getContentResolver();
ContentValues values = new ContentValues();
values.put("calendar_id", calendarId);
calendarUri = gcal.createTaskEvent(task, cr, values);
if(calendarUri != null) {
task.setCalendarUri(calendarUri.toString());
if (!hasEvent) {
// pop up the new event
Intent intent = new Intent(Intent.ACTION_VIEW, calendarUri);
intent.putExtra("beginTime", values.getAsLong("dtstart"));
intent.putExtra("endTime", values.getAsLong("dtend"));
activity.startActivity(intent);
}
}
} catch (Exception e) {
Timber.e(e, e.getMessage());
}
} else if(calendarUri != null) {
try {
ContentValues updateValues = new ContentValues();
// check if we need to update the item
ContentValues setValues = task.getSetValues();
if(setValues.containsKey(Task.TITLE.name)) {
updateValues.put("title", task.getTitle());
}
if(setValues.containsKey(Task.NOTES.name)) {
updateValues.put("description", task.getNotes());
}
if(setValues.containsKey(Task.DUE_DATE.name) || setValues.containsKey(Task.ESTIMATED_SECONDS.name)) {
gcal.createStartAndEndDate(task, updateValues);
}
ContentResolver cr = activity.getContentResolver();
cr.update(calendarUri, updateValues, null, null);
} catch (Exception e) {
Timber.e(e, "unable-to-update-calendar: %s", task.getCalendarURI());
}
}
}
private void viewCalendarEvent() {
if(calendarUri == null) {
return;
}
ContentResolver cr = activity.getContentResolver();
Intent intent = new Intent(Intent.ACTION_VIEW, calendarUri);
Cursor cursor = cr.query(calendarUri, new String[] { "dtstart", "dtend" },
null, null, null);
try {
if(cursor.getCount() == 0) {
// event no longer exists, recreate it
calendarUri = null;
writeToModel(model);
return;
}
cursor.moveToFirst();
intent.putExtra("beginTime", cursor.getLong(0));
intent.putExtra("endTime", cursor.getLong(1));
} catch (Exception e) {
Timber.e(e, e.getMessage());
Toast.makeText(activity, R.string.gcal_TEA_error, Toast.LENGTH_LONG).show();
} finally {
cursor.close();
}
activity.startActivity(intent);
}
private void refreshDisplayView() {
calendar.setTextColor(themeColor);
if (initialized) {
if (hasEvent) {
calendar.setText(R.string.gcal_TEA_showCalendar_label);
cancelButton.setVisibility(View.GONE);
} else if (calendarName != null) {
calendar.setText(calendarName);
cancelButton.setVisibility(View.VISIBLE);
} else {
calendar.setTextColor(unsetColor);
calendar.setText(R.string.gcal_TEA_addToCalendar_label);
cancelButton.setVisibility(View.GONE);
}
} else {
cancelButton.setVisibility(View.GONE);
if (TextUtils.isEmpty(model.getCalendarURI())) {
calendar.setTextColor(unsetColor);
calendar.setText(R.string.gcal_TEA_addToCalendar_label);
} else {
calendar.setText(R.string.gcal_TEA_showCalendar_label);
}
}
}
@Override
public int getIcon() {
return R.drawable.ic_event_24dp;
}
@Override
public void selectedCalendar(AndroidCalendar androidCalendar) {
this.calendarId = androidCalendar.getId();
this.calendarName = androidCalendar.getName();
refreshDisplayView();
}
@Override
public void dismiss() {
}
}

@ -194,7 +194,7 @@ public class GCalHelper {
return eventDeleted;
}
void createStartAndEndDate(Task task, ContentValues values) {
public void createStartAndEndDate(Task task, ContentValues values) {
long dueDate = task.getDueDate();
long tzCorrectedDueDate = dueDate + TimeZone.getDefault().getOffset(dueDate);
long tzCorrectedDueDateNow = DateUtilities.now() + TimeZone.getDefault().getOffset(DateUtilities.now());
@ -232,6 +232,26 @@ public class GCalHelper {
}
}
public AndroidCalendar getCalendar(String id) {
ContentResolver cr = context.getContentResolver();
Cursor c = cr.query(getCalendarContentUri(Calendars.CALENDAR_CONTENT_CALENDARS), Calendars.CALENDARS_PROJECTION,
Calendars.CALENDARS_WHERE + " AND Calendars._id=" + id, null, Calendars.CALENDARS_SORT);
try {
if (c.moveToFirst()) {
int nameColumn = c.getColumnIndex(Calendars.CALENDARS_DISPLAY_COL);
String name = c.getString(nameColumn);
return new AndroidCalendar(id, name);
}
} finally {
if(c != null) {
c.close();
}
}
return null;
}
/**
* Appends all user-modifiable calendars to listPreference.
*/

@ -1,15 +0,0 @@
package com.todoroo.astrid.helper;
import android.view.View;
import com.todoroo.astrid.data.Task;
public interface TaskEditControlSet {
View getView();
void readFromTask(Task task);
void writeToModel(Task task);
int getIcon();
}

@ -1,110 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.helper;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import com.todoroo.astrid.data.Task;
import org.tasks.R;
import static org.tasks.preferences.ResourceResolver.getData;
// --- interface
/**
* Interface for working with controls that alter task data
*/
public abstract class TaskEditControlSetBase implements TaskEditControlSet {
protected final Activity activity;
private final int viewLayout;
private boolean useTemplate;
private View view;
protected Task model;
protected boolean initialized = false;
protected final int themeColor;
protected final int unsetColor;
public TaskEditControlSetBase(Activity activity, int viewLayout) {
this(activity, viewLayout, true);
}
public TaskEditControlSetBase(Activity activity, int viewLayout, boolean useTemplate) {
this.activity = activity;
this.viewLayout = viewLayout;
this.useTemplate = useTemplate;
if (viewLayout == -1) {
initialized = true;
}
themeColor = getData(activity, R.attr.asTextColor);
unsetColor = getData(activity, R.attr.asTextColorHint);
}
protected View inflateWithTemplate(int layout) {
LayoutInflater layoutInflater = LayoutInflater.from(activity);
View template = layoutInflater.inflate(R.layout.control_set_template, null);
LinearLayout content = (LinearLayout) template.findViewById(R.id.content);
content.addView(layoutInflater.inflate(layout, null));
return template;
}
@Override
public View getView() {
if (view == null && !initialized) {
if (viewLayout != -1) {
view = useTemplate ? inflateWithTemplate(viewLayout) : LayoutInflater.from(activity).inflate(viewLayout, null);
afterInflate();
}
if (model != null) {
readFromTaskOnInitialize();
}
this.initialized = true;
}
return view;
}
/**
* Read data from model to update the control set
*/
@Override
public void readFromTask(Task task) {
this.model = task;
if (initialized) {
readFromTaskOnInitialize();
}
}
/**
* Called once to setup the ui with data from the task
*/
protected abstract void readFromTaskOnInitialize();
/**
* Write data from control set to model
*/
@Override
public void writeToModel(Task task) {
if (initialized) {
writeToModelAfterInitialized(task);
}
}
/**
* Write to model, if initialization logic has been called
*/
protected abstract void writeToModelAfterInitialized(Task task);
/**
* Called when views need to be inflated
*/
protected abstract void afterInflate();
}

@ -44,11 +44,10 @@ import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.UserActivity;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.timers.TimerActionControlSet.TimerActionListener;
import com.todoroo.astrid.timers.TimerActionListener;
import org.json.JSONObject;
import org.tasks.R;
import org.tasks.preferences.Preferences;
import java.util.ArrayList;
import java.util.Collections;
@ -67,14 +66,12 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
private Task task;
private ActFmCameraModule actFmCameraModule;
private final Preferences preferences;
private final MetadataDao metadataDao;
private final UserActivityDao userActivityDao;
private final TaskService taskService;
private final ArrayList<NoteOrUpdate> items = new ArrayList<>();
private EditText commentField;
private final View commentsBar;
private View timerView;
private View commentButton;
private int commentItems = 10;
private ImageButton pictureButton;
@ -94,7 +91,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
public EditNoteActivity(
ActFmCameraModule actFmCameraModule,
Preferences preferences,
MetadataDao metadataDao,
UserActivityDao userActivityDao,
TaskService taskService,
@ -103,7 +99,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
long t) {
super(fragment.getActivity());
this.actFmCameraModule = actFmCameraModule;
this.preferences = preferences;
this.metadataDao = metadataDao;
this.userActivityDao = userActivityDao;
this.taskService = taskService;
@ -141,26 +136,9 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
// --- UI preparation
private void setUpInterface() {
timerView = commentsBar.findViewById(R.id.timer_container);
commentButton = commentsBar.findViewById(R.id.commentButton);
commentField = (EditText) commentsBar.findViewById(R.id.commentField);
final boolean showTimerShortcut = preferences.getBoolean(R.string.p_show_timer_shortcut, false);
if (showTimerShortcut) {
commentField.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
timerView.setVisibility(View.GONE);
commentButton.setVisibility(View.VISIBLE);
} else {
timerView.setVisibility(View.VISIBLE);
commentButton.setVisibility(View.GONE);
}
}
});
}
commentField.setHorizontallyScrolling(false);
commentField.setMaxLines(Integer.MAX_VALUE);
commentField.setOnKeyListener(new OnKeyListener() {
@ -185,10 +163,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
public void afterTextChanged(Editable s) {
commentButton.setVisibility((s.length() > 0 || pendingCommentPicture != null) ? View.VISIBLE
: View.GONE);
if (showTimerShortcut) {
timerView.setVisibility((s.length() > 0 || pendingCommentPicture != null) ? View.GONE
: View.VISIBLE);
}
}
@Override
@ -241,7 +215,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
}
});
if(!TextUtils.isEmpty(task.getNotes())) {
TextView notes = new TextView(getContext());
TextView notes = new TextView(activity);
notes.setLinkTextColor(Color.rgb(100, 160, 255));
notes.setTextSize(18);
notes.setText(task.getNotes());
@ -294,7 +268,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
}
if (items.size() > commentItems) {
Button loadMore = new Button(getContext());
Button loadMore = new Button(activity);
loadMore.setText(R.string.TEA_load_more);
loadMore.setTextColor(activity.getResources().getColor(R.color.task_edit_deadline_gray));
loadMore.setBackgroundColor(Color.alpha(0));
@ -315,7 +289,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
}
public View getUpdateNotes(NoteOrUpdate note, ViewGroup parent) {
View convertView = ((Activity)getContext()).getLayoutInflater().inflate(
View convertView = ((Activity)activity).getLayoutInflater().inflate(
R.layout.comment_adapter_row, parent, false);
bindView(convertView, note);
return convertView;
@ -364,7 +338,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
private void addComment() {
addComment(commentField.getText().toString(), UserActivity.ACTION_TASK_COMMENT, task.getUuid(), true);
AndroidUtilities.hideSoftInputForViews(getContext(), commentField);
AndroidUtilities.hideSoftInputForViews(activity, commentField);
commentField.setCursorVisible(false);
}
@ -459,8 +433,8 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
@Override
public void timerStarted(Task t) {
addComment(String.format("%s %s", //$NON-NLS-1$
getContext().getString(R.string.TEA_timer_comment_started),
DateUtilities.getTimeString(getContext(), newDateTime())),
activity.getString(R.string.TEA_timer_comment_started),
DateUtilities.getTimeString(activity, newDateTime())),
UserActivity.ACTION_TASK_COMMENT,
t.getUuid(),
false);
@ -470,9 +444,9 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
public void timerStopped(Task t) {
String elapsedTime = DateUtils.formatElapsedTime(t.getElapsedSeconds());
addComment(String.format("%s %s\n%s %s", //$NON-NLS-1$
getContext().getString(R.string.TEA_timer_comment_stopped),
DateUtilities.getTimeString(getContext(), newDateTime()),
getContext().getString(R.string.TEA_timer_comment_spent),
activity.getString(R.string.TEA_timer_comment_stopped),
DateUtilities.getTimeString(activity, newDateTime()),
activity.getString(R.string.TEA_timer_comment_spent),
elapsedTime), UserActivity.ACTION_TASK_COMMENT,
t.getUuid(),
false);

@ -25,10 +25,12 @@ import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagService;
import org.tasks.injection.InjectingContentProvider;
import org.tasks.ui.CheckBoxes;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import javax.inject.Inject;
@ -80,6 +82,7 @@ public class Astrid2TaskProvider extends InjectingContentProvider {
@Inject Lazy<TaskService> taskService;
@Inject Lazy<TagService> tagService;
@Inject Lazy<CheckBoxes> checkBoxes;
static {
URI_MATCHER.addURI(AUTHORITY, "tasks", URI_TASKS);
@ -167,7 +170,7 @@ public class Astrid2TaskProvider extends InjectingContentProvider {
TaskCriteria.isVisible())).
orderBy(SortHelper.defaultTaskOrder()).limit(MAX_NUMBER_OF_TASKS));
try {
int[] importanceColors = Task.getImportanceColors(getContext().getResources());
List<Integer> importanceColors = checkBoxes.get().getPriorityColors();
for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToNext();
Task task = new Task(cursor);
@ -176,7 +179,7 @@ public class Astrid2TaskProvider extends InjectingContentProvider {
Object[] values = new Object[7];
values[0] = task.getTitle();
values[1] = importanceColors[task.getImportance()];
values[1] = importanceColors.get(task.getImportance());
values[2] = task.getDueDate();
values[3] = task.getDueDate();
values[4] = task.getImportance();

@ -7,10 +7,16 @@ package com.todoroo.astrid.repeats;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
@ -22,6 +28,7 @@ import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.google.common.base.Strings;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.google.ical.values.Weekday;
@ -30,21 +37,24 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.ui.NumberPickerDialog;
import com.todoroo.astrid.ui.NumberPickerDialog.OnNumberPickedListener;
import com.todoroo.astrid.ui.PopupControlSet;
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog;
import org.tasks.R;
import org.tasks.activities.DatePickerActivity;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.dialogs.MyDatePickerDialog;
import org.tasks.injection.ForActivity;
import org.tasks.preferences.ActivityPreferences;
import org.tasks.time.DateTime;
import org.tasks.ui.TaskEditControlFragment;
import java.text.DateFormatSymbols;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import timber.log.Timber;
import static org.tasks.date.DateTimeUtils.newDateTime;
@ -55,9 +65,16 @@ import static org.tasks.date.DateTimeUtils.newDateTime;
* @author Tim Su <tim@todoroo.com>
*
*/
public class RepeatControlSet extends PopupControlSet {
public class RepeatControlSet extends TaskEditControlFragment {
public interface RepeatChangedListener {
void repeatChanged(boolean repeat);
}
private static final String FRAG_TAG_REPEAT_UNTIL = "frag_tag_repeat_until";
private static final int REQUEST_PICK_DATE = 505;
private static final String EXTRA_RECURRENCE = "extra_recurrence";
private static final String EXTRA_REPEAT_UNTIL = "extra_repeat_until";
private static final String EXTRA_REPEAT_AFTER_COMPLETION = "extra_repeat_after_completion";
// --- spinner constants
@ -77,7 +94,14 @@ public class RepeatControlSet extends PopupControlSet {
private Spinner interval;
private Spinner type;
private Spinner repeatUntil;
private ImageView clear;
@Inject DialogBuilder dialogBuilder;
@Inject ActivityPreferences preferences;
@Inject @ForActivity Context context;
@Bind(R.id.clear) ImageView clear;
@Bind(R.id.display_row_edit) TextView displayView;
private ArrayAdapter<String> repeatUntilAdapter;
private final List<String> repeatUntilOptions = new ArrayList<>();
private LinearLayout daysOfWeekContainer;
@ -87,227 +111,49 @@ public class RepeatControlSet extends PopupControlSet {
private int repeatValue;
private int intervalValue;
private long repeatUntilValue;
private View dialogView;
private AlertDialog dialog;
private final List<RepeatChangedListener> listeners = new LinkedList<>();
public interface RepeatChangedListener {
void repeatChanged(boolean repeat);
}
// --- implementation
public RepeatControlSet(ActivityPreferences preferences, Activity activity, DialogBuilder dialogBuilder) {
super(preferences, activity, R.layout.control_set_repeat, R.layout.control_set_repeat_display, R.string.repeat_enabled, dialogBuilder);
clear = (ImageView) getView().findViewById(R.id.clear);
}
/** Set up the repeat value button */
private void setRepeatValue(int newValue) {
repeatValue = newValue;
value.setText(activity.getString(R.string.repeat_every, newValue));
}
private void setRepeatUntilValue(long newValue) {
repeatUntilValue = newValue;
updateRepeatUntilOptions();
}
protected void repeatValueClick() {
int dialogValue = repeatValue;
if(dialogValue == 0) {
dialogValue = 1;
}
new NumberPickerDialog(activity, preferences.getDialogTheme(), new OnNumberPickedListener() {
@Override
public void onNumberPicked(int number) {
setRepeatValue(number);
}
}, activity.getResources().getString(R.string.repeat_interval_prompt),
dialogValue, 1, 1, 365).show();
}
private void repeatUntilClick() {
MyDatePickerDialog dialog = new MyDatePickerDialog();
DateTime initial = repeatUntilValue > 0 ? newDateTime(repeatUntilValue) : newDateTime();
dialog.initialize(new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePickerDialog datePickerDialog, int year, int month, int day) {
setRepeatUntilValue(new DateTime(year, month + 1, day, 0, 0, 0, 0).getMillis());
}
}, initial.getYear(), initial.getMonthOfYear() - 1, initial.getDayOfMonth());
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
setRepeatUntilValue(repeatUntilValue);
}
});
dialog.show(activity.getFragmentManager(), FRAG_TAG_REPEAT_UNTIL);
}
private RepeatChangedListener callback;
public void addListener(RepeatChangedListener listener) {
listeners.add(listener);
}
private boolean repeatAfterCompletion;
@Nullable
@Override
public void readFromTask(Task task) {
super.readFromTask(task);
recurrence = model.sanitizedRecurrence();
if(recurrence == null) {
recurrence = "";
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
recurrence = savedInstanceState.getString(EXTRA_RECURRENCE);
repeatUntilValue = savedInstanceState.getLong(EXTRA_REPEAT_UNTIL);
repeatAfterCompletion = savedInstanceState.getBoolean(EXTRA_REPEAT_AFTER_COMPLETION);
}
repeatUntilValue = model.getRepeatUntil();
if(recurrence.length() > 0) {
try {
RRule rrule = new RRule(recurrence);
repeatValue = rrule.getInterval();
switch(rrule.getFreq()) {
case DAILY:
intervalValue = INTERVAL_DAYS;
break;
case WEEKLY: {
intervalValue = INTERVAL_WEEKS;
break;
}
case MONTHLY:
intervalValue = INTERVAL_MONTHS;
break;
case HOURLY:
intervalValue = INTERVAL_HOURS;
break;
case MINUTELY:
intervalValue = INTERVAL_MINUTES;
break;
case YEARLY:
intervalValue = INTERVAL_YEARS;
break;
default:
Timber.e(new Exception("Unhandled rrule frequency: " + recurrence), "repeat-unhandled-rule");
}
} catch (Exception e) {
// invalid RRULE
recurrence = ""; //$NON-NLS-1$
Timber.e(e, e.getMessage());
}
}
doRepeat = recurrence.length() > 0;
getDialogView();
refreshDisplayView();
}
@Override
public int getIcon() {
return R.drawable.ic_repeat_24dp;
}
@Override
protected void readFromTaskOnInitialize() {
DateTime date;
if(model.getDueDate() != 0) {
date = newDateTime(model.getDueDate());
int dayOfWeek = date.getDayOfWeek();
for(int i = 0; i < 7; i++) {
daysOfWeek[i].setChecked(i == dayOfWeek);
}
}
// read recurrence rule
if(recurrence.length() > 0) {
try {
RRule rrule = new RRule(recurrence);
setRepeatValue(rrule.getInterval());
setRepeatUntilValue(model.getRepeatUntil());
interval.setSelection(intervalValue);
// clear all day of week checks, then update them
for(int i = 0; i < 7; i++) {
daysOfWeek[i].setChecked(false);
}
for(WeekdayNum day : rrule.getByDay()) {
for(int i = 0; i < 7; i++) {
if (daysOfWeek[i].getTag().equals(day.wday)) {
daysOfWeek[i].setChecked(true);
}
}
}
} catch (Exception e) {
// invalid RRULE
recurrence = ""; //$NON-NLS-1$
Timber.e(e, e.getMessage());
}
}
doRepeat = recurrence.length() > 0;
// read flag
if(model.repeatAfterCompletion()) {
type.setSelection(TYPE_COMPLETION_DATE);
} else {
type.setSelection(TYPE_DUE_DATE);
}
refreshDisplayView();
}
@Override
protected void afterInflate() {
View dialogView = getDialogView();
dialogView = inflater.inflate(R.layout.control_set_repeat, null);
value = (Button) dialogView.findViewById(R.id.repeatValue);
interval = (Spinner) dialogView.findViewById(R.id.repeatInterval);
interval.setAdapter(new ArrayAdapter<String>(activity, android.R.layout.simple_spinner_item, activity.getResources().getStringArray(R.array.repeat_interval)) {{
interval.setAdapter(new ArrayAdapter<String>(context, android.R.layout.simple_spinner_item, getResources().getStringArray(R.array.repeat_interval)) {{
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}});
type = (Spinner) dialogView.findViewById(R.id.repeatType);
type.setAdapter(new ArrayAdapter<String>(activity, android.R.layout.simple_spinner_item, activity.getResources().getStringArray(R.array.repeat_type)) {{
type.setAdapter(new ArrayAdapter<String>(context, android.R.layout.simple_spinner_item, getResources().getStringArray(R.array.repeat_type)) {{
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}});
daysOfWeekContainer = (LinearLayout) dialogView.findViewById(R.id.repeatDayOfWeekContainer);
repeatUntil = (Spinner) dialogView.findViewById(R.id.repeat_until);
repeatUntilAdapter = new ArrayAdapter<>(activity, android.R.layout.simple_spinner_item, repeatUntilOptions);
repeatUntilAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, repeatUntilOptions);
repeatUntilAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
repeatUntil.setAdapter(repeatUntilAdapter);
setRepeatValue(1);
setRepeatUntilValue(0);
clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doRepeat = false;
refreshDisplayView();
for (RepeatChangedListener l : listeners) {
l.repeatChanged(doRepeat);
}
}
});
// set up days of week
DateFormatSymbols dfs = new DateFormatSymbols();
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1.0f/14);
LinearLayout.LayoutParams textLp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1.0f/14);
for(int i = 0; i < 7; i++) {
CheckBox checkBox = new CheckBox(activity);
CheckBox checkBox = (CheckBox) daysOfWeekContainer.getChildAt(i);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
checkBox.setPadding(0, 0, 0, 0);
checkBox.setLayoutParams(lp);
checkBox.setTag(Weekday.values()[dayOfWeek - 1]);
checkBox.setButtonDrawable(R.drawable.btn_check_small);
TextView label = new TextView(activity);
label.setTextAppearance(activity, R.style.TextAppearance);
label.setLayoutParams(textLp);
label.setTextSize(14);
label.setText(dfs.getShortWeekdays()[dayOfWeek].substring(0, 1));
checkBox.setText(dfs.getShortWeekdays()[dayOfWeek].substring(0, 1));
daysOfWeek[i] = checkBox;
calendar.add(Calendar.DATE, 1);
daysOfWeekContainer.addView(checkBox);
daysOfWeekContainer.addView(label);
}
// set up listeners
@ -318,6 +164,7 @@ public class RepeatControlSet extends PopupControlSet {
}
});
setRepeatValue(1);
interval.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parentView, View view, int position, long id) {
@ -331,6 +178,7 @@ public class RepeatControlSet extends PopupControlSet {
}
});
setRepeatUntilValue(repeatUntilValue);
repeatUntil.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
@ -356,10 +204,147 @@ public class RepeatControlSet extends PopupControlSet {
});
daysOfWeekContainer.setVisibility(View.GONE);
type.setSelection(repeatAfterCompletion ? TYPE_COMPLETION_DATE : TYPE_DUE_DATE);
applyRecurrence();
refreshDisplayView();
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_RECURRENCE, getRecurrence());
outState.putLong(EXTRA_REPEAT_UNTIL, repeatUntilValue);
outState.putBoolean(EXTRA_REPEAT_AFTER_COMPLETION, repeatAfterCompletion);
}
@Override
protected void writeToModelAfterInitialized(Task task) {
public void onAttach(Activity activity) {
super.onAttach(activity);
callback = (RepeatChangedListener) activity;
}
@OnClick(R.id.clear)
void clearRepeat(View view) {
doRepeat = false;
refreshDisplayView();
callback.repeatChanged(doRepeat);
}
@OnClick(R.id.display_row_edit)
void openPopup(View view) {
if (dialog == null) {
buildDialog();
}
dialog.show();
}
protected Dialog buildDialog() {
AlertDialog.Builder builder = dialogBuilder.newDialog()
.setView(dialogView)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
doRepeat = true;
callback.repeatChanged(doRepeat);
refreshDisplayView();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
refreshDisplayView();
}
});
dialog = builder.show();
return dialog;
}
@Override
protected int getLayout() {
return R.layout.control_set_repeat_display;
}
@Override
public int getIcon() {
return R.drawable.ic_repeat_24dp;
}
@Override
public void initialize(boolean isNewTask, Task task) {
repeatAfterCompletion = task.repeatAfterCompletion();
recurrence = task.sanitizedRecurrence();
repeatUntilValue = task.getRepeatUntil();
}
@Override
public void apply(Task task) {
String result = getRecurrence();
if (type.getSelectedItemPosition() == TYPE_COMPLETION_DATE && !TextUtils.isEmpty(result)) {
result += ";FROM=COMPLETION"; //$NON-NLS-1$
}
task.setRecurrence(result);
task.setRepeatUntil(repeatUntilValue);
}
private void applyRecurrence() {
doRepeat = !Strings.isNullOrEmpty(recurrence);
if (!doRepeat) {
return;
}
// read recurrence rule
try {
RRule rrule = new RRule(recurrence);
setRepeatValue(rrule.getInterval());
for(WeekdayNum day : rrule.getByDay()) {
for(int i = 0; i < 7; i++) {
if (daysOfWeek[i].getTag().equals(day.wday)) {
daysOfWeek[i].setChecked(true);
}
}
}
switch(rrule.getFreq()) {
case DAILY:
intervalValue = INTERVAL_DAYS;
break;
case WEEKLY:
intervalValue = INTERVAL_WEEKS;
break;
case MONTHLY:
intervalValue = INTERVAL_MONTHS;
break;
case HOURLY:
intervalValue = INTERVAL_HOURS;
break;
case MINUTELY:
intervalValue = INTERVAL_MINUTES;
break;
case YEARLY:
intervalValue = INTERVAL_YEARS;
break;
default:
Timber.e(new Exception("Unhandled rrule frequency: " + recurrence), "repeat-unhandled-rule");
}
interval.setSelection(intervalValue);
} catch (Exception e) {
// invalid RRULE
recurrence = ""; //$NON-NLS-1$
Timber.e(e, e.getMessage());
}
}
private String getRecurrence() {
String result;
if(!doRepeat) {
result = ""; //$NON-NLS-1$
@ -367,60 +352,93 @@ public class RepeatControlSet extends PopupControlSet {
RRule rrule = new RRule();
rrule.setInterval(repeatValue);
switch(interval.getSelectedItemPosition()) {
case INTERVAL_DAYS:
rrule.setFreq(Frequency.DAILY);
break;
case INTERVAL_WEEKS: {
rrule.setFreq(Frequency.WEEKLY);
ArrayList<WeekdayNum> days = new ArrayList<>();
for (CompoundButton dayOfWeek : daysOfWeek) {
if (dayOfWeek.isChecked()) {
days.add(new WeekdayNum(0, (Weekday) dayOfWeek.getTag()));
case INTERVAL_DAYS:
rrule.setFreq(Frequency.DAILY);
break;
case INTERVAL_WEEKS: {
rrule.setFreq(Frequency.WEEKLY);
ArrayList<WeekdayNum> days = new ArrayList<>();
for (CompoundButton dayOfWeek : daysOfWeek) {
if (dayOfWeek.isChecked()) {
days.add(new WeekdayNum(0, (Weekday) dayOfWeek.getTag()));
}
}
rrule.setByDay(days);
break;
}
rrule.setByDay(days);
break;
}
case INTERVAL_MONTHS:
rrule.setFreq(Frequency.MONTHLY);
break;
case INTERVAL_HOURS:
rrule.setFreq(Frequency.HOURLY);
break;
case INTERVAL_MINUTES:
rrule.setFreq(Frequency.MINUTELY);
break;
case INTERVAL_YEARS:
rrule.setFreq(Frequency.YEARLY);
break;
case INTERVAL_MONTHS:
rrule.setFreq(Frequency.MONTHLY);
break;
case INTERVAL_HOURS:
rrule.setFreq(Frequency.HOURLY);
break;
case INTERVAL_MINUTES:
rrule.setFreq(Frequency.MINUTELY);
break;
case INTERVAL_YEARS:
rrule.setFreq(Frequency.YEARLY);
break;
}
result = rrule.toIcal();
}
if (type.getSelectedItemPosition() == TYPE_COMPLETION_DATE && !TextUtils.isEmpty(result)) {
result = result + ";FROM=COMPLETION"; //$NON-NLS-1$
}
return result;
}
task.setRecurrence(result);
task.setRepeatUntil(repeatUntilValue);
/** Set up the repeat value button */
private void setRepeatValue(int newValue) {
repeatValue = newValue;
value.setText(getString(R.string.repeat_every, newValue));
}
if(task.repeatAfterCompletion()) {
type.setSelection(1);
private void setRepeatUntilValue(long newValue) {
repeatUntilValue = newValue;
updateRepeatUntilOptions();
}
protected void repeatValueClick() {
int dialogValue = repeatValue;
if(dialogValue == 0) {
dialogValue = 1;
}
new NumberPickerDialog(context, preferences.getDialogTheme(), new OnNumberPickedListener() {
@Override
public void onNumberPicked(int number) {
setRepeatValue(number);
}
}, getResources().getString(R.string.repeat_interval_prompt),
dialogValue, 1, 1, 365).show();
}
private void repeatUntilClick() {
startActivityForResult(new Intent(context, DatePickerActivity.class) {{
putExtra(DatePickerActivity.EXTRA_TIMESTAMP, repeatUntilValue > 0 ? repeatUntilValue : 0L);
}}, REQUEST_PICK_DATE);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_PICK_DATE) {
if (resultCode == Activity.RESULT_OK) {
setRepeatUntilValue(data.getLongExtra(DatePickerActivity.EXTRA_TIMESTAMP, 0L));
} else {
setRepeatUntilValue(repeatUntilValue);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
protected void refreshDisplayView() {
TextView repeatDisplay = (TextView) getView().findViewById(R.id.display_row_edit);
if (doRepeat) {
repeatDisplay.setText(getRepeatString());
repeatDisplay.setTextColor(themeColor);
displayView.setText(getRepeatString());
displayView.setAlpha(1.0f);
clear.setVisibility(View.VISIBLE);
} else {
repeatDisplay.setTextColor(unsetColor);
repeatDisplay.setText(R.string.repeat_never);
displayView.setAlpha(0.5f);
displayView.setText(R.string.repeat_never);
clear.setVisibility(View.GONE);
}
}
@ -428,40 +446,23 @@ public class RepeatControlSet extends PopupControlSet {
private String getRepeatString() {
int arrayResource = R.array.repeat_interval;
String[] dates = activity.getResources().getStringArray(
String[] dates = getResources().getStringArray(
arrayResource);
String date = String.format("%s %s", repeatValue, dates[intervalValue]); //$NON-NLS-1$
if (repeatUntilValue > 0) {
return activity.getString(R.string.repeat_detail_duedate_until, date, getDisplayString());
return getString(R.string.repeat_detail_duedate_until, date, getDisplayString());
} else {
return activity.getString(R.string.repeat_detail_duedate, date); // Every freq int
return getString(R.string.repeat_detail_duedate, date); // Every freq int
}
}
@Override
protected Dialog buildDialog(String title, final PopupDialogClickListener okListener, final DialogInterface.OnCancelListener cancelListener) {
PopupDialogClickListener doRepeatButton = new PopupDialogClickListener() {
@Override
public boolean onClick(DialogInterface d, int which) {
doRepeat = true;
for (RepeatChangedListener l : listeners) {
l.repeatChanged(doRepeat);
}
return okListener.onClick(d, which);
}
};
return super.buildDialog(title, doRepeatButton, cancelListener);
}
private void updateRepeatUntilOptions() {
repeatUntilOptions.clear();
if (repeatUntilValue > 0) {
repeatUntilOptions.add(activity.getString(R.string.repeat_until, getDisplayString()));
repeatUntilOptions.add(getString(R.string.repeat_until, getDisplayString()));
}
repeatUntilOptions.add(activity.getString(R.string.repeat_forever));
repeatUntilOptions.add(activity.getString(R.string.repeat_until, "").trim());
repeatUntilOptions.add(getString(R.string.repeat_forever));
repeatUntilOptions.add(getString(R.string.repeat_until, "").trim());
repeatUntilAdapter.notifyDataSetChanged();
repeatUntil.setSelection(0);
}
@ -473,7 +474,7 @@ public class RepeatControlSet extends PopupControlSet {
displayString.append(DateUtilities.getDateString(d));
if (Task.hasDueTime(repeatUntilValue)) {
displayString.append(", "); //$NON-NLS-1$ //$NON-NLS-2$
displayString.append(DateUtilities.getTimeString(activity, repeatUntilValue));
displayString.append(DateUtilities.getTimeString(context, repeatUntilValue));
}
}
return displayString.toString();

@ -49,8 +49,6 @@ import timber.log.Timber;
@Singleton
public class TaskService {
public static final String TRANS_EDIT_SAVE = "task-edit-save"; //$NON-NLS-1$
public static final String TRANS_REPEAT_COMPLETE = "repeat-complete"; //$NON-NLS-1$
private final TagDataDao tagDataDao;

@ -82,14 +82,14 @@ public final class TagService {
return tagDataDao.getByUuid(uuid, TagData.PROPERTIES);
}
public List<String> getTagNames(long taskId) {
public ArrayList<String> getTagNames(long taskId) {
Query query = Query.select(TaskToTagMetadata.TAG_NAME, TaskToTagMetadata.TAG_UUID).where(
Criterion.and(
MetadataCriteria.withKey(TaskToTagMetadata.KEY),
Metadata.DELETION_DATE.eq(0),
MetadataCriteria.byTask(taskId)))
.orderBy(Order.asc(Functions.upper(TaskToTagMetadata.TAG_NAME)));
final List<String> tagNames = new ArrayList<>();
final ArrayList<String> tagNames = new ArrayList<>();
metadataDao.query(query, new Callback<Metadata>() {
@Override
public void apply(Metadata entry) {

@ -5,13 +5,18 @@
*/
package com.todoroo.astrid.tags;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
@ -20,12 +25,12 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.google.common.base.Strings;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.todoroo.andlib.data.AbstractModel;
import com.google.common.collect.Sets;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
@ -34,21 +39,26 @@ import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.ui.PopupControlSet;
import com.todoroo.astrid.utility.Flags;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.preferences.ActivityPreferences;
import org.tasks.ui.TaskEditControlFragment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.transform;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.newHashSet;
@ -58,32 +68,23 @@ import static com.google.common.collect.Sets.newHashSet;
* @author Tim Su <tim@todoroo.com>
*
*/
public final class TagsControlSet extends PopupControlSet {
public final class TagsControlSet extends TaskEditControlFragment {
// --- instance variables
private static final String EXTRA_TAGS = "extra_tags";
private static final String TRANSITORY_TAGS = "tags";//$NON-NLS-1$
@Inject MetadataDao metadataDao;
@Inject TagDataDao tagDataDao;
@Inject TagService tagService;
@Inject DialogBuilder dialogBuilder;
private ArrayList<String> allTagNames;
@Bind(R.id.display_row_edit) TextView tagsDisplay;
private long taskId;
private ArrayList<String> allTagNames;
private LinearLayout newTags;
private ListView selectedTags;
private boolean populated = false;
//private final LinearLayout tagsContainer;
private final TextView tagsDisplay;
private final MetadataDao metadataDao;
private final TagDataDao tagDataDao;
private final TagService tagService;
public TagsControlSet(MetadataDao metadataDao, TagDataDao tagDataDao, ActivityPreferences preferences, TagService tagService, Activity activity, DialogBuilder dialogBuilder) {
super(preferences, activity, R.layout.control_set_tag_list, R.layout.control_set_tags, R.string.TEA_tags_label_long, dialogBuilder);
this.metadataDao = metadataDao;
this.tagDataDao = tagDataDao;
this.tagService = tagService;
tagsDisplay = (TextView) getView().findViewById(R.id.display_row_edit);
}
private View dialogView;
private AlertDialog dialog;
private String buildTagString() {
StringBuilder builder = new StringBuilder();
@ -103,6 +104,86 @@ public final class TagsControlSet extends PopupControlSet {
return builder.toString();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
ArrayList<String> selected;
if (savedInstanceState != null) {
selected = savedInstanceState.getStringArrayList(EXTRA_TAGS);
} else {
selected = tagService.getTagNames(taskId);
}
allTagNames = newArrayList(ImmutableSet.copyOf(transform(tagService.getTagList(), new Function<TagData, String>() {
@Override
public String apply(TagData tagData) {
return tagData.getName();
}
})));
dialogView = inflater.inflate(R.layout.control_set_tag_list, null);
newTags = (LinearLayout) dialogView.findViewById(R.id.newTags);
selectedTags = (ListView) dialogView.findViewById(R.id.existingTags);
selectedTags.setAdapter(new ArrayAdapter<>(getActivity(), R.layout.simple_list_item_multiple_choice_themed, allTagNames));
addTag("");
for (String tag : selected) {
setTagSelected(tag);
}
refreshDisplayView();
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putStringArrayList(EXTRA_TAGS, getTagList());
}
@Override
protected int getLayout() {
return R.layout.control_set_tags;
}
@Override
public void initialize(boolean isNewTask, Task task) {
taskId = task.getId();
}
@Override
public void apply(Task task) {
if (synchronizeTags(task.getId(), task.getUUID())) {
Flags.set(Flags.TAGS_CHANGED);
task.setModificationDate(DateUtilities.now());
}
}
@OnClick(R.id.display_row_edit)
void openPopup(View view) {
if (dialog == null) {
buildDialog();
}
dialog.show();
}
protected Dialog buildDialog() {
android.support.v7.app.AlertDialog.Builder builder = dialogBuilder.newDialog()
.setView(dialogView)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
refreshDisplayView();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
refreshDisplayView();
}
});
dialog = builder.show();
return dialog;
}
private void setTagSelected(String tag) {
int index = allTagNames.indexOf(tag);
if (index >= 0) {
@ -113,37 +194,31 @@ public final class TagsControlSet extends PopupControlSet {
}
}
private List<String> getTagList() {
private ArrayList<String> getTagList() {
Set<String> tags = new LinkedHashSet<>();
if (initialized) {
for(int i = 0; i < selectedTags.getAdapter().getCount(); i++) {
if (selectedTags.isItemChecked(i)) {
tags.add(allTagNames.get(i));
}
for(int i = 0; i < selectedTags.getAdapter().getCount(); i++) {
if (selectedTags.isItemChecked(i)) {
tags.add(allTagNames.get(i));
}
for (int i = newTags.getChildCount() - 1 ; i >= 0 ; i--) {
TextView tagName = (TextView) newTags.getChildAt(i).findViewById(R.id.text1);
final String text = tagName.getText().toString();
if (Strings.isNullOrEmpty(text)) {
continue;
}
TagData tagByName = tagDataDao.getTagByName(text, TagData.PROPERTIES);
if (tagByName != null) {
setTagSelected(tagByName.getName());
tags.add(tagByName.getName());
newTags.removeViewAt(i);
} else if (!Iterables.any(tags, new Predicate<String>() {
@Override
public boolean apply(String input) {
return text.equalsIgnoreCase(input);
}
})) {
tags.add(text);
}
}
for (int i = newTags.getChildCount() - 1 ; i >= 0 ; i--) {
TextView tagName = (TextView) newTags.getChildAt(i).findViewById(R.id.text1);
final String text = tagName.getText().toString();
if (Strings.isNullOrEmpty(text)) {
continue;
}
} else {
if (model.getTransitory(TRANSITORY_TAGS) != null) {
return (List<String>) model.getTransitory(TRANSITORY_TAGS);
TagData tagByName = tagDataDao.getTagByName(text, TagData.PROPERTIES);
if (tagByName != null) {
setTagSelected(tagByName.getName());
tags.add(tagByName.getName());
newTags.removeViewAt(i);
} else if (!Iterables.any(tags, new Predicate<String>() {
@Override
public boolean apply(String input) {
return text.equalsIgnoreCase(input);
}
})) {
tags.add(text);
}
}
return newArrayList(tags);
@ -151,7 +226,7 @@ public final class TagsControlSet extends PopupControlSet {
/** Adds a tag to the tag field */
void addTag(String tagName) {
LayoutInflater inflater = activity.getLayoutInflater();
LayoutInflater inflater = getActivity().getLayoutInflater();
// check if already exists
TextView lastText;
@ -235,142 +310,73 @@ public final class TagsControlSet extends PopupControlSet {
return (TextView) lastItem.findViewById(R.id.text1);
}
@Override
public void readFromTask(Task task) {
super.readFromTask(task);
if(model.getId() != AbstractModel.NO_ID) {
model.putTransitory(TRANSITORY_TAGS, tagService.getTagNames(model.getId()));
refreshDisplayView();
}
}
@Override
public int getIcon() {
return R.drawable.ic_label_24dp;
}
@Override
protected void readFromTaskOnInitialize() {
newTags.removeAllViews();
for (int i = 0; i < selectedTags.getCount(); i++) { // clear all selected items
selectedTags.setItemChecked(i, false);
}
if(model.getId() != AbstractModel.NO_ID) {
selectTagsFromModel();
}
addTag(""); //$NON-NLS-1$
refreshDisplayView();
populated = true;
}
private void selectTagsFromModel() {
List<String> tags = (List<String>) model.getTransitory(TRANSITORY_TAGS);
if (tags != null) {
for (String tag : tags) {
setTagSelected(tag);
}
}
}
@Override
protected void afterInflate() {
allTagNames = newArrayList(ImmutableSet.copyOf(transform(tagService.getTagList(), new Function<TagData, String>() {
@Override
public String apply(TagData tagData) {
return tagData.getName();
}
})));
selectedTags = (ListView) getDialogView().findViewById(R.id.existingTags);
selectedTags.setAdapter(new ArrayAdapter<>(activity,
R.layout.simple_list_item_multiple_choice_themed, allTagNames));
selectedTags.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
newTags = (LinearLayout) getDialogView().findViewById(R.id.newTags);
}
@Override
protected void writeToModelAfterInitialized(Task task) {
// this is a case where we're asked to save but the UI was not yet populated
if(!populated) {
return;
}
synchronizeTags(task.getId(), task.getUUID());
Flags.set(Flags.TAGS_CHANGED);
task.setModificationDate(DateUtilities.now());
}
@Override
protected void refreshDisplayView() {
String tagString = buildTagString();
if (!TextUtils.isEmpty(tagString)) {
tagsDisplay.setText(tagString);
tagsDisplay.setTextColor(themeColor);
tagsDisplay.setAlpha(1.0f);
} else {
tagsDisplay.setText(R.string.tag_FEx_untagged);
tagsDisplay.setTextColor(unsetColor);
tagsDisplay.setAlpha(0.5f);
}
}
/**
* Save the given array of tags into the database
*/
private void synchronizeTags(long taskId, String taskUuid) {
private boolean synchronizeTags(long taskId, String taskUuid) {
Query query = Query.select(Metadata.PROPERTIES).where(
Criterion.and(
TaskToTagMetadata.TASK_UUID.eq(taskUuid),
Metadata.DELETION_DATE.eq(0))
);
Set<String> existingTagIds = newHashSet(transform(metadataDao.toList(query), new Function<Metadata, String>() {
Set<TagData> existingTags = newHashSet(transform(metadataDao.toList(query), new Function<Metadata, TagData>() {
@Override
public String apply(Metadata tagData) {
return tagData.getValue(TaskToTagMetadata.TAG_UUID);
public TagData apply(Metadata metadata) {
return tagDataDao.getByUuid(metadata.getValue(TaskToTagMetadata.TAG_UUID));
}
}));
List<String> tags = getTagList();
// create missing tags
for (String tag : tags) {
TagData tagData = tagDataDao.getTagByName(tag, TagData.ID);
if (tagData == null) {
tagData = new TagData();
tagData.setName(tag);
tagDataDao.persist(tagData);
}
}
List<TagData> selectedTags = newArrayList(transform(tags, new Function<String, TagData>() {
Set<TagData> selectedTags = newHashSet(transform(getTagList(), new Function<String, TagData>() {
@Override
public TagData apply(String tag) {
return tagDataDao.getTagByName(tag, TagData.PROPERTIES);
public TagData apply(String tagName) {
TagData tagData = tagDataDao.getTagByName(tagName, TagData.PROPERTIES);
if (tagData == null) {
// create missing tags
tagData = new TagData();
tagData.setName(tagName);
tagDataDao.persist(tagData);
}
return tagData;
}
}));
deleteLinks(taskId, taskUuid, difference(existingTagIds, newHashSet(Iterables.transform(selectedTags, new Function<TagData, String>() {
@Override
public String apply(TagData tagData) {
return tagData.getUUID();
}
}))));
for (TagData tagData : selectedTags) {
if (!existingTagIds.contains(tagData.getUUID())) {
Metadata newLink = TaskToTagMetadata.newTagMetadata(taskId, taskUuid, tagData.getName(), tagData.getUUID());
metadataDao.createNew(newLink);
}
Sets.SetView<TagData> added = difference(selectedTags, existingTags);
Sets.SetView<TagData> removed = difference(existingTags, selectedTags);
deleteLinks(taskId, taskUuid, removed);
for (TagData tagData : added) {
Metadata newLink = TaskToTagMetadata.newTagMetadata(taskId, taskUuid, tagData.getName(), tagData.getUuid());
metadataDao.createNew(newLink);
}
return !removed.isEmpty() || !added.isEmpty();
}
/**
* Delete all links between the specified task and the list of tags
*/
private void deleteLinks(long taskId, String taskUuid, Iterable<String> tagUuids) {
private void deleteLinks(long taskId, String taskUuid, Iterable<TagData> tags) {
Metadata deleteTemplate = new Metadata();
deleteTemplate.setTask(taskId); // Need this for recording changes in outstanding table
deleteTemplate.setDeletionDate(DateUtilities.now());
for (String uuid : tagUuids) {
for (TagData tag : tags) {
// TODO: Right now this is in a loop because each deleteTemplate needs the individual tagUuid in order to record
// the outstanding entry correctly. If possible, this should be improved to a single query
deleteTemplate.setValue(TaskToTagMetadata.TAG_UUID, uuid); // Need this for recording changes in outstanding table
deleteTemplate.setValue(TaskToTagMetadata.TAG_UUID, tag.getUuid()); // Need this for recording changes in outstanding table
metadataDao.update(Criterion.and(MetadataDao.MetadataCriteria.withKey(TaskToTagMetadata.KEY), Metadata.DELETION_DATE.eq(0),
TaskToTagMetadata.TASK_UUID.eq(taskUuid), TaskToTagMetadata.TAG_UUID.eq(uuid)), deleteTemplate);
TaskToTagMetadata.TASK_UUID.eq(taskUuid), TaskToTagMetadata.TAG_UUID.eq(tag.getUuid())), deleteTemplate);
}
}
}

@ -1,132 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.timers;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.SystemClock;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.text.format.DateFormat;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Chronometer;
import android.widget.Chronometer.OnChronometerTickListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import com.todoroo.astrid.service.TaskService;
import org.tasks.R;
import org.tasks.notifications.NotificationManager;
import java.util.LinkedList;
import java.util.List;
public class TimerActionControlSet extends TaskEditControlSetBase {
private final ImageView timerButton;
private final Chronometer chronometer;
private boolean timerActive;
private final List<TimerActionListener> listeners = new LinkedList<>();
public TimerActionControlSet(final NotificationManager notificationManager, final TaskService taskService, final Activity activity, View parent) {
super(activity, -1);
LinearLayout timerContainer = (LinearLayout) parent.findViewById(R.id.timer_container);
timerButton = (ImageView) parent.findViewById(R.id.timer_button);
OnClickListener timerListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (timerActive) {
TimerPlugin.stopTimer(notificationManager, taskService, activity, model);
for (TimerActionListener listener : listeners) {
listener.timerStopped(model);
}
chronometer.stop();
} else {
TimerPlugin.startTimer(notificationManager, taskService, activity, model);
for (TimerActionListener listener : listeners) {
listener.timerStarted(model);
}
chronometer.start();
}
timerActive = !timerActive;
updateDisplay();
}
};
timerContainer.setOnClickListener(timerListener);
chronometer = (Chronometer) parent.findViewById(R.id.timer);
}
@Override
protected void readFromTaskOnInitialize() {
timerActive = model.getTimerStart() != 0;
updateDisplay();
}
@Override
protected void afterInflate() {
// Do nothing
}
@Override
protected void writeToModelAfterInitialized(Task task) {
// Nothing to do here
}
private void updateDisplay() {
int drawableRes = timerActive ? R.drawable.ic_pause_24dp : R.drawable.ic_play_arrow_24dp;
final Drawable drawable = DrawableCompat.wrap(activity.getResources().getDrawable(drawableRes));
DrawableCompat.setTint(drawable, activity.getResources().getColor(android.R.color.white));
timerButton.setImageDrawable(drawable);
long elapsed = model.getElapsedSeconds() * 1000L;
if (timerActive) {
chronometer.setVisibility(View.VISIBLE);
elapsed += DateUtilities.now() - model.getTimerStart();
chronometer.setBase(SystemClock.elapsedRealtime() - elapsed);
if (elapsed > DateUtilities.ONE_DAY) {
chronometer.setOnChronometerTickListener(new OnChronometerTickListener() {
@Override
public void onChronometerTick(Chronometer cArg) {
long t = SystemClock.elapsedRealtime() - cArg.getBase();
cArg.setText(DateFormat.format("d'd' h:mm", t)); //$NON-NLS-1$
}
});
}
chronometer.start();
} else {
chronometer.setVisibility(View.GONE);
chronometer.stop();
}
}
@Override
public int getIcon() {
return -1;
}
public interface TimerActionListener {
void timerStopped(Task task);
void timerStarted(Task task);
}
public void addListener(TimerActionListener listener) {
this.listeners.add(listener);
}
public void removeListener(TimerActionListener listener) {
if (listeners.contains(listener)) {
listeners.remove(listener);
}
}
}

@ -0,0 +1,8 @@
package com.todoroo.astrid.timers;
import com.todoroo.astrid.data.Task;
public interface TimerActionListener {
void timerStopped(Task task);
void timerStarted(Task task);
}

@ -1,26 +1,46 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* <p/>
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.timers;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Chronometer;
import android.widget.ImageView;
import android.widget.TextView;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import com.todoroo.astrid.timers.TimerActionControlSet.TimerActionListener;
import com.todoroo.astrid.ui.PopupControlSet;
import com.todoroo.astrid.notes.EditNoteActivity;
import com.todoroo.astrid.ui.TimeDurationControlSet;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ForActivity;
import org.tasks.preferences.ActivityPreferences;
import org.tasks.ui.TaskEditControlFragment;
import java.util.LinkedList;
import java.util.List;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
/**
* Control Set for managing repeats
@ -28,104 +48,162 @@ import org.tasks.preferences.ActivityPreferences;
* @author Tim Su <tim@todoroo.com>
*
*/
public class TimerControlSet extends PopupControlSet implements TimerActionListener {
public class TimerControlSet extends TaskEditControlFragment {
TimeDurationTaskEditControlSet estimated, elapsed;
private final TextView displayEdit;
private ActivityPreferences preferences;
public interface TimerControlSetCallback {
Task stopTimer();
Task startTimer();
}
public TimerControlSet(ActivityPreferences preferences, final Activity activity, DialogBuilder dialogBuilder) {
super(preferences, activity, R.layout.control_set_timers_dialog, R.layout.control_set_timers, R.string.TEA_timer_controls, dialogBuilder);
this.preferences = preferences;
private static final String EXTRA_STARTED = "extra_started";
private static final String EXTRA_ESTIMATED = "extra_estimated";
private static final String EXTRA_ELAPSED = "extra_elapsed";
displayEdit = (TextView) getView().findViewById(R.id.display_row_edit);
displayEdit.setText(R.string.TEA_timer_controls);
displayEdit.setTextColor(unsetColor);
@Inject ActivityPreferences preferences;
@Inject DialogBuilder dialogBuilder;
@Inject @ForActivity Context context;
estimated = new TimeDurationTaskEditControlSet(activity, getDialogView(), Task.ESTIMATED_SECONDS,R.id.estimatedDuration);
elapsed = new TimeDurationTaskEditControlSet(activity, getDialogView(), Task.ELAPSED_SECONDS, R.id.elapsedDuration);
}
@Bind(R.id.display_row_edit) TextView displayEdit;
@Bind(R.id.timer) Chronometer chronometer;
@Bind(R.id.timer_button) ImageView timerButton;
@Override
protected void readFromTaskOnInitialize() {
estimated.readFromTask(model);
elapsed.readFromTask(model);
}
@Override
protected void afterInflate() {
// Nothing to do here
}
private TimeDurationControlSet estimated;
private TimeDurationControlSet elapsed;
private long timerStarted;
private final List<TimerActionListener> listeners = new LinkedList<>();
protected AlertDialog dialog;
private View dialogView;
private int elapsedSeconds;
private int estimatedSeconds;
private TimerControlSetCallback callback;
@Nullable
@Override
protected void writeToModelAfterInitialized(Task task) {
if (initialized) {
estimated.writeToModel(task);
elapsed.writeToModel(task);
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
timerStarted = savedInstanceState.getLong(EXTRA_STARTED);
elapsedSeconds = savedInstanceState.getInt(EXTRA_ELAPSED);
estimatedSeconds = savedInstanceState.getInt(EXTRA_ESTIMATED);
}
dialogView = inflater.inflate(R.layout.control_set_timers_dialog, null);
estimated = new TimeDurationControlSet(context, dialogView, R.id.estimatedDuration, preferences);
elapsed = new TimeDurationControlSet(context, dialogView, R.id.elapsedDuration, preferences);
estimated.setTimeDuration(estimatedSeconds);
elapsed.setTimeDuration(elapsedSeconds);
refresh();
return view;
}
@Override
public int getIcon() {
return R.drawable.ic_timer_24dp;
public void onAttach(Activity activity) {
super.onAttach(activity);
callback = (TimerControlSetCallback) activity;
}
// --- TimeDurationTaskEditControlSet
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/**
* Control set for mapping a Property to a TimeDurationControlSet
* @author Tim Su <tim@todoroo.com>
*
*/
public class TimeDurationTaskEditControlSet extends TaskEditControlSetBase {
private final TimeDurationControlSet controlSet;
private final IntegerProperty property;
outState.putInt(EXTRA_ELAPSED, elapsed.getTimeDurationInSeconds());
outState.putInt(EXTRA_ESTIMATED, estimated.getTimeDurationInSeconds());
outState.putLong(EXTRA_STARTED, timerStarted);
}
public TimeDurationTaskEditControlSet(Activity activity, View v, IntegerProperty property, int timeButtonId) {
super(activity, -1);
this.property = property;
this.controlSet = new TimeDurationControlSet(activity, v, property, timeButtonId, preferences);
@OnClick(R.id.display_row_edit)
void openPopup(View view) {
if (dialog == null) {
buildDialog();
}
dialog.show();
}
@Override
public void readFromTaskOnInitialize() {
controlSet.setModel(model);
controlSet.setTimeDuration(model.getValue(property));
}
protected Dialog buildDialog() {
AlertDialog.Builder builder = dialogBuilder.newDialog()
.setView(dialogView)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
refreshDisplayView();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
refreshDisplayView();
}
});
dialog = builder.show();
return dialog;
}
@Override
protected void afterInflate() {
// Nothing
@OnClick(R.id.timer_container)
void timerClicked(View view) {
if (timerActive()) {
Task task = callback.stopTimer();
elapsed.setTimeDuration(task.getElapsedSeconds());
timerStarted = 0;
for (TimerActionListener listener : listeners) {
listener.timerStopped(task);
}
chronometer.stop();
refreshDisplayView();
} else {
Task task = callback.startTimer();
timerStarted = task.getTimerStart();
chronometer.start();
for (TimerActionListener listener : listeners) {
listener.timerStarted(task);
}
}
updateChronometer();
}
@Override
protected void writeToModelAfterInitialized(Task task) {
task.setValue(property, controlSet.getTimeDurationInSeconds());
}
@Override
protected int getLayout() {
return R.layout.control_set_timers;
}
public String getDisplayString() {
int seconds = controlSet.getTimeDurationInSeconds();
if (seconds > 0) {
return DateUtils.formatElapsedTime(controlSet.getTimeDurationInSeconds());
}
return null;
}
@Override
public int getIcon() {
return R.drawable.ic_timer_24dp;
}
@Override
public int getIcon() {
return -1;
}
@Override
public void initialize(boolean isNewTask, Task task) {
timerStarted = task.getTimerStart();
elapsedSeconds = task.getElapsedSeconds();
estimatedSeconds = task.getEstimatedSeconds();
}
@Override
protected void refreshDisplayView() {
String est = estimated.getDisplayString();
if (!TextUtils.isEmpty(est)) {
est = activity.getString(R.string.TEA_timer_est, est);
public void apply(Task task) {
task.setElapsedSeconds(elapsed.getTimeDurationInSeconds());
task.setEstimatedSeconds(estimated.getTimeDurationInSeconds());
}
public void setEditNotes(EditNoteActivity editNotes) {
removeListener(editNotes);
addListener(editNotes);
}
private void refresh() {
refreshDisplayView();
updateChronometer();
}
private void refreshDisplayView() {
String est = null;
int estimatedSeconds = estimated.getTimeDurationInSeconds();
if (estimatedSeconds > 0) {
est = getString(R.string.TEA_timer_est, DateUtils.formatElapsedTime(estimatedSeconds));
}
String elap = elapsed.getDisplayString();
if (!TextUtils.isEmpty(elap)) {
elap = activity.getString(R.string.TEA_timer_elap, elap);
String elap = null;
int elapsedSeconds = elapsed.getTimeDurationInSeconds();
if (elapsedSeconds > 0) {
elap = getString(R.string.TEA_timer_elap, DateUtils.formatElapsedTime(elapsedSeconds));
}
String toDisplay;
@ -142,20 +220,51 @@ public class TimerControlSet extends PopupControlSet implements TimerActionListe
if (!TextUtils.isEmpty(toDisplay)) {
displayEdit.setText(toDisplay);
displayEdit.setTextColor(themeColor);
displayEdit.setAlpha(1.0f);
} else {
displayEdit.setText(R.string.TEA_timer_controls);
displayEdit.setTextColor(unsetColor);
displayEdit.setAlpha(0.5f);
}
}
@Override
public void timerStopped(Task task) {
elapsed.readFromTask(task);
private void updateChronometer() {
timerButton.setImageResource(timerActive()
? R.drawable.ic_pause_24dp
: R.drawable.ic_play_arrow_24dp);
long elapsed = this.elapsed.getTimeDurationInSeconds() * 1000L;
if (timerActive()) {
chronometer.setVisibility(View.VISIBLE);
elapsed += DateUtilities.now() - timerStarted;
chronometer.setBase(SystemClock.elapsedRealtime() - elapsed);
if (elapsed > DateUtilities.ONE_DAY) {
chronometer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
@Override
public void onChronometerTick(Chronometer cArg) {
long t = SystemClock.elapsedRealtime() - cArg.getBase();
cArg.setText(DateFormat.format("d'd' h:mm", t)); //$NON-NLS-1$
}
});
}
chronometer.start();
} else {
chronometer.setVisibility(View.GONE);
chronometer.stop();
}
}
@Override
public void timerStarted(Task task) {
public void addListener(TimerActionListener listener) {
this.listeners.add(listener);
}
public void removeListener(TimerActionListener listener) {
if (listeners.contains(listener)) {
listeners.remove(listener);
}
}
private boolean timerActive() {
return timerStarted > 0;
}
}

@ -61,8 +61,7 @@ public class TimerPlugin {
if(task.getTimerStart() > 0) {
int newElapsed = (int)((DateUtilities.now() - task.getTimerStart()) / 1000L);
task.setTimerStart(0L);
task.setELAPSED_SECONDS(
task.getElapsedSeconds() + newElapsed);
task.setElapsedSeconds(task.getElapsedSeconds() + newElapsed);
}
}
taskService.save(task);

@ -1,32 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.ui;
import android.content.Context;
import android.util.AttributeSet;
import org.tasks.R;
public class DeadlineNumberPicker extends NumberPicker {
public DeadlineNumberPicker(Context context) {
super(context);
}
public DeadlineNumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected int getLayout() {
return R.layout.deadline_number_picker;
}
@Override
protected int getMaxDigits() {
return 2;
}
}

@ -1,43 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.ui;
import android.app.Activity;
import android.widget.EditText;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R;
public class DescriptionControlSet extends TaskEditControlSetBase {
protected EditText editText;
public DescriptionControlSet(Activity activity) {
super(activity, R.layout.control_set_description);
}
@Override
protected void afterInflate() {
editText = (EditText) getView().findViewById(R.id.notes);
}
@Override
protected void readFromTaskOnInitialize() {
editText.setTextKeepState(model.getNotes());
}
@Override
protected void writeToModelAfterInitialized(Task task) {
task.setNotes(editText.getText().toString().trim());
}
@Override
public int getIcon() {
return R.drawable.ic_event_note_24dp;
}
}

@ -7,53 +7,85 @@ package com.todoroo.astrid.ui;
import android.app.Activity;
import android.graphics.Paint;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSet;
import com.todoroo.astrid.repeats.RepeatControlSet.RepeatChangedListener;
import com.todoroo.astrid.service.TaskService;
import org.tasks.R;
import org.tasks.ui.CheckBoxes;
import org.tasks.ui.PriorityControlSet.ImportanceChangedListener;
import org.tasks.ui.TaskEditControlFragment;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* Control set for mapping a Property to an EditText
* @author Tim Su <tim@todoroo.com>
*
*/
public class EditTitleControlSet implements TaskEditControlSet, ImportanceChangedListener, RepeatChangedListener {
public class EditTitleControlSet extends TaskEditControlFragment {
private static final String EXTRA_COMPLETE = "extra_complete";
private static final String EXTRA_TITLE = "extra_title";
private static final String EXTRA_REPEATING = "extra_repeating";
private static final String EXTRA_PRIORITY = "extra_priority";
private final EditText editText;
private CheckableImageView completeBox;
@Inject TaskService taskService;
private final CheckBoxes checkBoxes;
@Bind(R.id.title) EditText editText;
@Bind(R.id.completeBox) CheckableImageView completeBox;
private CheckBoxes checkBoxes;
private boolean isComplete;
private boolean isRepeating;
private int importanceValue;
private Task model;
private final TaskService taskService;
private boolean isNewTask;
private String title;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
public EditTitleControlSet(TaskService taskService, final Activity activity, final EditText editText, CheckableImageView completeBox) {
this.checkBoxes = new CheckBoxes(activity);
this.editText = editText;
this.completeBox = completeBox;
this.taskService = taskService;
checkBoxes = new CheckBoxes(activity);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(getLayout(), null);
ButterKnife.bind(this, view);
if (savedInstanceState != null) {
isComplete = savedInstanceState.getBoolean(EXTRA_COMPLETE);
title = savedInstanceState.getString(EXTRA_TITLE);
isRepeating = savedInstanceState.getBoolean(EXTRA_REPEATING);
importanceValue = savedInstanceState.getInt(EXTRA_PRIORITY);
}
completeBox.setChecked(isComplete);
editText.setTextKeepState(title);
editText.setHorizontallyScrolling(false);
editText.setMaxLines(Integer.MAX_VALUE);
editText.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
AndroidUtilities.hideSoftInputForViews(activity, editText);
AndroidUtilities.hideSoftInputForViews(getActivity(), editText);
return true;
}
return false;
@ -74,39 +106,46 @@ public class EditTitleControlSet implements TaskEditControlSet, ImportanceChange
return false;
}
});
updateCompleteBox();
return view;
}
protected void readFromTaskOnInitialize() {
editText.setTextKeepState(model.getTitle());
completeBox.setChecked(model.isCompleted());
completeBox.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// set check box to actual action item state
updateCompleteBox();
}
});
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(EXTRA_COMPLETE, completeBox.isChecked());
outState.putString(EXTRA_TITLE, getTitle());
outState.putBoolean(EXTRA_REPEATING, isRepeating);
outState.putInt(EXTRA_PRIORITY, importanceValue);
}
@Override
public void importanceChanged(int i) {
importanceValue = i;
@OnClick(R.id.completeBox)
void toggleComplete(View view) {
updateCompleteBox();
}
@Override
public void repeatChanged(boolean repeat) {
isRepeating = repeat;
public void onStart() {
super.onStart();
if (isNewTask) {
editText.requestFocus();
editText.setCursorVisible(true);
getActivity().getWindow()
.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
}
public void setPriority(int priority) {
importanceValue = priority;
updateCompleteBox();
}
@Override
public void readFromTask(Task task) {
this.model = task;
readFromTaskOnInitialize();
isRepeating = !TextUtils.isEmpty(task.getRecurrence());
importanceValue = model.getImportance();
public void repeatChanged(boolean repeat) {
isRepeating = repeat;
updateCompleteBox();
}
private void updateCompleteBox() {
@ -128,12 +167,8 @@ public class EditTitleControlSet implements TaskEditControlSet, ImportanceChange
}
@Override
public void writeToModel(Task task) {
task.setTitle(editText.getText().toString());
boolean newState = completeBox.isChecked();
if (newState != task.isCompleted()) {
taskService.setComplete(task, newState);
}
protected int getLayout() {
return R.layout.control_set_title;
}
@Override
@ -142,7 +177,30 @@ public class EditTitleControlSet implements TaskEditControlSet, ImportanceChange
}
@Override
public View getView() {
throw new RuntimeException();
public void initialize(boolean isNewTask, Task task) {
this.isNewTask = isNewTask;
isComplete = task.isCompleted();
title = task.getTitle();
isRepeating = !TextUtils.isEmpty(task.getRecurrence());
importanceValue = task.getImportance();
}
@Override
public void apply(Task task) {
task.setTitle(getTitle());
boolean newState = completeBox.isChecked();
if (newState != task.isCompleted()) {
taskService.setComplete(task, newState);
}
}
public String getTitle() {
return editText.getText().toString();
}
public void hideKeyboard() {
AndroidUtilities.hideSoftInputForViews(getActivity(), editText);
editText.setCursorVisible(false);
}
}

@ -5,9 +5,14 @@
*/
package com.todoroo.astrid.ui;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
@ -16,18 +21,24 @@ import android.widget.Spinner;
import android.widget.TextView;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R;
import org.tasks.activities.DateAndTimePickerActivity;
import org.tasks.activities.TimePickerActivity;
import org.tasks.injection.ForActivity;
import org.tasks.time.DateTime;
import org.tasks.ui.HiddenTopArrayAdapter;
import org.tasks.ui.TaskEditControlFragment;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import static java.util.Arrays.asList;
import static org.tasks.date.DateTimeUtils.newDateTime;
@ -37,29 +48,145 @@ import static org.tasks.date.DateTimeUtils.newDateTime;
* @author Tim Su <tim@todoroo.com>
*
*/
public class HideUntilControlSet extends TaskEditControlSetBase implements OnItemSelectedListener {
public class HideUntilControlSet extends TaskEditControlFragment implements OnItemSelectedListener {
private static String EXTRA_CUSTOM = "extra_custom";
private static String EXTRA_SELECTION = "extra_selection";
private static final int SPECIFIC_DATE = -1;
private static final int EXISTING_TIME_UNSET = -2;
public static final int REQUEST_HIDE_UNTIL = 11011;
private static final int REQUEST_HIDE_UNTIL = 11011;
@Inject @ForActivity Context context;
//private final CheckBox enabled;
private Spinner spinner;
@Bind(R.id.hideUntil) Spinner spinner;
@Bind(R.id.clear) ImageView clearButton;
private ArrayAdapter<HideUntilValue> adapter;
private long initialHideUntil;
private int previousSetting = Task.HIDE_UNTIL_NONE;
private int selection;
private long existingDate = EXISTING_TIME_UNSET;
private TaskEditFragment taskEditFragment;
private TextView textDisplay;
private ImageView clearButton;
private final List<HideUntilValue> spinnerItems = new ArrayList<>();
public HideUntilControlSet(TaskEditFragment taskEditFragment) {
super(taskEditFragment.getActivity(), R.layout.control_set_hide);
this.taskEditFragment = taskEditFragment;
@OnClick(R.id.clear)
void clearHideUntil(View view) {
updateSpinnerOptions(0);
selection = 0;
spinner.setSelection(selection);
refreshDisplayView();
}
private ArrayAdapter<HideUntilValue> adapter;
@Nullable
@Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
adapter = new HiddenTopArrayAdapter<HideUntilValue>(context, android.R.layout.simple_spinner_item, spinnerItems) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
int selectedItemPosition = position;
if (parent instanceof AdapterView) {
selectedItemPosition = ((AdapterView) parent).getSelectedItemPosition();
}
TextView tv = (TextView) inflater.inflate(android.R.layout.simple_spinner_item, parent, false);
HideUntilValue value = getItem(selectedItemPosition);
if (value.setting == Task.HIDE_UNTIL_NONE) {
clearButton.setVisibility(View.GONE);
tv.setText(value.label);
} else {
String display = value.label;
if (value.setting != Task.HIDE_UNTIL_SPECIFIC_DAY && value.setting != Task.HIDE_UNTIL_SPECIFIC_DAY_TIME) {
display = display.toLowerCase();
}
tv.setText(getString(R.string.TEA_hideUntil_display, display));
}
return tv;
}
};
if (savedInstanceState == null) {
updateSpinnerOptions(initialHideUntil);
} else {
updateSpinnerOptions(savedInstanceState.getLong(EXTRA_CUSTOM));
selection = savedInstanceState.getInt(EXTRA_SELECTION);
}
spinner.setAdapter(adapter);
spinner.setSelection(selection);
spinner.setOnItemSelectedListener(this);
refreshDisplayView();
return view;
}
@Override
protected int getLayout() {
return R.layout.control_set_hide;
}
@Override
protected int getIcon() {
return R.drawable.ic_visibility_off_24dp;
}
@Override
public void initialize(boolean isNewTask, Task task) {
long dueDate = task.getDueDate();
long hideUntil = task.getHideUntil();
DateTime dueDay = newDateTime(dueDate)
.withHourOfDay(0)
.withMinuteOfHour(0)
.withSecondOfMinute(0)
.withMillisOfSecond(0);
// For the hide until due case, we need the time component
long dueTime = dueDate/1000L*1000L;
if(hideUntil == 0) {
selection = 0;
hideUntil = 0;
} else if(hideUntil == dueDay.getMillis()) {
selection = 1;
hideUntil = 0;
} else if (hideUntil == dueTime){
selection = 2;
hideUntil = 0;
} else if(hideUntil + DateUtilities.ONE_DAY == dueDay.getMillis()) {
selection = 3;
hideUntil = 0;
} else if(hideUntil + DateUtilities.ONE_WEEK == dueDay.getMillis()) {
selection = 4;
hideUntil = 0;
}
initialHideUntil = hideUntil;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_HIDE_UNTIL) {
if (resultCode == Activity.RESULT_OK) {
setCustomDate(data.getLongExtra(TimePickerActivity.EXTRA_TIMESTAMP, 0L));
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void apply(Task task) {
HideUntilValue selectedItem = (HideUntilValue) spinner.getSelectedItem();
long hideUntil = task.createHideUntil(selectedItem.setting, selectedItem.date);
task.setHideUntil(hideUntil);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(EXTRA_CUSTOM, existingDate);
outState.putInt(EXTRA_SELECTION, selection);
}
/**
* Container class for urgencies
@ -91,7 +218,7 @@ public class HideUntilControlSet extends TaskEditControlSetBase implements OnIte
private void updateSpinnerOptions(long specificDate) {
spinnerItems.clear();
// set up base values
String[] labels = activity.getResources().getStringArray(R.array.TEA_hideUntil);
String[] labels = getResources().getStringArray(R.array.TEA_hideUntil);
spinnerItems.addAll(new ArrayList<>(asList(
new HideUntilValue(labels[0], Task.HIDE_UNTIL_DUE),
new HideUntilValue(labels[1], Task.HIDE_UNTIL_DUE_TIME),
@ -100,22 +227,26 @@ public class HideUntilControlSet extends TaskEditControlSetBase implements OnIte
new HideUntilValue(labels[4], Task.HIDE_UNTIL_SPECIFIC_DAY, -1))));
if(specificDate > 0) {
DateTime hideUntilAsDate = newDateTime(specificDate);
if(hideUntilAsDate.getHourOfDay() == 0 && hideUntilAsDate.getMinuteOfHour() == 0 && hideUntilAsDate.getSecondOfMinute() == 0) {
spinnerItems.add(0, new HideUntilValue(DateUtilities.getDateString(newDateTime(specificDate)),
Task.HIDE_UNTIL_SPECIFIC_DAY, specificDate));
} else {
spinnerItems.add(0, new HideUntilValue(DateUtilities.getDateStringWithTime(activity, specificDate),
Task.HIDE_UNTIL_SPECIFIC_DAY_TIME, specificDate));
}
spinnerItems.add(0, getHideUntilValue(specificDate));
existingDate = specificDate;
} else {
spinnerItems.add(0, new HideUntilValue("", Task.HIDE_UNTIL_NONE));
spinnerItems.add(0, new HideUntilValue(getString(R.string.TEA_hideUntil_label), Task.HIDE_UNTIL_NONE));
existingDate = EXISTING_TIME_UNSET;
}
adapter.notifyDataSetChanged();
}
private HideUntilValue getHideUntilValue(long timestamp) {
DateTime hideUntilAsDate = newDateTime(timestamp);
if(hideUntilAsDate.getHourOfDay() == 0 && hideUntilAsDate.getMinuteOfHour() == 0 && hideUntilAsDate.getSecondOfMinute() == 0) {
return new HideUntilValue(DateUtilities.getDateString(newDateTime(timestamp)),
Task.HIDE_UNTIL_SPECIFIC_DAY, timestamp);
} else {
return new HideUntilValue(DateUtilities.getDateStringWithTime(context, timestamp),
Task.HIDE_UNTIL_SPECIFIC_DAY_TIME, timestamp);
}
}
// --- listening for events
@Override
@ -124,11 +255,12 @@ public class HideUntilControlSet extends TaskEditControlSetBase implements OnIte
// ... at conclusion of dialog, update our list
HideUntilValue item = adapter.getItem(position);
if(item.date == SPECIFIC_DATE) {
customDate =
final DateTime customDate =
newDateTime(existingDate == EXISTING_TIME_UNSET ? DateUtilities.now() : existingDate)
.withSecondOfMinute(0);
taskEditFragment.startActivityForResult(new Intent(taskEditFragment.getActivity(), DateAndTimePickerActivity.class) {{
final Activity activity = getActivity();
startActivityForResult(new Intent(activity, DateAndTimePickerActivity.class) {{
putExtra(DateAndTimePickerActivity.EXTRA_TIMESTAMP, customDate.getMillis());
}}, REQUEST_HIDE_UNTIL);
spinner.setSelection(previousSetting);
@ -140,8 +272,9 @@ public class HideUntilControlSet extends TaskEditControlSetBase implements OnIte
}
public void setCustomDate(long timestamp) {
customDate = new DateTime(timestamp);
customDateFinished();
updateSpinnerOptions(timestamp);
spinner.setSelection(0);
refreshDisplayView();
}
@Override
@ -149,121 +282,16 @@ public class HideUntilControlSet extends TaskEditControlSetBase implements OnIte
// ignore
}
DateTime customDate;
private void customDateFinished() {
updateSpinnerOptions(customDate.getMillis());
spinner.setSelection(0);
refreshDisplayView();
}
// --- setting up values
private void refreshDisplayView() {
HideUntilValue value = adapter.getItem(selection);
if (value.setting == Task.HIDE_UNTIL_NONE) {
textDisplay.setText(R.string.TEA_hideUntil_label);
textDisplay.setTextColor(unsetColor);
spinner.setAlpha(0.5f);
clearButton.setVisibility(View.GONE);
} else {
String display = value.toString();
if (value.setting != Task.HIDE_UNTIL_SPECIFIC_DAY && value.setting != Task.HIDE_UNTIL_SPECIFIC_DAY_TIME) {
display = display.toLowerCase();
}
textDisplay.setText(activity.getString(R.string.TEA_hideUntil_display, display));
textDisplay.setTextColor(themeColor);
spinner.setAlpha(1.0f);
clearButton.setVisibility(View.VISIBLE);
}
}
@Override
protected void afterInflate() {
textDisplay = (TextView) getView().findViewById(R.id.display_row_edit);
clearButton = (ImageView) getView().findViewById(R.id.clear);
clearButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateSpinnerOptions(0);
selection = 0;
spinner.setSelection(selection);
refreshDisplayView();
}
});
textDisplay.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (spinner == null) {
getView();
}
spinner.performClick();
}
});
this.spinner = (Spinner) getView().findViewById(R.id.hideUntil);
adapter = new HiddenTopArrayAdapter<>(activity, android.R.layout.simple_spinner_item, spinnerItems);
spinner.setAdapter(adapter);
this.spinner.setOnItemSelectedListener(this);
}
@Override
public void readFromTask(Task task) {
long date = task.getHideUntil();
DateTime dueDay = newDateTime(task.getDueDate())
.withHourOfDay(0)
.withMinuteOfHour(0)
.withSecondOfMinute(0)
.withMillisOfSecond(0);
// For the hide until due case, we need the time component
long dueTime = task.getDueDate()/1000L*1000L;
if(date == 0) {
selection = 0;
date = 0;
} else if(date == dueDay.getMillis()) {
selection = 1;
date = 0;
} else if (date == dueTime){
selection = 2;
date = 0;
} else if(date + DateUtilities.ONE_DAY == dueDay.getMillis()) {
selection = 3;
date = 0;
} else if(date + DateUtilities.ONE_WEEK == dueDay.getMillis()) {
selection = 4;
date = 0;
}
updateSpinnerOptions(date);
super.readFromTask(task);
}
@Override
public int getIcon() {
return R.drawable.ic_visibility_off_24dp;
}
@Override
protected void readFromTaskOnInitialize() {
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setSelection(selection);
refreshDisplayView();
}
@Override
protected void writeToModelAfterInitialized(Task task) {
if(adapter == null || spinner == null) {
return;
}
HideUntilValue item = adapter.getItem(spinner.getSelectedItemPosition());
if(item == null) {
return;
}
long value = task.createHideUntil(item.setting, item.date);
task.setHideUntil(value);
}
}

@ -1,130 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.ui;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.view.View.OnClickListener;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.preferences.ActivityPreferences;
public abstract class PopupControlSet extends TaskEditControlSetBase {
protected final View displayView;
protected final ActivityPreferences preferences;
private DialogBuilder dialogBuilder;
protected AlertDialog dialog;
private final String titleString;
public interface PopupDialogClickListener {
boolean onClick(DialogInterface d, int which);
}
final PopupDialogClickListener okListener = new PopupDialogClickListener() {
@Override
public boolean onClick(DialogInterface d, int which) {
onOkClick();
return true;
}
};
final DialogInterface.OnCancelListener cancelListener = new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface d) {
onCancelClick();
}
};
public PopupControlSet(ActivityPreferences preferences, Activity activity, int viewLayout,
int taskEditViewLayout, final int title, DialogBuilder dialogBuilder) {
super(activity, viewLayout, false);
this.preferences = preferences;
this.dialogBuilder = dialogBuilder;
if (taskEditViewLayout != -1) {
this.displayView = inflateWithTemplate(taskEditViewLayout);
} else {
this.displayView = null;
}
titleString = (title > 0) ? activity.getString(title) : ""; //$NON-NLS-1$
if (displayView != null) {
displayView.setOnClickListener(getDisplayClickListener());
}
}
@Override
public View getView() {
return displayView;
}
protected View getDialogView() {
return super.getView();
}
protected Dialog buildDialog(String title, final PopupDialogClickListener okClickListener, DialogInterface.OnCancelListener cancelClickListener) {
AlertDialog.Builder builder = dialogBuilder.newDialog()
.setView(getDialogView())
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (okClickListener.onClick(dialog, 0)) {
dialog.dismiss();
}
}
})
.setOnCancelListener(cancelClickListener);
dialog = builder.show();
return dialog;
}
protected OnClickListener getDisplayClickListener() {
return new OnClickListener() {
@Override
public void onClick(View v) {
if (dialog == null) {
buildDialog(titleString, okListener, cancelListener);
}
dialog.show();
}
};
}
protected void onOkClick() {
refreshDisplayView();
}
protected void onCancelClick() {
refreshDisplayView();
}
public Dialog getDialog() {
return dialog;
}
@Override
public void writeToModel(Task task) {
if (initialized && dialog != null) {
dialog.dismiss();
}
super.writeToModel(task);
}
@Override
public void readFromTask(Task task) {
super.readFromTask(task);
refreshDisplayView();
}
protected abstract void refreshDisplayView();
}

@ -5,13 +5,12 @@
*/
package com.todoroo.astrid.ui;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import org.tasks.R;
@ -27,35 +26,26 @@ public class RandomReminderControlSet {
private final int[] hours;
public RandomReminderControlSet(Activity activity, View parentView) {
public RandomReminderControlSet(Context context, View parentView, long reminderPeriod) {
periodSpinner = (Spinner) parentView.findViewById(R.id.reminder_random_interval);
periodSpinner.setVisibility(View.VISIBLE);
// create adapter
ArrayAdapter<String> adapter = new ArrayAdapter<>(
activity, android.R.layout.simple_spinner_item,
activity.getResources().getStringArray(R.array.TEA_reminder_random));
context, android.R.layout.simple_spinner_item,
context.getResources().getStringArray(R.array.TEA_reminder_random));
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
periodSpinner.setAdapter(adapter);
// create hour array
String[] hourStrings = activity.getResources().getStringArray(R.array.TEA_reminder_random_hours);
String[] hourStrings = context.getResources().getStringArray(R.array.TEA_reminder_random_hours);
hours = new int[hourStrings.length];
for(int i = 0; i < hours.length; i++) {
hours[i] = Integer.parseInt(hourStrings[i]);
}
}
public void readFromTaskOnInitialize(Task model) {
long time = model.getReminderPeriod();
if(time <= 0) {
/* default interval for spinner if date is unselected */
time = DateUtilities.ONE_WEEK * 2;
}
int i;
for(i = 0; i < hours.length - 1; i++) {
if (hours[i] * DateUtilities.ONE_HOUR >= time) {
if (hours[i] * DateUtilities.ONE_HOUR >= reminderPeriod) {
break;
}
}

@ -5,41 +5,55 @@
*/
package com.todoroo.astrid.ui;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.google.common.primitives.Longs;
import com.todoroo.andlib.data.Callback;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.alarms.AlarmFields;
import com.todoroo.astrid.alarms.AlarmService;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R;
import org.tasks.activities.DateAndTimePickerActivity;
import org.tasks.activities.TimePickerActivity;
import org.tasks.injection.ForActivity;
import org.tasks.location.Geofence;
import org.tasks.location.GeofenceService;
import org.tasks.location.PlacePicker;
import org.tasks.preferences.Device;
import org.tasks.preferences.PermissionRequestor;
import org.tasks.preferences.Preferences;
import org.tasks.ui.HiddenTopArrayAdapter;
import org.tasks.ui.TaskEditControlFragment;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import timber.log.Timber;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import butterknife.OnItemSelected;
import static com.google.common.collect.Lists.newArrayList;
import static com.todoroo.andlib.utility.DateUtilities.getLongDateStringWithTime;
import static org.tasks.date.DateTimeUtils.newDateTime;
@ -49,40 +63,227 @@ import static org.tasks.date.DateTimeUtils.newDateTime;
* @author Tim Su <tim@todoroo.com>
*
*/
public class ReminderControlSet extends TaskEditControlSetBase implements AdapterView.OnItemSelectedListener {
public class ReminderControlSet extends TaskEditControlFragment {
private static final int REQUEST_NEW_ALARM = 12152;
private static final int REQUEST_LOCATION_REMINDER = 12153;
private static final String EXTRA_TASK_ID = "extra_task_id";
private static final String EXTRA_FLAGS = "extra_flags";
private static final String EXTRA_RANDOM_REMINDER = "extra_random_reminder";
private static final String EXTRA_ALARMS = "extra_alarms";
private static final String EXTRA_GEOFENCES = "extra_geofences";
@Inject AlarmService alarmService;
@Inject GeofenceService geofenceService;
@Inject PermissionRequestor permissionRequestor;
@Inject Device device;
@Inject Preferences preferences;
@Inject @ForActivity Context context;
public static final int REQUEST_NEW_ALARM = 12152;
public static final int REQUEST_LOCATION_REMINDER = 12153;
@Bind(R.id.alert_container) LinearLayout alertContainer;
@Bind(R.id.reminder_alarm) Spinner mode;
@Bind(R.id.alarms_add_spinner) Spinner addSpinner;
private Spinner mode;
private Spinner addSpinner;
private TextView modeDisplay;
private long taskId;
private int flags;
private long randomReminder;
private RandomReminderControlSet randomControlSet;
private LinearLayout alertContainer;
private boolean whenDue;
private boolean whenOverdue;
private AlarmService alarmService;
private GeofenceService geofenceService;
private TaskEditFragment taskEditFragment;
private PermissionRequestor permissionRequestor;
private Device device;
private List<String> spinnerOptions = new ArrayList<>();
private ArrayAdapter<String> remindAdapter;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
remindAdapter = new HiddenTopArrayAdapter(context, android.R.layout.simple_spinner_item, spinnerOptions);
String[] modes = getResources().getStringArray(R.array.reminder_ring_modes);
ArrayAdapter<String> modeAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, modes);
modeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mode.setAdapter(modeAdapter);
if (savedInstanceState != null) {
taskId = savedInstanceState.getLong(EXTRA_TASK_ID);
flags = savedInstanceState.getInt(EXTRA_FLAGS);
randomReminder = savedInstanceState.getLong(EXTRA_RANDOM_REMINDER);
List<Geofence> geofences = new ArrayList<>();
List<Parcelable> geofenceArray = savedInstanceState.getParcelableArrayList(EXTRA_GEOFENCES);
for (Parcelable geofence : geofenceArray) {
geofences.add((Geofence) geofence);
}
setup(Longs.asList(savedInstanceState.getLongArray(EXTRA_ALARMS)), geofences);
} else {
final List<Long> alarms = new ArrayList<>();
alarmService.getAlarms(taskId, new Callback<Metadata>() {
@Override
public void apply(Metadata entry) {
alarms.add(entry.getValue(AlarmFields.TIME));
}
});
setup(alarms, geofenceService.getGeofences(taskId));
}
addSpinner.setAdapter(remindAdapter);
return view;
}
@OnItemSelected(R.id.alarms_add_spinner)
void addAlarm(int position) {
String selected = spinnerOptions.get(position);
if (selected.equals(getString(R.string.when_due))) {
addDue();
} else if(selected.equals(getString(R.string.when_overdue))) {
addOverdue();
} else if (selected.equals(getString(R.string.randomly))) {
addRandomReminder(TimeUnit.DAYS.toMillis(14));
} else if (selected.equals(getString(R.string.pick_a_date_and_time))) {
addNewAlarm();
} else if (selected.equals(getString(R.string.pick_a_location))) {
if (permissionRequestor.requestFineLocation()) {
pickLocation();
}
}
if (position != 0) {
updateSpinner();
}
}
@OnClick(R.id.alarms_add)
void addAlarm(View view) {
if (spinnerOptions.size() == 2) {
addNewAlarm();
} else {
addSpinner.performClick();
}
}
@Override
protected int getLayout() {
return R.layout.control_set_reminders;
}
@Override
public int getIcon() {
return R.drawable.ic_notifications_24dp;
}
@Override
public void initialize(boolean isNewTask, Task task) {
taskId = task.getId();
flags = task.getReminderFlags();
randomReminder = task.getReminderPeriod();
}
private void setup(List<Long> alarms, List<Geofence> geofences) {
setValue(flags);
alertContainer.removeAllViews();
if (whenDue) {
addDue();
}
if (whenOverdue) {
addOverdue();
}
if (randomReminder > 0) {
addRandomReminder(randomReminder);
}
for (long timestamp : alarms) {
addAlarmRow(timestamp);
}
for (Geofence geofence : geofences) {
addGeolocationReminder(geofence);
}
updateSpinner();
}
@Override
public void apply(Task task) {
task.setReminderFlags(getValue());
task.setReminderPeriod(getRandomReminderPeriod());
if(alarmService.synchronizeAlarms(task.getId(), getAlarms())) {
task.setModificationDate(DateUtilities.now());
}
if (geofenceService.synchronizeGeofences(task.getId(), getGeofences())) {
task.setModificationDate(DateUtilities.now());
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(EXTRA_TASK_ID, taskId);
outState.putInt(EXTRA_FLAGS, getValue());
outState.putLong(EXTRA_RANDOM_REMINDER, getRandomReminderPeriod());
outState.putLongArray(EXTRA_ALARMS, Longs.toArray(getAlarms()));
outState.putParcelableArrayList(EXTRA_GEOFENCES, newArrayList(getGeofences()));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_NEW_ALARM) {
if (resultCode == Activity.RESULT_OK) {
addAlarmRow(data.getLongExtra(TimePickerActivity.EXTRA_TIMESTAMP, 0L));
}
} else if (requestCode == REQUEST_LOCATION_REMINDER) {
if (resultCode == Activity.RESULT_OK) {
addGeolocationReminder(PlacePicker.getPlace(context, data, preferences));
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private Set<Long> getAlarms() {
Set<Long> alarms = new LinkedHashSet<>();
for (int i = 0 ; i < alertContainer.getChildCount() ; i++) {
Object tag = alertContainer.getChildAt(i).getTag();
if (tag instanceof Long) {
alarms.add((Long) tag);
}
}
return alarms;
}
private Set<Geofence> getGeofences() {
Set<Geofence> geofences = new LinkedHashSet<>();
for (int i = 0 ; i < alertContainer.getChildCount() ; i++) {
Object tag = alertContainer.getChildAt(i).getTag();
if (tag instanceof Geofence) {
geofences.add((Geofence) tag);
}
}
return geofences;
}
public ReminderControlSet(AlarmService alarmService, GeofenceService geofenceService,
TaskEditFragment taskEditFragment, PermissionRequestor permissionRequestor,
Device device) {
super(taskEditFragment.getActivity(), R.layout.control_set_reminders);
this.alarmService = alarmService;
this.geofenceService = geofenceService;
this.taskEditFragment = taskEditFragment;
this.permissionRequestor = permissionRequestor;
this.device = device;
public void addAlarmRow(final Long timestamp) {
addAlarmRow(getLongDateStringWithTime(context, timestamp), timestamp, null);
}
public void pickLocation() {
Intent intent = PlacePicker.getIntent(getActivity());
if (intent != null) {
startActivityForResult(intent, REQUEST_LOCATION_REMINDER);
}
}
public void addGeolocationReminder(final Geofence geofence) {
View alertItem = addAlarmRow(geofence.getName(), null, new OnClickListener() {
@Override
public void onClick(View v) {
}
});
alertItem.setTag(geofence);
}
public int getValue() {
private int getValue() {
int value = 0;
if(whenDue) {
value |= Task.NOTIFY_AT_DEADLINE;
@ -101,40 +302,18 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
return value;
}
private long getRandomReminderPeriod() {
return randomControlSet == null ? 0L : randomControlSet.getReminderPeriod();
}
private void addNewAlarm() {
taskEditFragment.startActivityForResult(new Intent(taskEditFragment.getActivity(), DateAndTimePickerActivity.class) {{
startActivityForResult(new Intent(getActivity(), DateAndTimePickerActivity.class) {{
putExtra(DateAndTimePickerActivity.EXTRA_TIMESTAMP, newDateTime().startOfDay().getMillis());
}}, REQUEST_NEW_ALARM);
}
public void addAlarmRow(final Long timestamp) {
final View alertItem = addAlarmRow(getLongDateStringWithTime(activity, timestamp), timestamp, null);
TextView display = (TextView) alertItem.findViewById(R.id.alarm_string);
display.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// pickNewAlarm(newDateTime(timestamp), new DateAndTimePickerDialog.OnDateTimePicked() {
// @Override
// public void onDateTimePicked(DateTime dateTime) {
// long millis = dateTime.getMillis();
// addAlarmRow(alertItem, getDisplayString(millis), millis, null);
// }
// });
}
});
}
public void addGeolocationReminder(final Geofence geofence) {
View alertItem = addAlarmRow(geofence.getName(), null, new OnClickListener() {
@Override
public void onClick(View v) {
}
});
alertItem.setTag(geofence);
}
private View addAlarmRow(String text, Long timestamp, final OnClickListener onRemove) {
final View alertItem = LayoutInflater.from(activity).inflate(R.layout.alarm_edit_row, null);
final View alertItem = getActivity().getLayoutInflater().inflate(R.layout.alarm_edit_row, null);
alertContainer.addView(alertItem);
addAlarmRow(alertItem, text, timestamp, onRemove);
return alertItem;
@ -162,106 +341,24 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
spinnerOptions.clear();
spinnerOptions.add("");
if (!whenDue) {
spinnerOptions.add(taskEditFragment.getString(R.string.when_due));
spinnerOptions.add(getString(R.string.when_due));
}
if (!whenOverdue) {
spinnerOptions.add(taskEditFragment.getString(R.string.when_overdue));
spinnerOptions.add(getString(R.string.when_overdue));
}
if (randomControlSet == null) {
spinnerOptions.add(taskEditFragment.getString(R.string.randomly));
spinnerOptions.add(getString(R.string.randomly));
}
if (device.supportsLocationServices()) {
spinnerOptions.add(taskEditFragment.getString(R.string.pick_a_location));
spinnerOptions.add(getString(R.string.pick_a_location));
}
spinnerOptions.add(taskEditFragment.getString(R.string.pick_a_date_and_time));
spinnerOptions.add(getString(R.string.pick_a_date_and_time));
remindAdapter.notifyDataSetChanged();
}
@Override
protected void afterInflate() {
alertContainer = (LinearLayout) getView().findViewById(R.id.alert_container);
getView().findViewById(R.id.alarms_add).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (spinnerOptions.size() == 2) {
addNewAlarm();
} else {
addSpinner.performClick();
}
}
});
addSpinner = (Spinner) getView().findViewById(R.id.alarms_add_spinner);
addSpinner.setOnItemSelectedListener(ReminderControlSet.this);
remindAdapter = new HiddenTopArrayAdapter(activity, android.R.layout.simple_spinner_item, spinnerOptions);
addSpinner.setAdapter(remindAdapter);
modeDisplay = (TextView) getView().findViewById(R.id.reminder_alarm_display);
mode = (Spinner) getView().findViewById(R.id.reminder_alarm);
modeDisplay.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mode.performClick();
}
});
String[] list = new String[] {
activity.getString(R.string.ring_once),
activity.getString(R.string.ring_five_times),
activity.getString(R.string.ring_nonstop),
};
final ArrayAdapter<String> modeAdapter = new ArrayAdapter<>(
activity, android.R.layout.simple_spinner_item, list);
modeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mode.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
modeDisplay.setText(modeAdapter.getItem(position));
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// TODO Auto-generated method stub
}
});
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
mode.setAdapter(modeAdapter);
}
});
}
@Override
protected void readFromTaskOnInitialize() {
setValue(model.getReminderFlags());
alertContainer.removeAllViews();
if (whenDue) {
addDue();
}
if (whenOverdue) {
addOverdue();
}
if (model.hasRandomReminder()) {
addRandomReminder();
}
alarmService.getAlarms(model.getId(), new Callback<Metadata>() {
@Override
public void apply(Metadata entry) {
addAlarmRow(entry.getValue(AlarmFields.TIME));
}
});
for (Geofence geofence : geofenceService.getGeofences(model.getId())) {
addGeolocationReminder(geofence);
}
updateSpinner();
}
private void addDue() {
whenDue = true;
addAlarmRow(taskEditFragment.getString(R.string.when_due), null, new OnClickListener() {
addAlarmRow(getString(R.string.when_due), null, new OnClickListener() {
@Override
public void onClick(View v) {
whenDue = false;
@ -271,7 +368,7 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
private void addOverdue() {
whenOverdue = true;
addAlarmRow(taskEditFragment.getString(R.string.when_overdue), null, new OnClickListener() {
addAlarmRow(getString(R.string.when_overdue), null, new OnClickListener() {
@Override
public void onClick(View v) {
whenOverdue = false;
@ -279,15 +376,14 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
});
}
private void addRandomReminder() {
View alarmRow = addAlarmRow(taskEditFragment.getString(R.string.randomly_once), null, new OnClickListener() {
private void addRandomReminder(long reminderPeriod) {
View alarmRow = addAlarmRow(getString(R.string.randomly_once), null, new OnClickListener() {
@Override
public void onClick(View v) {
randomControlSet = null;
}
});
randomControlSet = new RandomReminderControlSet(activity, alarmRow);
randomControlSet.readFromTaskOnInitialize(model);
randomControlSet = new RandomReminderControlSet(context, alarmRow, reminderPeriod);
}
private void setValue(int flags) {
@ -302,72 +398,4 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
mode.setSelection(0);
}
}
@Override
protected void writeToModelAfterInitialized(Task task) {
task.setReminderFlags(getValue());
task.setReminderPeriod(randomControlSet == null ? 0L : randomControlSet.getReminderPeriod());
Set<Long> alarms = new LinkedHashSet<>();
Set<Geofence> geofences = new LinkedHashSet<>();
for(int i = 0; i < alertContainer.getChildCount(); i++) {
Object tag = alertContainer.getChildAt(i).getTag();
//noinspection StatementWithEmptyBody
if (tag == null) {
} else if (tag instanceof Long) {
alarms.add((Long) tag);
} else if (tag instanceof Geofence) {
geofences.add((Geofence) tag);
} else {
Timber.e("Unexpected tag: %s", tag);
}
}
if(alarmService.synchronizeAlarms(task.getId(), alarms)) {
task.setModificationDate(DateUtilities.now());
}
if (geofenceService.synchronizeGeofences(task.getId(), geofences)) {
task.setModificationDate(DateUtilities.now());
}
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Timber.i("onItemSelected(%s, %s, %s, %s)", parent, view, position, id);
String selected = spinnerOptions.get(position);
if (selected.equals(taskEditFragment.getString(R.string.when_due))) {
addDue();
} else if(selected.equals(taskEditFragment.getString(R.string.when_overdue))) {
addOverdue();
} else if (selected.equals(taskEditFragment.getString(R.string.randomly))) {
addRandomReminder();
} else if (selected.equals(taskEditFragment.getString(R.string.pick_a_date_and_time))) {
addNewAlarm();
} else if (selected.equals(taskEditFragment.getString(R.string.pick_a_location))) {
if (permissionRequestor.requestFineLocation()) {
pickLocation();
}
}
if (position != 0) {
updateSpinner();
}
}
public void pickLocation() {
Intent intent = PlacePicker.getIntent(taskEditFragment.getActivity());
if (intent != null) {
taskEditFragment.startActivityForResult(intent, REQUEST_LOCATION_REMINDER);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
@Override
public int getIcon() {
return R.drawable.ic_notifications_24dp;
}
}

@ -5,7 +5,7 @@
*/
package com.todoroo.astrid.ui;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.text.format.DateUtils;
import android.view.View;
@ -20,29 +20,22 @@ import org.tasks.preferences.ActivityPreferences;
public class TimeDurationControlSet implements OnNNumberPickedListener, View.OnClickListener {
private final Activity activity;
private final Context context;
private final TextView timeButton;
private int timeDuration;
private int[] initialValues = null;
private NNumberPickerDialog dialog = null;
private Task model;
private final IntegerProperty property;
private ActivityPreferences activityPreferences;
public TimeDurationControlSet(Activity activity, View view, IntegerProperty property,
public TimeDurationControlSet(Context context, View view,
int timeButtonId, ActivityPreferences activityPreferences) {
this.activity = activity;
this.property = property;
this.context = context;
this.activityPreferences = activityPreferences;
timeButton = (TextView)view.findViewById(timeButtonId);
((View) timeButton.getParent()).setOnClickListener(this);
}
public void setModel(Task model) {
this.model = model;
}
public int getTimeDurationInSeconds() {
return timeDuration;
}
@ -54,7 +47,7 @@ public class TimeDurationControlSet implements OnNNumberPickedListener, View.OnC
timeDuration = timeDurationInSeconds;
Resources r = activity.getResources();
Resources r = context.getResources();
if(timeDurationInSeconds == 0) {
timeButton.setText(r.getString(R.string.WID_dateButtonUnset));
return;
@ -65,10 +58,6 @@ public class TimeDurationControlSet implements OnNNumberPickedListener, View.OnC
int hours = timeDuration / 3600;
int minutes = timeDuration / 60 - 60 * hours;
initialValues = new int[] { hours, minutes };
if (model != null) {
model.setValue(property, timeDuration);
}
}
/** Called when NumberPicker activity is completed */
@ -81,8 +70,8 @@ public class TimeDurationControlSet implements OnNNumberPickedListener, View.OnC
@Override
public void onClick(View v) {
if(dialog == null) {
dialog = new NNumberPickerDialog(activity, activityPreferences.getDialogTheme(), this,
activity.getResources().getString(R.string.DLG_hour_minutes),
dialog = new NNumberPickerDialog(context, activityPreferences.getDialogTheme(), this,
context.getResources().getString(R.string.DLG_hour_minutes),
new int[] {0, 0}, new int[] {1, 5}, new int[] {0, 0},
new int[] {999, 59}, new String[] {":", null});
final NumberPicker hourPicker = dialog.getPicker(0);

@ -2,11 +2,16 @@ package org.tasks.activities;
import android.app.FragmentManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import com.todoroo.astrid.gcal.AndroidCalendar;
import org.tasks.injection.InjectingAppCompatActivity;
import org.tasks.preferences.ActivityPreferences;
import org.tasks.preferences.PermissionRequestor;
import javax.inject.Inject;
public class CalendarSelectionActivity extends InjectingAppCompatActivity implements CalendarSelectionDialog.CalendarSelectionHandler {
@ -14,19 +19,20 @@ public class CalendarSelectionActivity extends InjectingAppCompatActivity implem
public static final String EXTRA_CALENDAR_ID = "extra_calendar_id";
public static final String EXTRA_CALENDAR_NAME = "extra_calendar_name";
public static final String EXTRA_SHOW_NONE = "extra_show_none";
@Inject PermissionRequestor permissionRequestor;
@Inject ActivityPreferences preferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fragmentManager = getFragmentManager();
CalendarSelectionDialog fragmentByTag = (CalendarSelectionDialog) fragmentManager.findFragmentByTag(FRAG_TAG_CALENDAR_PREFERENCE_SELECTION);
if (fragmentByTag == null) {
fragmentByTag = new CalendarSelectionDialog();
fragmentByTag.enableNone();
fragmentByTag.show(fragmentManager, FRAG_TAG_CALENDAR_PREFERENCE_SELECTION);
preferences.applyTheme();
if (permissionRequestor.requestCalendarPermissions()) {
showDialog();
}
fragmentByTag.setCalendarSelectionHandler(this);
}
@Override
@ -41,4 +47,30 @@ public class CalendarSelectionActivity extends InjectingAppCompatActivity implem
public void dismiss() {
finish();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == PermissionRequestor.REQUEST_CALENDAR) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showDialog();
} else {
finish();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
private void showDialog() {
FragmentManager fragmentManager = getFragmentManager();
CalendarSelectionDialog fragmentByTag = (CalendarSelectionDialog) fragmentManager.findFragmentByTag(FRAG_TAG_CALENDAR_PREFERENCE_SELECTION);
if (fragmentByTag == null) {
fragmentByTag = new CalendarSelectionDialog();
if (getIntent().getBooleanExtra(EXTRA_SHOW_NONE, false)) {
fragmentByTag.enableNone();
}
fragmentByTag.show(fragmentManager, FRAG_TAG_CALENDAR_PREFERENCE_SELECTION);
}
fragmentByTag.setCalendarSelectionHandler(this);
}
}

@ -0,0 +1,70 @@
package org.tasks.activities;
import android.app.FragmentManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog;
import org.tasks.R;
import org.tasks.dialogs.MyDatePickerDialog;
import org.tasks.injection.InjectingAppCompatActivity;
import org.tasks.preferences.ActivityPreferences;
import org.tasks.time.DateTime;
import javax.inject.Inject;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
public class DatePickerActivity extends InjectingAppCompatActivity
implements DatePickerDialog.OnDateSetListener, DialogInterface.OnDismissListener {
private static final String FRAG_TAG_DATE_PICKER = "frag_tag_date_picker";
public static final String EXTRA_TIMESTAMP = "extra_timestamp";
@Inject ActivityPreferences preferences;
private DateTime initial;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
preferences.applyTheme();
long timestamp = getIntent().getLongExtra(EXTRA_TIMESTAMP, currentTimeMillis());
initial = timestamp > 0 ? new DateTime(timestamp) : new DateTime().startOfDay();
FragmentManager fragmentManager = getFragmentManager();
MyDatePickerDialog dialog = (MyDatePickerDialog) fragmentManager.findFragmentByTag(FRAG_TAG_DATE_PICKER);
if (dialog == null) {
dialog = new MyDatePickerDialog();
dialog.initialize(null, initial.getYear(), initial.getMonthOfYear() - 1, initial.getDayOfMonth());
if (preferences.isDarkTheme()) {
dialog.setAccentColor(getResources().getColor(R.color.black_text_hint));
}
dialog.show(fragmentManager, FRAG_TAG_DATE_PICKER);
}
dialog.setOnDismissListener(this);
dialog.setOnDateSetListener(this);
}
@Override
public void onDateSet(DatePickerDialog view, final int year, final int monthOfYear, final int dayOfMonth) {
setResult(RESULT_OK, new Intent() {{
putExtra(EXTRA_TIMESTAMP, initial
.withYear(year)
.withMonthOfYear(monthOfYear + 1)
.withDayOfMonth(dayOfMonth)
.getMillis());
}});
}
@Override
public void onDismiss(DialogInterface dialog) {
finish();
}
}

@ -29,6 +29,7 @@ import org.tasks.activities.CameraActivity;
import org.tasks.activities.ClearAllDataActivity;
import org.tasks.activities.ClearGtaskDataActivity;
import org.tasks.activities.DateAndTimePickerActivity;
import org.tasks.activities.DatePickerActivity;
import org.tasks.activities.DeleteAllCalendarEventsActivity;
import org.tasks.activities.DeleteCompletedActivity;
import org.tasks.activities.DeleteCompletedEventsActivity;
@ -103,6 +104,7 @@ import dagger.Provides;
AddAttachmentActivity.class,
ShortcutActivity.class,
CameraActivity.class,
DatePickerActivity.class,
TimePickerActivity.class,
DateAndTimePickerActivity.class
})

@ -7,12 +7,23 @@ import android.content.Context;
import com.todoroo.astrid.actfm.TagViewFragment;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.files.FilesControlSet;
import com.todoroo.astrid.gtasks.GtasksListFragment;
import com.todoroo.astrid.repeats.RepeatControlSet;
import com.todoroo.astrid.subtasks.SubtasksListFragment;
import com.todoroo.astrid.subtasks.SubtasksTagListFragment;
import com.todoroo.astrid.tags.TagsControlSet;
import com.todoroo.astrid.timers.TimerControlSet;
import com.todoroo.astrid.ui.EditTitleControlSet;
import com.todoroo.astrid.ui.HideUntilControlSet;
import com.todoroo.astrid.ui.QuickAddBar;
import com.todoroo.astrid.ui.ReminderControlSet;
import org.tasks.ui.CalendarControlSet;
import org.tasks.ui.DeadlineControlSet;
import org.tasks.ui.DescriptionControlSet;
import org.tasks.ui.NavigationDrawerFragment;
import org.tasks.ui.PriorityControlSet;
import javax.inject.Singleton;
@ -28,7 +39,18 @@ import dagger.Provides;
TagViewFragment.class,
TaskEditFragment.class,
NavigationDrawerFragment.class,
QuickAddBar.class
QuickAddBar.class,
CalendarControlSet.class,
DeadlineControlSet.class,
PriorityControlSet.class,
DescriptionControlSet.class,
HideUntilControlSet.class,
ReminderControlSet.class,
FilesControlSet.class,
EditTitleControlSet.class,
TimerControlSet.class,
TagsControlSet.class,
RepeatControlSet.class
})
public class FragmentModule {

@ -1,10 +1,13 @@
package org.tasks.location;
import android.os.Parcel;
import android.os.Parcelable;
import com.todoroo.astrid.data.Metadata;
import java.io.Serializable;
public class Geofence implements Serializable {
public class Geofence implements Serializable, Parcelable {
private final String name;
private final double latitude;
private final double longitude;
@ -63,4 +66,31 @@ public class Geofence implements Serializable {
", metadataId=" + metadataId +
'}';
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeDouble(latitude);
out.writeDouble(longitude);
out.writeInt(radius);
}
public static final Parcelable.Creator<Geofence> CREATOR = new Parcelable.Creator<Geofence>() {
@Override
public Geofence createFromParcel(Parcel source) {
String name = source.readString();
double latitude = source.readDouble();
double longitude = source.readDouble();
int radius = source.readInt();
return new Geofence(name, latitude, longitude, radius);
}
@Override
public Geofence[] newArray(int size) {
return new Geofence[size];
}
};
}

@ -1,6 +1,7 @@
package org.tasks.preferences;
import android.app.Activity;
import android.content.Context;
import android.util.TypedValue;
import javax.inject.Inject;
@ -21,19 +22,9 @@ public class ResourceResolver {
return getData(activity, attr);
}
public int getResource(int attr) {
return getResource(activity, attr);
}
public static int getResource(Activity activity, int attr) {
TypedValue typedValue = new TypedValue();
activity.getTheme().resolveAttribute(attr, typedValue, true);
return typedValue.resourceId;
}
public static int getData(Activity activity, int attr) {
public static int getData(Context context, int attr) {
TypedValue typedValue = new TypedValue();
activity.getTheme().resolveAttribute(attr, typedValue, true);
context.getTheme().resolveAttribute(attr, typedValue, true);
return typedValue.data;
}
}

@ -0,0 +1,246 @@
package org.tasks.ui;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.google.common.base.Strings;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gcal.AndroidCalendar;
import com.todoroo.astrid.gcal.GCalHelper;
import org.tasks.R;
import org.tasks.activities.CalendarSelectionActivity;
import org.tasks.injection.ForActivity;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import timber.log.Timber;
import static com.google.common.base.Strings.isNullOrEmpty;
public class CalendarControlSet extends TaskEditControlFragment {
private static final int REQUEST_CODE_CALENDAR = 70;
private static final String EXTRA_URI = "extra_uri";
private static final String EXTRA_ID = "extra_id";
private static final String EXTRA_NAME = "extra_name";
@Bind(R.id.clear) View cancelButton;
@Bind(R.id.calendar_display_which) TextView calendar;
@Inject GCalHelper gcalHelper;
@Inject Preferences preferences;
@Inject @ForActivity Context context;
private String calendarId;
private String calendarName;
private String eventUri;
private boolean isNewTask;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
eventUri = savedInstanceState.getString(EXTRA_URI);
calendarName = savedInstanceState.getString(EXTRA_NAME);
calendarId = savedInstanceState.getString(EXTRA_ID);
} else if (isNewTask) {
calendarId = preferences.getDefaultCalendar();
if (!Strings.isNullOrEmpty(calendarId)) {
AndroidCalendar defaultCalendar = gcalHelper.getCalendar(calendarId);
if (defaultCalendar == null) {
calendarId = null;
} else {
calendarName = defaultCalendar.getName();
}
}
}
if (!calendarEntryExists(eventUri)) {
eventUri = null;
}
refreshDisplayView();
return view;
}
@Override
protected int getLayout() {
return R.layout.control_set_gcal_display;
}
@Override
protected int getIcon() {
return R.drawable.ic_event_24dp;
}
@Override
public void initialize(boolean isNewTask, Task task) {
this.isNewTask = isNewTask;
eventUri = task.getCalendarURI();
}
@Override
public void apply(Task task) {
if (!task.hasDueDate()) {
return;
}
if (calendarEntryExists(task.getCalendarURI())) {
ContentResolver cr = context.getContentResolver();
try {
ContentValues updateValues = new ContentValues();
// check if we need to update the item
ContentValues setValues = task.getSetValues();
if(setValues.containsKey(Task.TITLE.name)) {
updateValues.put("title", task.getTitle());
}
if(setValues.containsKey(Task.NOTES.name)) {
updateValues.put("description", task.getNotes());
}
if(setValues.containsKey(Task.DUE_DATE.name) || setValues.containsKey(Task.ESTIMATED_SECONDS.name)) {
gcalHelper.createStartAndEndDate(task, updateValues);
}
cr.update(Uri.parse(task.getCalendarURI()), updateValues, null, null);
} catch (Exception e) {
Timber.e(e, "unable-to-update-calendar: %s", task.getCalendarURI());
}
return;
}
if (!isNullOrEmpty(calendarId)) {
ContentResolver cr = context.getContentResolver();
try{
ContentValues values = new ContentValues();
values.put("calendar_id", calendarId);
Uri uri = gcalHelper.createTaskEvent(task, cr, values);
if(uri != null) {
task.setCalendarUri(uri.toString());
// pop up the new event
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra("beginTime", values.getAsLong("dtstart"));
intent.putExtra("endTime", values.getAsLong("dtend"));
startActivity(intent);
}
} catch (Exception e) {
Timber.e(e, e.getMessage());
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_URI, eventUri);
outState.putString(EXTRA_NAME, calendarName);
outState.putString(EXTRA_ID, calendarId);
}
@OnClick(R.id.clear)
void clearCalendar(View view) {
calendarName = null;
calendarId = null;
eventUri = null;
refreshDisplayView();
}
@OnClick(R.id.calendar_display_which)
void clickCalendar(View view) {
if (Strings.isNullOrEmpty(eventUri)) {
startActivityForResult(new Intent(context, CalendarSelectionActivity.class), REQUEST_CODE_CALENDAR);
} else {
ContentResolver cr = getActivity().getContentResolver();
Uri uri = Uri.parse(eventUri);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
Cursor cursor = cr.query(uri, new String[] { "dtstart", "dtend" }, null, null, null);
try {
if(cursor.getCount() == 0) {
// event no longer exists
eventUri = null;
refreshDisplayView();
return;
}
cursor.moveToFirst();
intent.putExtra("beginTime", cursor.getLong(0));
intent.putExtra("endTime", cursor.getLong(1));
} catch (Exception e) {
Timber.e(e, e.getMessage());
Toast.makeText(getActivity(), R.string.gcal_TEA_error, Toast.LENGTH_LONG).show();
} finally {
cursor.close();
}
startActivity(intent);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_CALENDAR) {
if (resultCode == Activity.RESULT_OK) {
calendarId = data.getStringExtra(CalendarSelectionActivity.EXTRA_CALENDAR_ID);
calendarName = data.getStringExtra(CalendarSelectionActivity.EXTRA_CALENDAR_NAME);
refreshDisplayView();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void refreshDisplayView() {
if (!Strings.isNullOrEmpty(eventUri)) {
calendar.setAlpha(1.0f);
calendar.setText(R.string.gcal_TEA_showCalendar_label);
cancelButton.setVisibility(View.GONE);
} else if (calendarName != null) {
calendar.setAlpha(1.0f);
calendar.setText(calendarName);
cancelButton.setVisibility(View.VISIBLE);
} else {
calendar.setAlpha(0.5f);
calendar.setText(R.string.gcal_TEA_addToCalendar_label);
cancelButton.setVisibility(View.GONE);
}
}
private boolean calendarEntryExists(String eventUri) {
if (isNullOrEmpty(eventUri)) {
return false;
}
try {
Uri uri = Uri.parse(eventUri);
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(uri, new String[]{"dtstart"}, null, null, null);
try {
if (cursor.getCount() != 0) {
return true;
}
} finally {
cursor.close();
}
} catch(Exception e) {
Timber.e(e, "%s: %s", eventUri, e.getMessage());
}
return false;
}
}

@ -5,6 +5,8 @@ import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.drawable.DrawableCompat;
import com.google.common.collect.ImmutableList;
import org.tasks.R;
import org.tasks.injection.ForApplication;
@ -14,8 +16,6 @@ import javax.inject.Inject;
import timber.log.Timber;
import static java.util.Arrays.asList;
public class CheckBoxes {
private static final int MAX_IMPORTANCE_INDEX = 3;
@ -24,6 +24,7 @@ public class CheckBoxes {
private static List<Drawable> checkboxes;
private static List<Drawable> repeatingCheckboxes;
private static List<Drawable> completedCheckboxes;
private static List<Integer> priorityColors;
@Inject
public CheckBoxes(@ForApplication Context context) {
@ -32,10 +33,19 @@ public class CheckBoxes {
checkboxes = wrapDrawable(context, R.drawable.ic_check_box_outline_blank_24dp);
repeatingCheckboxes = wrapDrawable(context, R.drawable.ic_repeat_24dp);
completedCheckboxes = wrapDrawable(context, R.drawable.ic_check_box_24dp);
priorityColors = ImmutableList.of(
context.getResources().getColor(R.color.importance_1),
context.getResources().getColor(R.color.importance_2),
context.getResources().getColor(R.color.importance_3),
context.getResources().getColor(R.color.importance_4));
initialized = true;
}
}
public List<Integer> getPriorityColors() {
return priorityColors;
}
List<Drawable> getCheckBoxes() {
return checkboxes;
}
@ -49,7 +59,7 @@ public class CheckBoxes {
}
private static List<Drawable> wrapDrawable(Context context, int resId) {
return asList(
return ImmutableList.of(
getDrawable(context, resId, 0),
getDrawable(context, resId, 1),
getDrawable(context, resId, 2),

@ -1,10 +1,12 @@
package org.tasks.ui;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -15,58 +17,65 @@ import android.widget.TextView;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog;
import com.wdullaer.materialdatetimepicker.time.RadialPickerLayout;
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
import org.tasks.R;
import org.tasks.dialogs.MyDatePickerDialog;
import org.tasks.dialogs.MyTimePickerDialog;
import org.tasks.activities.DatePickerActivity;
import org.tasks.activities.TimePickerActivity;
import org.tasks.injection.ForActivity;
import org.tasks.preferences.ActivityPreferences;
import org.tasks.time.DateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import butterknife.OnItemSelected;
import static java.util.Arrays.asList;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.preferences.ResourceResolver.getData;
public class DeadlineControlSet extends TaskEditControlSetBase {
public class DeadlineControlSet extends TaskEditControlFragment {
private static final String FRAG_TAG_PICK_A_DATE = "frag_tag_pick_a_date";
private static final String FRAG_TAG_PICK_A_TIME = "frag_tag_pick_a_time";
private static final int REQUEST_DATE = 504;
private static final int REQUEST_TIME = 505;
private static final String EXTRA_DATE = "extra_date";
private static final String EXTRA_TIME = "extra_time";
private final List<String> dueDateOptions = new ArrayList<>();
private final List<String> dueTimeOptions = new ArrayList<>();
private final List<String> dueTimeHint;
private final int dateShortcutMorning;
private final int dateShortcutAfternoon;
private final int dateShortcutEvening;
private final int dateShortcutNight;
private final String nightString;
private final String eveningString;
private final String afternoonString;
private final String morningString;
private final String noTimeString;
private final String todayString;
private final String tomorrowString;
private Activity activity;
private ActivityPreferences preferences;
private Spinner dueDateSpinner;
private Spinner dueTimeSpinner;
private View clearButton;
private List<String> dueTimeHint = new ArrayList<>();
private int dateShortcutMorning;
private int dateShortcutAfternoon;
private int dateShortcutEvening;
private int dateShortcutNight;
private String nightString;
private String eveningString;
private String afternoonString;
private String morningString;
private String noTimeString;
private String todayString;
private String tomorrowString;
@Inject ActivityPreferences preferences;
@Inject @ForActivity Context context;
@Bind(R.id.due_date) Spinner dueDateSpinner;
@Bind(R.id.due_time) Spinner dueTimeSpinner;
@Bind(R.id.clear) View clearButton;
private ArrayAdapter<String> dueDateAdapter;
private ArrayAdapter<String> dueTimeAdapter;
private long date = 0;
private int time = -1;
public DeadlineControlSet(Activity activity, ActivityPreferences preferences) {
super(activity, R.layout.control_set_deadline);
this.activity = activity;
this.preferences = preferences;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
dateShortcutMorning = preferences.getDateShortcutMorning();
dateShortcutAfternoon = preferences.getDateShortcutAfternoon();
dateShortcutEvening = preferences.getDateShortcutEvening();
@ -101,95 +110,35 @@ public class DeadlineControlSet extends TaskEditControlSetBase {
activity.getString(R.string.pick_a_time)));
}
private String getTimeHint(int millisOfDay) {
DateTime dateTime = newDateTime().withMillisOfDay(millisOfDay);
return DateUtilities.getTimeString(activity, dateTime);
}
private void refreshDisplayView() {
updateDueDateOptions();
updateDueTimeOptions();
clearButton.setVisibility(date > 0 ? View.VISIBLE : View.GONE);
}
private void updateDueDateOptions() {
DateTime today = newDateTime().startOfDay();
String nextWeekString = activity.getString(R.string.next, today.plusWeeks(1).toString("EEEE"));
if (date == 0) {
dueDateOptions.set(0, activity.getString(R.string.TEA_no_date));
} else {
if (date == today.getMillis()) {
dueDateOptions.set(0, todayString);
} else if (date == today.plusDays(1).getMillis()) {
dueDateOptions.set(0, tomorrowString);
} else if (date == today.plusWeeks(1).getMillis()) {
dueDateOptions.set(0, nextWeekString);
} else {
dueDateOptions.set(0, DateUtilities.getLongDateString(newDateTime(date)));
}
}
dueDateOptions.set(3, nextWeekString);
dueDateAdapter.notifyDataSetChanged();
dueDateSpinner.setSelection(0);
}
private void updateDueTimeOptions() {
if (time == -1) {
dueTimeOptions.set(0, noTimeString);
} else {
int compareTime = newDateTime()
.withMillisOfDay(time)
.withSecondOfMinute(0)
.withMillisOfSecond(0)
.getMillisOfDay();
if (compareTime == dateShortcutMorning) {
dueTimeOptions.set(0, morningString);
} else if (compareTime == dateShortcutAfternoon) {
dueTimeOptions.set(0, afternoonString);
} else if (compareTime == dateShortcutEvening) {
dueTimeOptions.set(0, eveningString);
} else if (compareTime == dateShortcutNight) {
dueTimeOptions.set(0, nightString);
} else {
dueTimeOptions.set(0, DateUtilities.getTimeString(activity, newDateTime().withMillisOfDay(time)));
}
}
dueTimeAdapter.notifyDataSetChanged();
dueTimeSpinner.setSelection(0);
}
@Nullable
@Override
protected void afterInflate() {
View view = getView();
clearButton = view.findViewById(R.id.clear);
clearButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
date = 0;
time = -1;
refreshDisplayView();
}
});
dueDateSpinner = (Spinner) view.findViewById(R.id.due_date);
dueDateAdapter = new HiddenTopArrayAdapter(activity, android.R.layout.simple_spinner_item, dueDateOptions) {
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
date = savedInstanceState.getLong(EXTRA_DATE);
time = savedInstanceState.getInt(EXTRA_TIME);
}
final int themeColor = getData(context, R.attr.asTextColor);
final int overdueColor = context.getResources().getColor(R.color.overdue);
dueDateAdapter = new HiddenTopArrayAdapter<String>(context, android.R.layout.simple_spinner_item, dueDateOptions) {
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
int selectedItemPosition = position;
if (parent instanceof AdapterView) {
selectedItemPosition = ((AdapterView) parent).getSelectedItemPosition();
}
TextView tv = (TextView) LayoutInflater.from(activity).inflate(android.R.layout.simple_spinner_item, parent, false);
TextView tv = (TextView) inflater.inflate(android.R.layout.simple_spinner_item, parent, false);
tv.setText(dueDateOptions.get(selectedItemPosition));
if (date == 0) {
dueDateSpinner.setAlpha(0.5f);
dueDateSpinner.setBackgroundDrawable(getThemedUnderline());
dueDateSpinner.setBackgroundDrawable(getUnderline(themeColor));
} else {
dueDateSpinner.setAlpha(1.0f);
if (date < newDateTime().startOfDay().getMillis()) {
dueDateSpinner.setBackgroundDrawable(getRedUnderline());
tv.setTextColor(activity.getResources().getColor(R.color.overdue));
dueDateSpinner.setBackgroundDrawable(getUnderline(overdueColor));
tv.setTextColor(overdueColor);
} else {
dueDateSpinner.setBackgroundDrawable(getThemedUnderline());
dueDateSpinner.setBackgroundDrawable(getUnderline(themeColor));
tv.setTextColor(themeColor);
}
}
@ -198,26 +147,25 @@ public class DeadlineControlSet extends TaskEditControlSetBase {
};
dueDateSpinner.setAdapter(dueDateAdapter);
dueTimeSpinner = (Spinner) view.findViewById(R.id.due_time);
dueTimeAdapter = new HiddenTopArrayAdapter(activity, android.R.layout.simple_spinner_item, dueTimeOptions, dueTimeHint) {
dueTimeAdapter = new HiddenTopArrayAdapter<String>(context, android.R.layout.simple_spinner_item, dueTimeOptions, dueTimeHint) {
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
int selectedItemPosition = position;
if (parent instanceof AdapterView) {
selectedItemPosition = ((AdapterView) parent).getSelectedItemPosition();
}
TextView tv = (TextView) LayoutInflater.from(activity).inflate(android.R.layout.simple_spinner_item, parent, false);
TextView tv = (TextView) inflater.inflate(android.R.layout.simple_spinner_item, parent, false);
tv.setText(dueTimeOptions.get(selectedItemPosition));
if (time == -1) {
dueTimeSpinner.setAlpha(0.5f);
dueTimeSpinner.setBackgroundDrawable(getThemedUnderline());
dueTimeSpinner.setBackgroundDrawable(getUnderline(themeColor));
} else {
dueTimeSpinner.setAlpha(1.0f);
if (newDateTime(date).withMillisOfDay(time).isBeforeNow()) {
dueTimeSpinner.setBackgroundDrawable(getRedUnderline());
tv.setTextColor(activity.getResources().getColor(R.color.overdue));
dueTimeSpinner.setBackgroundDrawable(getUnderline(overdueColor));
tv.setTextColor(overdueColor);
} else {
dueTimeSpinner.setBackgroundDrawable(getThemedUnderline());
dueTimeSpinner.setBackgroundDrawable(getUnderline(themeColor));
tv.setTextColor(themeColor);
}
}
@ -226,119 +174,197 @@ public class DeadlineControlSet extends TaskEditControlSetBase {
};
dueTimeSpinner.setAdapter(dueTimeAdapter);
dueDateSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
DateTime today = newDateTime().startOfDay();
switch (position) {
case 0:
return;
case 1:
setDate(today.getMillis());
break;
case 2:
setDate(today.plusDays(1).getMillis());
break;
case 3:
setDate(today.plusWeeks(1).getMillis());
break;
case 4:
MyDatePickerDialog dialog = new MyDatePickerDialog();
DateTime initial = date > 0 ? newDateTime(date) : today;
dialog.initialize(new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePickerDialog datePickerDialog, int year, int month, int day) {
setDate(new DateTime(year, month + 1, day, 0, 0, 0, 0).getMillis());
}
}, initial.getYear(), initial.getMonthOfYear() - 1, initial.getDayOfMonth());
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
refreshDisplayView();
}
});
if (preferences.isDarkTheme()) {
dialog.setAccentColor(activity.getResources().getColor(R.color.black_text_hint));
}
dialog.show(activity.getFragmentManager(), FRAG_TAG_PICK_A_DATE);
break;
}
}
refreshDisplayView();
@Override
public void onNothingSelected(AdapterView<?> parent) {
return view;
}
}
});
@OnClick(R.id.clear)
void clearTime(View view) {
date = 0;
time = -1;
refreshDisplayView();
}
dueTimeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
return;
case 1:
setTime(-1);
break;
case 2:
setTime(dateShortcutMorning);
break;
case 3:
setTime(dateShortcutAfternoon);
break;
case 4:
setTime(dateShortcutEvening);
break;
case 5:
setTime(dateShortcutNight);
break;
case 6:
MyTimePickerDialog dialog = new MyTimePickerDialog();
int initialHours = 0;
int initialMinutes = 0;
if (time >= 0) {
DateTime initial = newDateTime(date).withMillisOfDay(time);
initialHours = initial.getHourOfDay();
initialMinutes = initial.getMinuteOfHour();
}
dialog.initialize(new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(RadialPickerLayout radialPickerLayout, int hour, int minute, int seconds) {
setTime((int) TimeUnit.HOURS.toMillis(hour) + (int) TimeUnit.MINUTES.toMillis(minute));
}
}, initialHours, initialMinutes, 0, DateFormat.is24HourFormat(activity));
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
refreshDisplayView();
}
});
if (preferences.isDarkTheme()) {
dialog.setAccentColor(activity.getResources().getColor(R.color.black_text_hint));
}
dialog.show(activity.getFragmentManager(), FRAG_TAG_PICK_A_TIME);
break;
}
@OnItemSelected(R.id.due_date)
void onDateSelected(int position) {
DateTime today = newDateTime().startOfDay();
switch (position) {
case 0:
return;
case 1:
setDate(today.getMillis());
break;
case 2:
setDate(today.plusDays(1).getMillis());
break;
case 3:
setDate(today.plusWeeks(1).getMillis());
break;
case 4:
startActivityForResult(new Intent(context, DatePickerActivity.class) {{
putExtra(DatePickerActivity.EXTRA_TIMESTAMP, getDueDate());
}}, REQUEST_DATE);
break;
}
}
@OnItemSelected(R.id.due_time)
void onTimeSelected(int position) {
switch (position) {
case 0:
return;
case 1:
setTime(-1);
break;
case 2:
setTime(dateShortcutMorning);
break;
case 3:
setTime(dateShortcutAfternoon);
break;
case 4:
setTime(dateShortcutEvening);
break;
case 5:
setTime(dateShortcutNight);
break;
case 6:
startActivityForResult(new Intent(context, TimePickerActivity.class) {{
putExtra(TimePickerActivity.EXTRA_TIMESTAMP, getDueDate());
}}, REQUEST_TIME);
break;
}
}
@Override
protected int getLayout() {
return R.layout.control_set_deadline;
}
@Override
protected int getIcon() {
return R.drawable.ic_schedule_24dp;
}
@Override
public void initialize(boolean isNewTask, Task task) {
if (task.hasDueDate()) {
DateTime dateTime = newDateTime(task.getDueDate());
date = dateTime.startOfDay().getMillis();
time = task.hasDueTime() ? dateTime.getMillisOfDay() : -1;
} else {
date = 0;
time = -1;
}
}
@Override
public void apply(Task task) {
long dueDate = getDueDate();
if (dueDate != task.getDueDate()) {
task.setReminderSnooze(0L);
}
task.setDueDate(dueDate);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_DATE) {
if (resultCode == Activity.RESULT_OK) {
long timestamp = data.getLongExtra(DatePickerActivity.EXTRA_TIMESTAMP, 0L);
DateTime dateTime = new DateTime(timestamp);
setDate(dateTime.getMillis());
} else {
refreshDisplayView();
}
} else if (requestCode == REQUEST_TIME) {
if (resultCode == Activity.RESULT_OK) {
long timestamp = data.getLongExtra(TimePickerActivity.EXTRA_TIMESTAMP, 0L);
DateTime dateTime = new DateTime(timestamp);
setTime(dateTime.getMillisOfDay());
} else {
refreshDisplayView();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
private long getDueDate() {
return time >= 0
? Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, newDateTime(date).withMillisOfDay(time).getMillis())
: Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, date);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(EXTRA_DATE, date);
outState.putInt(EXTRA_TIME, time);
}
private String getTimeHint(int millisOfDay) {
DateTime dateTime = newDateTime().withMillisOfDay(millisOfDay);
return DateUtilities.getTimeString(context, dateTime);
}
private void refreshDisplayView() {
updateDueDateOptions();
updateDueTimeOptions();
clearButton.setVisibility(date > 0 ? View.VISIBLE : View.GONE);
}
private void updateDueDateOptions() {
DateTime today = newDateTime().startOfDay();
String nextWeekString = getString(R.string.next, today.plusWeeks(1).toString("EEEE"));
if (date == 0) {
dueDateOptions.set(0, getString(R.string.TEA_no_date));
} else {
if (date == today.getMillis()) {
dueDateOptions.set(0, todayString);
} else if (date == today.plusDays(1).getMillis()) {
dueDateOptions.set(0, tomorrowString);
} else if (date == today.plusWeeks(1).getMillis()) {
dueDateOptions.set(0, nextWeekString);
} else {
dueDateOptions.set(0, DateUtilities.getLongDateString(newDateTime(date)));
}
});
}
dueDateOptions.set(3, nextWeekString);
dueDateAdapter.notifyDataSetChanged();
dueDateSpinner.setSelection(0);
}
private Drawable getThemedUnderline() {
Drawable drawable = DrawableCompat.wrap(activity.getResources().getDrawable(R.drawable.textfield_underline_black));
DrawableCompat.setTint(drawable, activity.getResources().getColor(preferences.isDarkTheme()
? android.R.color.white
: android.R.color.black));
return drawable;
private void updateDueTimeOptions() {
if (time == -1) {
dueTimeOptions.set(0, noTimeString);
} else {
int compareTime = newDateTime()
.withMillisOfDay(time)
.withSecondOfMinute(0)
.withMillisOfSecond(0)
.getMillisOfDay();
if (compareTime == dateShortcutMorning) {
dueTimeOptions.set(0, morningString);
} else if (compareTime == dateShortcutAfternoon) {
dueTimeOptions.set(0, afternoonString);
} else if (compareTime == dateShortcutEvening) {
dueTimeOptions.set(0, eveningString);
} else if (compareTime == dateShortcutNight) {
dueTimeOptions.set(0, nightString);
} else {
dueTimeOptions.set(0, DateUtilities.getTimeString(context, newDateTime().withMillisOfDay(time)));
}
}
dueTimeAdapter.notifyDataSetChanged();
dueTimeSpinner.setSelection(0);
}
private Drawable getRedUnderline() {
Drawable drawable = DrawableCompat.wrap(activity.getResources().getDrawable(R.drawable.textfield_underline_black));
DrawableCompat.setTint(drawable, activity.getResources().getColor(R.color.overdue));
private Drawable getUnderline(int color) {
Drawable drawable = DrawableCompat.wrap(context.getResources().getDrawable(R.drawable.textfield_underline_black));
DrawableCompat.setTint(drawable, color);
return drawable;
}
@ -363,38 +389,4 @@ public class DeadlineControlSet extends TaskEditControlSetBase {
refreshDisplayView();
}
@Override
protected void readFromTaskOnInitialize() {
Long dueDate = model.getDueDate();
if (dueDate > 0) {
DateTime dateTime = newDateTime(dueDate);
date = dateTime.startOfDay().getMillis();
if (Task.hasDueTime(dateTime.getMillis())) {
setTime(dateTime.getMillisOfDay());
} else {
time = -1;
}
} else {
date = 0;
time = -1;
}
refreshDisplayView();
}
@Override
protected void writeToModelAfterInitialized(Task task) {
long dueDate = time >= 0
? Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, newDateTime(date).withMillisOfDay(time).getMillis())
: Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, date);
if (dueDate != task.getDueDate()) {
task.setReminderSnooze(0L);
}
task.setDueDate(dueDate);
}
@Override
public int getIcon() {
return R.drawable.ic_schedule_24dp;
}
}

@ -0,0 +1,70 @@
package org.tasks.ui;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import com.google.common.base.Strings;
import com.todoroo.astrid.data.Task;
import org.tasks.R;
import butterknife.Bind;
import butterknife.OnTextChanged;
public class DescriptionControlSet extends TaskEditControlFragment {
private static final String EXTRA_DESCRIPTION = "extra_description";
@Bind(R.id.notes) EditText editText;
private String description;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
description = savedInstanceState.getString(EXTRA_DESCRIPTION);
}
if (!Strings.isNullOrEmpty(description)) {
editText.setTextKeepState(description);
}
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_DESCRIPTION, description);
}
@Override
protected int getLayout() {
return R.layout.control_set_description;
}
@Override
protected int getIcon() {
return R.drawable.ic_event_note_24dp;
}
@OnTextChanged(R.id.notes)
void textChanged(CharSequence text) {
description = text.toString().trim();
}
@Override
public void initialize(boolean isNewTask, Task task) {
description = task.getNotes();
}
@Override
public void apply(Task task) {
task.setNotes(description);
}
}

@ -1,104 +1,115 @@
package org.tasks.ui;
import android.app.Activity;
import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatRadioButton;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RadioGroup;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import com.google.common.primitives.Ints;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.inject.Inject;
public class PriorityControlSet extends TaskEditControlSetBase {
import butterknife.Bind;
import butterknife.OnClick;
private final List<Integer> colors;
private final List<ImportanceChangedListener> listeners = new LinkedList<>();
private RadioGroup radioGroup;
public class PriorityControlSet extends TaskEditControlFragment {
public interface ImportanceChangedListener {
void importanceChanged(int i);
public interface OnPriorityChanged {
void onPriorityChange(int priority);
}
public PriorityControlSet(Activity activity) {
super(activity, R.layout.control_set_priority);
colors = Ints.asList(Task.getImportanceColors(activity.getResources()));
Collections.reverse(colors);
}
private static final String EXTRA_PRIORITY = "extra_priority";
public void notifyImportanceChange(Integer i) {
for (ImportanceChangedListener l : listeners) {
l.importanceChanged(i);
}
}
@Inject CheckBoxes checkBoxes;
private Integer getImportance(int checkedId) {
return getImportance(getView().findViewById(checkedId));
}
@Bind(R.id.priority_high) AppCompatRadioButton priorityHigh;
@Bind(R.id.priority_medium) AppCompatRadioButton priorityMedium;
@Bind(R.id.priority_low) AppCompatRadioButton priorityLow;
@Bind(R.id.priority_none) AppCompatRadioButton priorityNone;
private Integer getImportance(View view) {
return Integer.parseInt((String) view.getTag());
private OnPriorityChanged callback;
private int priority;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback = (OnPriorityChanged) activity;
}
public void addListener(ImportanceChangedListener listener) {
listeners.add(listener);
@OnClick({R.id.priority_high, R.id.priority_medium, R.id.priority_low, R.id.priority_none})
void onImportanceChanged(CompoundButton button) {
callback.onPriorityChange(getPriority());
}
@Nullable
@Override
protected void afterInflate() {
final View view = getView();
radioGroup = (RadioGroup) view.findViewById(R.id.importance_group);
radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
notifyImportanceChange(getImportance(checkedId));
}
});
for (int i = 0; i < radioGroup.getChildCount(); i++) {
AppCompatRadioButton radioButton = (AppCompatRadioButton) radioGroup.getChildAt(i);
radioButton.setSupportButtonTintList(new ColorStateList(new int[][]{
new int[]{-android.R.attr.state_checked}, new int[]{android.R.attr.state_checked}},
new int[]{colors.get(i), colors.get(i)}));
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
priority = savedInstanceState.getInt(EXTRA_PRIORITY);
}
if (priority == 0) {
priorityHigh.setChecked(true);
} else if(priority == 1) {
priorityMedium.setChecked(true);
} else if(priority == 2) {
priorityLow.setChecked(true);
} else {
priorityNone.setChecked(true);
}
tintRadioButton(priorityHigh, 0);
tintRadioButton(priorityMedium, 1);
tintRadioButton(priorityLow, 2);
tintRadioButton(priorityNone, 3);
return view;
}
@Override
public void readFromTask(Task task) {
super.readFromTask(task);
setSelected(model.getImportance());
protected int getLayout() {
return R.layout.control_set_priority;
}
@Override
public int getIcon() {
protected int getIcon() {
return R.drawable.ic_flag_24dp;
}
@Override
protected void readFromTaskOnInitialize() {
setSelected(model.getImportance());
public void initialize(boolean isNewTask, Task task) {
priority = task.getImportance();
}
private void setSelected(int importance) {
if (radioGroup == null) {
return;
}
@Override
public void apply(Task task) {
task.setImportance(getPriority());
}
for (int i = 0; i < radioGroup.getChildCount(); i++) {
AppCompatRadioButton radioButton = (AppCompatRadioButton) radioGroup.getChildAt(i);
if (importance == getImportance(radioButton)) {
radioButton.setChecked(true);
}
}
private void tintRadioButton(AppCompatRadioButton radioButton, int priority) {
int color = checkBoxes.getPriorityColors().get(priority);
radioButton.setSupportButtonTintList(new ColorStateList(new int[][]{
new int[]{-android.R.attr.state_checked}, new int[]{android.R.attr.state_checked}},
new int[]{color, color}));
}
@Override
protected void writeToModelAfterInitialized(Task task) {
task.setImportance(getImportance(radioGroup.getCheckedRadioButtonId()));
private int getPriority() {
if (priorityHigh.isChecked()) {
return 0;
}
if (priorityMedium.isChecked()) {
return 1;
}
if (priorityLow.isChecked()) {
return 2;
}
return 3;
}
}

@ -0,0 +1,39 @@
package org.tasks.ui;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.todoroo.astrid.data.Task;
import org.tasks.R;
import org.tasks.injection.InjectingFragment;
import butterknife.ButterKnife;
public abstract class TaskEditControlFragment extends InjectingFragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.control_set_template, null);
LinearLayout content = (LinearLayout) view.findViewById(R.id.content);
content.addView(inflater.inflate(getLayout(), null));
ImageView icon = (ImageView) view.findViewById(R.id.icon);
icon.setImageResource(getIcon());
ButterKnife.bind(this, view);
return view;
}
protected abstract int getLayout();
protected abstract int getIcon();
public abstract void initialize(boolean isNewTask, Task task);
public abstract void apply(Task task);
}

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false"
android:state_enabled="true"
android:drawable="@drawable/icn_check_off" />
<item android:state_checked="true"
android:state_enabled="true"
android:drawable="@drawable/icn_check_on" />
</selector>

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Enabled states -->
<item android:state_checked="true" android:state_pressed="true"
android:state_enabled="true"
android:drawable="@android:drawable/checkbox_off_background" />
<item android:state_checked="false" android:state_pressed="true"
android:state_enabled="true"
android:drawable="@android:drawable/checkbox_on_background" />
<item android:state_checked="false"
android:state_enabled="true"
android:drawable="@android:drawable/checkbox_off_background" />
<item android:state_checked="true"
android:state_enabled="true"
android:drawable="@android:drawable/checkbox_on_background" />
</selector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:gravity="start"
android:textColor="?attr/asThemeTextColor"
android:textColor="?attr/asTextColor"
android:textSize="@dimen/task_edit_text_size" />
</LinearLayout>

@ -1,32 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="fill_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_weight="100">
<TextView
android:id="@+id/display_row_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:textColor="?attr/asTextColor"
android:textColorHint="?attr/asTextColorHint"
android:textSize="@dimen/task_edit_text_size" />
android:layout_weight="100"
android:orientation="horizontal">
<Spinner
android:id="@+id/hideUntil"
android:layout_width="0dip"
android:layout_height="0dip" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"/>
</LinearLayout>

@ -17,7 +17,6 @@
android:textSize="@dimen/task_edit_text_size" />
<RadioGroup
android:id="@+id/importance_group"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="end"
@ -27,24 +26,20 @@
android:paddingStart="@dimen/task_edit_drawable_padding_left_right">
<android.support.v7.widget.AppCompatRadioButton
android:id="@+id/importance_3"
style="@style/priority_button"
android:tag="3" />
android:id="@+id/priority_none"
style="@style/priority_button"/>
<android.support.v7.widget.AppCompatRadioButton
android:id="@+id/importance_2"
style="@style/priority_button"
android:tag="2" />
android:id="@+id/priority_low"
style="@style/priority_button"/>
<android.support.v7.widget.AppCompatRadioButton
android:id="@+id/importance_1"
style="@style/priority_button"
android:tag="1" />
android:id="@+id/priority_medium"
style="@style/priority_button"/>
<android.support.v7.widget.AppCompatRadioButton
android:id="@+id/importance_0"
style="@style/priority_button"
android:tag="0" />
android:id="@+id/priority_high"
style="@style/priority_button"/>
</RadioGroup>
</LinearLayout>

@ -50,19 +50,14 @@
android:gravity="end"
android:layout_weight="50">
<TextView
android:id="@+id/reminder_alarm_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/task_edit_text_size"
android:paddingRight="10dip"
android:paddingEnd="10dip"
android:textColor="?attr/asTextColor" />
<Spinner
android:id="@+id/reminder_alarm"
android:layout_width="0dip"
android:layout_height="0dip" />
android:background="@null"
android:gravity="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="10dp"
android:paddingEnd="10dp"/>
</LinearLayout>

@ -28,14 +28,16 @@
android:id="@+id/repeatValue"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
android:layout_weight="1"
android:textColor="?attr/asTextColor"/>
<Spinner
android:id="@+id/repeatInterval"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:prompt="@string/repeat_interval_prompt" />
android:prompt="@string/repeat_interval_prompt"
android:textColor="?attr/asTextColor" />
</LinearLayout>
@ -44,7 +46,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:paddingTop="5dp" />
android:paddingTop="5dp"
android:textColor="?attr/asTextColor" />
<LinearLayout
android:id="@+id/repeatDayOfWeekContainer"
@ -53,7 +56,58 @@
android:gravity="center"
android:orientation="horizontal"
android:paddingBottom="5dp"
android:paddingTop="5dp" />
android:paddingTop="5dp">
<CheckBox
style="@style/TextAppearance"
android:layout_weight="1"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<CheckBox
style="@style/TextAppearance"
android:layout_weight="1"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<CheckBox
style="@style/TextAppearance"
android:layout_weight="1"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<CheckBox
style="@style/TextAppearance"
android:layout_weight="1"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<CheckBox
style="@style/TextAppearance"
android:layout_weight="1"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<CheckBox
style="@style/TextAppearance"
android:layout_weight="1"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<CheckBox
style="@style/TextAppearance"
android:layout_weight="1"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
<Spinner
@ -61,7 +115,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:paddingTop="5dp" />
android:paddingTop="5dp"
android:textColor="?attr/asTextColor" />
</LinearLayout>

@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:gravity="start"
android:textColor="?attr/asThemeTextColor"
android:textColor="?attr/asTextColor"
android:textSize="@dimen/task_edit_text_size" />
</LinearLayout>

@ -32,6 +32,7 @@
android:layout_height="fill_parent"
android:layout_marginLeft="3dip"
android:layout_marginRight="3dip"
android:layout_weight="100"/>
android:layout_weight="100"
android:choiceMode="multipleChoice"/>
</LinearLayout>

@ -8,5 +8,5 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:textColor="?attr/asThemeTextColor"
android:textColor="?attr/asTextColor"
android:textSize="@dimen/task_edit_text_size" />

@ -3,9 +3,61 @@
**
** See the file "LICENSE" for the full license governing this code.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/display_row_edit"
android:layout_width="match_parent"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="?attr/asThemeTextColor"
android:textSize="@dimen/task_edit_text_size" />
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="100"
android:orientation="vertical">
<TextView
android:id="@+id/display_row_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/asTextColor"
android:text="@string/TEA_timer_controls"
android:alpha="@dimen/alpha_secondary_text_icons"
android:textSize="@dimen/task_edit_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/timer_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="10dp"
android:paddingEnd="10dp"
android:layout_marginLeft="2dip"
android:layout_weight="1"
android:orientation="vertical">
<ImageView
android:id="@+id/timer_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical|center_horizontal"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:alpha="@dimen/alpha_secondary_text_icons"
android:scaleType="centerInside"
android:tint="?attr/icon_tint" />
<Chronometer
android:id="@+id/timer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
android:gravity="center_horizontal"
android:textColor="?attr/asTextColor"
android:alpha="@dimen/alpha_secondary_text_icons"
android:textSize="10sp"
android:visibility="visible" />
</LinearLayout>
</LinearLayout>

@ -32,7 +32,7 @@
android:layout_weight="1"
android:gravity="right"
android:paddingLeft="10dip"
android:textColor="?attr/asThemeTextColor" />
android:textColor="?attr/asTextColor" />
</LinearLayout>
<View
android:id="@+id/TEA_Separator"
@ -59,7 +59,7 @@
android:layout_weight="1"
android:gravity="right"
android:paddingLeft="10dip"
android:textColor="?attr/asThemeTextColor" />
android:textColor="?attr/asTextColor" />
</LinearLayout>
</LinearLayout>

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
** Copyright (c) 2012 Todoroo Inc
**
** See the file "LICENSE" for the full license governing this code.
@ -7,40 +6,52 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/task_edit_title_padding_top_bottom"
android:paddingBottom="@dimen/task_edit_title_padding_top_bottom"
android:paddingLeft="@dimen/task_edit_drawable_padding_left_right"
android:paddingStart="@dimen/task_edit_drawable_padding_left_right"
android:paddingRight="@dimen/task_edit_drawable_padding_left_right"
android:paddingEnd="@dimen/task_edit_drawable_padding_left_right"
android:gravity="center_vertical"
android:orientation="horizontal" >
android:orientation="vertical">
<com.todoroo.astrid.ui.CheckableImageView
android:id="@+id/completeBox"
android:layout_width="wrap_content"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingRight="@dimen/task_edit_drawable_padding_left_right"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="@dimen/task_edit_title_padding_top_bottom"
android:paddingEnd="@dimen/task_edit_drawable_padding_left_right"
android:scaleType="center"
android:src="@drawable/btn_check" />
android:paddingLeft="@dimen/task_edit_drawable_padding_left_right"
android:paddingRight="@dimen/task_edit_drawable_padding_left_right"
android:paddingStart="@dimen/task_edit_drawable_padding_left_right"
android:paddingTop="@dimen/task_edit_title_padding_top_bottom">
<EditText
style="@style/edit_text_style"
android:id="@+id/title"
android:cursorVisible="false"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="100"
android:background="?attr/asEditTextBackground"
android:freezesText="true"
android:hint="@string/TEA_title_hint"
android:inputType="textCapSentences"
android:scrollbars="vertical"
android:imeOptions="flagNoExtractUi|actionDone"
android:text=""
android:textColor="?attr/asTextColor"
android:textColorHint="?attr/asTextColorHint" />
<com.todoroo.astrid.ui.CheckableImageView
android:id="@+id/completeBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingEnd="@dimen/task_edit_drawable_padding_left_right"
android:paddingRight="@dimen/task_edit_drawable_padding_left_right"
android:scaleType="center" />
<EditText
android:id="@+id/title"
style="@style/edit_text_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="100"
android:background="?attr/asEditTextBackground"
android:cursorVisible="false"
android:freezesText="true"
android:hint="@string/TEA_title_hint"
android:imeOptions="flagNoExtractUi|actionDone"
android:inputType="textCapSentences"
android:scrollbars="vertical"
android:text=""
android:textColor="?attr/asTextColor"
android:textColorHint="?attr/asTextColorHint" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="1px"
android:background="?attr/task_edit_divider" />
</LinearLayout>

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<com.todoroo.astrid.ui.NumberPickerButton android:id="@+id/increment"
android:layout_width="fill_parent"
android:layout_height="38dip"
android:src="@drawable/icn_arrow_up"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText android:id="@+id/timepicker_input"
android:layout_width="wrap_content"
android:layout_height="40dip"
android:layout_weight="1"
style="?android:attr/textAppearanceLarge"
android:gravity="center"
android:inputType="phone"
android:textSize="30sp"
android:textColor="?attr/asThemeTextColor"
android:background="@android:color/transparent"
/>
</LinearLayout>
<com.todoroo.astrid.ui.NumberPickerButton android:id="@+id/decrement"
android:layout_width="fill_parent"
android:layout_height="38dip"
android:src="@drawable/icn_arrow_down"
android:paddingTop="15dip"
/>
</merge>

@ -24,13 +24,6 @@
android:orientation="vertical"
android:gravity="center_horizontal" >
<include layout="@layout/control_set_title"/>
<View
android:layout_width="fill_parent"
android:layout_height="1px"
android:background="?attr/task_edit_divider"/>
<LinearLayout
android:id="@+id/basic_controls"
android:layout_width="fill_parent"
@ -105,35 +98,6 @@
<!-- Extended Add Button -->
<LinearLayout
android:id="@+id/timer_container"
android:layout_width="39dip"
android:layout_height="39dip"
android:layout_marginLeft="2dip"
android:layout_weight="1"
android:orientation="vertical" >
<ImageView
android:id="@+id/timer_button"
android:layout_width="fill_parent"
android:layout_height="25dip"
android:layout_weight="1"
android:gravity="center_vertical|center_horizontal"
android:paddingRight="5dip"
android:paddingLeft="5dip"
android:scaleType="centerInside" />
<Chronometer
android:id="@+id/timer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:gravity="center_horizontal"
android:textColor="@android:color/white"
android:textSize="10sp"
android:visibility="visible" />
</LinearLayout>
<ImageButton
android:id="@+id/commentButton"
android:layout_width="39dip"

@ -7,18 +7,32 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/empty_text"
android:textSize="20sp"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:drawableTop="@drawable/ic_inbox_168px"
android:alpha="@dimen/alpha_disabled_hint_text"
android:gravity="center"
android:textColor="?attr/asTextColor"
android:text="@string/TLA_no_items" />
android:orientation="vertical"
android:layout_gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:alpha="@dimen/alpha_disabled_hint_text"
android:src="@drawable/ic_inbox_168px"
android:tint="?attr/icon_tint" />
<TextView
android:id="@+id/empty_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:alpha="@dimen/alpha_disabled_hint_text"
android:text="@string/TLA_no_items"
android:textColor="?attr/asTextColor"
android:textSize="20sp" />
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>

@ -65,7 +65,6 @@
<string name="CRA_list_created_title">أنشئت القائمة</string>
<string name="EPr_appearance_header">ظهور</string>
<string name="EPr_edit_screen_options">تعديل خيارات الشاشه</string>
<string name="EPr_show_timer_shortcut">إظهار إختصار المنبه</string>
<string name="EPr_beastMode_reset">الإعادة إلى الإفتراضي</string>
<string name="EPr_fullTask_title">إظهار عنوان المهمه</string>
<string name="task_list_options">خيارات قائمة المهام</string>
@ -159,7 +158,6 @@
<item>من تاريخ الموعد</item>
<item>من تاريخ الاتمام</item>
</string-array>
<string name="TEA_tags_label_long">أضف للقائمة</string>
<string name="tag_no_title_error">أدخل اسم للقائمة أولاً</string>
<string name="tag_FEx_untagged">غير مصنف</string>
<string name="TEA_timer_controls">مؤقت</string>

@ -116,7 +116,6 @@
<string name="EPr_appearance_header">Изглед</string>
<string name="EPr_filters_to_show_title">Филтри за показване</string>
<string name="EPr_edit_screen_options">Редактиране на опциите екрана</string>
<string name="EPr_show_timer_shortcut">Покажи прекия път към таймера</string>
<string name="EPr_beastMode_desc">Настройване изгледа на екрана за редактиране на задача</string>
<string name="EPr_beastMode_reset">Възстанови по подразбиране</string>
<string name="EPr_beastMode_hint">Настройване изгледа на екрана за редактиране на задача чрез изтегляне на елементите във всеки ред</string>
@ -255,7 +254,6 @@
<item>месечно</item>
<item>на два месеца</item>
</string-array>
<string name="repeat_enabled">Повторения</string>
<string name="repeat_every">Всеки %d</string>
<string name="repeat_interval_prompt">Интервал на повторение</string>
<string name="repeat_never">Без повторение</string>
@ -276,7 +274,6 @@
<string name="repeat_forever">Повтаряй завинаги</string>
<string name="repeat_until">Повтаряй до %s</string>
<string name="repeat_snackbar">%1$s е насрочено за %2$s</string>
<string name="TEA_tags_label_long">Добави към списъци</string>
<string name="new_tag">Нов таг</string>
<string name="tag_no_title_error">Моля, първо въведете име за този списък!</string>
<string name="tag_FEx_untagged">Без категория</string>

@ -125,7 +125,6 @@
<string name="rmd_EPr_quiet_hours_start_title">Inici de Silenci</string>
<string name="rmd_EPr_quiet_hours_end_title">Final de Silenci</string>
<string name="rmd_EPr_defaultRemind_title">Notificacions al Atzar</string>
<string name="repeat_enabled">Repeticions</string>
<string name="repeat_every">Cada %d</string>
<string name="repeat_interval_prompt">Interval de Repecitiò</string>
<string name="repeat_never">Sense repetir</string>

@ -220,7 +220,6 @@
<item>měsíčně</item>
<item>každý druhý měsíc</item>
</string-array>
<string name="repeat_enabled">Opakování</string>
<string name="repeat_every">Každý %d</string>
<string name="repeat_interval_prompt">Opakovací interval</string>
<string name="repeat_never">Bez opakování</string>
@ -240,7 +239,6 @@
<string name="repeat_detail_duedate_until">Každý %1$s\n až do %2$s</string>
<string name="repeat_forever">Opakovat stále</string>
<string name="repeat_until">Opakovat až do %s</string>
<string name="TEA_tags_label_long">Přidat na seznam</string>
<string name="tag_no_title_error">Prosím zadejte jméno pro první seznam</string>
<string name="tag_FEx_untagged">Nezařazené</string>
<string name="TPl_notification">Aktivní časovače pro %s!</string>

@ -120,7 +120,6 @@
<string name="rmd_EPr_quiet_hours_start_title">Stille timer start</string>
<string name="rmd_EPr_quiet_hours_end_title">Stille timer slut</string>
<string name="rmd_EPr_defaultRemind_title">Tilfældige påmindelser</string>
<string name="repeat_enabled">Gentagelser</string>
<string name="repeat_every">Hver %d</string>
<string name="repeat_interval_prompt">Interval for gentagelse</string>
<string name="repeat_never">Gentages ikke</string>

@ -109,7 +109,6 @@
<string name="EPr_appearance_header">Erscheinungsbild</string>
<string name="EPr_filters_to_show_title">Angezeigte Filter</string>
<string name="EPr_edit_screen_options">Bildschirmeinstellungen bearbeiten</string>
<string name="EPr_show_timer_shortcut">Verknüpfung zu Timer anzeigen</string>
<string name="EPr_beastMode_desc">Erscheinungsbild Aufgabenseite anpassen</string>
<string name="EPr_beastMode_reset">Auf Standardeinstellungen zurücksetzen</string>
<string name="EPr_beastMode_hint">Du kannst die Eingabeseite für Aufgaben mit den Schiebereglern links anpassen</string>
@ -235,7 +234,6 @@
<item>monatlich</item>
<item>alle zwei Monate</item>
</string-array>
<string name="repeat_enabled">Wiederholungen</string>
<string name="repeat_every">Jeden %d</string>
<string name="repeat_interval_prompt">Wiederholungsintervall</string>
<string name="repeat_never">Nicht wiederholen</string>
@ -256,7 +254,6 @@
<string name="repeat_forever">Endlos wiederholen</string>
<string name="repeat_until">Wiederhole bis %s</string>
<string name="repeat_snackbar">%1$s verschoben um %2$s</string>
<string name="TEA_tags_label_long">Zur Liste hinzufügen</string>
<string name="new_tag">Neues Schlagwort</string>
<string name="tag_no_title_error">Bitte gib zuerst einen Namen für die Liste ein!</string>
<string name="tag_FEx_untagged">Nicht kategorisiert</string>

@ -103,7 +103,6 @@
<string name="EPr_appearance_header">Εμφάνιση</string>
<string name="EPr_filters_to_show_title">Φίλτρα για εμφάνιση</string>
<string name="EPr_edit_screen_options">Επεξεργασία επιλογών εμφάνισης</string>
<string name="EPr_show_timer_shortcut">Εμφάνιση συντόμευσης χρονομέτρου</string>
<string name="EPr_beastMode_desc">Προσαρμόστε τη διάταξη της οθόνης επεξεργασίας της εργασίας</string>
<string name="EPr_beastMode_reset">Επαναφορά προεπιλογών</string>
<string name="EPr_beastMode_hint">Προσαρμόστε τη διάταξη της οθόνης επεξεργασίας της εργασίας τραβώντας την αριστερή λαβή σε κάθε σειρά</string>
@ -223,7 +222,6 @@
<item>μηνιαία</item>
<item>διμηνιαία</item>
</string-array>
<string name="repeat_enabled">Επαναλήψεις</string>
<string name="repeat_every">Κάθε %d</string>
<string name="repeat_interval_prompt">Διάστημα επανάληψης</string>
<string name="repeat_never">Μή επαναλαμβανόμενα</string>
@ -243,7 +241,6 @@
<string name="repeat_detail_duedate_until">Κάθε %1$s\nμέχρι %2$s</string>
<string name="repeat_forever">Επανάληψη επ\'αορίστου</string>
<string name="repeat_until">Επανάληψη μέχρι %s</string>
<string name="TEA_tags_label_long">Προσθήκη στις λίστες</string>
<string name="tag_no_title_error">Παρακαλώ εισάγετε ενα όνομα για την λίστα πρώτα!</string>
<string name="tag_FEx_untagged">Μη κατηγοριοποιημένο</string>
<string name="TPl_notification">Χρονοδιακόπτες ενεργοί για %s!</string>

@ -113,7 +113,6 @@
<string name="EPr_appearance_header">Apariencia</string>
<string name="EPr_filters_to_show_title">Filtros a mostrar</string>
<string name="EPr_edit_screen_options">Editar opciones de pantalla</string>
<string name="EPr_show_timer_shortcut">Mostrar atajo del temporizador</string>
<string name="EPr_beastMode_desc">Personalizar la disposición de la pantalla de edición de tareas</string>
<string name="EPr_beastMode_reset">Restablecer valores predeterminados</string>
<string name="EPr_beastMode_hint">Personalice su pantalla de edición de tareas arrastrando el asa izquierda en cada fila</string>
@ -245,7 +244,6 @@
<item>mensualmente</item>
<item>bimensualmente</item>
</string-array>
<string name="repeat_enabled">Repeticiones</string>
<string name="repeat_every">Cada %d</string>
<string name="repeat_interval_prompt">Intervalo de repetición</string>
<string name="repeat_never">Sin repetir</string>
@ -266,7 +264,6 @@
<string name="repeat_forever">Repetir por siempre</string>
<string name="repeat_until">Repetir hasta %s</string>
<string name="repeat_snackbar">%1$s He reprogramado esta tarea recurrente para %2$s</string>
<string name="TEA_tags_label_long">Añadir a listas</string>
<string name="new_tag">Nueva Etiqueta</string>
<string name="tag_no_title_error">Por favor, ¡introduce un nombre para la primera lista!</string>
<string name="tag_FEx_untagged">Sin Categoría</string>

@ -80,7 +80,6 @@
<string name="EPr_appearance_header">ظاهر</string>
<string name="EPr_filters_to_show_title">فیلترها برای نمایش</string>
<string name="EPr_edit_screen_options">ویرایش تنظیمات صفحه</string>
<string name="EPr_show_timer_shortcut">نمایش میانبر تایمر</string>
<string name="EPr_beastMode_desc">شخصی سازی قالب صفحه ویرایش وظیفه</string>
<string name="EPr_beastMode_reset">برگرداندن به پیش فرض</string>
<string name="EPr_fullTask_title">نمایش عنوان کامل وظیفه</string>
@ -178,7 +177,6 @@
<item>هر ماه</item>
<item>یک ماه درمیان</item>
</string-array>
<string name="repeat_enabled">تکرارها</string>
<string name="repeat_interval_prompt">دوره تکرار</string>
<string name="repeat_never">بدون تکرار</string>
<string-array name="repeat_interval">
@ -194,7 +192,6 @@
<item>از تاریخ اتمام</item>
</string-array>
<string name="repeat_forever">تکرار بینهایت</string>
<string name="TEA_tags_label_long">اضافه به لیست</string>
<string name="new_tag">تگ جدید</string>
<string name="tag_no_title_error">لطفا ابتدا یک نام برای لیست انتخاب نمایید</string>
<string name="tag_FEx_untagged">دسته بندی نشده</string>

@ -54,7 +54,6 @@
<string name="gtasks_GLA_errorIOAuth">Valitettavasti meillä on yhteysongelmia Google palvelimiin. Ole hyvä ja yritä myöhemmin uudestaa.</string>
<string name="gtasks_GPr_header">Google tehtävät</string>
<string name="rmd_EPr_defaultRemind_title">Satunnainen muistutus</string>
<string name="repeat_enabled">Ei toistoa</string>
<string name="repeat_every">Toista %d</string>
<string name="repeat_interval_prompt">Toistoväli</string>
<string name="repeat_never">Ei toistuva</string>
@ -62,7 +61,6 @@
<string name="repeat_detail_duedate_until">Joka %1$s\nkunnes %2$s</string>
<string name="repeat_forever">Toista loputtomiin</string>
<string name="repeat_until">Toista kunnes %s</string>
<string name="TEA_tags_label_long">Lisää listaan</string>
<string name="tag_no_title_error">Ole hyvä syötä ensin nimi tälle listalle!</string>
<string name="delete_task">Poista tehtävä</string>
<string name="source_code">Lähdekoodi</string>

@ -112,7 +112,6 @@
<string name="EPr_appearance_header">Apparence</string>
<string name="EPr_filters_to_show_title">Filtres à afficher</string>
<string name="EPr_edit_screen_options">Edition des options d\'affichage</string>
<string name="EPr_show_timer_shortcut">Afficher le raccourci de minuterie</string>
<string name="EPr_beastMode_desc">Personnaliser la disposition de l\'écran d\'édition de tâche</string>
<string name="EPr_beastMode_reset">Rétablir les valeurs par défaut</string>
<string name="EPr_beastMode_hint">Personnalisez votre écran d\'ajout de tâche en faisant glisser chaque ligne vers la gauche</string>
@ -247,7 +246,6 @@
<item>mensuel</item>
<item>bi-mensuel</item>
</string-array>
<string name="repeat_enabled">Répétitions</string>
<string name="repeat_every">Tous les %d</string>
<string name="repeat_interval_prompt">Interval de répétition</string>
<string name="repeat_never">Une fois seulement</string>
@ -268,7 +266,6 @@
<string name="repeat_forever">Répéter indéfiniment</string>
<string name="repeat_until">Répéter jusqu\'à %s</string>
<string name="repeat_snackbar">%1$s replanifiée à %2$s</string>
<string name="TEA_tags_label_long">Ajouter à la liste</string>
<string name="new_tag">Nouveau tag</string>
<string name="tag_no_title_error">Veuillez d\'abord entrer un nom pour cette liste.</string>
<string name="tag_FEx_untagged">Non classé</string>

@ -100,7 +100,6 @@
<string name="rmd_NoA_done">Befejezve</string>
<string name="rmd_NoA_snooze">Szundi</string>
<string name="rmd_EPr_defaultRemind_title">Véletlenszerű emlékeztetők</string>
<string name="repeat_enabled">Ismétlések</string>
<string name="repeat_every">Minden %d</string>
<string name="repeat_interval_prompt">Ismétlési időköz</string>
<string name="repeat_never">Nem ismétlődő</string>
@ -108,7 +107,6 @@
<string name="repeat_detail_duedate_until">Minden %1$s\neddig: %2$s</string>
<string name="repeat_forever">Ismétlés örökké</string>
<string name="repeat_until">Ismétlés eddig: %s</string>
<string name="TEA_tags_label_long">Hozzáadás listákhoz</string>
<string name="tag_no_title_error">Kérlek, először add meg a lista nevét!</string>
<string name="delete_task">Feladat törlése</string>
<plurals name="Ntasks">

@ -114,7 +114,6 @@
<string name="EPr_appearance_header">Aspetto</string>
<string name="EPr_filters_to_show_title">Filtri da visualizzare</string>
<string name="EPr_edit_screen_options">Modifica opzioni schermo</string>
<string name="EPr_show_timer_shortcut">Mostra scorciatoia timer</string>
<string name="EPr_beastMode_desc">Personalizza il layout della schermata di modifica attività</string>
<string name="EPr_beastMode_reset">Ripristina predefiniti</string>
<string name="EPr_beastMode_hint">Personalizza la schermata di modifica attività trascinando lindicatore a sinistra di ogni riga</string>
@ -248,7 +247,6 @@ Se visualizzi questo errore più volte, ti consigliamo di cancellare tutti i dat
<item>mensilmente</item>
<item>bi-mensilmente</item>
</string-array>
<string name="repeat_enabled">Ripetizioni</string>
<string name="repeat_every">Ogni %d</string>
<string name="repeat_interval_prompt">Intervallo di ripetizione</string>
<string name="repeat_never">Non ricorrente</string>
@ -269,7 +267,6 @@ Se visualizzi questo errore più volte, ti consigliamo di cancellare tutti i dat
<string name="repeat_forever">Ripeti all\'infinito</string>
<string name="repeat_until">Ripeti fino %s</string>
<string name="repeat_snackbar">%1$s ripianificata per %2$s</string>
<string name="TEA_tags_label_long">Aggiungi alle liste</string>
<string name="new_tag">Nuova etichetta</string>
<string name="tag_no_title_error">Per cortesia, prima inserisci un nome per questa lista</string>
<string name="tag_FEx_untagged">Non classificato</string>

@ -223,7 +223,6 @@
<item>חודשי</item>
<item>דו חודשי</item>
</string-array>
<string name="repeat_enabled">חזרה</string>
<string name="repeat_every">כל %d</string>
<string name="repeat_interval_prompt">אינטרוול חזרות</string>
<string-array name="repeat_interval">
@ -242,7 +241,6 @@
<string name="repeat_detail_duedate_until">כל %1$s\nעד %2$s</string>
<string name="repeat_forever">חזור לנצח</string>
<string name="repeat_until">חזרה עד %s</string>
<string name="TEA_tags_label_long">הוסף לרשימות</string>
<string name="tag_no_title_error">אנא הכנס שם לרשימה זו</string>
<string name="TPl_notification">קוצב זמן הופעל עבור %s משימות</string>
<string name="TFE_workingOn">משימות עם הערכת זמן</string>

@ -114,7 +114,6 @@
<string name="EPr_appearance_header">外観</string>
<string name="EPr_filters_to_show_title">表示のフィルター</string>
<string name="EPr_edit_screen_options">編集画面オプション</string>
<string name="EPr_show_timer_shortcut">タイマーのショートカットを表示</string>
<string name="EPr_beastMode_desc">タスク編集画面のレイアウトをカスタマイズ</string>
<string name="EPr_beastMode_reset">デフォルトにリセット</string>
<string name="EPr_beastMode_hint">各行の左側をドラッグしてタスク編集画面をカスタマイズしてください</string>
@ -254,7 +253,6 @@
<item>毎月</item>
<item>一ヶ月おき</item>
</string-array>
<string name="repeat_enabled">繰り返し</string>
<string name="repeat_every">%d 毎</string>
<string name="repeat_interval_prompt">繰り返し間隔</string>
<string name="repeat_never">繰り返しなし</string>
@ -275,7 +273,6 @@
<string name="repeat_forever">永久に繰り返す</string>
<string name="repeat_until">%s まで繰り返す</string>
<string name="repeat_snackbar">%1$s を %2$s にスケジュール変更しました</string>
<string name="TEA_tags_label_long">リストに追加</string>
<string name="new_tag">新しいタグ</string>
<string name="tag_no_title_error">まずリスト名を記入してください!</string>
<string name="tag_FEx_untagged">未分類</string>

@ -117,7 +117,6 @@
<string name="EPr_appearance_header">보기 설정</string>
<string name="EPr_filters_to_show_title">표시할 필터</string>
<string name="EPr_edit_screen_options">화면 설정 편집하기</string>
<string name="EPr_show_timer_shortcut">타이머 바로가기 보기</string>
<string name="EPr_beastMode_desc">일정 편집화면 레이아웃 설정하기</string>
<string name="EPr_beastMode_reset">기본값으로 초기화하기</string>
<string name="EPr_beastMode_hint">각 줄의 왼쪽 손잡이를 끌어서 일정 편집 화면을 사용자 정의하기</string>
@ -255,7 +254,6 @@ Tasks의 백업에서 당신의 일정을 복구하시기 바랍니다.
<item>매달</item>
<item>격월로</item>
</string-array>
<string name="repeat_enabled">반복 설정</string>
<string name="repeat_every">매 %d</string>
<string name="repeat_interval_prompt">반복 주기</string>
<string name="repeat_never">반복하지 않기</string>
@ -276,7 +274,6 @@ Tasks의 백업에서 당신의 일정을 복구하시기 바랍니다.
<string name="repeat_forever">영원히 반복하기</string>
<string name="repeat_until">%s 까지 반복</string>
<string name="repeat_snackbar">%1$s 이 %2$s 로 변경되었습니다</string>
<string name="TEA_tags_label_long">목록에 추가하기</string>
<string name="new_tag">새 태그</string>
<string name="tag_no_title_error">이 목록의 이름을 먼저 입력하세요!</string>
<string name="tag_FEx_untagged">미분류 일정</string>

@ -110,7 +110,6 @@
<string name="rmd_EPr_quiet_hours_start_title">Stilletimer start</string>
<string name="rmd_EPr_quiet_hours_end_title">Stilletimer slutt</string>
<string name="rmd_EPr_defaultRemind_title">Tilfeldige påminnelser</string>
<string name="repeat_enabled">Gjentakelser</string>
<string name="repeat_every">Hver %d</string>
<string name="repeat_interval_prompt">Gjentakelsesintervall</string>
<string name="repeat_detail_duedate">Hver %s</string>

@ -114,7 +114,6 @@
<string name="EPr_appearance_header">Uiterlijk</string>
<string name="EPr_filters_to_show_title">Te tonen filters</string>
<string name="EPr_edit_screen_options">Scherm opties bewerken</string>
<string name="EPr_show_timer_shortcut">Toon timer snelkoppeling</string>
<string name="EPr_beastMode_desc">Pas de layout van het Taak Wijzigingsscherm aan</string>
<string name="EPr_beastMode_reset">Standaardinstellingen herstellen</string>
<string name="EPr_beastMode_hint">Pas uw taak bewerken scherm aan door de linker handle van elke rij te verslepen</string>
@ -251,7 +250,6 @@
<item>maandelijks</item>
<item>tweemaandelijks</item>
</string-array>
<string name="repeat_enabled">Herhalingen</string>
<string name="repeat_every">Elke %d</string>
<string name="repeat_interval_prompt">Herhaal interval</string>
<string name="repeat_never">Niet herhalend</string>
@ -272,7 +270,6 @@
<string name="repeat_forever">Altijd herhalen</string>
<string name="repeat_until">Herhalen tot %s</string>
<string name="repeat_snackbar">%1$s opnieuw ingepland op %2$s</string>
<string name="TEA_tags_label_long">Voeg toe aan lijsten</string>
<string name="new_tag">Nieuwe Label</string>
<string name="tag_no_title_error">Voer a.u.b. eerst een naam in voor deze lijst!</string>
<string name="tag_FEx_untagged">Niet gecategoriseerd</string>

@ -102,7 +102,6 @@
<string name="EPr_appearance_header">Wygląd</string>
<string name="EPr_filters_to_show_title">Pokazywane filtry</string>
<string name="EPr_edit_screen_options">Edytuj ustawienia ekranu</string>
<string name="EPr_show_timer_shortcut">Pokaż skrót minutnika</string>
<string name="EPr_beastMode_desc">Dostosuj układ strony edycji zadania</string>
<string name="EPr_beastMode_reset">Przywróć domyślne</string>
<string name="EPr_beastMode_hint">Dostosuj swój ekran edycji zadania przez przeciąganie za uchwyt po prawej w każdym wierszu</string>
@ -226,7 +225,6 @@ i odzyskanie zadań z kopi zapasowej (Settings-&gt;Sync and backup-&gt;Backup-&g
<item>raz w miesiącu</item>
<item>raz na dwa miesiące</item>
</string-array>
<string name="repeat_enabled">Powtarza się</string>
<string name="repeat_every">Co %d</string>
<string name="repeat_interval_prompt">Odstęp powtarzania</string>
<string name="repeat_never">Jednorazowo</string>
@ -246,7 +244,6 @@ i odzyskanie zadań z kopi zapasowej (Settings-&gt;Sync and backup-&gt;Backup-&g
<string name="repeat_detail_duedate_until">Co %1$s\ndo %2$s</string>
<string name="repeat_forever">Powtarzaj bez końca</string>
<string name="repeat_until">Powtarzaj do %s</string>
<string name="TEA_tags_label_long">Dodaj do list</string>
<string name="tag_no_title_error">Wpisz nazwę dla tej listy najpierw!</string>
<string name="tag_FEx_untagged">Na żadnej liście</string>
<string name="TPl_notification">Minutnkiki aktywne przez %s!</string>

@ -102,7 +102,6 @@
<string name="EPr_appearance_header">Aparência</string>
<string name="EPr_filters_to_show_title">Filtros a mostrar</string>
<string name="EPr_edit_screen_options">Editar opções da tela</string>
<string name="EPr_show_timer_shortcut">Exibir atalho para temporizador</string>
<string name="EPr_beastMode_desc">Personalize o layout da tela de edição</string>
<string name="EPr_beastMode_reset">Restaurar valores padrão</string>
<string name="EPr_beastMode_hint">Personalize suas tarefas na tela de edição arrastando-as para a esquerda</string>
@ -223,7 +222,6 @@
<item>mensalmente</item>
<item>a cada dois meses</item>
</string-array>
<string name="repeat_enabled">Repetir</string>
<string name="repeat_every">A cada %d</string>
<string name="repeat_interval_prompt">Intervalo de repetição</string>
<string name="repeat_never">Não repetir</string>
@ -243,7 +241,6 @@
<string name="repeat_detail_duedate_until">Todo %1$s\naté %2$s</string>
<string name="repeat_forever">Repetir para sempre</string>
<string name="repeat_until">Repetir até %s</string>
<string name="TEA_tags_label_long">Adicionar às listas</string>
<string name="tag_no_title_error">Entre com o nome da lista primeiro!</string>
<string name="tag_FEx_untagged">Sem categoria</string>
<string name="TPl_notification">Temporizador ativado para %s!</string>

@ -113,7 +113,6 @@
<string name="EPr_appearance_header">Aspeto</string>
<string name="EPr_filters_to_show_title">Filtros a mostrar</string>
<string name="EPr_edit_screen_options">Opções do ecrã de edição</string>
<string name="EPr_show_timer_shortcut">Mostrar atalho do temporizador</string>
<string name="EPr_beastMode_desc">Personalizar o esquema do ecrã de edição de tarefas</string>
<string name="EPr_beastMode_reset">Restaurar definições originas</string>
<string name="EPr_beastMode_hint">Personalize o ecrã de edição de tarefas arrastando a guia em cada linha</string>
@ -246,7 +245,6 @@ das tarefas através de um backup em Definições-&gt;Sincronização e backup-&
<item>mensal</item>
<item>bimensal</item>
</string-array>
<string name="repeat_enabled">Repete</string>
<string name="repeat_every">Cada %d</string>
<string name="repeat_interval_prompt">Intervalo de repetição</string>
<string name="repeat_never">Não repete</string>
@ -267,7 +265,6 @@ das tarefas através de um backup em Definições-&gt;Sincronização e backup-&
<string name="repeat_forever">Repetir eternamente</string>
<string name="repeat_until">Repetir até %s</string>
<string name="repeat_snackbar">%1$s agendada para %2$s</string>
<string name="TEA_tags_label_long">Adicionar às listas</string>
<string name="new_tag">Nova etiqueta</string>
<string name="tag_no_title_error">Introduza o nome para esta lista!</string>
<string name="tag_FEx_untagged">Sem categoria</string>

@ -114,7 +114,6 @@
<string name="EPr_appearance_header">Интерфейс</string>
<string name="EPr_filters_to_show_title">Показать фильтры</string>
<string name="EPr_edit_screen_options">Редактировать настройки экрана</string>
<string name="EPr_show_timer_shortcut">Показать вкладку таймера</string>
<string name="EPr_beastMode_desc">Настроить внешний вид экрана правки задачи</string>
<string name="EPr_beastMode_reset">Настройки по умолчанию</string>
<string name="EPr_beastMode_hint">Настройте экран редактирования задачи, перетаскивая элементы</string>
@ -253,7 +252,6 @@
<item>ежемесячно</item>
<item>каждые два месяца</item>
</string-array>
<string name="repeat_enabled">Повторения</string>
<string name="repeat_every">С интервалом в %d</string>
<string name="repeat_interval_prompt">Интервал повтора</string>
<string name="repeat_never">Без повторений</string>
@ -274,7 +272,6 @@
<string name="repeat_forever">Повторять всегда</string>
<string name="repeat_until">Повторять до %s</string>
<string name="repeat_snackbar">«%1$s» перенесено на %2$s</string>
<string name="TEA_tags_label_long">Добавить в список</string>
<string name="new_tag">Новый тег</string>
<string name="tag_no_title_error">Пожалуйста, для начала введите название списка!</string>
<string name="tag_FEx_untagged">Без тега</string>

@ -111,7 +111,6 @@
<string name="EPr_appearance_header">Vzhľad</string>
<string name="EPr_filters_to_show_title">Filtre pre zobrazenie</string>
<string name="EPr_edit_screen_options">Možnosti úprav zobrazenia</string>
<string name="EPr_show_timer_shortcut">Zobraziť skratku časovača</string>
<string name="EPr_beastMode_desc">Prispôsobiť rozvrhnutie zobrazenia úpravy úloh</string>
<string name="EPr_beastMode_reset">Obnoviť na predvolené</string>
<string name="EPr_beastMode_hint">Prispôsob si rozvrhnutie zobrazenia úpravy úloh potiahnutím ľavej rukoväte na každom riadku</string>
@ -239,7 +238,6 @@
<item>mesačne</item>
<item>každý druhý mesiac</item>
</string-array>
<string name="repeat_enabled">Opakovanie</string>
<string name="repeat_every">Každý %d</string>
<string name="repeat_interval_prompt">Interval opakovania</string>
<string name="repeat_never">Neopakovať</string>
@ -260,7 +258,6 @@
<string name="repeat_forever">Opakovať donekonečna</string>
<string name="repeat_until">Opakovať do %s</string>
<string name="repeat_snackbar">%1$s preplánované na %2$s</string>
<string name="TEA_tags_label_long">Pridať do zoznamov</string>
<string name="new_tag">Nasledujúci tag</string>
<string name="tag_no_title_error">Prosím, uveďte najprv názov pre tento zoznam!</string>
<string name="tag_FEx_untagged">Nezaradené</string>

@ -106,7 +106,6 @@
<string name="EPr_appearance_header">Izgled</string>
<string name="EPr_filters_to_show_title">Filtri za prikaz</string>
<string name="EPr_edit_screen_options">Uredi možnosti zaslona</string>
<string name="EPr_show_timer_shortcut">Prikaži bližnjico za štoparico</string>
<string name="EPr_beastMode_desc">Po meri postavi stran za urejevanje opravkov </string>
<string name="EPr_beastMode_reset">Povrni privzeto nastavitev</string>
<string name="EPr_beastMode_hint">Stran za urejanje opravkov se postavi po meri s potegom leve ročke posamezne vrstice</string>
@ -227,7 +226,6 @@
<item>mesečno</item>
<item>vsake dva meseca</item>
</string-array>
<string name="repeat_enabled">Ponovitve</string>
<string name="repeat_every">Vsakih %d</string>
<string name="repeat_interval_prompt">Pogostost ponovitev</string>
<string name="repeat_never">Brez ponovitev</string>
@ -247,7 +245,6 @@
<string name="repeat_detail_duedate_until">Vsak %1$s\n do %2$s</string>
<string name="repeat_forever">Ponavljaj do preklica</string>
<string name="repeat_until">Ponavljaj do %s</string>
<string name="TEA_tags_label_long">Dodaj na sezname</string>
<string name="tag_no_title_error">Prosimo, vnesite naziv tega seznama najprej!</string>
<string name="tag_FEx_untagged">Nerazvrščen</string>
<string name="TPl_notification">Merilniki časa aktivni za %s!</string>

@ -218,7 +218,6 @@ och återställer dina aktuella uppgifter från en backup
<item>varje månad</item>
<item>varannan månad</item>
</string-array>
<string name="repeat_enabled">Upprepningar</string>
<string name="repeat_every">Varje %d</string>
<string name="repeat_interval_prompt">Upprepningsintervall</string>
<string name="repeat_never">Inte upprepande</string>
@ -238,7 +237,6 @@ och återställer dina aktuella uppgifter från en backup
<string name="repeat_detail_duedate_until">Varje %1$s\ntill %2$s </string>
<string name="repeat_forever">Repetera oändligt</string>
<string name="repeat_until">Repetera till %s</string>
<string name="TEA_tags_label_long">Lägg till på listor</string>
<string name="tag_no_title_error">Ange först ett namn på listan!</string>
<string name="tag_FEx_untagged">Ingen kategori</string>
<string name="TPl_notification">Tidtagarur aktivt för %s!</string>

@ -65,7 +65,6 @@
<string name="gcal_completed_title">%s (เสร็จสิ้นแล้ว)</string>
<string name="rmd_NoA_done">เสร็จเรียบร้อยแล้ว!</string>
<string name="rmd_NoA_snooze">หลับ..</string>
<string name="repeat_enabled">การเกิดซ้ำ</string>
<string name="today">วันนี้</string>
<string name="tomorrow">พรุ่งนี้</string>
<string name="yesterday">เมื่อวาน</string>

@ -198,7 +198,6 @@
<item>ayda bir</item>
<item>ayda 2 kez</item>
</string-array>
<string name="repeat_enabled">Tekrarlar</string>
<string name="repeat_every">Her %d</string>
<string name="repeat_interval_prompt">Yineleme Aralığı</string>
<string-array name="repeat_interval">

@ -109,7 +109,6 @@
<string name="EPr_appearance_header">Інтерфейс</string>
<string name="EPr_filters_to_show_title">Показати фільтри</string>
<string name="EPr_edit_screen_options">Редагувати налаштування екрану</string>
<string name="EPr_show_timer_shortcut">Показати ярлик таймера</string>
<string name="EPr_beastMode_desc">Налаштувати зовнішній вигляд екрану редагування завдань</string>
<string name="EPr_beastMode_reset">Налаштування за замовчуванням</string>
<string name="EPr_beastMode_hint">Налаштуйте Ваш екран редагування нотатки перетягуванням лівої частини рядків</string>
@ -239,7 +238,6 @@
<item>щомісяця</item>
<item>два рази на місяць</item>
</string-array>
<string name="repeat_enabled">Повторення</string>
<string name="repeat_every">З інтервалом в %d</string>
<string name="repeat_interval_prompt">Інтервал повторення</string>
<string name="repeat_never">Не повторювати</string>
@ -247,7 +245,6 @@
<string name="repeat_detail_duedate_until">Кожні %1$s\nпоки %2$s</string>
<string name="repeat_forever">Повторювати завжди</string>
<string name="repeat_until">Повторювати до %s</string>
<string name="TEA_tags_label_long">Додати до списку</string>
<string name="tag_no_title_error">Введіть назву списку спочатку!</string>
<string name="tag_FEx_untagged">Без категорії</string>
<string name="TPl_notification">Для %s діють таймери!</string>

@ -177,7 +177,6 @@
<item>每月</item>
<item>每两个月</item>
</string-array>
<string name="repeat_enabled">重复</string>
<string name="repeat_every">每 %d</string>
<string name="repeat_interval_prompt">重复间隔</string>
<string-array name="repeat_interval">

@ -208,7 +208,6 @@
<item>每月</item>
<item>每2個月</item>
</string-array>
<string name="repeat_enabled">重複</string>
<string name="repeat_every">每 %d</string>
<string name="repeat_interval_prompt">重複間隔</string>
<string-array name="repeat_interval">
@ -227,7 +226,6 @@
<string name="repeat_detail_duedate_until">每隔 %1$s\n直到 %2$s</string>
<string name="repeat_forever">永遠重複</string>
<string name="repeat_until">重複到 %s</string>
<string name="TEA_tags_label_long">加入至列表</string>
<string name="tag_no_title_error">請先為此列表鍵入一個名稱!</string>
<string name="tag_FEx_untagged">未分類</string>
<string name="TPl_notification">秒錶啟動了 %s</string>

@ -62,4 +62,10 @@
<item>@string/none</item>
</string-array>
<string-array name="reminder_ring_modes">
<item>@string/ring_once</item>
<item>@string/ring_five_times</item>
<item>@string/ring_nonstop</item>
</string-array>
</resources>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save