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 @Override
public void setUp() { public void setUp() {
Preferences preferences = new Preferences(getContext(), null, null); Preferences preferences = new Preferences(getContext(), null);
reminderService = new ReminderService(getContext(), preferences, mock(AlarmManager.class)); reminderService = new ReminderService(getContext(), preferences, mock(AlarmManager.class));
freezeAt(new DateTime(2014, 1, 24, 17, 23, 37)); freezeAt(new DateTime(2014, 1, 24, 17, 23, 37));
} }

@ -233,14 +233,14 @@ public class TitleParserTest extends DatabaseTestCase {
String title = "Jog " + acceptedString; String title = "Jog " + acceptedString;
task.setTitle(title); //test at end of task. should set importance. task.setTitle(title); //test at end of task. should set importance.
taskService.createWithValues(task, null, title); taskService.createWithValues(task, null, title);
assertEquals((int)task.getImportance(), Task.IMPORTANCE_LEAST); assertEquals((int)task.getImportance(), Task.IMPORTANCE_NONE);
} }
for (String acceptedString:acceptedStrings){ for (String acceptedString:acceptedStrings){
task = new Task(); task = new Task();
String title = acceptedString + " jog"; String title = acceptedString + " jog";
task.setTitle(title); //test at beginning of task. should not set importance. task.setTitle(title); //test at beginning of task. should not set importance.
taskService.createWithValues(task, null, title); 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; package org.tasks.location;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
@ -10,7 +11,7 @@ public class PlacePicker {
return null; return null;
} }
public static Geofence getPlace(Activity activity, Intent data, Preferences preferences) { public static Geofence getPlace(Context context, Intent data, Preferences preferences) {
return null; return null;
} }
} }

@ -97,6 +97,10 @@
<!-- ====================================================== Activities = --> <!-- ====================================================== Activities = -->
<activity
android:name=".activities.DatePickerActivity"
android:theme="@style/Theme.AppCompat.Dialog" />
<activity <activity
android:name=".activities.TimePickerActivity" android:name=".activities.TimePickerActivity"
android:theme="@style/Theme.AppCompat.Dialog" /> android:theme="@style/Theme.AppCompat.Dialog" />
@ -377,7 +381,7 @@
<activity <activity
android:name=".activities.CalendarSelectionActivity" android:name=".activities.CalendarSelectionActivity"
android:theme="@style/TranslucentDialog" /> android:theme="@style/Theme.AppCompat.Dialog" />
<activity <activity
android:name="com.todoroo.astrid.gcal.CalendarReminderActivity" 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.Filter;
import com.todoroo.astrid.api.FilterWithCustomIntent; import com.todoroo.astrid.api.FilterWithCustomIntent;
import com.todoroo.astrid.data.TagData; 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.StartupService;
import com.todoroo.astrid.service.UpgradeActivity; import com.todoroo.astrid.service.UpgradeActivity;
import com.todoroo.astrid.subtasks.SubtasksHelper; import com.todoroo.astrid.subtasks.SubtasksHelper;
import com.todoroo.astrid.timers.TimerControlSet;
import org.tasks.R; import org.tasks.R;
import org.tasks.injection.InjectingAppCompatActivity; import org.tasks.injection.InjectingAppCompatActivity;
import org.tasks.ui.NavigationDrawerFragment; import org.tasks.ui.NavigationDrawerFragment;
import org.tasks.ui.PriorityControlSet;
import javax.inject.Inject; import javax.inject.Inject;
@ -37,8 +41,11 @@ import timber.log.Timber;
* @author Arne * @author Arne
* *
*/ */
public abstract class AstridActivity extends InjectingAppCompatActivity public abstract class AstridActivity extends InjectingAppCompatActivity implements
implements TaskListFragment.OnTaskListItemClickedListener { TaskListFragment.OnTaskListItemClickedListener,
PriorityControlSet.OnPriorityChanged,
TimerControlSet.TimerControlSetCallback,
RepeatControlSet.RepeatChangedListener {
public static final int LAYOUT_SINGLE = 0; public static final int LAYOUT_SINGLE = 0;
public static final int LAYOUT_DOUBLE = 1; public static final int LAYOUT_DOUBLE = 1;
@ -150,14 +157,36 @@ public abstract class AstridActivity extends InjectingAppCompatActivity
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_UPGRADE && resultCode == RESULT_OK) { if (requestCode == REQUEST_UPGRADE) {
if (data != null && data.getBooleanExtra(UpgradeActivity.EXTRA_RESTART, false)) { if (resultCode == RESULT_OK) {
Timber.w("Upgrade requires restart"); if (data != null && data.getBooleanExtra(UpgradeActivity.EXTRA_RESTART, false)) {
finish(); Timber.w("Upgrade requires restart");
startActivity(getIntent()); finish();
startActivity(getIntent());
}
} }
} else { } else {
super.onActivityResult(requestCode, resultCode, data); 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) { for (String s : itemsArray) {
if (!s.equals(context.getString(R.string.TEA_ctrl_title_pref)) && if (!s.equals(context.getString(R.string.TEA_ctrl_share_pref)) &&
!s.equals(context.getString(R.string.TEA_ctrl_share_pref)) &&
!s.equals(context.getString(R.string.TEA_ctrl_more_pref))) { !s.equals(context.getString(R.string.TEA_ctrl_more_pref))) {
list.add(s); list.add(s);
} }

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

@ -6,8 +6,10 @@
package com.todoroo.astrid.activity; package com.todoroo.astrid.activity;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
@ -25,69 +27,53 @@ import android.view.View.MeasureSpec;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent; import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.EditText; import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ScrollView; import android.widget.ScrollView;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.actfm.ActFmCameraModule; import com.todoroo.astrid.actfm.ActFmCameraModule;
import com.todoroo.astrid.alarms.AlarmService;
import com.todoroo.astrid.api.PermaSql; import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.dao.MetadataDao; 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.dao.UserActivityDao;
import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment; import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.files.AACRecordingActivity; import com.todoroo.astrid.files.AACRecordingActivity;
import com.todoroo.astrid.files.FilesControlSet; 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.notes.EditNoteActivity;
import com.todoroo.astrid.repeats.RepeatControlSet; import com.todoroo.astrid.repeats.RepeatControlSet;
import com.todoroo.astrid.service.TaskDeleter; import com.todoroo.astrid.service.TaskDeleter;
import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.tags.TagsControlSet; import com.todoroo.astrid.tags.TagsControlSet;
import com.todoroo.astrid.timers.TimerActionControlSet;
import com.todoroo.astrid.timers.TimerControlSet; import com.todoroo.astrid.timers.TimerControlSet;
import com.todoroo.astrid.timers.TimerPlugin; 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.EditTitleControlSet;
import com.todoroo.astrid.ui.HideUntilControlSet; import com.todoroo.astrid.ui.HideUntilControlSet;
import com.todoroo.astrid.ui.PopupControlSet;
import com.todoroo.astrid.ui.ReminderControlSet; import com.todoroo.astrid.ui.ReminderControlSet;
import com.todoroo.astrid.utility.Flags; import com.todoroo.astrid.utility.Flags;
import org.tasks.R; import org.tasks.R;
import org.tasks.activities.AddAttachmentActivity;
import org.tasks.activities.CameraActivity; import org.tasks.activities.CameraActivity;
import org.tasks.activities.TimePickerActivity;
import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ForActivity;
import org.tasks.injection.InjectingFragment; 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.notifications.NotificationManager;
import org.tasks.preferences.ActivityPreferences; import org.tasks.preferences.ActivityPreferences;
import org.tasks.preferences.Device; import org.tasks.ui.CalendarControlSet;
import org.tasks.preferences.PermissionRequestor;
import org.tasks.ui.DeadlineControlSet; import org.tasks.ui.DeadlineControlSet;
import org.tasks.ui.DescriptionControlSet;
import org.tasks.ui.MenuColorizer; import org.tasks.ui.MenuColorizer;
import org.tasks.ui.PriorityControlSet; import org.tasks.ui.PriorityControlSet;
import org.tasks.ui.TaskEditControlFragment;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
@ -95,6 +81,8 @@ import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import timber.log.Timber; import timber.log.Timber;
import static android.app.Activity.RESULT_OK;
/** /**
* This activity is responsible for creating new tasks and editing existing * 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) * 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_VALUES = "v"; //$NON-NLS-1$
public static final String TOKEN_OPEN_CONTROL = "open_control"; //$NON-NLS-1$
/** /**
* Task in progress (during orientation change) * Task in progress (during orientation change)
*/ */
@ -139,8 +125,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
// --- request codes // --- request codes
public static final int REQUEST_CODE_RECORD = 30; public static final int REQUEST_CODE_RECORD = 30; // TODO: move this to file control set
public static final int REQUEST_ADD_ATTACHMENT = 50;
public static final int REQUEST_CODE_CAMERA = 60; public static final int REQUEST_CODE_CAMERA = 60;
// --- result codes // --- result codes
@ -154,42 +139,27 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
public static final int TAB_VIEW_UPDATES = 0; public static final int TAB_VIEW_UPDATES = 0;
@Inject TaskService taskService; @Inject TaskService taskService;
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject TagService tagService;
@Inject MetadataDao metadataDao; @Inject MetadataDao metadataDao;
@Inject UserActivityDao userActivityDao; @Inject UserActivityDao userActivityDao;
@Inject TaskDeleter taskDeleter; @Inject TaskDeleter taskDeleter;
@Inject NotificationManager notificationManager; @Inject NotificationManager notificationManager;
@Inject AlarmService alarmService;
@Inject GCalHelper gcalHelper;
@Inject ActivityPreferences preferences; @Inject ActivityPreferences preferences;
@Inject TagDataDao tagDataDao;
@Inject ActFmCameraModule actFmCameraModule; @Inject ActFmCameraModule actFmCameraModule;
@Inject GeofenceService geofenceService;
@Inject DialogBuilder dialogBuilder; @Inject DialogBuilder dialogBuilder;
@Inject PermissionRequestor permissionRequestor; @Inject @ForActivity Context context;
@Inject Device device;
// --- UI components // --- UI components
private final HashMap<String, TaskEditControlSet> controlSetMap = new HashMap<>(); private final Map<String, Integer> controlSetFragments = new HashMap<>();
private FilesControlSet filesControlSet; private final List<Integer> displayedFragments = new ArrayList<>();
private TimerActionControlSet timerAction;
private EditNoteActivity editNotes; private EditNoteActivity editNotes;
private HideUntilControlSet hideUntilControls;
private ReminderControlSet reminderControlSet;
@Bind(R.id.title) EditText title;
@Bind(R.id.pager) ViewPager mPager; @Bind(R.id.pager) ViewPager mPager;
@Bind(R.id.updatesFooter) View commentsBar; @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.basic_controls) LinearLayout basicControls;
@Bind(R.id.edit_scroll) ScrollView scrollView; @Bind(R.id.edit_scroll) ScrollView scrollView;
@Bind(R.id.commentField) EditText commentField; @Bind(R.id.commentField) EditText commentField;
private final List<TaskEditControlSet> controls = Collections.synchronizedList(new ArrayList<TaskEditControlSet>());
// --- other instance variables // --- other instance variables
/** true if editing started with a new task */ /** 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 String uuid = RemoteModel.NO_UUID;
private boolean showEditComments; 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); 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) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.task_edit_activity, container, false); View view = inflater.inflate(R.layout.task_edit_activity, container, false);
ButterKnife.bind(this, view); 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; 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 @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(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 // Load task data in background
new TaskEditBackgroundLoader().start(); new TaskEditBackgroundLoader().start();
} }
@ -349,7 +323,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
private void instantiateEditNotes() { private void instantiateEditNotes() {
if (showEditComments) { if (showEditComments) {
long idParam = getActivity().getIntent().getLongExtra(TOKEN_ID, -1L); 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); taskService, this, getView(), idParam);
editNotes.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, editNotes.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT)); LayoutParams.WRAP_CONTENT));
@ -373,12 +347,11 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
editNotes.loadViewForTaskID(idParam); editNotes.loadViewForTaskID(idParam);
} }
if (timerAction != null && editNotes != null) {
timerAction.removeListener(editNotes);
timerAction.addListener(editNotes);
}
if (editNotes != null) { if (editNotes != null) {
TimerControlSet timerControl = getTimerControl();
if (timerControl != null) {
timerControl.setEditNotes(editNotes);
}
editNotes.addListener(this); editNotes.addListener(this);
} }
@ -406,40 +379,14 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
mPager.setCurrentItem(position); mPager.setCurrentItem(position);
} }
private void loadEditPageOrder() { public Task stopTimer() {
ArrayList<String> controlOrder = BeastModePreferences.constructOrderedControlList(preferences, getActivity()); TimerPlugin.stopTimer(notificationManager, taskService, context, model);
String[] itemOrder = controlOrder.toArray(new String[controlOrder.size()]); return model;
}
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();
}
}
}
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) { public void repopulateFromScratch(Intent intent) {
model = null; model = null;
uuid = RemoteModel.NO_UUID; uuid = RemoteModel.NO_UUID;
populateFields(intent);
loadMoreContainer(); loadMoreContainer();
} }
/** Populate UI component values from the model */ private String getTitle() {
public void populateFields(Intent intent) { return getEditTitleControlSet().getTitle();
loadItem(intent);
synchronized (controls) {
for (TaskEditControlSet controlSet : controls) {
controlSet.readFromTask(model);
}
}
} }
/** Save task model from values in UI components */ /** Save task model from values in UI components */
public void save(boolean onPause) { public void save(boolean onPause) {
String title = getTitle();
if (title == null) { if (title == null) {
return; return;
} }
if (title.getText().length() > 0) { if (title.length() > 0) {
model.setDeletionDate(0L); model.setDeletionDate(0L);
} }
if (title.getText().length() == 0) { if (title.length() == 0) {
return; return;
} }
synchronized (controls) { if (!onPause) {
for (TaskEditControlSet controlSet : controls) { for (Integer fragmentId : displayedFragments) {
if (controlSet instanceof PopupControlSet) { // Save open control set getFragment(fragmentId).apply(model);
PopupControlSet popup = (PopupControlSet) controlSet;
Dialog d = popup.getDialog();
if (d != null && d.isShowing()) {
getActivity().getIntent().putExtra(TOKEN_OPEN_CONTROL, popup.getClass());
}
}
controlSet.writeToModel(model);
} }
} taskService.save(model);
boolean tagsChanged = Flags.check(Flags.TAGS_CHANGED);
model.putTransitory(TaskService.TRANS_EDIT_SAVE, true); // TODO: not used?
taskService.save(model);
if (!onPause) {
boolean taskEditActivity = (getActivity() instanceof TaskEditActivity); boolean taskEditActivity = (getActivity() instanceof TaskEditActivity);
boolean tagsChanged = Flags.check(Flags.TAGS_CHANGED);
if (taskEditActivity) { if (taskEditActivity) {
Intent data = new Intent(); Intent data = new Intent();
data.putExtra(TOKEN_TAGS_CHANGED, tagsChanged); data.putExtra(TOKEN_TAGS_CHANGED, tagsChanged);
data.putExtra(TOKEN_ID, model.getId()); data.putExtra(TOKEN_ID, model.getId());
data.putExtra(TOKEN_UUID, model.getUuid()); data.putExtra(TOKEN_UUID, model.getUuid());
getActivity().setResult(Activity.RESULT_OK, data); getActivity().setResult(RESULT_OK, data);
} else { } else {
// Notify task list fragment in multi-column case // Notify task list fragment in multi-column case
@ -611,13 +541,29 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
removeExtrasFromIntent(getActivity().getIntent()); removeExtrasFromIntent(getActivity().getIntent());
shouldSaveState = false; shouldSaveState = false;
getActivity().onBackPressed(); 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) { public boolean onKeyDown(int keyCode) {
if (keyCode == KeyEvent.KEYCODE_BACK) { if (keyCode == KeyEvent.KEYCODE_BACK) {
if(title.getText().length() == 0) { if(getTitle().length() == 0) {
discardButtonClick(); discardButtonClick();
} else { } else {
saveButtonClick(); saveButtonClick();
@ -640,11 +586,11 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
} }
if (activity instanceof TaskListActivity) { 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); taskDeleter.delete(model);
} }
} else if (activity instanceof TaskEditActivity) { } 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); taskDeleter.delete(model);
} }
} }
@ -674,7 +620,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
shouldSaveState = false; shouldSaveState = false;
// abandon editing in this case // 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) { if (isNewTask) {
TimerPlugin.stopTimer(notificationManager, taskService, getActivity(), model); TimerPlugin.stopTimer(notificationManager, taskService, getActivity(), model);
taskDeleter.delete(model); taskDeleter.delete(model);
@ -700,7 +646,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
Activity a = getActivity(); Activity a = getActivity();
if (a instanceof TaskEditActivity) { if (a instanceof TaskEditActivity) {
getActivity().setResult(Activity.RESULT_OK); getActivity().setResult(RESULT_OK);
getActivity().onBackPressed(); getActivity().onBackPressed();
} else if (a instanceof TaskListActivity) { } else if (a instanceof TaskListActivity) {
discardButtonClick(); discardButtonClick();
@ -738,7 +684,7 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
deleteButtonClick(); deleteButtonClick();
return true; return true;
case android.R.id.home: case android.R.id.home:
if (title.getText().toString().trim().length() == 0) { if (getTitle().trim().length() == 0) {
discardButtonClick(); discardButtonClick();
} else { } else {
saveButtonClick(); 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 @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (editNotes == null) { if (editNotes == null) {
instantiateEditNotes(); instantiateEditNotes();
} }
if (requestCode == HideUntilControlSet.REQUEST_HIDE_UNTIL && resultCode == Activity.RESULT_OK) { if (requestCode == REQUEST_CODE_RECORD && resultCode == 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) {
String recordedAudioPath = data.getStringExtra(AACRecordingActivity.RESULT_OUTFILE); String recordedAudioPath = data.getStringExtra(AACRecordingActivity.RESULT_OUTFILE);
String recordedAudioName = data.getStringExtra(AACRecordingActivity.RESULT_FILENAME); String recordedAudioName = data.getStringExtra(AACRecordingActivity.RESULT_FILENAME);
filesControlSet.createNewFileAttachment(recordedAudioPath, recordedAudioName, TaskAttachment.FILE_TYPE_AUDIO + "m4a"); //$NON-NLS-1$ getFilesControlSet().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);
} else if (requestCode == REQUEST_CODE_CAMERA) { } 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); Uri uri = data.getParcelableExtra(CameraActivity.EXTRA_URI);
editNotes.setPictureUri(uri); editNotes.setPictureUri(uri);
} }
@ -901,8 +806,16 @@ public final class TaskEditFragment extends InjectingFragment implements EditNot
} }
private void hideKeyboard() { private void hideKeyboard() {
AndroidUtilities.hideSoftInputForViews(getActivity(), title, commentField); getEditTitleControlSet().hideKeyboard();
title.setCursorVisible(false); AndroidUtilities.hideSoftInputForViews(getActivity(), commentField);
commentField.setCursorVisible(false); 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) { private void showFilesDialog(Task task) {
FilesControlSet filesControlSet = new FilesControlSet( // TODO: reimplement this
preferences, taskAttachmentDao, fragment); // FilesControlSet filesControlSet = new FilesControlSet();
filesControlSet.hideAddAttachmentButton(); // filesControlSet.hideAddAttachmentButton();
filesControlSet.readFromTask(task); // filesControlSet.readFromTask(task);
filesControlSet.getView().performClick(); // filesControlSet.getView().performClick();
} }
/* ====================================================================== /* ======================================================================

@ -73,7 +73,9 @@ public class DefaultsPreferences extends InjectingPreferenceActivity {
} }
private void startCalendarSelectionActivity() { 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 @Override

@ -55,6 +55,10 @@ public class TagDataDao {
.orderBy(Order.asc(TagData.ID))); .orderBy(Order.asc(TagData.ID)));
} }
public TagData getByUuid(String uuid) {
return getByUuid(uuid, TagData.PROPERTIES);
}
public TagData getByUuid(String uuid, Property<?>... properties) { public TagData getByUuid(String uuid, Property<?>... properties) {
return dao.getFirst(Query.select(properties).where(TagData.UUID.eq(uuid))); 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); setValue(LAST_AUTOSYNC, lastAutosync);
} }
// TODO: remove?
public String getUUID() { public String getUUID() {
return getValue(UUID); return getValue(UUID);
} }

@ -150,17 +150,6 @@ public class Task extends RemoteModel {
/** /**
* @return colors that correspond to importance values * @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 // --- defaults
@ -544,10 +533,14 @@ public class Task extends RemoteModel {
return getValue(ESTIMATED_SECONDS); return getValue(ESTIMATED_SECONDS);
} }
public void setELAPSED_SECONDS(Integer elapsedSeconds) { public void setElapsedSeconds(Integer elapsedSeconds) {
setValue(ELAPSED_SECONDS, elapsedSeconds); setValue(ELAPSED_SECONDS, elapsedSeconds);
} }
public void setEstimatedSeconds(Integer estimatedSeconds) {
setValue(ESTIMATED_SECONDS, estimatedSeconds);
}
public void setCalendarUri(String calendarUri) { public void setCalendarUri(String calendarUri) {
setValue(CALENDAR_URI, calendarUri); setValue(CALENDAR_URI, calendarUri);
} }

@ -6,17 +6,20 @@
package com.todoroo.astrid.files; package com.todoroo.astrid.files;
import android.app.Activity; import android.app.Activity;
import android.app.Fragment;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.ClipData; import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
@ -24,46 +27,112 @@ import android.widget.Toast;
import com.todoroo.andlib.data.Callback; import com.todoroo.andlib.data.Callback;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.dao.TaskAttachmentDao; import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.data.SyncFlags; import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment; import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R; import org.tasks.R;
import org.tasks.activities.AddAttachmentActivity; import org.tasks.activities.AddAttachmentActivity;
import org.tasks.dialogs.DialogBuilder; 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.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import timber.log.Timber; import timber.log.Timber;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop; import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop;
public class FilesControlSet extends TaskEditControlSetBase { public class FilesControlSet extends TaskEditControlFragment {
private final ArrayList<TaskAttachment> files = new ArrayList<>(); private static final int REQUEST_ADD_ATTACHMENT = 50;
private final LayoutInflater inflater; private static final String EXTRA_UUID = "extra_uuid";
private final TaskAttachmentDao taskAttachmentDao;
private final Fragment fragment; @Inject TaskAttachmentDao taskAttachmentDao;
private final DialogBuilder dialogBuilder; @Inject DialogBuilder dialogBuilder;
private LinearLayout attachmentContainer; @Inject @ForActivity Context context;
private TextView addAttachment;
@Bind(R.id.attachment_container) LinearLayout attachmentContainer;
public FilesControlSet(ActivityPreferences preferences, TaskAttachmentDao taskAttachmentDao, @Bind(R.id.add_attachment) TextView addAttachment;
Fragment fragment) {
super(fragment.getActivity(), R.layout.control_set_files); private String taskUuid;
this.taskAttachmentDao = taskAttachmentDao;
this.fragment = fragment; @Nullable
this.dialogBuilder = new DialogBuilder(activity, preferences); @Override
inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); 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) { 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); fileRow.setTag(taskAttachment);
attachmentContainer.addView(fileRow); attachmentContainer.addView(fileRow);
addAttachment(taskAttachment, fileRow); addAttachment(taskAttachment, fileRow);
@ -71,7 +140,6 @@ public class FilesControlSet extends TaskEditControlSetBase {
private void addAttachment(final TaskAttachment taskAttachment, final View fileRow) { private void addAttachment(final TaskAttachment taskAttachment, final View fileRow) {
TextView nameView = (TextView) fileRow.findViewById(R.id.file_text); TextView nameView = (TextView) fileRow.findViewById(R.id.file_text);
nameView.setTextColor(themeColor);
String name = taskAttachment.getName(); String name = taskAttachment.getName();
nameView.setText(name); nameView.setText(name);
nameView.setOnClickListener(new OnClickListener() { nameView.setOnClickListener(new OnClickListener() {
@ -93,7 +161,6 @@ public class FilesControlSet extends TaskEditControlSetBase {
File f = new File(taskAttachment.getFilePath()); File f = new File(taskAttachment.getFilePath());
f.delete(); f.delete();
} }
files.remove(taskAttachment);
attachmentContainer.removeView(fileRow); attachmentContainer.removeView(fileRow);
} }
}) })
@ -103,28 +170,7 @@ public class FilesControlSet extends TaskEditControlSetBase {
}); });
} }
@Override private void validateFiles(List<TaskAttachment> files) {
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() {
for (int i = 0; i < files.size(); i++) { for (int i = 0; i < files.size(); i++) {
TaskAttachment m = files.get(i); TaskAttachment m = files.get(i);
if (m.containsNonNullValue(TaskAttachment.FILE_PATH)) { 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() { public void hideAddAttachmentButton() {
addAttachment.setVisibility(View.GONE); addAttachment.setVisibility(View.GONE);
} }
@ -210,10 +228,10 @@ public class FilesControlSet extends TaskEditControlSetBase {
if (atLeastLollipop()) { if (atLeastLollipop()) {
intent.setClipData(ClipData.newRawUri(null, uri)); intent.setClipData(ClipData.newRawUri(null, uri));
} }
activity.startActivity(intent); getActivity().startActivity(intent);
} catch(ActivityNotFoundException e) { } catch(ActivityNotFoundException e) {
Timber.e(e, e.getMessage()); 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 { } else {
String useType = fileType; String useType = fileType;
@ -240,17 +258,16 @@ public class FilesControlSet extends TaskEditControlSetBase {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(file)), type); intent.setDataAndType(Uri.fromFile(new File(file)), type);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(intent); getActivity().startActivity(intent);
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
Timber.e(e, e.getMessage()); 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) { 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); taskAttachmentDao.createNew(attachment);
refreshMetadata();
addAttachment(attachment); addAttachment(attachment);
} }
} }

@ -19,7 +19,6 @@ public class Calendars {
public static final String ID_COLUMN_NAME = "_id"; 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_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_DTSTART_COL = CalendarContract.Events.DTSTART;
public static final String EVENTS_DTEND_COL = CalendarContract.Events.DTEND; public static final String EVENTS_DTEND_COL = CalendarContract.Events.DTEND;
public static final String EVENTS_NAME_COL = CalendarContract.Events.TITLE; 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 // Only show calendars that the user can modify. Access level 500
// corresponds to Calendars.CONTRIBUTOR_ACCESS // 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"; 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; return eventDeleted;
} }
void createStartAndEndDate(Task task, ContentValues values) { public void createStartAndEndDate(Task task, ContentValues values) {
long dueDate = task.getDueDate(); long dueDate = task.getDueDate();
long tzCorrectedDueDate = dueDate + TimeZone.getDefault().getOffset(dueDate); long tzCorrectedDueDate = dueDate + TimeZone.getDefault().getOffset(dueDate);
long tzCorrectedDueDateNow = DateUtilities.now() + TimeZone.getDefault().getOffset(DateUtilities.now()); 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. * 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.Task;
import com.todoroo.astrid.data.UserActivity; import com.todoroo.astrid.data.UserActivity;
import com.todoroo.astrid.service.TaskService; 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.json.JSONObject;
import org.tasks.R; import org.tasks.R;
import org.tasks.preferences.Preferences;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -67,14 +66,12 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
private Task task; private Task task;
private ActFmCameraModule actFmCameraModule; private ActFmCameraModule actFmCameraModule;
private final Preferences preferences;
private final MetadataDao metadataDao; private final MetadataDao metadataDao;
private final UserActivityDao userActivityDao; private final UserActivityDao userActivityDao;
private final TaskService taskService; private final TaskService taskService;
private final ArrayList<NoteOrUpdate> items = new ArrayList<>(); private final ArrayList<NoteOrUpdate> items = new ArrayList<>();
private EditText commentField; private EditText commentField;
private final View commentsBar; private final View commentsBar;
private View timerView;
private View commentButton; private View commentButton;
private int commentItems = 10; private int commentItems = 10;
private ImageButton pictureButton; private ImageButton pictureButton;
@ -94,7 +91,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
public EditNoteActivity( public EditNoteActivity(
ActFmCameraModule actFmCameraModule, ActFmCameraModule actFmCameraModule,
Preferences preferences,
MetadataDao metadataDao, MetadataDao metadataDao,
UserActivityDao userActivityDao, UserActivityDao userActivityDao,
TaskService taskService, TaskService taskService,
@ -103,7 +99,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
long t) { long t) {
super(fragment.getActivity()); super(fragment.getActivity());
this.actFmCameraModule = actFmCameraModule; this.actFmCameraModule = actFmCameraModule;
this.preferences = preferences;
this.metadataDao = metadataDao; this.metadataDao = metadataDao;
this.userActivityDao = userActivityDao; this.userActivityDao = userActivityDao;
this.taskService = taskService; this.taskService = taskService;
@ -141,26 +136,9 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
// --- UI preparation // --- UI preparation
private void setUpInterface() { private void setUpInterface() {
timerView = commentsBar.findViewById(R.id.timer_container);
commentButton = commentsBar.findViewById(R.id.commentButton); commentButton = commentsBar.findViewById(R.id.commentButton);
commentField = (EditText) commentsBar.findViewById(R.id.commentField); 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.setHorizontallyScrolling(false);
commentField.setMaxLines(Integer.MAX_VALUE); commentField.setMaxLines(Integer.MAX_VALUE);
commentField.setOnKeyListener(new OnKeyListener() { commentField.setOnKeyListener(new OnKeyListener() {
@ -185,10 +163,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
commentButton.setVisibility((s.length() > 0 || pendingCommentPicture != null) ? View.VISIBLE commentButton.setVisibility((s.length() > 0 || pendingCommentPicture != null) ? View.VISIBLE
: View.GONE); : View.GONE);
if (showTimerShortcut) {
timerView.setVisibility((s.length() > 0 || pendingCommentPicture != null) ? View.GONE
: View.VISIBLE);
}
} }
@Override @Override
@ -241,7 +215,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
} }
}); });
if(!TextUtils.isEmpty(task.getNotes())) { if(!TextUtils.isEmpty(task.getNotes())) {
TextView notes = new TextView(getContext()); TextView notes = new TextView(activity);
notes.setLinkTextColor(Color.rgb(100, 160, 255)); notes.setLinkTextColor(Color.rgb(100, 160, 255));
notes.setTextSize(18); notes.setTextSize(18);
notes.setText(task.getNotes()); notes.setText(task.getNotes());
@ -294,7 +268,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
} }
if (items.size() > commentItems) { if (items.size() > commentItems) {
Button loadMore = new Button(getContext()); Button loadMore = new Button(activity);
loadMore.setText(R.string.TEA_load_more); loadMore.setText(R.string.TEA_load_more);
loadMore.setTextColor(activity.getResources().getColor(R.color.task_edit_deadline_gray)); loadMore.setTextColor(activity.getResources().getColor(R.color.task_edit_deadline_gray));
loadMore.setBackgroundColor(Color.alpha(0)); loadMore.setBackgroundColor(Color.alpha(0));
@ -315,7 +289,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
} }
public View getUpdateNotes(NoteOrUpdate note, ViewGroup parent) { 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); R.layout.comment_adapter_row, parent, false);
bindView(convertView, note); bindView(convertView, note);
return convertView; return convertView;
@ -364,7 +338,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
private void addComment() { private void addComment() {
addComment(commentField.getText().toString(), UserActivity.ACTION_TASK_COMMENT, task.getUuid(), true); addComment(commentField.getText().toString(), UserActivity.ACTION_TASK_COMMENT, task.getUuid(), true);
AndroidUtilities.hideSoftInputForViews(getContext(), commentField); AndroidUtilities.hideSoftInputForViews(activity, commentField);
commentField.setCursorVisible(false); commentField.setCursorVisible(false);
} }
@ -459,8 +433,8 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
@Override @Override
public void timerStarted(Task t) { public void timerStarted(Task t) {
addComment(String.format("%s %s", //$NON-NLS-1$ addComment(String.format("%s %s", //$NON-NLS-1$
getContext().getString(R.string.TEA_timer_comment_started), activity.getString(R.string.TEA_timer_comment_started),
DateUtilities.getTimeString(getContext(), newDateTime())), DateUtilities.getTimeString(activity, newDateTime())),
UserActivity.ACTION_TASK_COMMENT, UserActivity.ACTION_TASK_COMMENT,
t.getUuid(), t.getUuid(),
false); false);
@ -470,9 +444,9 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
public void timerStopped(Task t) { public void timerStopped(Task t) {
String elapsedTime = DateUtils.formatElapsedTime(t.getElapsedSeconds()); String elapsedTime = DateUtils.formatElapsedTime(t.getElapsedSeconds());
addComment(String.format("%s %s\n%s %s", //$NON-NLS-1$ addComment(String.format("%s %s\n%s %s", //$NON-NLS-1$
getContext().getString(R.string.TEA_timer_comment_stopped), activity.getString(R.string.TEA_timer_comment_stopped),
DateUtilities.getTimeString(getContext(), newDateTime()), DateUtilities.getTimeString(activity, newDateTime()),
getContext().getString(R.string.TEA_timer_comment_spent), activity.getString(R.string.TEA_timer_comment_spent),
elapsedTime), UserActivity.ACTION_TASK_COMMENT, elapsedTime), UserActivity.ACTION_TASK_COMMENT,
t.getUuid(), t.getUuid(),
false); false);

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

@ -7,10 +7,16 @@ package com.todoroo.astrid.repeats;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface; 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.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener; import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
@ -22,6 +28,7 @@ import android.widget.LinearLayout;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import com.google.common.base.Strings;
import com.google.ical.values.Frequency; import com.google.ical.values.Frequency;
import com.google.ical.values.RRule; import com.google.ical.values.RRule;
import com.google.ical.values.Weekday; 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.data.Task;
import com.todoroo.astrid.ui.NumberPickerDialog; import com.todoroo.astrid.ui.NumberPickerDialog;
import com.todoroo.astrid.ui.NumberPickerDialog.OnNumberPickedListener; 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.R;
import org.tasks.activities.DatePickerActivity;
import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.DialogBuilder;
import org.tasks.dialogs.MyDatePickerDialog; import org.tasks.injection.ForActivity;
import org.tasks.preferences.ActivityPreferences; import org.tasks.preferences.ActivityPreferences;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
import org.tasks.ui.TaskEditControlFragment;
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import timber.log.Timber; import timber.log.Timber;
import static org.tasks.date.DateTimeUtils.newDateTime; import static org.tasks.date.DateTimeUtils.newDateTime;
@ -55,9 +65,16 @@ import static org.tasks.date.DateTimeUtils.newDateTime;
* @author Tim Su <tim@todoroo.com> * @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 // --- spinner constants
@ -77,7 +94,14 @@ public class RepeatControlSet extends PopupControlSet {
private Spinner interval; private Spinner interval;
private Spinner type; private Spinner type;
private Spinner repeatUntil; 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 ArrayAdapter<String> repeatUntilAdapter;
private final List<String> repeatUntilOptions = new ArrayList<>(); private final List<String> repeatUntilOptions = new ArrayList<>();
private LinearLayout daysOfWeekContainer; private LinearLayout daysOfWeekContainer;
@ -87,227 +111,49 @@ public class RepeatControlSet extends PopupControlSet {
private int repeatValue; private int repeatValue;
private int intervalValue; private int intervalValue;
private long repeatUntilValue; private long repeatUntilValue;
private View dialogView;
private AlertDialog dialog;
private final List<RepeatChangedListener> listeners = new LinkedList<>(); private RepeatChangedListener callback;
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);
}
public void addListener(RepeatChangedListener listener) { private boolean repeatAfterCompletion;
listeners.add(listener);
}
@Nullable
@Override @Override
public void readFromTask(Task task) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.readFromTask(task); View view = super.onCreateView(inflater, container, savedInstanceState);
recurrence = model.sanitizedRecurrence(); if (savedInstanceState != null) {
if(recurrence == null) { recurrence = savedInstanceState.getString(EXTRA_RECURRENCE);
recurrence = ""; repeatUntilValue = savedInstanceState.getLong(EXTRA_REPEAT_UNTIL);
repeatAfterCompletion = savedInstanceState.getBoolean(EXTRA_REPEAT_AFTER_COMPLETION);
} }
repeatUntilValue = model.getRepeatUntil(); dialogView = inflater.inflate(R.layout.control_set_repeat, null);
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();
value = (Button) dialogView.findViewById(R.id.repeatValue); value = (Button) dialogView.findViewById(R.id.repeatValue);
interval = (Spinner) dialogView.findViewById(R.id.repeatInterval); 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); setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}}); }});
type = (Spinner) dialogView.findViewById(R.id.repeatType); 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); setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}}); }});
daysOfWeekContainer = (LinearLayout) dialogView.findViewById(R.id.repeatDayOfWeekContainer); daysOfWeekContainer = (LinearLayout) dialogView.findViewById(R.id.repeatDayOfWeekContainer);
repeatUntil = (Spinner) dialogView.findViewById(R.id.repeat_until); 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); repeatUntilAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
repeatUntil.setAdapter(repeatUntilAdapter); 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 // set up days of week
DateFormatSymbols dfs = new DateFormatSymbols(); DateFormatSymbols dfs = new DateFormatSymbols();
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); 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++) { 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); int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
checkBox.setPadding(0, 0, 0, 0);
checkBox.setLayoutParams(lp);
checkBox.setTag(Weekday.values()[dayOfWeek - 1]); checkBox.setTag(Weekday.values()[dayOfWeek - 1]);
checkBox.setButtonDrawable(R.drawable.btn_check_small); checkBox.setText(dfs.getShortWeekdays()[dayOfWeek].substring(0, 1));
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));
daysOfWeek[i] = checkBox; daysOfWeek[i] = checkBox;
calendar.add(Calendar.DATE, 1); calendar.add(Calendar.DATE, 1);
daysOfWeekContainer.addView(checkBox);
daysOfWeekContainer.addView(label);
} }
// set up listeners // set up listeners
@ -318,6 +164,7 @@ public class RepeatControlSet extends PopupControlSet {
} }
}); });
setRepeatValue(1);
interval.setOnItemSelectedListener(new OnItemSelectedListener() { interval.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> parentView, View view, int position, long id) { 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() { repeatUntil.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
@ -356,10 +204,147 @@ public class RepeatControlSet extends PopupControlSet {
}); });
daysOfWeekContainer.setVisibility(View.GONE); 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 @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; String result;
if(!doRepeat) { if(!doRepeat) {
result = ""; //$NON-NLS-1$ result = ""; //$NON-NLS-1$
@ -367,60 +352,93 @@ public class RepeatControlSet extends PopupControlSet {
RRule rrule = new RRule(); RRule rrule = new RRule();
rrule.setInterval(repeatValue); rrule.setInterval(repeatValue);
switch(interval.getSelectedItemPosition()) { switch(interval.getSelectedItemPosition()) {
case INTERVAL_DAYS: case INTERVAL_DAYS:
rrule.setFreq(Frequency.DAILY); rrule.setFreq(Frequency.DAILY);
break; break;
case INTERVAL_WEEKS: { case INTERVAL_WEEKS: {
rrule.setFreq(Frequency.WEEKLY); rrule.setFreq(Frequency.WEEKLY);
ArrayList<WeekdayNum> days = new ArrayList<>(); ArrayList<WeekdayNum> days = new ArrayList<>();
for (CompoundButton dayOfWeek : daysOfWeek) { for (CompoundButton dayOfWeek : daysOfWeek) {
if (dayOfWeek.isChecked()) { if (dayOfWeek.isChecked()) {
days.add(new WeekdayNum(0, (Weekday) dayOfWeek.getTag())); days.add(new WeekdayNum(0, (Weekday) dayOfWeek.getTag()));
}
} }
rrule.setByDay(days);
break;
} }
rrule.setByDay(days); case INTERVAL_MONTHS:
break; rrule.setFreq(Frequency.MONTHLY);
} break;
case INTERVAL_MONTHS: case INTERVAL_HOURS:
rrule.setFreq(Frequency.MONTHLY); rrule.setFreq(Frequency.HOURLY);
break; break;
case INTERVAL_HOURS: case INTERVAL_MINUTES:
rrule.setFreq(Frequency.HOURLY); rrule.setFreq(Frequency.MINUTELY);
break; break;
case INTERVAL_MINUTES: case INTERVAL_YEARS:
rrule.setFreq(Frequency.MINUTELY); rrule.setFreq(Frequency.YEARLY);
break; break;
case INTERVAL_YEARS:
rrule.setFreq(Frequency.YEARLY);
break;
} }
result = rrule.toIcal(); result = rrule.toIcal();
} }
if (type.getSelectedItemPosition() == TYPE_COMPLETION_DATE && !TextUtils.isEmpty(result)) { return result;
result = result + ";FROM=COMPLETION"; //$NON-NLS-1$ }
}
task.setRecurrence(result); /** Set up the repeat value button */
task.setRepeatUntil(repeatUntilValue); private void setRepeatValue(int newValue) {
repeatValue = newValue;
value.setText(getString(R.string.repeat_every, newValue));
}
if(task.repeatAfterCompletion()) { private void setRepeatUntilValue(long newValue) {
type.setSelection(1); 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 @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() { protected void refreshDisplayView() {
TextView repeatDisplay = (TextView) getView().findViewById(R.id.display_row_edit);
if (doRepeat) { if (doRepeat) {
repeatDisplay.setText(getRepeatString()); displayView.setText(getRepeatString());
repeatDisplay.setTextColor(themeColor); displayView.setAlpha(1.0f);
clear.setVisibility(View.VISIBLE); clear.setVisibility(View.VISIBLE);
} else { } else {
repeatDisplay.setTextColor(unsetColor); displayView.setAlpha(0.5f);
repeatDisplay.setText(R.string.repeat_never); displayView.setText(R.string.repeat_never);
clear.setVisibility(View.GONE); clear.setVisibility(View.GONE);
} }
} }
@ -428,40 +446,23 @@ public class RepeatControlSet extends PopupControlSet {
private String getRepeatString() { private String getRepeatString() {
int arrayResource = R.array.repeat_interval; int arrayResource = R.array.repeat_interval;
String[] dates = activity.getResources().getStringArray( String[] dates = getResources().getStringArray(
arrayResource); arrayResource);
String date = String.format("%s %s", repeatValue, dates[intervalValue]); //$NON-NLS-1$ String date = String.format("%s %s", repeatValue, dates[intervalValue]); //$NON-NLS-1$
if (repeatUntilValue > 0) { 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 { } 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() { private void updateRepeatUntilOptions() {
repeatUntilOptions.clear(); repeatUntilOptions.clear();
if (repeatUntilValue > 0) { 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(getString(R.string.repeat_forever));
repeatUntilOptions.add(activity.getString(R.string.repeat_until, "").trim()); repeatUntilOptions.add(getString(R.string.repeat_until, "").trim());
repeatUntilAdapter.notifyDataSetChanged(); repeatUntilAdapter.notifyDataSetChanged();
repeatUntil.setSelection(0); repeatUntil.setSelection(0);
} }
@ -473,7 +474,7 @@ public class RepeatControlSet extends PopupControlSet {
displayString.append(DateUtilities.getDateString(d)); displayString.append(DateUtilities.getDateString(d));
if (Task.hasDueTime(repeatUntilValue)) { if (Task.hasDueTime(repeatUntilValue)) {
displayString.append(", "); //$NON-NLS-1$ //$NON-NLS-2$ displayString.append(", "); //$NON-NLS-1$ //$NON-NLS-2$
displayString.append(DateUtilities.getTimeString(activity, repeatUntilValue)); displayString.append(DateUtilities.getTimeString(context, repeatUntilValue));
} }
} }
return displayString.toString(); return displayString.toString();

@ -49,8 +49,6 @@ import timber.log.Timber;
@Singleton @Singleton
public class TaskService { 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$ public static final String TRANS_REPEAT_COMPLETE = "repeat-complete"; //$NON-NLS-1$
private final TagDataDao tagDataDao; private final TagDataDao tagDataDao;

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

@ -5,13 +5,18 @@
*/ */
package com.todoroo.astrid.tags; 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.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
@ -20,12 +25,12 @@ import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import com.google.common.base.Strings;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; 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.Criterion;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities; 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.Metadata;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.ui.PopupControlSet;
import com.todoroo.astrid.utility.Flags; import com.todoroo.astrid.utility.Flags;
import org.tasks.R; import org.tasks.R;
import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.DialogBuilder;
import org.tasks.preferences.ActivityPreferences; import org.tasks.ui.TaskEditControlFragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; 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.newArrayList;
import static com.google.common.collect.Lists.transform;
import static com.google.common.collect.Sets.difference; import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.newHashSet; 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> * @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 LinearLayout newTags;
private ListView selectedTags; private ListView selectedTags;
private boolean populated = false; private View dialogView;
private AlertDialog dialog;
//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 String buildTagString() { private String buildTagString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@ -103,6 +104,86 @@ public final class TagsControlSet extends PopupControlSet {
return builder.toString(); 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) { private void setTagSelected(String tag) {
int index = allTagNames.indexOf(tag); int index = allTagNames.indexOf(tag);
if (index >= 0) { 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<>(); Set<String> tags = new LinkedHashSet<>();
if (initialized) { for(int i = 0; i < selectedTags.getAdapter().getCount(); i++) {
for(int i = 0; i < selectedTags.getAdapter().getCount(); i++) { if (selectedTags.isItemChecked(i)) {
if (selectedTags.isItemChecked(i)) { tags.add(allTagNames.get(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); for (int i = newTags.getChildCount() - 1 ; i >= 0 ; i--) {
final String text = tagName.getText().toString(); TextView tagName = (TextView) newTags.getChildAt(i).findViewById(R.id.text1);
if (Strings.isNullOrEmpty(text)) { final String text = tagName.getText().toString();
continue; 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);
}
} }
} else { TagData tagByName = tagDataDao.getTagByName(text, TagData.PROPERTIES);
if (model.getTransitory(TRANSITORY_TAGS) != null) { if (tagByName != null) {
return (List<String>) model.getTransitory(TRANSITORY_TAGS); 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); return newArrayList(tags);
@ -151,7 +226,7 @@ public final class TagsControlSet extends PopupControlSet {
/** Adds a tag to the tag field */ /** Adds a tag to the tag field */
void addTag(String tagName) { void addTag(String tagName) {
LayoutInflater inflater = activity.getLayoutInflater(); LayoutInflater inflater = getActivity().getLayoutInflater();
// check if already exists // check if already exists
TextView lastText; TextView lastText;
@ -235,142 +310,73 @@ public final class TagsControlSet extends PopupControlSet {
return (TextView) lastItem.findViewById(R.id.text1); 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 @Override
public int getIcon() { public int getIcon() {
return R.drawable.ic_label_24dp; 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() { protected void refreshDisplayView() {
String tagString = buildTagString(); String tagString = buildTagString();
if (!TextUtils.isEmpty(tagString)) { if (!TextUtils.isEmpty(tagString)) {
tagsDisplay.setText(tagString); tagsDisplay.setText(tagString);
tagsDisplay.setTextColor(themeColor); tagsDisplay.setAlpha(1.0f);
} else { } else {
tagsDisplay.setText(R.string.tag_FEx_untagged); tagsDisplay.setText(R.string.tag_FEx_untagged);
tagsDisplay.setTextColor(unsetColor); tagsDisplay.setAlpha(0.5f);
} }
} }
/** /**
* Save the given array of tags into the database * 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( Query query = Query.select(Metadata.PROPERTIES).where(
Criterion.and( Criterion.and(
TaskToTagMetadata.TASK_UUID.eq(taskUuid), TaskToTagMetadata.TASK_UUID.eq(taskUuid),
Metadata.DELETION_DATE.eq(0)) 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 @Override
public String apply(Metadata tagData) { public TagData apply(Metadata metadata) {
return tagData.getValue(TaskToTagMetadata.TAG_UUID); return tagDataDao.getByUuid(metadata.getValue(TaskToTagMetadata.TAG_UUID));
} }
})); }));
List<String> tags = getTagList(); Set<TagData> selectedTags = newHashSet(transform(getTagList(), new Function<String, TagData>() {
// 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>() {
@Override @Override
public TagData apply(String tag) { public TagData apply(String tagName) {
return tagDataDao.getTagByName(tag, TagData.PROPERTIES); 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>() { Sets.SetView<TagData> added = difference(selectedTags, existingTags);
@Override Sets.SetView<TagData> removed = difference(existingTags, selectedTags);
public String apply(TagData tagData) { deleteLinks(taskId, taskUuid, removed);
return tagData.getUUID(); for (TagData tagData : added) {
} Metadata newLink = TaskToTagMetadata.newTagMetadata(taskId, taskUuid, tagData.getName(), tagData.getUuid());
})))); metadataDao.createNew(newLink);
for (TagData tagData : selectedTags) {
if (!existingTagIds.contains(tagData.getUUID())) {
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 * 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(); Metadata deleteTemplate = new Metadata();
deleteTemplate.setTask(taskId); // Need this for recording changes in outstanding table deleteTemplate.setTask(taskId); // Need this for recording changes in outstanding table
deleteTemplate.setDeletionDate(DateUtilities.now()); 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 // 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 // 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), 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 * Copyright (c) 2012 Todoroo Inc
* * <p/>
* See the file "LICENSE" for the full license governing this code. * See the file "LICENSE" for the full license governing this code.
*/ */
package com.todoroo.astrid.timers; package com.todoroo.astrid.timers;
import android.app.Activity; 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.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.Chronometer;
import android.widget.ImageView;
import android.widget.TextView; 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.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase; import com.todoroo.astrid.notes.EditNoteActivity;
import com.todoroo.astrid.timers.TimerActionControlSet.TimerActionListener;
import com.todoroo.astrid.ui.PopupControlSet;
import com.todoroo.astrid.ui.TimeDurationControlSet; import com.todoroo.astrid.ui.TimeDurationControlSet;
import org.tasks.R; import org.tasks.R;
import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.DialogBuilder;
import org.tasks.injection.ForActivity;
import org.tasks.preferences.ActivityPreferences; 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 * Control Set for managing repeats
@ -28,104 +48,162 @@ import org.tasks.preferences.ActivityPreferences;
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
public class TimerControlSet extends PopupControlSet implements TimerActionListener { public class TimerControlSet extends TaskEditControlFragment {
TimeDurationTaskEditControlSet estimated, elapsed; public interface TimerControlSetCallback {
private final TextView displayEdit; Task stopTimer();
private ActivityPreferences preferences; Task startTimer();
}
public TimerControlSet(ActivityPreferences preferences, final Activity activity, DialogBuilder dialogBuilder) { private static final String EXTRA_STARTED = "extra_started";
super(preferences, activity, R.layout.control_set_timers_dialog, R.layout.control_set_timers, R.string.TEA_timer_controls, dialogBuilder); private static final String EXTRA_ESTIMATED = "extra_estimated";
this.preferences = preferences; private static final String EXTRA_ELAPSED = "extra_elapsed";
displayEdit = (TextView) getView().findViewById(R.id.display_row_edit); @Inject ActivityPreferences preferences;
displayEdit.setText(R.string.TEA_timer_controls); @Inject DialogBuilder dialogBuilder;
displayEdit.setTextColor(unsetColor); @Inject @ForActivity Context context;
estimated = new TimeDurationTaskEditControlSet(activity, getDialogView(), Task.ESTIMATED_SECONDS,R.id.estimatedDuration); @Bind(R.id.display_row_edit) TextView displayEdit;
elapsed = new TimeDurationTaskEditControlSet(activity, getDialogView(), Task.ELAPSED_SECONDS, R.id.elapsedDuration); @Bind(R.id.timer) Chronometer chronometer;
} @Bind(R.id.timer_button) ImageView timerButton;
@Override private TimeDurationControlSet estimated;
protected void readFromTaskOnInitialize() { private TimeDurationControlSet elapsed;
estimated.readFromTask(model); private long timerStarted;
elapsed.readFromTask(model); private final List<TimerActionListener> listeners = new LinkedList<>();
} protected AlertDialog dialog;
private View dialogView;
@Override private int elapsedSeconds;
protected void afterInflate() { private int estimatedSeconds;
// Nothing to do here private TimerControlSetCallback callback;
}
@Nullable
@Override @Override
protected void writeToModelAfterInitialized(Task task) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (initialized) { View view = super.onCreateView(inflater, container, savedInstanceState);
estimated.writeToModel(task); if (savedInstanceState != null) {
elapsed.writeToModel(task); 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 @Override
public int getIcon() { public void onAttach(Activity activity) {
return R.drawable.ic_timer_24dp; super.onAttach(activity);
callback = (TimerControlSetCallback) activity;
} }
// --- TimeDurationTaskEditControlSet @Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/** outState.putInt(EXTRA_ELAPSED, elapsed.getTimeDurationInSeconds());
* Control set for mapping a Property to a TimeDurationControlSet outState.putInt(EXTRA_ESTIMATED, estimated.getTimeDurationInSeconds());
* @author Tim Su <tim@todoroo.com> outState.putLong(EXTRA_STARTED, timerStarted);
* }
*/
public class TimeDurationTaskEditControlSet extends TaskEditControlSetBase {
private final TimeDurationControlSet controlSet;
private final IntegerProperty property;
public TimeDurationTaskEditControlSet(Activity activity, View v, IntegerProperty property, int timeButtonId) { @OnClick(R.id.display_row_edit)
super(activity, -1); void openPopup(View view) {
this.property = property; if (dialog == null) {
this.controlSet = new TimeDurationControlSet(activity, v, property, timeButtonId, preferences); buildDialog();
} }
dialog.show();
}
@Override protected Dialog buildDialog() {
public void readFromTaskOnInitialize() { AlertDialog.Builder builder = dialogBuilder.newDialog()
controlSet.setModel(model); .setView(dialogView)
controlSet.setTimeDuration(model.getValue(property)); .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 @OnClick(R.id.timer_container)
protected void afterInflate() { void timerClicked(View view) {
// Nothing 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 @Override
protected void writeToModelAfterInitialized(Task task) { protected int getLayout() {
task.setValue(property, controlSet.getTimeDurationInSeconds()); return R.layout.control_set_timers;
} }
public String getDisplayString() { @Override
int seconds = controlSet.getTimeDurationInSeconds(); public int getIcon() {
if (seconds > 0) { return R.drawable.ic_timer_24dp;
return DateUtils.formatElapsedTime(controlSet.getTimeDurationInSeconds()); }
}
return null;
}
@Override @Override
public int getIcon() { public void initialize(boolean isNewTask, Task task) {
return -1; timerStarted = task.getTimerStart();
} elapsedSeconds = task.getElapsedSeconds();
estimatedSeconds = task.getEstimatedSeconds();
} }
@Override @Override
protected void refreshDisplayView() { public void apply(Task task) {
String est = estimated.getDisplayString(); task.setElapsedSeconds(elapsed.getTimeDurationInSeconds());
if (!TextUtils.isEmpty(est)) { task.setEstimatedSeconds(estimated.getTimeDurationInSeconds());
est = activity.getString(R.string.TEA_timer_est, est); }
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(); String elap = null;
if (!TextUtils.isEmpty(elap)) { int elapsedSeconds = elapsed.getTimeDurationInSeconds();
elap = activity.getString(R.string.TEA_timer_elap, elap); if (elapsedSeconds > 0) {
elap = getString(R.string.TEA_timer_elap, DateUtils.formatElapsedTime(elapsedSeconds));
} }
String toDisplay; String toDisplay;
@ -142,20 +220,51 @@ public class TimerControlSet extends PopupControlSet implements TimerActionListe
if (!TextUtils.isEmpty(toDisplay)) { if (!TextUtils.isEmpty(toDisplay)) {
displayEdit.setText(toDisplay); displayEdit.setText(toDisplay);
displayEdit.setTextColor(themeColor); displayEdit.setAlpha(1.0f);
} else { } else {
displayEdit.setText(R.string.TEA_timer_controls); displayEdit.setText(R.string.TEA_timer_controls);
displayEdit.setTextColor(unsetColor); displayEdit.setAlpha(0.5f);
} }
} }
@Override private void updateChronometer() {
public void timerStopped(Task task) { timerButton.setImageResource(timerActive()
elapsed.readFromTask(task); ? 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 addListener(TimerActionListener listener) {
public void timerStarted(Task task) { 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) { if(task.getTimerStart() > 0) {
int newElapsed = (int)((DateUtilities.now() - task.getTimerStart()) / 1000L); int newElapsed = (int)((DateUtilities.now() - task.getTimerStart()) / 1000L);
task.setTimerStart(0L); task.setTimerStart(0L);
task.setELAPSED_SECONDS( task.setElapsedSeconds(task.getElapsedSeconds() + newElapsed);
task.getElapsedSeconds() + newElapsed);
} }
} }
taskService.save(task); 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.app.Activity;
import android.graphics.Paint; import android.graphics.Paint;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.View.OnKeyListener; import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.data.Task; 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 com.todoroo.astrid.service.TaskService;
import org.tasks.R;
import org.tasks.ui.CheckBoxes; 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 * Control set for mapping a Property to an EditText
* @author Tim Su <tim@todoroo.com> * @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; @Inject TaskService taskService;
private CheckableImageView completeBox;
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 boolean isRepeating;
private int importanceValue; private int importanceValue;
private Task model; private boolean isNewTask;
private final TaskService taskService; private String title;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
public EditTitleControlSet(TaskService taskService, final Activity activity, final EditText editText, CheckableImageView completeBox) { checkBoxes = new CheckBoxes(activity);
this.checkBoxes = new CheckBoxes(activity); }
this.editText = editText;
this.completeBox = completeBox;
this.taskService = taskService;
@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.setHorizontallyScrolling(false);
editText.setMaxLines(Integer.MAX_VALUE); editText.setMaxLines(Integer.MAX_VALUE);
editText.setOnKeyListener(new OnKeyListener() { editText.setOnKeyListener(new OnKeyListener() {
@Override @Override
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER) { if (keyCode == KeyEvent.KEYCODE_ENTER) {
AndroidUtilities.hideSoftInputForViews(activity, editText); AndroidUtilities.hideSoftInputForViews(getActivity(), editText);
return true; return true;
} }
return false; return false;
@ -74,39 +106,46 @@ public class EditTitleControlSet implements TaskEditControlSet, ImportanceChange
return false; return false;
} }
}); });
updateCompleteBox();
return view;
} }
protected void readFromTaskOnInitialize() { @Override
editText.setTextKeepState(model.getTitle()); public void onSaveInstanceState(Bundle outState) {
completeBox.setChecked(model.isCompleted()); super.onSaveInstanceState(outState);
completeBox.setOnClickListener(new OnClickListener() {
@Override outState.putBoolean(EXTRA_COMPLETE, completeBox.isChecked());
public void onClick(View v) { outState.putString(EXTRA_TITLE, getTitle());
// set check box to actual action item state outState.putBoolean(EXTRA_REPEATING, isRepeating);
updateCompleteBox(); outState.putInt(EXTRA_PRIORITY, importanceValue);
}
});
} }
@Override @OnClick(R.id.completeBox)
public void importanceChanged(int i) { void toggleComplete(View view) {
importanceValue = i;
updateCompleteBox(); updateCompleteBox();
} }
@Override @Override
public void repeatChanged(boolean repeat) { public void onStart() {
isRepeating = repeat; 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(); updateCompleteBox();
} }
@Override public void repeatChanged(boolean repeat) {
public void readFromTask(Task task) { isRepeating = repeat;
this.model = task; updateCompleteBox();
readFromTaskOnInitialize();
isRepeating = !TextUtils.isEmpty(task.getRecurrence());
importanceValue = model.getImportance();
} }
private void updateCompleteBox() { private void updateCompleteBox() {
@ -128,12 +167,8 @@ public class EditTitleControlSet implements TaskEditControlSet, ImportanceChange
} }
@Override @Override
public void writeToModel(Task task) { protected int getLayout() {
task.setTitle(editText.getText().toString()); return R.layout.control_set_title;
boolean newState = completeBox.isChecked();
if (newState != task.isCompleted()) {
taskService.setComplete(task, newState);
}
} }
@Override @Override
@ -142,7 +177,30 @@ public class EditTitleControlSet implements TaskEditControlSet, ImportanceChange
} }
@Override @Override
public View getView() { public void initialize(boolean isNewTask, Task task) {
throw new RuntimeException(); 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; package com.todoroo.astrid.ui;
import android.app.Activity;
import android.content.Context;
import android.content.Intent; 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;
import android.view.View.OnClickListener; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener; import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
@ -16,18 +21,24 @@ import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R; import org.tasks.R;
import org.tasks.activities.DateAndTimePickerActivity; import org.tasks.activities.DateAndTimePickerActivity;
import org.tasks.activities.TimePickerActivity;
import org.tasks.injection.ForActivity;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
import org.tasks.ui.HiddenTopArrayAdapter; import org.tasks.ui.HiddenTopArrayAdapter;
import org.tasks.ui.TaskEditControlFragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.OnClick;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.tasks.date.DateTimeUtils.newDateTime; import static org.tasks.date.DateTimeUtils.newDateTime;
@ -37,29 +48,145 @@ import static org.tasks.date.DateTimeUtils.newDateTime;
* @author Tim Su <tim@todoroo.com> * @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 SPECIFIC_DATE = -1;
private static final int EXISTING_TIME_UNSET = -2; 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 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 previousSetting = Task.HIDE_UNTIL_NONE;
private int selection; private int selection;
private long existingDate = EXISTING_TIME_UNSET; private long existingDate = EXISTING_TIME_UNSET;
private TaskEditFragment taskEditFragment;
private TextView textDisplay;
private ImageView clearButton;
private final List<HideUntilValue> spinnerItems = new ArrayList<>(); private final List<HideUntilValue> spinnerItems = new ArrayList<>();
public HideUntilControlSet(TaskEditFragment taskEditFragment) { @OnClick(R.id.clear)
super(taskEditFragment.getActivity(), R.layout.control_set_hide); void clearHideUntil(View view) {
this.taskEditFragment = taskEditFragment; 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 * Container class for urgencies
@ -91,7 +218,7 @@ public class HideUntilControlSet extends TaskEditControlSetBase implements OnIte
private void updateSpinnerOptions(long specificDate) { private void updateSpinnerOptions(long specificDate) {
spinnerItems.clear(); spinnerItems.clear();
// set up base values // 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( spinnerItems.addAll(new ArrayList<>(asList(
new HideUntilValue(labels[0], Task.HIDE_UNTIL_DUE), new HideUntilValue(labels[0], Task.HIDE_UNTIL_DUE),
new HideUntilValue(labels[1], Task.HIDE_UNTIL_DUE_TIME), 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)))); new HideUntilValue(labels[4], Task.HIDE_UNTIL_SPECIFIC_DAY, -1))));
if(specificDate > 0) { if(specificDate > 0) {
DateTime hideUntilAsDate = newDateTime(specificDate); spinnerItems.add(0, getHideUntilValue(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));
}
existingDate = specificDate; existingDate = specificDate;
} else { } 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; existingDate = EXISTING_TIME_UNSET;
} }
adapter.notifyDataSetChanged(); 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 // --- listening for events
@Override @Override
@ -124,11 +255,12 @@ public class HideUntilControlSet extends TaskEditControlSetBase implements OnIte
// ... at conclusion of dialog, update our list // ... at conclusion of dialog, update our list
HideUntilValue item = adapter.getItem(position); HideUntilValue item = adapter.getItem(position);
if(item.date == SPECIFIC_DATE) { if(item.date == SPECIFIC_DATE) {
customDate = final DateTime customDate =
newDateTime(existingDate == EXISTING_TIME_UNSET ? DateUtilities.now() : existingDate) newDateTime(existingDate == EXISTING_TIME_UNSET ? DateUtilities.now() : existingDate)
.withSecondOfMinute(0); .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()); putExtra(DateAndTimePickerActivity.EXTRA_TIMESTAMP, customDate.getMillis());
}}, REQUEST_HIDE_UNTIL); }}, REQUEST_HIDE_UNTIL);
spinner.setSelection(previousSetting); spinner.setSelection(previousSetting);
@ -140,8 +272,9 @@ public class HideUntilControlSet extends TaskEditControlSetBase implements OnIte
} }
public void setCustomDate(long timestamp) { public void setCustomDate(long timestamp) {
customDate = new DateTime(timestamp); updateSpinnerOptions(timestamp);
customDateFinished(); spinner.setSelection(0);
refreshDisplayView();
} }
@Override @Override
@ -149,121 +282,16 @@ public class HideUntilControlSet extends TaskEditControlSetBase implements OnIte
// ignore // ignore
} }
DateTime customDate;
private void customDateFinished() {
updateSpinnerOptions(customDate.getMillis());
spinner.setSelection(0);
refreshDisplayView();
}
// --- setting up values // --- setting up values
private void refreshDisplayView() { private void refreshDisplayView() {
HideUntilValue value = adapter.getItem(selection); HideUntilValue value = adapter.getItem(selection);
if (value.setting == Task.HIDE_UNTIL_NONE) { if (value.setting == Task.HIDE_UNTIL_NONE) {
textDisplay.setText(R.string.TEA_hideUntil_label); spinner.setAlpha(0.5f);
textDisplay.setTextColor(unsetColor);
clearButton.setVisibility(View.GONE); clearButton.setVisibility(View.GONE);
} else { } else {
String display = value.toString(); spinner.setAlpha(1.0f);
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);
clearButton.setVisibility(View.VISIBLE); 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; package com.todoroo.astrid.ui;
import android.app.Activity; import android.content.Context;
import android.view.View; import android.view.View;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Spinner; import android.widget.Spinner;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import org.tasks.R; import org.tasks.R;
@ -27,35 +26,26 @@ public class RandomReminderControlSet {
private final int[] hours; 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 = (Spinner) parentView.findViewById(R.id.reminder_random_interval);
periodSpinner.setVisibility(View.VISIBLE); periodSpinner.setVisibility(View.VISIBLE);
// create adapter // create adapter
ArrayAdapter<String> adapter = new ArrayAdapter<>( ArrayAdapter<String> adapter = new ArrayAdapter<>(
activity, android.R.layout.simple_spinner_item, context, android.R.layout.simple_spinner_item,
activity.getResources().getStringArray(R.array.TEA_reminder_random)); context.getResources().getStringArray(R.array.TEA_reminder_random));
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
periodSpinner.setAdapter(adapter); periodSpinner.setAdapter(adapter);
// create hour array // 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]; hours = new int[hourStrings.length];
for(int i = 0; i < hours.length; i++) { for(int i = 0; i < hours.length; i++) {
hours[i] = Integer.parseInt(hourStrings[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; int i;
for(i = 0; i < hours.length - 1; i++) { for(i = 0; i < hours.length - 1; i++) {
if (hours[i] * DateUtilities.ONE_HOUR >= time) { if (hours[i] * DateUtilities.ONE_HOUR >= reminderPeriod) {
break; break;
} }
} }

@ -5,41 +5,55 @@
*/ */
package com.todoroo.astrid.ui; package com.todoroo.astrid.ui;
import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.AdapterView; import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import com.google.common.primitives.Longs;
import com.todoroo.andlib.data.Callback; import com.todoroo.andlib.data.Callback;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.alarms.AlarmFields; import com.todoroo.astrid.alarms.AlarmFields;
import com.todoroo.astrid.alarms.AlarmService; import com.todoroo.astrid.alarms.AlarmService;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R; import org.tasks.R;
import org.tasks.activities.DateAndTimePickerActivity; import org.tasks.activities.DateAndTimePickerActivity;
import org.tasks.activities.TimePickerActivity;
import org.tasks.injection.ForActivity;
import org.tasks.location.Geofence; import org.tasks.location.Geofence;
import org.tasks.location.GeofenceService; import org.tasks.location.GeofenceService;
import org.tasks.location.PlacePicker; import org.tasks.location.PlacePicker;
import org.tasks.preferences.Device; import org.tasks.preferences.Device;
import org.tasks.preferences.PermissionRequestor; import org.tasks.preferences.PermissionRequestor;
import org.tasks.preferences.Preferences;
import org.tasks.ui.HiddenTopArrayAdapter; import org.tasks.ui.HiddenTopArrayAdapter;
import org.tasks.ui.TaskEditControlFragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; 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 com.todoroo.andlib.utility.DateUtilities.getLongDateStringWithTime;
import static org.tasks.date.DateTimeUtils.newDateTime; import static org.tasks.date.DateTimeUtils.newDateTime;
@ -49,40 +63,227 @@ import static org.tasks.date.DateTimeUtils.newDateTime;
* @author Tim Su <tim@todoroo.com> * @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; @Bind(R.id.alert_container) LinearLayout alertContainer;
public static final int REQUEST_LOCATION_REMINDER = 12153; @Bind(R.id.reminder_alarm) Spinner mode;
@Bind(R.id.alarms_add_spinner) Spinner addSpinner;
private Spinner mode; private long taskId;
private Spinner addSpinner; private int flags;
private TextView modeDisplay; private long randomReminder;
private RandomReminderControlSet randomControlSet; private RandomReminderControlSet randomControlSet;
private LinearLayout alertContainer;
private boolean whenDue; private boolean whenDue;
private boolean whenOverdue; 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 List<String> spinnerOptions = new ArrayList<>();
private ArrayAdapter<String> remindAdapter; 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, public void addAlarmRow(final Long timestamp) {
TaskEditFragment taskEditFragment, PermissionRequestor permissionRequestor, addAlarmRow(getLongDateStringWithTime(context, timestamp), timestamp, null);
Device device) { }
super(taskEditFragment.getActivity(), R.layout.control_set_reminders);
this.alarmService = alarmService; public void pickLocation() {
this.geofenceService = geofenceService; Intent intent = PlacePicker.getIntent(getActivity());
this.taskEditFragment = taskEditFragment; if (intent != null) {
this.permissionRequestor = permissionRequestor; startActivityForResult(intent, REQUEST_LOCATION_REMINDER);
this.device = device; }
}
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; int value = 0;
if(whenDue) { if(whenDue) {
value |= Task.NOTIFY_AT_DEADLINE; value |= Task.NOTIFY_AT_DEADLINE;
@ -101,40 +302,18 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
return value; return value;
} }
private long getRandomReminderPeriod() {
return randomControlSet == null ? 0L : randomControlSet.getReminderPeriod();
}
private void addNewAlarm() { private void addNewAlarm() {
taskEditFragment.startActivityForResult(new Intent(taskEditFragment.getActivity(), DateAndTimePickerActivity.class) {{ startActivityForResult(new Intent(getActivity(), DateAndTimePickerActivity.class) {{
putExtra(DateAndTimePickerActivity.EXTRA_TIMESTAMP, newDateTime().startOfDay().getMillis()); putExtra(DateAndTimePickerActivity.EXTRA_TIMESTAMP, newDateTime().startOfDay().getMillis());
}}, REQUEST_NEW_ALARM); }}, 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) { 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); alertContainer.addView(alertItem);
addAlarmRow(alertItem, text, timestamp, onRemove); addAlarmRow(alertItem, text, timestamp, onRemove);
return alertItem; return alertItem;
@ -162,106 +341,24 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
spinnerOptions.clear(); spinnerOptions.clear();
spinnerOptions.add(""); spinnerOptions.add("");
if (!whenDue) { if (!whenDue) {
spinnerOptions.add(taskEditFragment.getString(R.string.when_due)); spinnerOptions.add(getString(R.string.when_due));
} }
if (!whenOverdue) { if (!whenOverdue) {
spinnerOptions.add(taskEditFragment.getString(R.string.when_overdue)); spinnerOptions.add(getString(R.string.when_overdue));
} }
if (randomControlSet == null) { if (randomControlSet == null) {
spinnerOptions.add(taskEditFragment.getString(R.string.randomly)); spinnerOptions.add(getString(R.string.randomly));
} }
if (device.supportsLocationServices()) { 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(); 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() { private void addDue() {
whenDue = true; whenDue = true;
addAlarmRow(taskEditFragment.getString(R.string.when_due), null, new OnClickListener() { addAlarmRow(getString(R.string.when_due), null, new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
whenDue = false; whenDue = false;
@ -271,7 +368,7 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
private void addOverdue() { private void addOverdue() {
whenOverdue = true; whenOverdue = true;
addAlarmRow(taskEditFragment.getString(R.string.when_overdue), null, new OnClickListener() { addAlarmRow(getString(R.string.when_overdue), null, new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
whenOverdue = false; whenOverdue = false;
@ -279,15 +376,14 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
}); });
} }
private void addRandomReminder() { private void addRandomReminder(long reminderPeriod) {
View alarmRow = addAlarmRow(taskEditFragment.getString(R.string.randomly_once), null, new OnClickListener() { View alarmRow = addAlarmRow(getString(R.string.randomly_once), null, new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
randomControlSet = null; randomControlSet = null;
} }
}); });
randomControlSet = new RandomReminderControlSet(activity, alarmRow); randomControlSet = new RandomReminderControlSet(context, alarmRow, reminderPeriod);
randomControlSet.readFromTaskOnInitialize(model);
} }
private void setValue(int flags) { private void setValue(int flags) {
@ -302,72 +398,4 @@ public class ReminderControlSet extends TaskEditControlSetBase implements Adapte
mode.setSelection(0); 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; package com.todoroo.astrid.ui;
import android.app.Activity; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.View; import android.view.View;
@ -20,29 +20,22 @@ import org.tasks.preferences.ActivityPreferences;
public class TimeDurationControlSet implements OnNNumberPickedListener, View.OnClickListener { public class TimeDurationControlSet implements OnNNumberPickedListener, View.OnClickListener {
private final Activity activity; private final Context context;
private final TextView timeButton; private final TextView timeButton;
private int timeDuration; private int timeDuration;
private int[] initialValues = null; private int[] initialValues = null;
private NNumberPickerDialog dialog = null; private NNumberPickerDialog dialog = null;
private Task model;
private final IntegerProperty property;
private ActivityPreferences activityPreferences; private ActivityPreferences activityPreferences;
public TimeDurationControlSet(Activity activity, View view, IntegerProperty property, public TimeDurationControlSet(Context context, View view,
int timeButtonId, ActivityPreferences activityPreferences) { int timeButtonId, ActivityPreferences activityPreferences) {
this.activity = activity; this.context = context;
this.property = property;
this.activityPreferences = activityPreferences; this.activityPreferences = activityPreferences;
timeButton = (TextView)view.findViewById(timeButtonId); timeButton = (TextView)view.findViewById(timeButtonId);
((View) timeButton.getParent()).setOnClickListener(this); ((View) timeButton.getParent()).setOnClickListener(this);
} }
public void setModel(Task model) {
this.model = model;
}
public int getTimeDurationInSeconds() { public int getTimeDurationInSeconds() {
return timeDuration; return timeDuration;
} }
@ -54,7 +47,7 @@ public class TimeDurationControlSet implements OnNNumberPickedListener, View.OnC
timeDuration = timeDurationInSeconds; timeDuration = timeDurationInSeconds;
Resources r = activity.getResources(); Resources r = context.getResources();
if(timeDurationInSeconds == 0) { if(timeDurationInSeconds == 0) {
timeButton.setText(r.getString(R.string.WID_dateButtonUnset)); timeButton.setText(r.getString(R.string.WID_dateButtonUnset));
return; return;
@ -65,10 +58,6 @@ public class TimeDurationControlSet implements OnNNumberPickedListener, View.OnC
int hours = timeDuration / 3600; int hours = timeDuration / 3600;
int minutes = timeDuration / 60 - 60 * hours; int minutes = timeDuration / 60 - 60 * hours;
initialValues = new int[] { hours, minutes }; initialValues = new int[] { hours, minutes };
if (model != null) {
model.setValue(property, timeDuration);
}
} }
/** Called when NumberPicker activity is completed */ /** Called when NumberPicker activity is completed */
@ -81,8 +70,8 @@ public class TimeDurationControlSet implements OnNNumberPickedListener, View.OnC
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if(dialog == null) { if(dialog == null) {
dialog = new NNumberPickerDialog(activity, activityPreferences.getDialogTheme(), this, dialog = new NNumberPickerDialog(context, activityPreferences.getDialogTheme(), this,
activity.getResources().getString(R.string.DLG_hour_minutes), context.getResources().getString(R.string.DLG_hour_minutes),
new int[] {0, 0}, new int[] {1, 5}, new int[] {0, 0}, new int[] {0, 0}, new int[] {1, 5}, new int[] {0, 0},
new int[] {999, 59}, new String[] {":", null}); new int[] {999, 59}, new String[] {":", null});
final NumberPicker hourPicker = dialog.getPicker(0); final NumberPicker hourPicker = dialog.getPicker(0);

@ -2,11 +2,16 @@ package org.tasks.activities;
import android.app.FragmentManager; import android.app.FragmentManager;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import com.todoroo.astrid.gcal.AndroidCalendar; import com.todoroo.astrid.gcal.AndroidCalendar;
import org.tasks.injection.InjectingAppCompatActivity; 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 { 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_ID = "extra_calendar_id";
public static final String EXTRA_CALENDAR_NAME = "extra_calendar_name"; 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 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
FragmentManager fragmentManager = getFragmentManager(); preferences.applyTheme();
CalendarSelectionDialog fragmentByTag = (CalendarSelectionDialog) fragmentManager.findFragmentByTag(FRAG_TAG_CALENDAR_PREFERENCE_SELECTION);
if (fragmentByTag == null) { if (permissionRequestor.requestCalendarPermissions()) {
fragmentByTag = new CalendarSelectionDialog(); showDialog();
fragmentByTag.enableNone();
fragmentByTag.show(fragmentManager, FRAG_TAG_CALENDAR_PREFERENCE_SELECTION);
} }
fragmentByTag.setCalendarSelectionHandler(this);
} }
@Override @Override
@ -41,4 +47,30 @@ public class CalendarSelectionActivity extends InjectingAppCompatActivity implem
public void dismiss() { public void dismiss() {
finish(); 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.ClearAllDataActivity;
import org.tasks.activities.ClearGtaskDataActivity; import org.tasks.activities.ClearGtaskDataActivity;
import org.tasks.activities.DateAndTimePickerActivity; import org.tasks.activities.DateAndTimePickerActivity;
import org.tasks.activities.DatePickerActivity;
import org.tasks.activities.DeleteAllCalendarEventsActivity; import org.tasks.activities.DeleteAllCalendarEventsActivity;
import org.tasks.activities.DeleteCompletedActivity; import org.tasks.activities.DeleteCompletedActivity;
import org.tasks.activities.DeleteCompletedEventsActivity; import org.tasks.activities.DeleteCompletedEventsActivity;
@ -103,6 +104,7 @@ import dagger.Provides;
AddAttachmentActivity.class, AddAttachmentActivity.class,
ShortcutActivity.class, ShortcutActivity.class,
CameraActivity.class, CameraActivity.class,
DatePickerActivity.class,
TimePickerActivity.class, TimePickerActivity.class,
DateAndTimePickerActivity.class DateAndTimePickerActivity.class
}) })

@ -7,12 +7,23 @@ import android.content.Context;
import com.todoroo.astrid.actfm.TagViewFragment; import com.todoroo.astrid.actfm.TagViewFragment;
import com.todoroo.astrid.activity.TaskEditFragment; import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.files.FilesControlSet;
import com.todoroo.astrid.gtasks.GtasksListFragment; import com.todoroo.astrid.gtasks.GtasksListFragment;
import com.todoroo.astrid.repeats.RepeatControlSet;
import com.todoroo.astrid.subtasks.SubtasksListFragment; import com.todoroo.astrid.subtasks.SubtasksListFragment;
import com.todoroo.astrid.subtasks.SubtasksTagListFragment; 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.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.NavigationDrawerFragment;
import org.tasks.ui.PriorityControlSet;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -28,7 +39,18 @@ import dagger.Provides;
TagViewFragment.class, TagViewFragment.class,
TaskEditFragment.class, TaskEditFragment.class,
NavigationDrawerFragment.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 { public class FragmentModule {

@ -1,10 +1,13 @@
package org.tasks.location; package org.tasks.location;
import android.os.Parcel;
import android.os.Parcelable;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Metadata;
import java.io.Serializable; import java.io.Serializable;
public class Geofence implements Serializable { public class Geofence implements Serializable, Parcelable {
private final String name; private final String name;
private final double latitude; private final double latitude;
private final double longitude; private final double longitude;
@ -63,4 +66,31 @@ public class Geofence implements Serializable {
", metadataId=" + metadataId + ", 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; package org.tasks.preferences;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.util.TypedValue; import android.util.TypedValue;
import javax.inject.Inject; import javax.inject.Inject;
@ -21,19 +22,9 @@ public class ResourceResolver {
return getData(activity, attr); return getData(activity, attr);
} }
public int getResource(int attr) { public static int getData(Context context, 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) {
TypedValue typedValue = new TypedValue(); TypedValue typedValue = new TypedValue();
activity.getTheme().resolveAttribute(attr, typedValue, true); context.getTheme().resolveAttribute(attr, typedValue, true);
return typedValue.data; 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.graphics.drawable.Drawable;
import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v4.graphics.drawable.DrawableCompat;
import com.google.common.collect.ImmutableList;
import org.tasks.R; import org.tasks.R;
import org.tasks.injection.ForApplication; import org.tasks.injection.ForApplication;
@ -14,8 +16,6 @@ import javax.inject.Inject;
import timber.log.Timber; import timber.log.Timber;
import static java.util.Arrays.asList;
public class CheckBoxes { public class CheckBoxes {
private static final int MAX_IMPORTANCE_INDEX = 3; private static final int MAX_IMPORTANCE_INDEX = 3;
@ -24,6 +24,7 @@ public class CheckBoxes {
private static List<Drawable> checkboxes; private static List<Drawable> checkboxes;
private static List<Drawable> repeatingCheckboxes; private static List<Drawable> repeatingCheckboxes;
private static List<Drawable> completedCheckboxes; private static List<Drawable> completedCheckboxes;
private static List<Integer> priorityColors;
@Inject @Inject
public CheckBoxes(@ForApplication Context context) { public CheckBoxes(@ForApplication Context context) {
@ -32,10 +33,19 @@ public class CheckBoxes {
checkboxes = wrapDrawable(context, R.drawable.ic_check_box_outline_blank_24dp); checkboxes = wrapDrawable(context, R.drawable.ic_check_box_outline_blank_24dp);
repeatingCheckboxes = wrapDrawable(context, R.drawable.ic_repeat_24dp); repeatingCheckboxes = wrapDrawable(context, R.drawable.ic_repeat_24dp);
completedCheckboxes = wrapDrawable(context, R.drawable.ic_check_box_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; initialized = true;
} }
} }
public List<Integer> getPriorityColors() {
return priorityColors;
}
List<Drawable> getCheckBoxes() { List<Drawable> getCheckBoxes() {
return checkboxes; return checkboxes;
} }
@ -49,7 +59,7 @@ public class CheckBoxes {
} }
private static List<Drawable> wrapDrawable(Context context, int resId) { private static List<Drawable> wrapDrawable(Context context, int resId) {
return asList( return ImmutableList.of(
getDrawable(context, resId, 0), getDrawable(context, resId, 0),
getDrawable(context, resId, 1), getDrawable(context, resId, 1),
getDrawable(context, resId, 2), getDrawable(context, resId, 2),

@ -1,10 +1,12 @@
package org.tasks.ui; package org.tasks.ui;
import android.app.Activity; import android.app.Activity;
import android.content.DialogInterface; import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v4.graphics.drawable.DrawableCompat;
import android.text.format.DateFormat;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -15,58 +17,65 @@ import android.widget.TextView;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task; 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.R;
import org.tasks.dialogs.MyDatePickerDialog; import org.tasks.activities.DatePickerActivity;
import org.tasks.dialogs.MyTimePickerDialog; import org.tasks.activities.TimePickerActivity;
import org.tasks.injection.ForActivity;
import org.tasks.preferences.ActivityPreferences; import org.tasks.preferences.ActivityPreferences;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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 java.util.Arrays.asList;
import static org.tasks.date.DateTimeUtils.newDateTime; 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 int REQUEST_DATE = 504;
private static final String FRAG_TAG_PICK_A_TIME = "frag_tag_pick_a_time"; 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> dueDateOptions = new ArrayList<>();
private final List<String> dueTimeOptions = new ArrayList<>(); private final List<String> dueTimeOptions = new ArrayList<>();
private final List<String> dueTimeHint; private List<String> dueTimeHint = new ArrayList<>();
private final int dateShortcutMorning; private int dateShortcutMorning;
private final int dateShortcutAfternoon; private int dateShortcutAfternoon;
private final int dateShortcutEvening; private int dateShortcutEvening;
private final int dateShortcutNight; private int dateShortcutNight;
private final String nightString; private String nightString;
private final String eveningString; private String eveningString;
private final String afternoonString; private String afternoonString;
private final String morningString; private String morningString;
private final String noTimeString; private String noTimeString;
private final String todayString; private String todayString;
private final String tomorrowString; private String tomorrowString;
private Activity activity; @Inject ActivityPreferences preferences;
private ActivityPreferences preferences; @Inject @ForActivity Context context;
private Spinner dueDateSpinner;
private Spinner dueTimeSpinner; @Bind(R.id.due_date) Spinner dueDateSpinner;
private View clearButton; @Bind(R.id.due_time) Spinner dueTimeSpinner;
@Bind(R.id.clear) View clearButton;
private ArrayAdapter<String> dueDateAdapter; private ArrayAdapter<String> dueDateAdapter;
private ArrayAdapter<String> dueTimeAdapter; private ArrayAdapter<String> dueTimeAdapter;
private long date = 0; private long date = 0;
private int time = -1; private int time = -1;
public DeadlineControlSet(Activity activity, ActivityPreferences preferences) { @Override
super(activity, R.layout.control_set_deadline); public void onAttach(Activity activity) {
this.activity = activity; super.onAttach(activity);
this.preferences = preferences;
dateShortcutMorning = preferences.getDateShortcutMorning(); dateShortcutMorning = preferences.getDateShortcutMorning();
dateShortcutAfternoon = preferences.getDateShortcutAfternoon(); dateShortcutAfternoon = preferences.getDateShortcutAfternoon();
dateShortcutEvening = preferences.getDateShortcutEvening(); dateShortcutEvening = preferences.getDateShortcutEvening();
@ -101,95 +110,35 @@ public class DeadlineControlSet extends TaskEditControlSetBase {
activity.getString(R.string.pick_a_time))); activity.getString(R.string.pick_a_time)));
} }
private String getTimeHint(int millisOfDay) { @Nullable
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);
}
@Override @Override
protected void afterInflate() { public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = getView(); View view = super.onCreateView(inflater, container, savedInstanceState);
clearButton = view.findViewById(R.id.clear); if (savedInstanceState != null) {
clearButton.setOnClickListener(new View.OnClickListener() { date = savedInstanceState.getLong(EXTRA_DATE);
@Override time = savedInstanceState.getInt(EXTRA_TIME);
public void onClick(View v) { }
date = 0; final int themeColor = getData(context, R.attr.asTextColor);
time = -1; final int overdueColor = context.getResources().getColor(R.color.overdue);
refreshDisplayView(); dueDateAdapter = new HiddenTopArrayAdapter<String>(context, android.R.layout.simple_spinner_item, dueDateOptions) {
}
});
dueDateSpinner = (Spinner) view.findViewById(R.id.due_date);
dueDateAdapter = new HiddenTopArrayAdapter(activity, android.R.layout.simple_spinner_item, dueDateOptions) {
@Override @Override
public View getView(final int position, final View convertView, final ViewGroup parent) { public View getView(final int position, final View convertView, final ViewGroup parent) {
int selectedItemPosition = position; int selectedItemPosition = position;
if (parent instanceof AdapterView) { if (parent instanceof AdapterView) {
selectedItemPosition = ((AdapterView) parent).getSelectedItemPosition(); 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)); tv.setText(dueDateOptions.get(selectedItemPosition));
if (date == 0) { if (date == 0) {
dueDateSpinner.setAlpha(0.5f); dueDateSpinner.setAlpha(0.5f);
dueDateSpinner.setBackgroundDrawable(getThemedUnderline()); dueDateSpinner.setBackgroundDrawable(getUnderline(themeColor));
} else { } else {
dueDateSpinner.setAlpha(1.0f); dueDateSpinner.setAlpha(1.0f);
if (date < newDateTime().startOfDay().getMillis()) { if (date < newDateTime().startOfDay().getMillis()) {
dueDateSpinner.setBackgroundDrawable(getRedUnderline()); dueDateSpinner.setBackgroundDrawable(getUnderline(overdueColor));
tv.setTextColor(activity.getResources().getColor(R.color.overdue)); tv.setTextColor(overdueColor);
} else { } else {
dueDateSpinner.setBackgroundDrawable(getThemedUnderline()); dueDateSpinner.setBackgroundDrawable(getUnderline(themeColor));
tv.setTextColor(themeColor); tv.setTextColor(themeColor);
} }
} }
@ -198,26 +147,25 @@ public class DeadlineControlSet extends TaskEditControlSetBase {
}; };
dueDateSpinner.setAdapter(dueDateAdapter); dueDateSpinner.setAdapter(dueDateAdapter);
dueTimeSpinner = (Spinner) view.findViewById(R.id.due_time); dueTimeAdapter = new HiddenTopArrayAdapter<String>(context, android.R.layout.simple_spinner_item, dueTimeOptions, dueTimeHint) {
dueTimeAdapter = new HiddenTopArrayAdapter(activity, android.R.layout.simple_spinner_item, dueTimeOptions, dueTimeHint) {
@Override @Override
public View getView(final int position, final View convertView, final ViewGroup parent) { public View getView(final int position, final View convertView, final ViewGroup parent) {
int selectedItemPosition = position; int selectedItemPosition = position;
if (parent instanceof AdapterView) { if (parent instanceof AdapterView) {
selectedItemPosition = ((AdapterView) parent).getSelectedItemPosition(); 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)); tv.setText(dueTimeOptions.get(selectedItemPosition));
if (time == -1) { if (time == -1) {
dueTimeSpinner.setAlpha(0.5f); dueTimeSpinner.setAlpha(0.5f);
dueTimeSpinner.setBackgroundDrawable(getThemedUnderline()); dueTimeSpinner.setBackgroundDrawable(getUnderline(themeColor));
} else { } else {
dueTimeSpinner.setAlpha(1.0f); dueTimeSpinner.setAlpha(1.0f);
if (newDateTime(date).withMillisOfDay(time).isBeforeNow()) { if (newDateTime(date).withMillisOfDay(time).isBeforeNow()) {
dueTimeSpinner.setBackgroundDrawable(getRedUnderline()); dueTimeSpinner.setBackgroundDrawable(getUnderline(overdueColor));
tv.setTextColor(activity.getResources().getColor(R.color.overdue)); tv.setTextColor(overdueColor);
} else { } else {
dueTimeSpinner.setBackgroundDrawable(getThemedUnderline()); dueTimeSpinner.setBackgroundDrawable(getUnderline(themeColor));
tv.setTextColor(themeColor); tv.setTextColor(themeColor);
} }
} }
@ -226,119 +174,197 @@ public class DeadlineControlSet extends TaskEditControlSetBase {
}; };
dueTimeSpinner.setAdapter(dueTimeAdapter); dueTimeSpinner.setAdapter(dueTimeAdapter);
dueDateSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { refreshDisplayView();
@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;
}
}
@Override return view;
public void onNothingSelected(AdapterView<?> parent) { }
} @OnClick(R.id.clear)
}); void clearTime(View view) {
date = 0;
time = -1;
refreshDisplayView();
}
dueTimeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @OnItemSelected(R.id.due_date)
@Override void onDateSelected(int position) {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { DateTime today = newDateTime().startOfDay();
switch (position) { switch (position) {
case 0: case 0:
return; return;
case 1: case 1:
setTime(-1); setDate(today.getMillis());
break; break;
case 2: case 2:
setTime(dateShortcutMorning); setDate(today.plusDays(1).getMillis());
break; break;
case 3: case 3:
setTime(dateShortcutAfternoon); setDate(today.plusWeeks(1).getMillis());
break; break;
case 4: case 4:
setTime(dateShortcutEvening); startActivityForResult(new Intent(context, DatePickerActivity.class) {{
break; putExtra(DatePickerActivity.EXTRA_TIMESTAMP, getDueDate());
case 5: }}, REQUEST_DATE);
setTime(dateShortcutNight); break;
break; }
case 6: }
MyTimePickerDialog dialog = new MyTimePickerDialog();
int initialHours = 0; @OnItemSelected(R.id.due_time)
int initialMinutes = 0; void onTimeSelected(int position) {
if (time >= 0) { switch (position) {
DateTime initial = newDateTime(date).withMillisOfDay(time); case 0:
initialHours = initial.getHourOfDay(); return;
initialMinutes = initial.getMinuteOfHour(); case 1:
} setTime(-1);
dialog.initialize(new TimePickerDialog.OnTimeSetListener() { break;
@Override case 2:
public void onTimeSet(RadialPickerLayout radialPickerLayout, int hour, int minute, int seconds) { setTime(dateShortcutMorning);
setTime((int) TimeUnit.HOURS.toMillis(hour) + (int) TimeUnit.MINUTES.toMillis(minute)); break;
} case 3:
}, initialHours, initialMinutes, 0, DateFormat.is24HourFormat(activity)); setTime(dateShortcutAfternoon);
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { break;
@Override case 4:
public void onDismiss(DialogInterface dialog) { setTime(dateShortcutEvening);
refreshDisplayView(); break;
} case 5:
}); setTime(dateShortcutNight);
if (preferences.isDarkTheme()) { break;
dialog.setAccentColor(activity.getResources().getColor(R.color.black_text_hint)); case 6:
} startActivityForResult(new Intent(context, TimePickerActivity.class) {{
dialog.show(activity.getFragmentManager(), FRAG_TAG_PICK_A_TIME); putExtra(TimePickerActivity.EXTRA_TIMESTAMP, getDueDate());
break; }}, 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 private long getDueDate() {
public void onNothingSelected(AdapterView<?> parent) { 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() { private void updateDueTimeOptions() {
Drawable drawable = DrawableCompat.wrap(activity.getResources().getDrawable(R.drawable.textfield_underline_black)); if (time == -1) {
DrawableCompat.setTint(drawable, activity.getResources().getColor(preferences.isDarkTheme() dueTimeOptions.set(0, noTimeString);
? android.R.color.white } else {
: android.R.color.black)); int compareTime = newDateTime()
return drawable; .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() { private Drawable getUnderline(int color) {
Drawable drawable = DrawableCompat.wrap(activity.getResources().getDrawable(R.drawable.textfield_underline_black)); Drawable drawable = DrawableCompat.wrap(context.getResources().getDrawable(R.drawable.textfield_underline_black));
DrawableCompat.setTint(drawable, activity.getResources().getColor(R.color.overdue)); DrawableCompat.setTint(drawable, color);
return drawable; return drawable;
} }
@ -363,38 +389,4 @@ public class DeadlineControlSet extends TaskEditControlSetBase {
refreshDisplayView(); 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; package org.tasks.ui;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatRadioButton; import android.support.v7.widget.AppCompatRadioButton;
import android.view.LayoutInflater;
import android.view.View; 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.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSetBase;
import org.tasks.R; import org.tasks.R;
import java.util.Collections; import javax.inject.Inject;
import java.util.LinkedList;
import java.util.List;
public class PriorityControlSet extends TaskEditControlSetBase { import butterknife.Bind;
import butterknife.OnClick;
private final List<Integer> colors; public class PriorityControlSet extends TaskEditControlFragment {
private final List<ImportanceChangedListener> listeners = new LinkedList<>();
private RadioGroup radioGroup;
public interface ImportanceChangedListener { public interface OnPriorityChanged {
void importanceChanged(int i); void onPriorityChange(int priority);
} }
public PriorityControlSet(Activity activity) { private static final String EXTRA_PRIORITY = "extra_priority";
super(activity, R.layout.control_set_priority);
colors = Ints.asList(Task.getImportanceColors(activity.getResources()));
Collections.reverse(colors);
}
public void notifyImportanceChange(Integer i) { @Inject CheckBoxes checkBoxes;
for (ImportanceChangedListener l : listeners) {
l.importanceChanged(i);
}
}
private Integer getImportance(int checkedId) { @Bind(R.id.priority_high) AppCompatRadioButton priorityHigh;
return getImportance(getView().findViewById(checkedId)); @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) { private OnPriorityChanged callback;
return Integer.parseInt((String) view.getTag()); private int priority;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback = (OnPriorityChanged) activity;
} }
public void addListener(ImportanceChangedListener listener) { @OnClick({R.id.priority_high, R.id.priority_medium, R.id.priority_low, R.id.priority_none})
listeners.add(listener); void onImportanceChanged(CompoundButton button) {
callback.onPriorityChange(getPriority());
} }
@Nullable
@Override @Override
protected void afterInflate() { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = getView(); final View view = super.onCreateView(inflater, container, savedInstanceState);
radioGroup = (RadioGroup) view.findViewById(R.id.importance_group); if (savedInstanceState != null) {
radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { priority = savedInstanceState.getInt(EXTRA_PRIORITY);
@Override }
public void onCheckedChanged(RadioGroup group, int checkedId) { if (priority == 0) {
notifyImportanceChange(getImportance(checkedId)); priorityHigh.setChecked(true);
} } else if(priority == 1) {
}); priorityMedium.setChecked(true);
for (int i = 0; i < radioGroup.getChildCount(); i++) { } else if(priority == 2) {
AppCompatRadioButton radioButton = (AppCompatRadioButton) radioGroup.getChildAt(i); priorityLow.setChecked(true);
radioButton.setSupportButtonTintList(new ColorStateList(new int[][]{ } else {
new int[]{-android.R.attr.state_checked}, new int[]{android.R.attr.state_checked}}, priorityNone.setChecked(true);
new int[]{colors.get(i), colors.get(i)}));
} }
tintRadioButton(priorityHigh, 0);
tintRadioButton(priorityMedium, 1);
tintRadioButton(priorityLow, 2);
tintRadioButton(priorityNone, 3);
return view;
} }
@Override @Override
public void readFromTask(Task task) { protected int getLayout() {
super.readFromTask(task); return R.layout.control_set_priority;
setSelected(model.getImportance());
} }
@Override @Override
public int getIcon() { protected int getIcon() {
return R.drawable.ic_flag_24dp; return R.drawable.ic_flag_24dp;
} }
@Override @Override
protected void readFromTaskOnInitialize() { public void initialize(boolean isNewTask, Task task) {
setSelected(model.getImportance()); priority = task.getImportance();
} }
private void setSelected(int importance) { @Override
if (radioGroup == null) { public void apply(Task task) {
return; task.setImportance(getPriority());
} }
for (int i = 0; i < radioGroup.getChildCount(); i++) { private void tintRadioButton(AppCompatRadioButton radioButton, int priority) {
AppCompatRadioButton radioButton = (AppCompatRadioButton) radioGroup.getChildAt(i); int color = checkBoxes.getPriorityColors().get(priority);
if (importance == getImportance(radioButton)) { radioButton.setSupportButtonTintList(new ColorStateList(new int[][]{
radioButton.setChecked(true); new int[]{-android.R.attr.state_checked}, new int[]{android.R.attr.state_checked}},
} new int[]{color, color}));
}
} }
@Override private int getPriority() {
protected void writeToModelAfterInitialized(Task task) { if (priorityHigh.isChecked()) {
task.setImportance(getImportance(radioGroup.getCheckedRadioButtonId())); 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_height="wrap_content"
android:layout_gravity="top" android:layout_gravity="top"
android:gravity="start" android:gravity="start"
android:textColor="?attr/asThemeTextColor" android:textColor="?attr/asTextColor"
android:textSize="@dimen/task_edit_text_size" /> android:textSize="@dimen/task_edit_text_size" />
</LinearLayout> </LinearLayout>

@ -1,32 +1,20 @@
<?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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:layout_weight="100"
android:layout_weight="100"> android:orientation="horizontal">
<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" />
<Spinner <Spinner
android:id="@+id/hideUntil" android:id="@+id/hideUntil"
android:layout_width="0dip" android:layout_width="wrap_content"
android:layout_height="0dip" /> android:layout_height="wrap_content"
android:background="@null"/>
</LinearLayout> </LinearLayout>

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

@ -50,19 +50,14 @@
android:gravity="end" android:gravity="end"
android:layout_weight="50"> 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 <Spinner
android:id="@+id/reminder_alarm" android:id="@+id/reminder_alarm"
android:layout_width="0dip" android:background="@null"
android:layout_height="0dip" /> android:gravity="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="10dp"
android:paddingEnd="10dp"/>
</LinearLayout> </LinearLayout>

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

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

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

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

@ -3,9 +3,61 @@
** **
** See the file "LICENSE" for the full license governing this code. ** See the file "LICENSE" for the full license governing this code.
--> -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/display_row_edit" android:layout_width="fill_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="?attr/asThemeTextColor" android:orientation="horizontal">
android:textSize="@dimen/task_edit_text_size" />
<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:layout_weight="1"
android:gravity="right" android:gravity="right"
android:paddingLeft="10dip" android:paddingLeft="10dip"
android:textColor="?attr/asThemeTextColor" /> android:textColor="?attr/asTextColor" />
</LinearLayout> </LinearLayout>
<View <View
android:id="@+id/TEA_Separator" android:id="@+id/TEA_Separator"
@ -59,7 +59,7 @@
android:layout_weight="1" android:layout_weight="1"
android:gravity="right" android:gravity="right"
android:paddingLeft="10dip" android:paddingLeft="10dip"
android:textColor="?attr/asThemeTextColor" /> android:textColor="?attr/asTextColor" />
</LinearLayout> </LinearLayout>
</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 ** Copyright (c) 2012 Todoroo Inc
** **
** See the file "LICENSE" for the full license governing this code. ** 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" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="@dimen/task_edit_title_padding_top_bottom" android:orientation="vertical">
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" >
<com.todoroo.astrid.ui.CheckableImageView <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/completeBox" android:layout_width="fill_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="top" android:gravity="center_vertical"
android:paddingRight="@dimen/task_edit_drawable_padding_left_right" android:orientation="horizontal"
android:paddingBottom="@dimen/task_edit_title_padding_top_bottom"
android:paddingEnd="@dimen/task_edit_drawable_padding_left_right" android:paddingEnd="@dimen/task_edit_drawable_padding_left_right"
android:scaleType="center" android:paddingLeft="@dimen/task_edit_drawable_padding_left_right"
android:src="@drawable/btn_check" /> 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 <com.todoroo.astrid.ui.CheckableImageView
style="@style/edit_text_style" android:id="@+id/completeBox"
android:id="@+id/title" android:layout_width="wrap_content"
android:cursorVisible="false" android:layout_height="wrap_content"
android:layout_width="0dp" android:layout_gravity="top"
android:layout_height="wrap_content" android:paddingEnd="@dimen/task_edit_drawable_padding_left_right"
android:layout_weight="100" android:paddingRight="@dimen/task_edit_drawable_padding_left_right"
android:background="?attr/asEditTextBackground" android:scaleType="center" />
android:freezesText="true"
android:hint="@string/TEA_title_hint" <EditText
android:inputType="textCapSentences" android:id="@+id/title"
android:scrollbars="vertical" style="@style/edit_text_style"
android:imeOptions="flagNoExtractUi|actionDone" android:layout_width="0dp"
android:text="" android:layout_height="wrap_content"
android:textColor="?attr/asTextColor" android:layout_weight="100"
android:textColorHint="?attr/asTextColorHint" /> 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> </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:orientation="vertical"
android:gravity="center_horizontal" > 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 <LinearLayout
android:id="@+id/basic_controls" android:id="@+id/basic_controls"
android:layout_width="fill_parent" android:layout_width="fill_parent"
@ -105,35 +98,6 @@
<!-- Extended Add Button --> <!-- 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 <ImageButton
android:id="@+id/commentButton" android:id="@+id/commentButton"
android:layout_width="39dip" android:layout_width="39dip"

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

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

@ -116,7 +116,6 @@
<string name="EPr_appearance_header">Изглед</string> <string name="EPr_appearance_header">Изглед</string>
<string name="EPr_filters_to_show_title">Филтри за показване</string> <string name="EPr_filters_to_show_title">Филтри за показване</string>
<string name="EPr_edit_screen_options">Редактиране на опциите екрана</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_desc">Настройване изгледа на екрана за редактиране на задача</string>
<string name="EPr_beastMode_reset">Възстанови по подразбиране</string> <string name="EPr_beastMode_reset">Възстанови по подразбиране</string>
<string name="EPr_beastMode_hint">Настройване изгледа на екрана за редактиране на задача чрез изтегляне на елементите във всеки ред</string> <string name="EPr_beastMode_hint">Настройване изгледа на екрана за редактиране на задача чрез изтегляне на елементите във всеки ред</string>
@ -255,7 +254,6 @@
<item>месечно</item> <item>месечно</item>
<item>на два месеца</item> <item>на два месеца</item>
</string-array> </string-array>
<string name="repeat_enabled">Повторения</string>
<string name="repeat_every">Всеки %d</string> <string name="repeat_every">Всеки %d</string>
<string name="repeat_interval_prompt">Интервал на повторение</string> <string name="repeat_interval_prompt">Интервал на повторение</string>
<string name="repeat_never">Без повторение</string> <string name="repeat_never">Без повторение</string>
@ -276,7 +274,6 @@
<string name="repeat_forever">Повтаряй завинаги</string> <string name="repeat_forever">Повтаряй завинаги</string>
<string name="repeat_until">Повтаряй до %s</string> <string name="repeat_until">Повтаряй до %s</string>
<string name="repeat_snackbar">%1$s е насрочено за %2$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="new_tag">Нов таг</string>
<string name="tag_no_title_error">Моля, първо въведете име за този списък!</string> <string name="tag_no_title_error">Моля, първо въведете име за този списък!</string>
<string name="tag_FEx_untagged">Без категория</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_start_title">Inici de Silenci</string>
<string name="rmd_EPr_quiet_hours_end_title">Final 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="rmd_EPr_defaultRemind_title">Notificacions al Atzar</string>
<string name="repeat_enabled">Repeticions</string>
<string name="repeat_every">Cada %d</string> <string name="repeat_every">Cada %d</string>
<string name="repeat_interval_prompt">Interval de Repecitiò</string> <string name="repeat_interval_prompt">Interval de Repecitiò</string>
<string name="repeat_never">Sense repetir</string> <string name="repeat_never">Sense repetir</string>

@ -220,7 +220,6 @@
<item>měsíčně</item> <item>měsíčně</item>
<item>každý druhý měsíc</item> <item>každý druhý měsíc</item>
</string-array> </string-array>
<string name="repeat_enabled">Opakování</string>
<string name="repeat_every">Každý %d</string> <string name="repeat_every">Každý %d</string>
<string name="repeat_interval_prompt">Opakovací interval</string> <string name="repeat_interval_prompt">Opakovací interval</string>
<string name="repeat_never">Bez opakování</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_detail_duedate_until">Každý %1$s\n až do %2$s</string>
<string name="repeat_forever">Opakovat stále</string> <string name="repeat_forever">Opakovat stále</string>
<string name="repeat_until">Opakovat až do %s</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_no_title_error">Prosím zadejte jméno pro první seznam</string>
<string name="tag_FEx_untagged">Nezařazené</string> <string name="tag_FEx_untagged">Nezařazené</string>
<string name="TPl_notification">Aktivní časovače pro %s!</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_start_title">Stille timer start</string>
<string name="rmd_EPr_quiet_hours_end_title">Stille timer slut</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="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_every">Hver %d</string>
<string name="repeat_interval_prompt">Interval for gentagelse</string> <string name="repeat_interval_prompt">Interval for gentagelse</string>
<string name="repeat_never">Gentages ikke</string> <string name="repeat_never">Gentages ikke</string>

@ -109,7 +109,6 @@
<string name="EPr_appearance_header">Erscheinungsbild</string> <string name="EPr_appearance_header">Erscheinungsbild</string>
<string name="EPr_filters_to_show_title">Angezeigte Filter</string> <string name="EPr_filters_to_show_title">Angezeigte Filter</string>
<string name="EPr_edit_screen_options">Bildschirmeinstellungen bearbeiten</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_desc">Erscheinungsbild Aufgabenseite anpassen</string>
<string name="EPr_beastMode_reset">Auf Standardeinstellungen zurücksetzen</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> <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>monatlich</item>
<item>alle zwei Monate</item> <item>alle zwei Monate</item>
</string-array> </string-array>
<string name="repeat_enabled">Wiederholungen</string>
<string name="repeat_every">Jeden %d</string> <string name="repeat_every">Jeden %d</string>
<string name="repeat_interval_prompt">Wiederholungsintervall</string> <string name="repeat_interval_prompt">Wiederholungsintervall</string>
<string name="repeat_never">Nicht wiederholen</string> <string name="repeat_never">Nicht wiederholen</string>
@ -256,7 +254,6 @@
<string name="repeat_forever">Endlos wiederholen</string> <string name="repeat_forever">Endlos wiederholen</string>
<string name="repeat_until">Wiederhole bis %s</string> <string name="repeat_until">Wiederhole bis %s</string>
<string name="repeat_snackbar">%1$s verschoben um %2$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="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_no_title_error">Bitte gib zuerst einen Namen für die Liste ein!</string>
<string name="tag_FEx_untagged">Nicht kategorisiert</string> <string name="tag_FEx_untagged">Nicht kategorisiert</string>

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

@ -113,7 +113,6 @@
<string name="EPr_appearance_header">Apariencia</string> <string name="EPr_appearance_header">Apariencia</string>
<string name="EPr_filters_to_show_title">Filtros a mostrar</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_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_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_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> <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>mensualmente</item>
<item>bimensualmente</item> <item>bimensualmente</item>
</string-array> </string-array>
<string name="repeat_enabled">Repeticiones</string>
<string name="repeat_every">Cada %d</string> <string name="repeat_every">Cada %d</string>
<string name="repeat_interval_prompt">Intervalo de repetición</string> <string name="repeat_interval_prompt">Intervalo de repetición</string>
<string name="repeat_never">Sin repetir</string> <string name="repeat_never">Sin repetir</string>
@ -266,7 +264,6 @@
<string name="repeat_forever">Repetir por siempre</string> <string name="repeat_forever">Repetir por siempre</string>
<string name="repeat_until">Repetir hasta %s</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="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="new_tag">Nueva Etiqueta</string>
<string name="tag_no_title_error">Por favor, ¡introduce un nombre para la primera lista!</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> <string name="tag_FEx_untagged">Sin Categoría</string>

@ -80,7 +80,6 @@
<string name="EPr_appearance_header">ظاهر</string> <string name="EPr_appearance_header">ظاهر</string>
<string name="EPr_filters_to_show_title">فیلترها برای نمایش</string> <string name="EPr_filters_to_show_title">فیلترها برای نمایش</string>
<string name="EPr_edit_screen_options">ویرایش تنظیمات صفحه</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_desc">شخصی سازی قالب صفحه ویرایش وظیفه</string>
<string name="EPr_beastMode_reset">برگرداندن به پیش فرض</string> <string name="EPr_beastMode_reset">برگرداندن به پیش فرض</string>
<string name="EPr_fullTask_title">نمایش عنوان کامل وظیفه</string> <string name="EPr_fullTask_title">نمایش عنوان کامل وظیفه</string>
@ -178,7 +177,6 @@
<item>هر ماه</item> <item>هر ماه</item>
<item>یک ماه درمیان</item> <item>یک ماه درمیان</item>
</string-array> </string-array>
<string name="repeat_enabled">تکرارها</string>
<string name="repeat_interval_prompt">دوره تکرار</string> <string name="repeat_interval_prompt">دوره تکرار</string>
<string name="repeat_never">بدون تکرار</string> <string name="repeat_never">بدون تکرار</string>
<string-array name="repeat_interval"> <string-array name="repeat_interval">
@ -194,7 +192,6 @@
<item>از تاریخ اتمام</item> <item>از تاریخ اتمام</item>
</string-array> </string-array>
<string name="repeat_forever">تکرار بینهایت</string> <string name="repeat_forever">تکرار بینهایت</string>
<string name="TEA_tags_label_long">اضافه به لیست</string>
<string name="new_tag">تگ جدید</string> <string name="new_tag">تگ جدید</string>
<string name="tag_no_title_error">لطفا ابتدا یک نام برای لیست انتخاب نمایید</string> <string name="tag_no_title_error">لطفا ابتدا یک نام برای لیست انتخاب نمایید</string>
<string name="tag_FEx_untagged">دسته بندی نشده</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_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="gtasks_GPr_header">Google tehtävät</string>
<string name="rmd_EPr_defaultRemind_title">Satunnainen muistutus</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_every">Toista %d</string>
<string name="repeat_interval_prompt">Toistoväli</string> <string name="repeat_interval_prompt">Toistoväli</string>
<string name="repeat_never">Ei toistuva</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_detail_duedate_until">Joka %1$s\nkunnes %2$s</string>
<string name="repeat_forever">Toista loputtomiin</string> <string name="repeat_forever">Toista loputtomiin</string>
<string name="repeat_until">Toista kunnes %s</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="tag_no_title_error">Ole hyvä syötä ensin nimi tälle listalle!</string>
<string name="delete_task">Poista tehtävä</string> <string name="delete_task">Poista tehtävä</string>
<string name="source_code">Lähdekoodi</string> <string name="source_code">Lähdekoodi</string>

@ -112,7 +112,6 @@
<string name="EPr_appearance_header">Apparence</string> <string name="EPr_appearance_header">Apparence</string>
<string name="EPr_filters_to_show_title">Filtres à afficher</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_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_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_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> <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>mensuel</item>
<item>bi-mensuel</item> <item>bi-mensuel</item>
</string-array> </string-array>
<string name="repeat_enabled">Répétitions</string>
<string name="repeat_every">Tous les %d</string> <string name="repeat_every">Tous les %d</string>
<string name="repeat_interval_prompt">Interval de répétition</string> <string name="repeat_interval_prompt">Interval de répétition</string>
<string name="repeat_never">Une fois seulement</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_forever">Répéter indéfiniment</string>
<string name="repeat_until">Répéter jusqu\'à %s</string> <string name="repeat_until">Répéter jusqu\'à %s</string>
<string name="repeat_snackbar">%1$s replanifiée à %2$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="new_tag">Nouveau tag</string>
<string name="tag_no_title_error">Veuillez d\'abord entrer un nom pour cette liste.</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> <string name="tag_FEx_untagged">Non classé</string>

@ -100,7 +100,6 @@
<string name="rmd_NoA_done">Befejezve</string> <string name="rmd_NoA_done">Befejezve</string>
<string name="rmd_NoA_snooze">Szundi</string> <string name="rmd_NoA_snooze">Szundi</string>
<string name="rmd_EPr_defaultRemind_title">Véletlenszerű emlékeztetők</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_every">Minden %d</string>
<string name="repeat_interval_prompt">Ismétlési időköz</string> <string name="repeat_interval_prompt">Ismétlési időköz</string>
<string name="repeat_never">Nem ismétlődő</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_detail_duedate_until">Minden %1$s\neddig: %2$s</string>
<string name="repeat_forever">Ismétlés örökké</string> <string name="repeat_forever">Ismétlés örökké</string>
<string name="repeat_until">Ismétlés eddig: %s</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="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> <string name="delete_task">Feladat törlése</string>
<plurals name="Ntasks"> <plurals name="Ntasks">

@ -114,7 +114,6 @@
<string name="EPr_appearance_header">Aspetto</string> <string name="EPr_appearance_header">Aspetto</string>
<string name="EPr_filters_to_show_title">Filtri da visualizzare</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_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_desc">Personalizza il layout della schermata di modifica attività</string>
<string name="EPr_beastMode_reset">Ripristina predefiniti</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> <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>mensilmente</item>
<item>bi-mensilmente</item> <item>bi-mensilmente</item>
</string-array> </string-array>
<string name="repeat_enabled">Ripetizioni</string>
<string name="repeat_every">Ogni %d</string> <string name="repeat_every">Ogni %d</string>
<string name="repeat_interval_prompt">Intervallo di ripetizione</string> <string name="repeat_interval_prompt">Intervallo di ripetizione</string>
<string name="repeat_never">Non ricorrente</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_forever">Ripeti all\'infinito</string>
<string name="repeat_until">Ripeti fino %s</string> <string name="repeat_until">Ripeti fino %s</string>
<string name="repeat_snackbar">%1$s ripianificata per %2$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="new_tag">Nuova etichetta</string>
<string name="tag_no_title_error">Per cortesia, prima inserisci un nome per questa lista</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> <string name="tag_FEx_untagged">Non classificato</string>

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

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

@ -117,7 +117,6 @@
<string name="EPr_appearance_header">보기 설정</string> <string name="EPr_appearance_header">보기 설정</string>
<string name="EPr_filters_to_show_title">표시할 필터</string> <string name="EPr_filters_to_show_title">표시할 필터</string>
<string name="EPr_edit_screen_options">화면 설정 편집하기</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_desc">일정 편집화면 레이아웃 설정하기</string>
<string name="EPr_beastMode_reset">기본값으로 초기화하기</string> <string name="EPr_beastMode_reset">기본값으로 초기화하기</string>
<string name="EPr_beastMode_hint">각 줄의 왼쪽 손잡이를 끌어서 일정 편집 화면을 사용자 정의하기</string> <string name="EPr_beastMode_hint">각 줄의 왼쪽 손잡이를 끌어서 일정 편집 화면을 사용자 정의하기</string>
@ -255,7 +254,6 @@ Tasks의 백업에서 당신의 일정을 복구하시기 바랍니다.
<item>매달</item> <item>매달</item>
<item>격월로</item> <item>격월로</item>
</string-array> </string-array>
<string name="repeat_enabled">반복 설정</string>
<string name="repeat_every">매 %d</string> <string name="repeat_every">매 %d</string>
<string name="repeat_interval_prompt">반복 주기</string> <string name="repeat_interval_prompt">반복 주기</string>
<string name="repeat_never">반복하지 않기</string> <string name="repeat_never">반복하지 않기</string>
@ -276,7 +274,6 @@ Tasks의 백업에서 당신의 일정을 복구하시기 바랍니다.
<string name="repeat_forever">영원히 반복하기</string> <string name="repeat_forever">영원히 반복하기</string>
<string name="repeat_until">%s 까지 반복</string> <string name="repeat_until">%s 까지 반복</string>
<string name="repeat_snackbar">%1$s 이 %2$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="new_tag">새 태그</string>
<string name="tag_no_title_error">이 목록의 이름을 먼저 입력하세요!</string> <string name="tag_no_title_error">이 목록의 이름을 먼저 입력하세요!</string>
<string name="tag_FEx_untagged">미분류 일정</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_start_title">Stilletimer start</string>
<string name="rmd_EPr_quiet_hours_end_title">Stilletimer slutt</string> <string name="rmd_EPr_quiet_hours_end_title">Stilletimer slutt</string>
<string name="rmd_EPr_defaultRemind_title">Tilfeldige påminnelser</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_every">Hver %d</string>
<string name="repeat_interval_prompt">Gjentakelsesintervall</string> <string name="repeat_interval_prompt">Gjentakelsesintervall</string>
<string name="repeat_detail_duedate">Hver %s</string> <string name="repeat_detail_duedate">Hver %s</string>

@ -114,7 +114,6 @@
<string name="EPr_appearance_header">Uiterlijk</string> <string name="EPr_appearance_header">Uiterlijk</string>
<string name="EPr_filters_to_show_title">Te tonen filters</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_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_desc">Pas de layout van het Taak Wijzigingsscherm aan</string>
<string name="EPr_beastMode_reset">Standaardinstellingen herstellen</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> <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>maandelijks</item>
<item>tweemaandelijks</item> <item>tweemaandelijks</item>
</string-array> </string-array>
<string name="repeat_enabled">Herhalingen</string>
<string name="repeat_every">Elke %d</string> <string name="repeat_every">Elke %d</string>
<string name="repeat_interval_prompt">Herhaal interval</string> <string name="repeat_interval_prompt">Herhaal interval</string>
<string name="repeat_never">Niet herhalend</string> <string name="repeat_never">Niet herhalend</string>
@ -272,7 +270,6 @@
<string name="repeat_forever">Altijd herhalen</string> <string name="repeat_forever">Altijd herhalen</string>
<string name="repeat_until">Herhalen tot %s</string> <string name="repeat_until">Herhalen tot %s</string>
<string name="repeat_snackbar">%1$s opnieuw ingepland op %2$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="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_no_title_error">Voer a.u.b. eerst een naam in voor deze lijst!</string>
<string name="tag_FEx_untagged">Niet gecategoriseerd</string> <string name="tag_FEx_untagged">Niet gecategoriseerd</string>

@ -102,7 +102,6 @@
<string name="EPr_appearance_header">Wygląd</string> <string name="EPr_appearance_header">Wygląd</string>
<string name="EPr_filters_to_show_title">Pokazywane filtry</string> <string name="EPr_filters_to_show_title">Pokazywane filtry</string>
<string name="EPr_edit_screen_options">Edytuj ustawienia ekranu</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_desc">Dostosuj układ strony edycji zadania</string>
<string name="EPr_beastMode_reset">Przywróć domyślne</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> <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 w miesiącu</item>
<item>raz na dwa miesiące</item> <item>raz na dwa miesiące</item>
</string-array> </string-array>
<string name="repeat_enabled">Powtarza się</string>
<string name="repeat_every">Co %d</string> <string name="repeat_every">Co %d</string>
<string name="repeat_interval_prompt">Odstęp powtarzania</string> <string name="repeat_interval_prompt">Odstęp powtarzania</string>
<string name="repeat_never">Jednorazowo</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_detail_duedate_until">Co %1$s\ndo %2$s</string>
<string name="repeat_forever">Powtarzaj bez końca</string> <string name="repeat_forever">Powtarzaj bez końca</string>
<string name="repeat_until">Powtarzaj do %s</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_no_title_error">Wpisz nazwę dla tej listy najpierw!</string>
<string name="tag_FEx_untagged">Na żadnej liście</string> <string name="tag_FEx_untagged">Na żadnej liście</string>
<string name="TPl_notification">Minutnkiki aktywne przez %s!</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_appearance_header">Aparência</string>
<string name="EPr_filters_to_show_title">Filtros a mostrar</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_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_desc">Personalize o layout da tela de edição</string>
<string name="EPr_beastMode_reset">Restaurar valores padrã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> <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>mensalmente</item>
<item>a cada dois meses</item> <item>a cada dois meses</item>
</string-array> </string-array>
<string name="repeat_enabled">Repetir</string>
<string name="repeat_every">A cada %d</string> <string name="repeat_every">A cada %d</string>
<string name="repeat_interval_prompt">Intervalo de repetição</string> <string name="repeat_interval_prompt">Intervalo de repetição</string>
<string name="repeat_never">Não repetir</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_detail_duedate_until">Todo %1$s\naté %2$s</string>
<string name="repeat_forever">Repetir para sempre</string> <string name="repeat_forever">Repetir para sempre</string>
<string name="repeat_until">Repetir até %s</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_no_title_error">Entre com o nome da lista primeiro!</string>
<string name="tag_FEx_untagged">Sem categoria</string> <string name="tag_FEx_untagged">Sem categoria</string>
<string name="TPl_notification">Temporizador ativado para %s!</string> <string name="TPl_notification">Temporizador ativado para %s!</string>

@ -113,7 +113,6 @@
<string name="EPr_appearance_header">Aspeto</string> <string name="EPr_appearance_header">Aspeto</string>
<string name="EPr_filters_to_show_title">Filtros a mostrar</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_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_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_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> <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>mensal</item>
<item>bimensal</item> <item>bimensal</item>
</string-array> </string-array>
<string name="repeat_enabled">Repete</string>
<string name="repeat_every">Cada %d</string> <string name="repeat_every">Cada %d</string>
<string name="repeat_interval_prompt">Intervalo de repetição</string> <string name="repeat_interval_prompt">Intervalo de repetição</string>
<string name="repeat_never">Não repete</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_forever">Repetir eternamente</string>
<string name="repeat_until">Repetir até %s</string> <string name="repeat_until">Repetir até %s</string>
<string name="repeat_snackbar">%1$s agendada para %2$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="new_tag">Nova etiqueta</string>
<string name="tag_no_title_error">Introduza o nome para esta lista!</string> <string name="tag_no_title_error">Introduza o nome para esta lista!</string>
<string name="tag_FEx_untagged">Sem categoria</string> <string name="tag_FEx_untagged">Sem categoria</string>

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

@ -111,7 +111,6 @@
<string name="EPr_appearance_header">Vzhľad</string> <string name="EPr_appearance_header">Vzhľad</string>
<string name="EPr_filters_to_show_title">Filtre pre zobrazenie</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_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_desc">Prispôsobiť rozvrhnutie zobrazenia úpravy úloh</string>
<string name="EPr_beastMode_reset">Obnoviť na predvolené</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> <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>mesačne</item>
<item>každý druhý mesiac</item> <item>každý druhý mesiac</item>
</string-array> </string-array>
<string name="repeat_enabled">Opakovanie</string>
<string name="repeat_every">Každý %d</string> <string name="repeat_every">Každý %d</string>
<string name="repeat_interval_prompt">Interval opakovania</string> <string name="repeat_interval_prompt">Interval opakovania</string>
<string name="repeat_never">Neopakovať</string> <string name="repeat_never">Neopakovať</string>
@ -260,7 +258,6 @@
<string name="repeat_forever">Opakovať donekonečna</string> <string name="repeat_forever">Opakovať donekonečna</string>
<string name="repeat_until">Opakovať do %s</string> <string name="repeat_until">Opakovať do %s</string>
<string name="repeat_snackbar">%1$s preplánované na %2$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="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_no_title_error">Prosím, uveďte najprv názov pre tento zoznam!</string>
<string name="tag_FEx_untagged">Nezaradené</string> <string name="tag_FEx_untagged">Nezaradené</string>

@ -106,7 +106,6 @@
<string name="EPr_appearance_header">Izgled</string> <string name="EPr_appearance_header">Izgled</string>
<string name="EPr_filters_to_show_title">Filtri za prikaz</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_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_desc">Po meri postavi stran za urejevanje opravkov </string>
<string name="EPr_beastMode_reset">Povrni privzeto nastavitev</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> <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>mesečno</item>
<item>vsake dva meseca</item> <item>vsake dva meseca</item>
</string-array> </string-array>
<string name="repeat_enabled">Ponovitve</string>
<string name="repeat_every">Vsakih %d</string> <string name="repeat_every">Vsakih %d</string>
<string name="repeat_interval_prompt">Pogostost ponovitev</string> <string name="repeat_interval_prompt">Pogostost ponovitev</string>
<string name="repeat_never">Brez 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_detail_duedate_until">Vsak %1$s\n do %2$s</string>
<string name="repeat_forever">Ponavljaj do preklica</string> <string name="repeat_forever">Ponavljaj do preklica</string>
<string name="repeat_until">Ponavljaj do %s</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_no_title_error">Prosimo, vnesite naziv tega seznama najprej!</string>
<string name="tag_FEx_untagged">Nerazvrščen</string> <string name="tag_FEx_untagged">Nerazvrščen</string>
<string name="TPl_notification">Merilniki časa aktivni za %s!</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>varje månad</item>
<item>varannan månad</item> <item>varannan månad</item>
</string-array> </string-array>
<string name="repeat_enabled">Upprepningar</string>
<string name="repeat_every">Varje %d</string> <string name="repeat_every">Varje %d</string>
<string name="repeat_interval_prompt">Upprepningsintervall</string> <string name="repeat_interval_prompt">Upprepningsintervall</string>
<string name="repeat_never">Inte upprepande</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_detail_duedate_until">Varje %1$s\ntill %2$s </string>
<string name="repeat_forever">Repetera oändligt</string> <string name="repeat_forever">Repetera oändligt</string>
<string name="repeat_until">Repetera till %s</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_no_title_error">Ange först ett namn på listan!</string>
<string name="tag_FEx_untagged">Ingen kategori</string> <string name="tag_FEx_untagged">Ingen kategori</string>
<string name="TPl_notification">Tidtagarur aktivt för %s!</string> <string name="TPl_notification">Tidtagarur aktivt för %s!</string>

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

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

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

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

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

@ -62,4 +62,10 @@
<item>@string/none</item> <item>@string/none</item>
</string-array> </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> </resources>

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

Loading…
Cancel
Save