public void testCompletedTasksAreCompletedWhenParsed() {
    XmlPullParser xmlParser = Xml.newPullParser();

    try {
      xmlParser.setInput(
          new StringReader(
              "<todo>"
                  + "<completed-at type=\"datetime\" nil=\"true\"/>"
                  + "<context-id type=\"integer\">3711</context-id>"
                  + "<created-at type=\"datetime\">2009-10-26T22:23:42+01:00</created-at>"
                  + "<description>Läs getting things done igen</description>"
                  + "<due type=\"datetime\" nil=\"true\"/>"
                  + "<id type=\"integer\">25076</id>"
                  + "<ip-address>90.232.35.15</ip-address>"
                  + "<notes>Primärt kring idéer och projekt</notes>"
                  + "<project-id type=\"integer\">4558</project-id>"
                  + "<recurring-todo-id type=\"integer\" nil=\"true\"/>"
                  + "<show-from type=\"datetime\" nil=\"true\"/>"
                  + "<state>completed</state>"
                  + "<updated-at type=\"datetime\">2010-02-03T10:37:19+01:00</updated-at>"
                  + "</todo>"));
    } catch (XmlPullParserException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    TaskParser parser = CreateSUT();
    Task task = parser.parseSingle(xmlParser).getResult();
    assertEquals("completed date isn't parsed correctly", true, task.isComplete());
  }
  public void testTaskParserBasicParsingTest() {
    XmlPullParser xmlParser = Xml.newPullParser();

    try {
      xmlParser.setInput(
          new StringReader(
              "<todo>"
                  + "<completed-at type=\"datetime\" nil=\"true\"/>"
                  + "<context-id type=\"integer\">3711</context-id>"
                  + "<created-at type=\"datetime\">2009-10-26T22:23:42+01:00</created-at>"
                  + "<description>Läs getting things done igen</description>"
                  + "<due type=\"datetime\" nil=\"true\"/>"
                  + "<id type=\"integer\">25076</id>"
                  + "<ip-address>90.232.35.15</ip-address>"
                  + "<notes>Primärt kring idéer och projekt</notes>"
                  + "<project-id type=\"integer\">4558</project-id>"
                  + "<recurring-todo-id type=\"integer\" nil=\"true\"/>"
                  + "<show-from type=\"datetime\" nil=\"true\"/>"
                  + "<state>active</state>"
                  + "<updated-at type=\"datetime\">2010-02-03T10:37:19+01:00</updated-at>"
                  + "</todo>"));
    } catch (XmlPullParserException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    TaskParser parser = CreateSUT();
    Task task = parser.parseSingle(xmlParser).getResult();
    Assert.assertEquals("Läs getting things done igen", task.getDescription());
    Assert.assertEquals(Id.create(25076), task.getTracksId());
    Assert.assertEquals(1265189839000l, task.getModifiedDate());
    assertEquals("Primärt kring idéer och projekt", task.getDetails());
    assertEquals("context id was wrong", Id.create(1234), task.getContextId());
    assertEquals("project id was wrong", Id.create(2345), task.getProjectId());
    assertEquals("due date was wrong", 0, task.getDueDate());
    assertEquals("start date was wrong", 0, task.getStartDate());
  }
  @Override
  protected Task createItemFromUI() {
    Builder builder = Task.newBuilder();
    if (mOriginalItem != null) {
      builder.mergeFrom(mOriginalItem);
    }

    final String description = mDescriptionWidget.getText().toString();
    final long modified = System.currentTimeMillis();
    final String details = mDetailsWidget.getText().toString();
    final Id contextId = getSpinnerSelectedId(mContextSpinner, mContextIds);
    final Id projectId = getSpinnerSelectedId(mProjectSpinner, mProjectIds);
    final boolean allDay = mAllDayCheckBox.isChecked();
    final boolean complete = mCompletedCheckBox.isChecked();
    final boolean hasAlarms = !mReminderItems.isEmpty();
    final int order = calculateTaskOrder(projectId);
    final boolean deleted = mDeletedCheckBox.isChecked();
    final boolean active = true;

    builder
        .setDescription(description)
        .setModifiedDate(modified)
        .setDetails(details)
        .setContextId(contextId)
        .setProjectId(projectId)
        .setAllDay(allDay)
        .setComplete(complete)
        .setDeleted(deleted)
        .setActive(active)
        .setHasAlarm(hasAlarms)
        .setOrder(order);

    // If we are creating a new task, set the creation date
    if (mState == State.STATE_INSERT) {
      builder.setCreatedDate(modified);
    }

    String timezone;
    long startMillis = 0L;
    long dueMillis = 0L;

    if (allDay) {
      // Reset start and end time, increment the monthDay by 1, and set
      // the timezone to UTC, as required for all-day events.
      timezone = Time.TIMEZONE_UTC;
      mStartTime.hour = 0;
      mStartTime.minute = 0;
      mStartTime.second = 0;
      mStartTime.timezone = timezone;
      startMillis = mStartTime.normalize(true);

      mDueTime.hour = 0;
      mDueTime.minute = 0;
      mDueTime.second = 0;
      mDueTime.monthDay++;
      mDueTime.timezone = timezone;
      dueMillis = mDueTime.normalize(true);
    } else {
      if (mShowStart && !Time.isEpoch(mStartTime)) {
        startMillis = mStartTime.toMillis(true);
      }

      if (mShowDue && !Time.isEpoch(mDueTime)) {
        dueMillis = mDueTime.toMillis(true);
      }

      if (mState == State.STATE_INSERT) {
        // The timezone for a new task is the currently displayed timezone
        timezone = TimeZone.getDefault().getID();
      } else {
        timezone = mOriginalItem.getTimezone();

        // The timezone might be null if we are changing an existing
        // all-day task to a non-all-day event.  We need to assign
        // a timezone to the non-all-day task.
        if (TextUtils.isEmpty(timezone)) {
          timezone = TimeZone.getDefault().getID();
        }
      }
    }

    builder.setTimezone(timezone).setStartDate(startMillis).setDueDate(dueMillis);

    Id eventId = mOriginalItem == null ? Id.NONE : mOriginalItem.getCalendarEventId();
    final boolean updateCalendar = mUpdateCalendarCheckBox.isChecked();

    if (updateCalendar) {
      Uri calEntryUri =
          addOrUpdateCalendarEvent(
              eventId,
              description,
              details,
              projectId,
              contextId,
              timezone,
              startMillis,
              dueMillis,
              allDay);
      if (calEntryUri != null) {
        eventId = Id.create(ContentUris.parseId(calEntryUri));
        mNextIntent = new Intent(Intent.ACTION_EDIT, calEntryUri);
        mNextIntent.putExtra("beginTime", startMillis);
        mNextIntent.putExtra("endTime", dueMillis);
      }
      Log.i(cTag, "Updated calendar event " + eventId);
    }
    builder.setCalendarEventId(eventId);

    return builder.build();
  }
  @Override
  protected void updateUIFromItem(Task task) {
    // If we hadn't previously retrieved the original task, do so
    // now.  This allows the user to revert their changes.
    if (mOriginalItem == null) {
      mOriginalItem = task;
    }

    final String details = task.getDetails();
    mDetailsWidget.setTextKeepState(details == null ? "" : details);

    mDescriptionWidget.setTextKeepState(task.getDescription());

    final Id contextId = task.getContextId();
    if (contextId.isInitialised()) {
      setSpinnerSelection(mContextSpinner, mContextIds, contextId.getId());
    }

    final Id projectId = task.getProjectId();
    if (projectId.isInitialised()) {
      setSpinnerSelection(mProjectSpinner, mProjectIds, projectId.getId());
    }

    boolean allDay = task.isAllDay();
    if (allDay) {
      String tz = mStartTime.timezone;
      mStartTime.timezone = Time.TIMEZONE_UTC;
      mStartTime.set(task.getStartDate());
      mStartTime.timezone = tz;

      // Calling normalize to calculate isDst
      mStartTime.normalize(true);
    } else {
      mStartTime.set(task.getStartDate());
    }

    if (allDay) {
      String tz = mStartTime.timezone;
      mDueTime.timezone = Time.TIMEZONE_UTC;
      mDueTime.set(task.getDueDate());
      mDueTime.timezone = tz;

      // Calling normalize to calculate isDst
      mDueTime.normalize(true);
    } else {
      mDueTime.set(task.getDueDate());
    }

    setWhenDefaults();
    populateWhen();

    // show scheduling section if either start or due date are set
    mSchedulingExpanded = mShowStart || mShowDue;
    setSchedulingVisibility(mSchedulingExpanded);

    mAllDayCheckBox.setChecked(allDay);
    updateTimeVisibility(!allDay);

    mCompletedCheckBox.setChecked(task.isComplete());

    mDeletedEntry.setVisibility(task.isDeleted() ? View.VISIBLE : View.GONE);
    mDeletedCheckBox.setChecked(task.isDeleted());

    updateCalendarPanel();

    // Load reminders (if there are any)
    if (task.hasAlarms()) {
      Uri uri = ReminderProvider.Reminders.CONTENT_URI;
      ContentResolver cr = getContentResolver();
      Cursor reminderCursor =
          cr.query(
              uri,
              ReminderProvider.Reminders.cFullProjection,
              REMINDERS_WHERE,
              new String[] {String.valueOf(task.getLocalId().getId())},
              null);
      try {
        // First pass: collect all the custom reminder minutes (e.g.,
        // a reminder of 8 minutes) into a global list.
        while (reminderCursor.moveToNext()) {
          int minutes = reminderCursor.getInt(ReminderProvider.Reminders.MINUTES_INDEX);
          addMinutesToList(this, mReminderValues, mReminderLabels, minutes);
        }

        // Second pass: create the reminder spinners
        reminderCursor.moveToPosition(-1);
        while (reminderCursor.moveToNext()) {
          int minutes = reminderCursor.getInt(ReminderProvider.Reminders.MINUTES_INDEX);
          mOriginalMinutes.add(minutes);
          addReminder(this, this, mReminderItems, mReminderValues, mReminderLabels, minutes);
        }
      } finally {
        reminderCursor.close();
      }
    }
    updateRemindersVisibility();
  }
  static void updateAppWidget(
      final android.content.Context androidContext,
      AppWidgetManager appWidgetManager,
      int appWidgetId,
      String queryName) {
    Log.d(cTag, "updateAppWidget appWidgetId=" + appWidgetId + " queryName=" + queryName);

    // TODO inject
    ContentResolverProvider provider =
        new ContentResolverProvider() {
          @Override
          public ContentResolver get() {
            return androidContext.getContentResolver();
          }
        };

    TaskPersister taskPersister = new TaskPersister(provider);
    ProjectPersister projectPersister = new ProjectPersister(provider);
    EntityCache<Project> projectCache = new DefaultEntityCache<Project>(projectPersister);
    ContextPersister contextPersister = new ContextPersister(provider);
    EntityCache<Context> contextCache = new DefaultEntityCache<Context>(contextPersister);

    RemoteViews views = new RemoteViews(androidContext.getPackageName(), R.layout.widget);

    TaskQuery query = StandardTaskQueries.getQuery(queryName);
    if (query == null) return;

    int titleId = getIdentifier(androidContext, "title_" + queryName, cStringType);
    views.setTextViewText(R.id.title, androidContext.getString(titleId));

    Intent intent = new Intent(Intent.ACTION_INSERT, TaskProvider.Tasks.CONTENT_URI);
    PendingIntent pendingIntent = PendingIntent.getActivity(androidContext, 0, intent, 0);
    views.setOnClickPendingIntent(R.id.add_task, pendingIntent);

    Cursor taskCursor =
        androidContext
            .getContentResolver()
            .query(
                TaskProvider.Tasks.CONTENT_URI,
                TaskProvider.Tasks.cFullProjection,
                query.getSelection(),
                query.getSelectionArgs(),
                query.getSortOrder());

    for (int taskCount = 1; taskCount <= 4; taskCount++) {
      Task task = null;
      Project project = null;
      Context context = null;
      if (taskCursor.moveToNext()) {
        task = taskPersister.read(taskCursor);
        project = projectCache.findById(task.getProjectId());
        context = contextCache.findById(task.getContextId());
      }

      int descriptionViewId = getIdIdentifier(androidContext, "description_" + taskCount);
      views.setTextViewText(descriptionViewId, task != null ? task.getDescription() : "");
      views.setInt(descriptionViewId, "setLines", project == null ? 2 : 1);

      int projectViewId = getIdIdentifier(androidContext, "project_" + taskCount);
      views.setViewVisibility(projectViewId, project == null ? View.GONE : View.VISIBLE);
      views.setTextViewText(projectViewId, project != null ? project.getName() : "");

      int contextIconId = getIdIdentifier(androidContext, "context_icon_" + taskCount);
      String iconName = context != null ? context.getIconName() : null;
      ContextIcon icon = ContextIcon.createIcon(iconName, androidContext.getResources());
      if (icon != ContextIcon.NONE) {
        views.setImageViewResource(contextIconId, icon.smallIconId);
        views.setViewVisibility(contextIconId, View.VISIBLE);
      } else {
        views.setViewVisibility(contextIconId, View.INVISIBLE);
      }

      if (task != null) {
        Uri.Builder builder = TaskProvider.Tasks.CONTENT_URI.buildUpon();
        ContentUris.appendId(builder, task.getLocalId().getId());
        Uri taskUri = builder.build();
        intent = new Intent(Intent.ACTION_EDIT, taskUri);
        Log.d(cTag, "Adding pending event for viewing uri " + taskUri);
        pendingIntent = PendingIntent.getActivity(androidContext, 0, intent, 0);
        views.setOnClickPendingIntent(descriptionViewId, pendingIntent);
        views.setOnClickPendingIntent(projectViewId, pendingIntent);
        views.setOnClickPendingIntent(contextIconId, pendingIntent);
      }
    }
    taskCursor.close();

    appWidgetManager.updateAppWidget(appWidgetId, views);
  }