Created 1.0 tag, along with binary release.

pull/14/head
Tim Su 16 years ago
parent 7444cc7d1e
commit 4cda12b558

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="lib" path="lib/android-src.jar"/>
<classpathentry kind="lib" path="lib/android.jar" sourcepath="/home/timsu/projects/android-astrid/lib/android-src.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>astrid</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

@ -0,0 +1,60 @@
#Wed Dec 17 03:02:43 PST 2008
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
org.eclipse.jdt.core.compiler.problem.nullReference=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning

File diff suppressed because one or more lines are too long

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.timsu.astrid"
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".activities.TaskList"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activities.TaskView"/>
<activity android:name=".activities.TaskEdit"/>
</application>
</manifest>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

@ -0,0 +1,30 @@
<?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_pressed="false" android:state_enabled="true"
android:state_focused="false" android:drawable="@drawable/timepicker_down_normal" />
<item android:state_pressed="true" android:state_enabled="true"
android:drawable="@drawable/timepicker_down_pressed" />
<item android:state_pressed="false" android:state_enabled="true"
android:state_focused="true" android:drawable="@drawable/timepicker_down_selected" />
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="false" android:drawable="@drawable/timepicker_down_disabled" />
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="true" android:drawable="@drawable/timepicker_down_disabled_focused" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,30 @@
<?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_pressed="false" android:state_enabled="true"
android:state_focused="false" android:drawable="@drawable/timepicker_input_normal" />
<item android:state_pressed="true" android:state_enabled="true"
android:drawable="@drawable/timepicker_input_pressed" />
<item android:state_pressed="false" android:state_enabled="true"
android:state_focused="true" android:drawable="@drawable/timepicker_input_selected" />
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="false" android:drawable="@drawable/timepicker_input_disabled" />
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="true" android:drawable="@drawable/timepicker_input_normal" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

@ -0,0 +1,30 @@
<?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_pressed="false" android:state_enabled="true"
android:state_focused="false" android:drawable="@drawable/timepicker_up_normal" />
<item android:state_pressed="true" android:state_enabled="true"
android:drawable="@drawable/timepicker_up_pressed" />
<item android:state_pressed="false" android:state_enabled="true"
android:state_focused="true" android:drawable="@drawable/timepicker_up_selected" />
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="false" android:drawable="@drawable/timepicker_up_disabled" />
<item android:state_pressed="false" android:state_enabled="false"
android:state_focused="true" android:drawable="@drawable/timepicker_up_disabled_focused" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:state_pressed="false"
android:drawable="@drawable/highlight_selected" />
<item android:state_focused="true" android:state_pressed="true"
android:drawable="@drawable/highlight_pressed" />
<item android:state_focused="false" android:state_pressed="true"
android:drawable="@drawable/highlight_pressed" />
</selector>

@ -0,0 +1,44 @@
<?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.timsu.astrid.widget.NumberPickerButton android:id="@+id/increment"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/timepicker_up_btn"
/>
<EditText android:id="@+id/timepicker_input"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:singleLine="true"
style="?android:attr/textAppearanceLargeInverse"
android:textSize="30sp"
android:background="@drawable/timepicker_input"
/>
<com.timsu.astrid.widget.NumberPickerButton android:id="@+id/decrement"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/timepicker_down_btn"
/>
</merge>

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2007, 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.
*/
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dip">
<com.timsu.astrid.widget.NumberPicker android:id="@+id/numberPicker"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</FrameLayout>

@ -0,0 +1,266 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
ASTRID: Android's Simple Task Recording Dame
Copyright (c) 2009 Tim Su
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:id="@+id/event"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- TASK NAME -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dip">
<TextView android:id="@+id/name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/name_label"
style="@style/TextAppearance.EditEvent_Label"/>
<EditText android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/name_hint"
android:capitalize="words"/>
</LinearLayout>
<!-- PROPERTIES -->
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="@android:drawable/divider_horizontal_dark"
/>
<LinearLayout android:id="@+id/properties_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dip">
<TextView android:id="@+id/estimatedDuration_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/estimatedDuration_label"
style="@style/TextAppearance.EditEvent_Label"/>
<Button android:id="@+id/estimatedDuration"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<TextView android:id="@+id/importance_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/importance_label"
style="@style/TextAppearance.EditEvent_Label"/>
<Spinner android:id="@+id/importance"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<!-- <TextView android:id="@+id/tags_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tags_label"
style="@style/TextAppearance.EditEvent_Label"/>
<LinearLayout android:id="@+id/tags_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</LinearLayout> -->
</LinearLayout>
<!-- DATES -->
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="@android:drawable/divider_horizontal_dark"
/>
<LinearLayout android:id="@+id/dates_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dip">
<TextView android:id="@+id/definiteDueDate_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/definiteDueDate_label"
style="@style/TextAppearance.EditEvent_Label"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/definiteDueDate_notnull"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button android:id="@+id/definiteDueDate_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button android:id="@+id/definiteDueDate_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<TextView android:id="@+id/preferredDueDate_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/preferredDueDate_label"
style="@style/TextAppearance.EditEvent_Label"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/preferredDueDate_notnull"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button android:id="@+id/preferredDueDate_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button android:id="@+id/preferredDueDate_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<TextView android:id="@+id/hiddenUntil_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hiddenUntil_label"
style="@style/TextAppearance.EditEvent_Label"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/hiddenUntil_notnull"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button android:id="@+id/hiddenUntil_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button android:id="@+id/hiddenUntil_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
<!-- TODO: blocking on -->
<!-- MISC FIELDS -->
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="@android:drawable/divider_horizontal_dark"
/>
<LinearLayout android:id="@+id/dates_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dip">
<TextView android:id="@+id/notes_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/notes_label"
style="@style/TextAppearance.EditEvent_Label"/>
<EditText android:id="@+id/notes"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:scrollbars="vertical"
android:gravity="top"
android:hint="@string/notes_hint"
android:capitalize="sentences"
android:singleLine="false" />
<TextView android:id="@+id/elapsedDuration_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/elapsedDuration_label"
style="@style/TextAppearance.EditEvent_Label"/>
<Button android:id="@+id/elapsedDuration"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<!-- SAVE -->
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="@android:drawable/divider_horizontal_dark"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="5dip"
android:baselineAligned="false">
<Button android:id="@+id/save"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/save_label"
/>
<Button android:id="@+id/discard"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/discard_label"
/>
<Button android:id="@+id/delete"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/delete_label"
/>
</LinearLayout>
</LinearLayout>
</ScrollView>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/tasklist_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView android:id="@+id/tasklist"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button android:id="@+id/addtask"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="@string/addtask_label"/>
</LinearLayout>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/row_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:focusable="true"
android:background="@drawable/transparent_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<CheckBox android:id="@+id/cb1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dip"/>
<ImageView android:id="@+id/image1"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:paddingLeft="5dip"/>
<TextView android:id="@+id/text1"
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:paddingLeft="5dip"
android:singleLine="true"/>
</LinearLayout>

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:id="@+id/view_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="@+id/name"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceLargeInverse"
android:gravity="center_horizontal"
android:padding="5dip"/>
<!-- Button Row -->
<LinearLayout android:id="@+id/button_layout"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="15dip">
<Button android:id="@+id/timerButton"
android:gravity="center"
android:layout_weight="0.5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/startTimer_label"/>
<Button android:id="@+id/progress"
android:gravity="center"
android:layout_weight="0.5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px" />
<Button android:id="@+id/edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/edit_label"/>
<!-- Elapsed Time -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:text="@string/taskView_elapsed"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_elapsed"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<!-- Estimated Time -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:src="@android:drawable/divider_horizontal_bright" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:text="@string/taskView_estimated"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_estimated"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<!-- Absolute Deadline -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:src="@android:drawable/divider_horizontal_bright" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:text="@string/taskView_definiteDueDate"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_definiteDueDate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<!-- Goal Deadline -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:src="@android:drawable/divider_horizontal_bright" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:text="@string/taskView_preferredDueDate"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_preferredDueDate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<!-- Notes -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:src="@android:drawable/divider_horizontal_bright" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:text="@string/taskView_notes"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_notes"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
</LinearLayout>
</ScrollView>

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 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.
-->
<resources>
<!-- Choices for the "Reminder minutes" spinner.
These must be kept in sync with the reminder_minutes_values array.
-->
<string-array name="reminder_minutes_labels">
<item>5 minutes</item>
<item>10 minutes</item>
<item>15 minutes</item>
<item>20 minutes</item>
<item>25 minutes</item>
<item>30 minutes</item>
<item>45 minutes</item>
<item>1 hour</item>
<item>2 hours</item>
<item>3 hours</item>
<item>12 hours</item>
<item>24 hours</item>
<item>2 days</item>
<item>1 week</item>
</string-array>
<string-array name="reminder_minutes_values">
<item>"5"</item>
<item>"10"</item>
<item>"15"</item>
<item>"20"</item>
<item>"25"</item>
<item>"30"</item>
<item>"45"</item>
<item>"60"</item>
<item>"120"</item>
<item>"180"</item>
<item>"720"</item>
<item>"1440"</item>
<item>"2880"</item>
<item>"10080"</item>
</string-array>
<string-array name="preferences_default_reminder_labels">
<item>None</item>
<item>5 minutes</item>
<item>10 minutes</item>
<item>15 minutes</item>
<item>20 minutes</item>
<item>25 minutes</item>
<item>30 minutes</item>
<item>45 minutes</item>
<item>1 hour</item>
<item>2 hours</item>
<item>3 hours</item>
<item>12 hours</item>
<item>24 hours</item>
<item>2 days</item>
<item>1 week</item>
</string-array>
<string-array name="preferences_default_reminder_values">
<item>"0"</item>
<item>"5"</item>
<item>"10"</item>
<item>"15"</item>
<item>"20"</item>
<item>"25"</item>
<item>"30"</item>
<item>"45"</item>
<item>"60"</item>
<item>"120"</item>
<item>"180"</item>
<item>"720"</item>
<item>"1440"</item>
<item>"2880"</item>
<item>"10080"</item>
</string-array>
<string-array name="preferences_alert_type_labels">
<item>Alert</item>
<item>Status bar notification</item>
<item>Off</item>
</string-array>
<string-array name="preferences_alert_type_values">
<item>"0"</item>
<item>"1"</item>
<item>"2"</item>
</string-array>
<string-array name="availability">
<item>Busy</item>
<item>Available</item>
</string-array>
<string-array name="visibility">
<item>Default</item>
<item>Private</item>
<item>Public</item>
</string-array>
<!-- Order matters, and note that the preference for which day the week starts on is handled
elsewhere (and needn't be addressed here). -->
<string-array name="day_labels">
<item>Sunday</item>
<item>Monday</item>
<item>Tuesday</item>
<item>Wednesday</item>
<item>Thursday</item>
<item>Friday</item>
<item>Saturday</item>
</string-array>
<string-array name="ordinal_labels">
<item>first</item>
<item>second</item>
<item>third</item>
<item>fourth</item>
<item>last</item>
</string-array>
<!-- Invitation responses -->
<string-array name="response_labels1">
<item>(No response)</item>
<item>Yes</item>
<item>Maybe</item>
<item>No</item>
</string-array>
<string-array name="response_labels2">
<item>Yes</item>
<item>Maybe</item>
<item>No</item>
</string-array>
<!-- The corresponding indices are defined in DeleteEventHelper.java -->
<string-array name="delete_repeating_labels">
<item>Only this event</item>
<item>This &amp; future events</item>
<item>All events</item>
</string-array>
<!-- The corresponding indices are defined in DeleteEventHelper.java -->
<!-- This is the same array as above (the "delete_repeating_labels" array,
except that the first element "Only this event" is removed. This
array exists to work-around a bug in the CalendarProvider and sync
code where you can't delete one instance of a repeating event that
was created on the phone until that event has been synced to the server.
-->
<string-array name="delete_repeating_labels_no_selected">
<item>This &amp; future events</item>
<item>All events</item>
</string-array>
</resources>

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/Calendar/assets/res/any/colors.xml
**
** Copyright 2006, 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.
*/
-->
<resources>
<color name="task_list_overdue">#ffff4444</color>
<color name="task_list_done">#ffaaaaaa</color>
<color name="view_header_done">#ff83ffa9</color>
<color name="view_table_values">#ffbbbbbb</color>
<color name="view_table_overdue">#ffff0000</color>
<color name="importance_1">#ffe36150</color>
<color name="importance_2">#ffe3ad50</color>
<color name="importance_3">#ffffffff</color>
<color name="importance_4">#ffb5b0a8</color>
</resources>

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- application -->
<string name="app_name">Astrid</string>
<!-- General String Constants -->
<skip />
<!-- Importance Labels -->
<string name="importance_1">I need to do this</string>
<string name="importance_2">I really should do this</string>
<string name="importance_3">It would be nice to do this</string>
<string name="importance_4">Who cares!</string>
<!-- Plurals -->
<plurals name="Ntasks">
<item quantity="one">1 Task</item>
<item quantity="other">%d Tasks</item>
</plurals>
<!-- Time Constants -->
<plurals name="Ndays">
<item quantity="one">1 Day</item>
<item quantity="other">%d Days</item>
</plurals>
<plurals name="Nhours">
<item quantity="one">1 Hour</item>
<item quantity="other">%d Hours</item>
</plurals>
<plurals name="Nminutes">
<item quantity="one">1 Minute</item>
<item quantity="other">%d Minutes</item>
</plurals>
<plurals name="Nseconds">
<item quantity="one">1 Second</item>
<item quantity="other">%d Seconds</item>
</plurals>
<!-- TaskList -->
<skip />
<string name="taskList_titlePrefix">Astrid: </string>
<string name="taskList_hiddenSuffix"> hidden</string>
<string name="addtask_label">New Task</string>
<string name="taskList_menu_insert">Add</string>
<string name="taskList_menu_tags">Tags</string>
<string name="taskList_menu_settings">Settings</string>
<string name="taskList_menu_filters">Filters</string>
<string name="taskList_context_edit">Edit Task</string>
<string name="taskList_context_delete">Delete Task</string>
<string name="taskList_filter_title">Filters</string>
<string name="taskList_filter_hidden">Hidden/Blocked Tasks</string>
<string name="taskList_filter_done">Completed Tasks</string>
<!-- TaskEdit -->
<skip />
<string name="taskEdit_titleGeneric">Astrid: Editing Task</string>
<string name="taskEdit_titlePrefix">Astrid: Editing </string>
<string name="name_label">What</string>
<string name="name_hint">Task Description</string>
<string name="estimatedDuration_label">Estimated Time Needed</string>
<string name="elapsedDuration_label">Time Already Spent on Task</string>
<string name="importance_label">How important is it?</string>
<string name="tags_label">Tags</string>
<string name="definiteDueDate_label">Absolute Deadline</string>
<string name="preferredDueDate_label">Goal Deadline</string>
<string name="hiddenUntil_label">Hide Until This Date</string>
<string name="blockingOn_label">Blocking On</string>
<string name="notes_label">Notes</string>
<string name="notes_hint">Enter Task Notes</string>
<string name="minutes_dialog">Time (in minutes)</string>
<string name="save_label">Save</string>
<string name="discard_label">Discard</string>
<string name="delete_label">Delete</string>
<string name="blank_button_title">Click to Set</string>
<string name="taskEdit_menu_save">Save</string>
<!-- TaskView -->
<skip />
<string name="taskView_title">Astrid: Task Properties</string>
<string name="startTimer_label">Start Timer</string>
<string name="stopTimer_label">Stop Timer</string>
<string name="progress_suffix">% Done</string>
<string name="edit_label">Edit Task</string>
<string name="taskView_elapsed">Elapsed Time</string>
<string name="taskView_estimated">Estimated Time</string>
<string name="taskView_definiteDueDate">Absolute Deadline</string>
<string name="taskView_preferredDueDate">Goal Deadline</string>
<string name="taskView_notes">Task Notes</string>
<string name="overdue_suffix"> Overdue</string>
<string name="progress_dialog">% of Task Finished</string>
<!-- Utilities -->
<skip />
<string name="delete_title">Delete</string>
<string name="delete_this_task_title">Delete this task?</string>
</resources>

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/Calendar/assets/res/any/styles.xml
**
** Copyright 2006, 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.
*/
-->
<resources>
<style name="Alert" parent="android:Theme.Dialog">
<item name="android:windowBackground">@null</item>
</style>
<style name="MonthView_DayLabel">
<item name="android:layout_width">29dip</item>
<item name="android:layout_height">fill_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:gravity">center</item>
<item name="android:paddingTop">2dip</item>
<item name="android:paddingBottom">2dip</item>
<item name="android:textAppearance">@style/TextAppearance.MonthView_DayLabel</item>
</style>
<style name="TextAppearance" parent="android:TextAppearance">
</style>
<style name="TextAppearance.MonthView_DayLabel">
<item name="android:textSize">14sp</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">@android:color/white</item>
</style>
<style name="TextAppearance.AgendaView_ValueLabel">
<item name="android:textSize">14sp</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="TextAppearance.EditEvent_Label">
<item name="android:textSize">14sp</item>
<item name="android:textStyle">bold</item>
</style>
<style name="TextAppearance.Alert_Title">
<item name="android:textSize">18sp</item>
<item name="android:textColor">@android:color/white</item>
</style>
<style name="TextAppearance.Alert_Label">
<item name="android:textSize">14sp</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">@android:color/white</item>
</style>
<style name="TextAppearance.Alert_Value">
<item name="android:textSize">14sp</item>
<item name="android:textColor">@android:color/white</item>
</style>
</resources>

@ -0,0 +1,223 @@
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package com.timsu.astrid;
public final class R {
public static final class array {
public static final int availability=0x7f040006;
/** Order matters, and note that the preference for which day the week starts on is handled
elsewhere (and needn't be addressed here).
*/
public static final int day_labels=0x7f040008;
/** The corresponding indices are defined in DeleteEventHelper.java
*/
public static final int delete_repeating_labels=0x7f04000c;
/** The corresponding indices are defined in DeleteEventHelper.java
This is the same array as above (the "delete_repeating_labels" array,
except that the first element "Only this event" is removed. This
array exists to work-around a bug in the CalendarProvider and sync
code where you can't delete one instance of a repeating event that
was created on the phone until that event has been synced to the server.
*/
public static final int delete_repeating_labels_no_selected=0x7f04000d;
public static final int ordinal_labels=0x7f040009;
public static final int preferences_alert_type_labels=0x7f040004;
public static final int preferences_alert_type_values=0x7f040005;
public static final int preferences_default_reminder_labels=0x7f040002;
public static final int preferences_default_reminder_values=0x7f040003;
/** Choices for the "Reminder minutes" spinner.
These must be kept in sync with the reminder_minutes_values array.
*/
public static final int reminder_minutes_labels=0x7f040000;
public static final int reminder_minutes_values=0x7f040001;
/** Invitation responses
*/
public static final int response_labels1=0x7f04000a;
public static final int response_labels2=0x7f04000b;
public static final int visibility=0x7f040007;
}
public static final class attr {
}
public static final class color {
public static final int importance_1=0x7f050005;
public static final int importance_2=0x7f050006;
public static final int importance_3=0x7f050007;
public static final int importance_4=0x7f050008;
public static final int task_list_done=0x7f050001;
public static final int task_list_overdue=0x7f050000;
public static final int view_header_done=0x7f050002;
public static final int view_table_overdue=0x7f050004;
public static final int view_table_values=0x7f050003;
}
public static final class drawable {
public static final int highlight_pressed=0x7f020000;
public static final int highlight_selected=0x7f020001;
public static final int ic_dialog_time=0x7f020002;
public static final int icon=0x7f020003;
public static final int strikeout=0x7f020004;
public static final int timepicker_down_btn=0x7f020005;
public static final int timepicker_down_disabled=0x7f020006;
public static final int timepicker_down_disabled_focused=0x7f020007;
public static final int timepicker_down_normal=0x7f020008;
public static final int timepicker_down_pressed=0x7f020009;
public static final int timepicker_down_selected=0x7f02000a;
public static final int timepicker_input=0x7f02000b;
public static final int timepicker_input_disabled=0x7f02000c;
public static final int timepicker_input_normal=0x7f02000d;
public static final int timepicker_input_pressed=0x7f02000e;
public static final int timepicker_input_selected=0x7f02000f;
public static final int timepicker_up_btn=0x7f020010;
public static final int timepicker_up_disabled=0x7f020011;
public static final int timepicker_up_disabled_focused=0x7f020012;
public static final int timepicker_up_normal=0x7f020013;
public static final int timepicker_up_pressed=0x7f020014;
public static final int timepicker_up_selected=0x7f020015;
public static final int transparent_button=0x7f020016;
}
public static final class id {
public static final int addtask=0x7f090023;
public static final int button_layout=0x7f090029;
public static final int cb1=0x7f090025;
public static final int cell_definiteDueDate=0x7f09002f;
public static final int cell_elapsed=0x7f09002d;
public static final int cell_estimated=0x7f09002e;
public static final int cell_notes=0x7f090031;
public static final int cell_preferredDueDate=0x7f090030;
public static final int dates_container=0x7f09000d;
public static final int decrement=0x7f090002;
public static final int definiteDueDate_date=0x7f090010;
public static final int definiteDueDate_label=0x7f09000e;
public static final int definiteDueDate_notnull=0x7f09000f;
public static final int definiteDueDate_time=0x7f090011;
public static final int delete=0x7f090020;
public static final int discard=0x7f09001f;
public static final int edit=0x7f09002c;
public static final int elapsedDuration=0x7f09001d;
public static final int elapsedDuration_label=0x7f09001c;
public static final int estimatedDuration=0x7f09000a;
public static final int estimatedDuration_label=0x7f090009;
public static final int event=0x7f090005;
public static final int hiddenUntil_date=0x7f090018;
public static final int hiddenUntil_label=0x7f090016;
public static final int hiddenUntil_notnull=0x7f090017;
public static final int hiddenUntil_time=0x7f090019;
public static final int image1=0x7f090026;
public static final int importance=0x7f09000c;
public static final int importance_label=0x7f09000b;
public static final int increment=0x7f090000;
public static final int name=0x7f090007;
public static final int name_label=0x7f090006;
public static final int notes=0x7f09001b;
public static final int notes_label=0x7f09001a;
public static final int numberPicker=0x7f090003;
public static final int preferredDueDate_date=0x7f090014;
public static final int preferredDueDate_label=0x7f090012;
public static final int preferredDueDate_notnull=0x7f090013;
public static final int preferredDueDate_time=0x7f090015;
public static final int progress=0x7f09002b;
public static final int properties_container=0x7f090008;
public static final int row_layout=0x7f090024;
public static final int save=0x7f09001e;
public static final int scroll_view=0x7f090004;
public static final int tasklist=0x7f090022;
public static final int tasklist_layout=0x7f090021;
public static final int text1=0x7f090027;
public static final int timepicker_input=0x7f090001;
public static final int timerButton=0x7f09002a;
public static final int view_layout=0x7f090028;
}
public static final class layout {
public static final int number_picker=0x7f030000;
public static final int number_picker_dialog=0x7f030001;
public static final int task_edit=0x7f030002;
public static final int task_list=0x7f030003;
public static final int task_list_row=0x7f030004;
public static final int task_view=0x7f030005;
}
public static final class plurals {
/** Time Constants
*/
public static final int Ndays=0x7f070001;
public static final int Nhours=0x7f070002;
public static final int Nminutes=0x7f070003;
public static final int Nseconds=0x7f070004;
/** Plurals
*/
public static final int Ntasks=0x7f070000;
}
public static final class string {
public static final int addtask_label=0x7f060007;
/** application
*/
public static final int app_name=0x7f060000;
public static final int blank_button_title=0x7f060023;
public static final int blockingOn_label=0x7f06001c;
public static final int definiteDueDate_label=0x7f060019;
public static final int delete_label=0x7f060022;
public static final int delete_this_task_title=0x7f060032;
public static final int delete_title=0x7f060031;
public static final int discard_label=0x7f060021;
public static final int edit_label=0x7f060029;
public static final int elapsedDuration_label=0x7f060016;
public static final int estimatedDuration_label=0x7f060015;
public static final int hiddenUntil_label=0x7f06001b;
/** Importance Labels
*/
public static final int importance_1=0x7f060001;
public static final int importance_2=0x7f060002;
public static final int importance_3=0x7f060003;
public static final int importance_4=0x7f060004;
public static final int importance_label=0x7f060017;
public static final int minutes_dialog=0x7f06001f;
public static final int name_hint=0x7f060014;
public static final int name_label=0x7f060013;
public static final int notes_hint=0x7f06001e;
public static final int notes_label=0x7f06001d;
public static final int overdue_suffix=0x7f06002f;
public static final int preferredDueDate_label=0x7f06001a;
public static final int progress_dialog=0x7f060030;
public static final int progress_suffix=0x7f060028;
public static final int save_label=0x7f060020;
public static final int startTimer_label=0x7f060026;
public static final int stopTimer_label=0x7f060027;
public static final int tags_label=0x7f060018;
public static final int taskEdit_menu_save=0x7f060024;
public static final int taskEdit_titleGeneric=0x7f060011;
public static final int taskEdit_titlePrefix=0x7f060012;
public static final int taskList_context_delete=0x7f06000d;
public static final int taskList_context_edit=0x7f06000c;
public static final int taskList_filter_done=0x7f060010;
public static final int taskList_filter_hidden=0x7f06000f;
public static final int taskList_filter_title=0x7f06000e;
public static final int taskList_hiddenSuffix=0x7f060006;
public static final int taskList_menu_filters=0x7f06000b;
public static final int taskList_menu_insert=0x7f060008;
public static final int taskList_menu_settings=0x7f06000a;
public static final int taskList_menu_tags=0x7f060009;
public static final int taskList_titlePrefix=0x7f060005;
public static final int taskView_definiteDueDate=0x7f06002c;
public static final int taskView_elapsed=0x7f06002a;
public static final int taskView_estimated=0x7f06002b;
public static final int taskView_notes=0x7f06002e;
public static final int taskView_preferredDueDate=0x7f06002d;
public static final int taskView_title=0x7f060025;
}
public static final class style {
public static final int Alert=0x7f080000;
public static final int MonthView_DayLabel=0x7f080001;
public static final int TextAppearance=0x7f080002;
public static final int TextAppearance_AgendaView_ValueLabel=0x7f080004;
public static final int TextAppearance_Alert_Label=0x7f080007;
public static final int TextAppearance_Alert_Title=0x7f080006;
public static final int TextAppearance_Alert_Value=0x7f080008;
public static final int TextAppearance_EditEvent_Label=0x7f080005;
public static final int TextAppearance_MonthView_DayLabel=0x7f080003;
}
}

@ -0,0 +1,459 @@
/*
* ASTRID: Android's Simple Task Recording Dame
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.timsu.astrid.activities;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.CompoundButton.OnCheckedChangeListener;
import com.timsu.astrid.R;
import com.timsu.astrid.data.enums.Importance;
import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForEdit;
import com.timsu.astrid.utilities.DateUtilities;
import com.timsu.astrid.widget.NumberPicker;
import com.timsu.astrid.widget.NumberPickerDialog;
import com.timsu.astrid.widget.NumberPickerDialog.OnNumberPickedListener;
public class TaskEdit extends TaskModificationActivity<TaskModelForEdit> {
private static final int SAVE_ID = Menu.FIRST;
private static final int DISCARD_ID = Menu.FIRST + 1;
private static final int DELETE_ID = Menu.FIRST + 2;
private EditText name;
private Spinner importance;
private TimeDurationControlSet estimatedDuration;
private TimeDurationControlSet elapsedDuration;
private DateControlSet definiteDueDate;
private DateControlSet preferredDueDate;
private DateControlSet hiddenUntil;
private EditText notes;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.task_edit);
setUpUIComponents();
setUpListeners();
populateFields();
}
@Override
protected TaskModelForEdit getModel(TaskIdentifier identifier) {
if (identifier != null)
return controller.fetchTaskForEdit(identifier);
else
return controller.createNewTaskForEdit();
}
// --- data saving and retrieving
private void populateFields() {
Resources r = getResources();
if(model.getCursor() != null)
startManagingCursor(model.getCursor());
name.setText(model.getName());
if(model.getName().length() > 0)
setTitle(new StringBuilder().
append(r.getString(R.string.taskEdit_titlePrefix)).
append(" ").
append(model.getName()));
estimatedDuration.setTimeElapsed(model.getEstimatedSeconds());
elapsedDuration.setTimeElapsed(model.getElapsedSeconds());
importance.setSelection(model.getImportance().ordinal());
definiteDueDate.setDate(model.getDefiniteDueDate());
preferredDueDate.setDate(model.getPreferredDueDate());
hiddenUntil.setDate(model.getHiddenUntil());
notes.setText(model.getNotes());
}
private void save() {
model.setName(name.getText().toString());
model.setEstimatedSeconds(estimatedDuration.getTimeDurationInSeconds());
model.setElapsedSeconds(elapsedDuration.getTimeDurationInSeconds());
model.setImportance(Importance.values()[importance.getSelectedItemPosition()]);
model.setDefiniteDueDate(definiteDueDate.getDate());
model.setPreferredDueDate(preferredDueDate.getDate());
model.setHiddenUntil(hiddenUntil.getDate());
model.setNotes(notes.getText().toString());
try {
if(!controller.saveTask(model))
throw new RuntimeException("Unable to save task: false");
} catch (RuntimeException e) {
Log.e(getClass().getSimpleName(), "Error saving task!", e);
}
}
// --- user interface components
private void setUpUIComponents() {
Resources r = getResources();
setTitle(new StringBuilder()
.append(r.getString(R.string.app_name))
.append(": ")
.append(r.getString(R.string.taskEdit_titleGeneric)));
name = (EditText)findViewById(R.id.name);
importance = (Spinner)findViewById(R.id.importance);
estimatedDuration = new TimeDurationControlSet(R.id.estimatedDuration);
elapsedDuration = new TimeDurationControlSet(R.id.elapsedDuration);
definiteDueDate = new DateControlSet(R.id.definiteDueDate_notnull,
R.id.definiteDueDate_date, R.id.definiteDueDate_time);
preferredDueDate = new DateControlSet(R.id.preferredDueDate_notnull,
R.id.preferredDueDate_date, R.id.preferredDueDate_time);
hiddenUntil = new DateControlSet(R.id.hiddenUntil_notnull,
R.id.hiddenUntil_date, R.id.hiddenUntil_time);
notes = (EditText)findViewById(R.id.notes);
// set up for each field
ImportanceAdapter importanceAdapter = new ImportanceAdapter(this,
android.R.layout.simple_spinner_item,
android.R.layout.simple_spinner_dropdown_item,
Importance.values());
importance.setAdapter(importanceAdapter);
}
/** Display importance with proper formatting */
private class ImportanceAdapter extends ArrayAdapter<Importance> {
private int textViewResourceId, dropDownResourceId;
private LayoutInflater inflater;
public ImportanceAdapter(Context context, int textViewResourceId,
int dropDownResourceId, Importance[] objects) {
super(context, textViewResourceId, objects);
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.textViewResourceId = textViewResourceId;
this.dropDownResourceId = dropDownResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent, textViewResourceId, true);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent, dropDownResourceId, true);
}
public View getView(int position, View convertView, ViewGroup parent,
int resource, boolean setColors) {
View view;
TextView text;
Resources r = getResources();
if (convertView == null) {
view = inflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
text = (TextView) view;
} catch (ClassCastException e) {
Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"ArrayAdapter requires the resource ID to be a TextView", e);
}
text.setText(r.getString(getItem(position).getLabelResource()));
if(setColors)
text.setBackgroundColor(r.getColor(getItem(position).getColorResource()));
return view;
}
}
/** Set up button listeners */
private void setUpListeners() {
Button saveButton = (Button) findViewById(R.id.save);
saveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
saveButtonClick();
}
});
Button discardButton = (Button) findViewById(R.id.discard);
discardButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
discardButtonClick();
}
});
Button deleteButton = (Button) findViewById(R.id.delete);
if(model.getTaskIdentifier() == null)
deleteButton.setVisibility(View.GONE);
deleteButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
deleteButtonClick();
}
});
}
private void saveButtonClick() {
save();
setResult(RESULT_OK);
finish();
}
private void discardButtonClick() {
setResult(RESULT_CANCELED);
finish();
}
private void deleteButtonClick() {
new AlertDialog.Builder(this)
.setTitle(R.string.delete_title)
.setMessage(R.string.delete_this_task_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
controller.deleteTask(model.getTaskIdentifier());
setResult(RESULT_OK);
finish();
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
case SAVE_ID:
saveButtonClick();
return true;
case DISCARD_ID:
discardButtonClick();
return true;
case DELETE_ID:
deleteButtonClick();
return true;
}
return super.onMenuItemSelected(featureId, item);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem item;
item = menu.add(Menu.NONE, SAVE_ID, 0, R.string.save_label);
item.setIcon(android.R.drawable.ic_menu_save);
item.setAlphabeticShortcut('s');
item = menu.add(Menu.NONE, DISCARD_ID, 0, R.string.discard_label);
item.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
item.setAlphabeticShortcut('c');
item = menu.add(Menu.NONE, DISCARD_ID, 0, R.string.delete_label);
item.setIcon(android.R.drawable.ic_menu_delete);
item.setAlphabeticShortcut('d');
return true;
}
@Override
protected void onPause() {
super.onPause();
save();
}
@Override
protected void onResume() {
super.onResume();
populateFields();
}
// --- date/time methods and helper classes
private class TimeDurationControlSet implements OnNumberPickedListener,
View.OnClickListener {
private Button timeButton;
private int timeDuration;
private final NumberPickerDialog dialog =
new NumberPickerDialog(TaskEdit.this, this,
getResources().getString(R.string.minutes_dialog),
0, 5, 0, 999);
public TimeDurationControlSet(int timeButtonId) {
timeButton = (Button)findViewById(timeButtonId);
timeButton.setOnClickListener(this);
}
public int getTimeDurationInSeconds() {
return timeDuration;
}
public void setTimeElapsed(Integer timeDurationInSeconds) {
if(timeDurationInSeconds == null)
timeDurationInSeconds = 0;
timeDuration = timeDurationInSeconds;
Resources r = getResources();
if(timeDurationInSeconds == 0) {
timeButton.setText(r.getString(R.string.blank_button_title));
return;
}
timeButton.setText(DateUtilities.getDurationString(r,
timeDurationInSeconds, 2));
dialog.setInitialValue(timeDuration/60);
}
@Override
/** Called when NumberPicker activity is completed */
public void onNumberPicked(NumberPicker view, int value) {
setTimeElapsed(value * 60);
}
/** Called when time button is clicked */
public void onClick(View v) {
dialog.show();
}
}
private static final Format dateFormatter = new SimpleDateFormat("EEE, MMM d, yyyy");
private static final Format timeFormatter = new SimpleDateFormat("h:mm a");
private class DateControlSet implements OnTimeSetListener,
OnDateSetListener, View.OnClickListener {
private CheckBox activatedCheckBox;
private Button dateButton;
private Button timeButton;
private Date date;
public DateControlSet(int checkBoxId, int dateButtonId, int timeButtonId) {
activatedCheckBox = (CheckBox)findViewById(checkBoxId);
dateButton = (Button)findViewById(dateButtonId);
timeButton = (Button)findViewById(timeButtonId);
activatedCheckBox.setOnCheckedChangeListener(
new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
dateButton.setEnabled(isChecked);
timeButton.setEnabled(isChecked);
}
});
dateButton.setOnClickListener(this);
timeButton.setOnClickListener(this);
}
public Date getDate() {
if(!activatedCheckBox.isChecked())
return null;
return date;
}
/** Initialize the components for the given date field */
public void setDate(Date newDate) {
this.date = newDate;
if(newDate == null) {
date = new Date();
date.setMinutes(0);
}
activatedCheckBox.setChecked(newDate != null);
dateButton.setEnabled(newDate != null);
timeButton.setEnabled(newDate != null);
updateDate();
updateTime();
}
public void onDateSet(DatePicker view, int year, int month, int monthDay) {
date.setYear(year - 1900);
date.setMonth(month);
date.setDate(monthDay);
updateDate();
}
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
date.setHours(hourOfDay);
date.setMinutes(minute);
updateTime();
}
public void updateDate() {
dateButton.setText(dateFormatter.format(date));
}
public void updateTime() {
timeButton.setText(timeFormatter.format(date));
}
public void onClick(View v) {
if(v == timeButton)
new TimePickerDialog(TaskEdit.this, this, date.getHours(),
date.getMinutes(), false).show();
else
new DatePickerDialog(TaskEdit.this, this, 1900 +
date.getYear(), date.getMonth(), date.getDate()).show();
}
}
}

@ -0,0 +1,401 @@
/*
* ASTRID: Android's Simple Task Recording Dame
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.timsu.astrid.activities;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.CompoundButton.OnCheckedChangeListener;
import com.timsu.astrid.R;
import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForList;
/** Primary view for the Astrid Application. Lists all of the tasks in the
* system, and allows users to edit them.
*
* @author Tim Su (timsu@stanfordalumni.org)
*
*/
public class TaskList extends Activity {
private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_VIEW = 1;
private static final int ACTIVITY_EDIT = 2;
private static final int INSERT_ID = Menu.FIRST;
private static final int FILTERS_ID = Menu.FIRST + 1;
private static final int TAGS_ID = Menu.FIRST + 2;
private static final int CONTEXT_EDIT_ID = Menu.FIRST + 10;
private static final int CONTEXT_DELETE_ID = Menu.FIRST + 11;
private static final int CONTEXT_FILTER_HIDDEN = Menu.FIRST + 20;
private static final int CONTEXT_FILTER_DONE = Menu.FIRST + 21;
private TaskController controller;
private ListView listView;
private Button addButton;
private boolean filterShowHidden = false;
private boolean filterShowDone = false;
/** Called when loading up the activity for the first time */
private void onLoad() {
controller = new TaskController(this);
controller.open();
listView = (ListView)findViewById(R.id.tasklist);
addButton = (Button)findViewById(R.id.addtask);
addButton.setOnClickListener(new
View.OnClickListener() {
@Override
public void onClick(View v) {
createTask();
}
});
fillData();
// filters context menu
listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
if(menu.hasVisibleItems())
return;
MenuItem item = menu.add(Menu.NONE, CONTEXT_FILTER_HIDDEN, Menu.NONE,
R.string.taskList_filter_hidden);
item.setCheckable(true);
item.setChecked(filterShowHidden);
item = menu.add(Menu.NONE, CONTEXT_FILTER_DONE, Menu.NONE,
R.string.taskList_filter_done);
item.setCheckable(true);
item.setChecked(filterShowDone);
menu.setHeaderTitle(R.string.taskList_filter_title);
}
});
}
/** Fill in the Task List with our tasks */
private void fillData() {
Resources r = getResources();
// get the database cursor
Cursor tasksCursor;
if(filterShowDone)
tasksCursor = controller.getAllTaskListCursor();
else
tasksCursor = controller.getActiveTaskListCursor();
startManagingCursor(tasksCursor);
int totalTasks = tasksCursor.getCount();
List<TaskModelForList> taskArray =
controller.createTaskListFromCursor(tasksCursor, !filterShowHidden);
int hiddenTasks = totalTasks - taskArray.size();
// hide "add" button if we have a few tasks
if(taskArray.size() > 2)
addButton.setVisibility(View.GONE);
// set up the title
StringBuilder title = new StringBuilder().
append(r.getString(R.string.taskList_titlePrefix)).
append(" ").append(r.getQuantityString(R.plurals.Ntasks,
taskArray.size(), taskArray.size()));
if(hiddenTasks > 0)
title.append(" (").append(hiddenTasks).append(" ").
append(r.getString(R.string.taskList_hiddenSuffix)).append(")");
setTitle(title);
// set up our adapter
TaskListAdapter tasks = new TaskListAdapter(this,
R.layout.task_list_row, taskArray);
listView.setAdapter(tasks);
listView.setItemsCanFocus(true);
// list view listener
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
TaskModelForList task = (TaskModelForList)view.getTag();
Intent intent = new Intent(TaskList.this, TaskView.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.
getTaskIdentifier().getId());
startActivityForResult(intent, ACTIVITY_VIEW);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
fillData();
}
// --- list adapter
private class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
private List<TaskModelForList> objects;
private int resource;
private LayoutInflater inflater;
public TaskListAdapter(Context context, int resource,
List<TaskModelForList> objects) {
super(context, resource, objects);
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.objects = objects;
this.resource = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
view = inflater.inflate(resource, parent, false);
setupView(view, objects.get(position));
addListeners(position, view);
return view;
}
public void setupView(View view, final TaskModelForList task) {
Resources r = getResources();
// set up basic properties
final TextView name = ((TextView)view.findViewById(R.id.text1));
final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1));
final ImageView timer = ((ImageView)view.findViewById(R.id.image1));
view.setTag(task);
progress.setTag(task);
name.setText(task.getName());
if(task.getTimerStart() != null)
timer.setImageDrawable(r.getDrawable(R.drawable.ic_dialog_time));
progress.setChecked(task.isTaskCompleted());
setTaskAppearance(name, task);
}
public void addListeners(final int position, final View view) {
final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1));
final TextView name = ((TextView)view.findViewById(R.id.text1));
// clicking the check box
progress.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
TaskModelForList task = (TaskModelForList)buttonView.getTag();
int newProgressPercentage;
if(isChecked)
newProgressPercentage = TaskModelForList.getCompletedPercentage();
else
newProgressPercentage = 0;
// this takes care of initial checked change
if(newProgressPercentage != task.getProgressPercentage()) {
task.setProgressPercentage(newProgressPercentage);
controller.saveTask(task);
setTaskAppearance(name, task);
}
}
});
// interacting with the text field
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listView.performItemClick(view, position, 0);
}
});
view.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
TaskModelForList task = (TaskModelForList)v.getTag();
int id = (int)task.getTaskIdentifier().getId();
menu.add(id, CONTEXT_EDIT_ID, Menu.NONE,
R.string.taskList_context_edit);
menu.add(id, CONTEXT_DELETE_ID, Menu.NONE,
R.string.taskList_context_delete);
menu.setHeaderTitle(task.getName());
}
});
}
public void setTaskAppearance(TextView name, TaskModelForList task) {
Resources r = getResources();
if(task.isTaskCompleted()) {
name.setBackgroundDrawable(r.getDrawable(R.drawable.strikeout));
name.setTextColor(r.getColor(R.color.task_list_done));
} else {
name.setBackgroundDrawable(null);
name.setTextColor(r.getColor(task.getTaskColorResource()));
}
}
}
// --- ui control handlers
private void createTask() {
Intent i = new Intent(this, TaskEdit.class);
startActivityForResult(i, ACTIVITY_CREATE);
}
private void deleteTask(final TaskIdentifier taskId) {
new AlertDialog.Builder(this)
.setTitle(R.string.delete_title)
.setMessage(R.string.delete_this_task_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
controller.deleteTask(taskId);
fillData();
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
case INSERT_ID:
createTask();
return true;
case FILTERS_ID:
listView.showContextMenu();
return true;
case TAGS_ID:
// TODO
return true;
case CONTEXT_EDIT_ID:
long id = item.getGroupId(); // hackhack =(
Intent intent = new Intent(TaskList.this, TaskEdit.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, id);
startActivityForResult(intent, ACTIVITY_EDIT);
return true;
case CONTEXT_DELETE_ID:
id = item.getGroupId();
deleteTask(new TaskIdentifier(id));
return true;
case CONTEXT_FILTER_HIDDEN:
filterShowHidden = !filterShowHidden;
fillData();
return true;
case CONTEXT_FILTER_DONE:
filterShowDone = !filterShowDone;
fillData();
return true;
}
return super.onMenuItemSelected(featureId, item);
}
// --- creating stuff
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.task_list);
onLoad();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem item;
item = menu.add(Menu.NONE, INSERT_ID, Menu.NONE,
R.string.taskList_menu_insert);
item.setIcon(android.R.drawable.ic_menu_add);
item.setAlphabeticShortcut('n');
item = menu.add(Menu.NONE, FILTERS_ID, Menu.NONE,
R.string.taskList_menu_filters);
item.setIcon(android.R.drawable.ic_menu_view);
item.setAlphabeticShortcut('f');
/*item = menu.add(Menu.NONE, TAGS_ID, Menu.NONE,
R.string.taskList_menu_tags);
item.setIcon(android.R.drawable.ic_menu_myplaces);
item.setAlphabeticShortcut('t');*/
/*item = menu.add(Menu.NONE, SETTINGS_ID, Menu.NONE,
R.string.taskList_menu_settings);
item.setIcon(android.R.drawable.ic_menu_preferences);
item.setAlphabeticShortcut('p');*/
return true;
}
@Override
protected void onDestroy() {
super.onDestroy();
controller.close();
}
}

@ -0,0 +1,48 @@
package com.timsu.astrid.activities;
import android.app.Activity;
import android.os.Bundle;
import com.timsu.astrid.data.task.AbstractTaskModel;
import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.data.task.TaskIdentifier;
public abstract class TaskModificationActivity<MODEL_TYPE extends
AbstractTaskModel> extends Activity {
public static final String LOAD_INSTANCE_TOKEN = "id";
protected TaskController controller;
protected MODEL_TYPE model;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
controller = new TaskController(this);
controller.open();
// check if we have a TaskIdentifier
TaskIdentifier identifier = null;
Bundle extras = getIntent().getExtras();
if(savedInstanceState != null) {
identifier = new TaskIdentifier(savedInstanceState.getLong(
LOAD_INSTANCE_TOKEN));
} else if(extras != null)
identifier = new TaskIdentifier(extras.getLong(
LOAD_INSTANCE_TOKEN));
model = getModel(identifier);
}
abstract protected MODEL_TYPE getModel(TaskIdentifier identifier);
@Override
protected void onDestroy() {
super.onDestroy();
controller.close();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(LOAD_INSTANCE_TOKEN, model.getTaskIdentifier().getId());
}
}

@ -0,0 +1,264 @@
/*
* ASTRID: Android's Simple Task Recording Dame
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.timsu.astrid.activities;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.timsu.astrid.R;
import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForView;
import com.timsu.astrid.utilities.DateUtilities;
import com.timsu.astrid.widget.NumberPicker;
import com.timsu.astrid.widget.NumberPickerDialog;
/** Task Properties view for the Astrid Application.
*
* @author Tim Su (timsu@stanfordalumni.org)
*
*/
public class TaskView extends TaskModificationActivity<TaskModelForView> {
private static final int ACTIVITY_EDIT = 0;
private TextView name;
private TextView elapsed;
private TextView estimated;
private TextView definiteDueDate;
private TextView preferredDueDate;
private TextView notes;
private Button timerButton;
private Button progress;
private NumberPickerDialog progressDialog;
private Handler handler;
private Timer updateTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.task_view);
handler = new Handler();
setUpUIComponents();
setUpListeners();
populateFields();
}
@Override
protected TaskModelForView getModel(TaskIdentifier identifier) {
if(identifier == null)
throw new IllegalArgumentException("Can't view null task!");
return controller.fetchTaskForView(identifier);
}
private void setUpUIComponents() {
Resources r = getResources();
setTitle(r.getString(R.string.taskView_title));
name = (TextView)findViewById(R.id.name);
elapsed = (TextView)findViewById(R.id.cell_elapsed);
estimated = (TextView)findViewById(R.id.cell_estimated);
definiteDueDate = (TextView)findViewById(R.id.cell_definiteDueDate);
preferredDueDate = (TextView)findViewById(R.id.cell_preferredDueDate);
notes = (TextView)findViewById(R.id.cell_notes);
timerButton = (Button)findViewById(R.id.timerButton);
progress = (Button)findViewById(R.id.progress);
progressDialog = new NumberPickerDialog(this,
new NumberPickerDialog.OnNumberPickedListener() {
@Override
public void onNumberPicked(NumberPicker view, int number) {
model.setProgressPercentage(number);
controller.saveTask(model);
updateProgressComponents();
}
}, r.getString(R.string.progress_dialog), 0, 10, 0, 100);
name.setTextSize(36);
final TimerTask elapsedTimeUpdater = new TimerTask() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
updateElapsedTimeText();
}
});
}
};
// update the UI
updateTimer = new Timer();
updateTimer.scheduleAtFixedRate(elapsedTimeUpdater, 0, 1000);
}
private void setUpListeners() {
Button edit = (Button)findViewById(R.id.edit);
edit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(TaskView.this, TaskEdit.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN,
model.getTaskIdentifier().getId());
startActivityForResult(intent, ACTIVITY_EDIT);
}
});
timerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(model.getTimerStart() == null) {
model.setTimerStart(new Date());
controller.saveTask(model);
} else {
long start = model.getTimerStart().getTime();
model.setTimerStart(null);
long secondsElapsed = (System.currentTimeMillis() - start)/1000;
model.setElapsedSeconds((int) (model.getElapsedSeconds() +
secondsElapsed));
controller.saveTask(model);
}
updateTimerButtonText();
}
});
progress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
progressDialog.show();
}
});
}
private void populateFields() {
final Resources r = getResources();
name.setText(model.getName());
estimated.setText(DateUtilities.getDurationString(r,
model.getEstimatedSeconds(), 2));
elapsed.setText(DateUtilities.getDurationString(r,
model.getElapsedSeconds(), 2));
formatDeadline(model.getDefiniteDueDate(), definiteDueDate);
formatDeadline(model.getPreferredDueDate(), preferredDueDate);
updateTimerButtonText();
updateProgressComponents();
if(model.getNotes().length() == 0)
((View)notes.getParent()).setVisibility(View.GONE);
else
notes.setText(model.getNotes());
}
private void formatDeadline(Date deadline, TextView view) {
Resources r = getResources();
if(deadline == null || model.isTaskCompleted()) {
((View)view.getParent()).setVisibility(View.GONE);
return;
}
int secondsToDeadline = (int) ((deadline.getTime() -
System.currentTimeMillis())/1000);
String text = DateUtilities.getDurationString(r,
Math.abs(secondsToDeadline), 2);
if(secondsToDeadline < 0) {
view.setTextColor(r.getColor(R.color.view_table_overdue));
view.setText(text + r.getString(R.string.overdue_suffix));
} else
view.setText(text);
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
if(resultCode == RESULT_CANCELED)
return;
// if user edits a task, take them straight to the listing page
setResult(resultCode);
finish();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
populateFields();
}
/** Update components that depend on elapsed time */
private void updateElapsedTimeText() {
Resources r = getResources();
int timeElapsed = model.getElapsedSeconds();
if(model.getTimerStart() != null) {
timeElapsed += (int) (System.currentTimeMillis() -
model.getTimerStart().getTime())/1000;
}
elapsed.setText(DateUtilities.getDurationString(r,
timeElapsed, Integer.MAX_VALUE));
}
/** Update components that depend on timer status */
private void updateTimerButtonText() {
Resources r = getResources();
if(model.getTimerStart() == null)
timerButton.setText(r.getString(R.string.startTimer_label));
else
timerButton.setText(r.getString(R.string.stopTimer_label));
}
/** Update components that depend on task progress */
private void updateProgressComponents() {
Resources r = getResources();
progress.setText(model.getProgressPercentage() +
r.getString(R.string.progress_suffix));
if(model.isTaskCompleted())
name.setBackgroundColor(r.getColor(R.color.view_header_done));
else
name.setBackgroundColor(r.getColor(model.getImportance().getColorResource()));
progressDialog.setInitialValue(model.getProgressPercentage());
}
@Override
/** Cancel the timer thread */
protected void onDestroy() {
super.onDestroy();
updateTimer.cancel();
}
}

@ -0,0 +1,38 @@
/*
* ASTRID: Android's Simple Task Recording Dame
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.timsu.astrid.data;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
abstract public class AbstractController {
protected Context context;
protected SQLiteDatabase database;
// special columns
public static final String KEY_ROWID = "_id";
// database names
protected static final String TASK_TABLE_NAME = "tasks";
}

@ -0,0 +1,183 @@
/*
* ASTRID: Android's Simple Task Recording Dame
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.timsu.astrid.data;
import java.util.Date;
import android.content.ContentValues;
import android.database.Cursor;
public abstract class AbstractModel {
/* Data Source Ordering:
*
* In order to return the best data, we want to check first what the user
* has explicitly set (setValues), then the values we have read out of
* the database (values), the database itself (cursor), then defaults
* (getDefaultValues)
*/
/** User set values */
protected ContentValues setValues = new ContentValues();
/** Cached values from database */
private ContentValues values = new ContentValues();
/** Cursor into the database */
private Cursor cursor = null;
// --- constructors
/** Construct a model from scratch */
public AbstractModel() {
// ...
}
/** Construct a model from a database object */
public AbstractModel(Cursor cursor) {
this.cursor = cursor;
}
// --- data source getters
/** Get the user-set values for this object */
public ContentValues getSetValues() {
return setValues;
}
/** Get the default values for this object */
abstract public ContentValues getDefaultValues();
/** Get a list of all field/value pairs merged across data sources */
public ContentValues getMergedValues() {
ContentValues mergedValues = new ContentValues();
mergedValues.putAll(getDefaultValues());
mergedValues.putAll(values);
mergedValues.putAll(setValues);
return mergedValues;
}
/** Return the database cursor */
public Cursor getCursor() {
return cursor;
}
// --- data retrieval for the different object types
protected String retrieveString(String field) {
if(setValues.containsKey(field))
return setValues.getAsString(field);
if(values.containsKey(field))
return values.getAsString(field);
// if we have a database to hit, do that now
if(cursor != null) {
String value = cursor.getString(cursor.getColumnIndexOrThrow(field));
values.put(field, value);
return value;
}
// do we have defaults?
ContentValues defaults = getDefaultValues();
if(defaults != null && defaults.containsKey(field))
return defaults.getAsString(field);
throw new UnsupportedOperationException("Could not read field " + field);
}
protected Integer retrieveInteger(String field) {
if(setValues.containsKey(field))
return setValues.getAsInteger(field);
if(values.containsKey(field))
return values.getAsInteger(field);
// if we have a database to hit, do that now
if(cursor != null) {
Integer value = cursor.getInt(cursor.getColumnIndexOrThrow(field));
values.put(field, value);
return value;
}
// do we have defaults?
ContentValues defaults = getDefaultValues();
if(defaults != null && defaults.containsKey(field))
return defaults.getAsInteger(field);
throw new UnsupportedOperationException("Could not read field " + field);
}
protected Long retrieveLong(String field) {
if(setValues.containsKey(field))
return setValues.getAsLong(field);
if(values.containsKey(field))
return values.getAsLong(field);
// if we have a database to hit, do that now
if(cursor != null) {
Long value = cursor.getLong(cursor.getColumnIndexOrThrow(field));
values.put(field, value);
return value;
}
// do we have defaults?
ContentValues defaults = getDefaultValues();
if(defaults != null && defaults.containsKey(field))
return defaults.getAsLong(field);
throw new UnsupportedOperationException("Could not read field " + field);
}
protected Double retrieveDouble(String field) {
if(setValues.containsKey(field))
return setValues.getAsDouble(field);
if(values.containsKey(field))
return values.getAsDouble(field);
// if we have a database to hit, do that now
if(cursor != null) {
Double value = cursor.getDouble(cursor.getColumnIndexOrThrow(field));
values.put(field, value);
return value;
}
// do we have defaults?
ContentValues defaults = getDefaultValues();
if(defaults != null && defaults.containsKey(field))
return defaults.getAsDouble(field);
throw new UnsupportedOperationException("Could not read field " + field);
}
// --- retrieving composite objects
protected Date retrieveDate(String field) {
Long time = retrieveLong(field);
if(time == null || time == 0)
return null;
return new Date(time);
}
}

@ -0,0 +1,32 @@
package com.timsu.astrid.data.enums;
import com.timsu.astrid.R;
public enum Importance {
// MOST IMPORTANT
LEVEL_1(R.string.importance_1, R.color.importance_1),
LEVEL_2(R.string.importance_2, R.color.importance_2),
LEVEL_3(R.string.importance_3, R.color.importance_3),
LEVEL_4(R.string.importance_4, R.color.importance_4),
// LEAST IMPORTANT
;
int label;
int color;
public static final Importance DEFAULT = LEVEL_2;
private Importance(int label, int color) {
this.label = label;
this.color = color;
}
public int getLabelResource() {
return label;
}
public int getColorResource() {
return color;
}
}

@ -0,0 +1,20 @@
package com.timsu.astrid.data.location;
public class GeoPoint {
private int latitude, longitude;
public GeoPoint(int latitude, int longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
public int getLatitudeE6() {
return latitude;
}
public int getLongitudeE6() {
return longitude;
}
}

@ -0,0 +1,301 @@
package com.timsu.astrid.data.task;
import java.util.Date;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.timsu.astrid.R;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.AbstractModel;
import com.timsu.astrid.data.enums.Importance;
/** Abstract model of a task. Subclasses implement the getters and setters
* they are interested in.
*
* @author timsu
*
*/
public abstract class AbstractTaskModel extends AbstractModel {
/** Version number of this model */
static final int VERSION = 1;
public static final int COMPLETE_PERCENTAGE = 100;
// field names
static final String NAME = "name";
static final String NOTES = "notes";
static final String PROGRESS_PERCENTAGE = "progressPercentage";
static final String IMPORTANCE = "importance";
static final String ESTIMATED_SECONDS = "estimatedSeconds";
static final String ELAPSED_SECONDS = "elapsedSeconds";
static final String TIMER_START = "timerStart";
static final String DEFINITE_DUE_DATE = "definiteDueDate";
static final String PREFERRED_DUE_DATE = "preferredDueDate";
static final String HIDDEN_UNTIL = "hiddenUntil";
static final String BLOCKING_ON = "blockingOn";
static final String NOTIFICATIONS = "notifications";
static final String CREATION_DATE = "creationDate";
static final String COMPLETION_DATE = "completionDate";
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
static {
defaultValues.put(NAME, "");
defaultValues.put(NOTES, "");
defaultValues.put(PROGRESS_PERCENTAGE, 0);
defaultValues.put(IMPORTANCE, Importance.DEFAULT.ordinal());
defaultValues.put(ESTIMATED_SECONDS, 0);
defaultValues.put(ELAPSED_SECONDS, 0);
defaultValues.put(TIMER_START, (Long)null);
defaultValues.put(DEFINITE_DUE_DATE, (Long)null);
defaultValues.put(PREFERRED_DUE_DATE, (Long)null);
defaultValues.put(HIDDEN_UNTIL, (Long)null);
defaultValues.put(BLOCKING_ON, (Long)null);
defaultValues.put(NOTIFICATIONS, 0);
defaultValues.put(COMPLETION_DATE, (Long)null);
}
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- database helper
/** Database Helper manages creating new tables and updating old ones */
static class TaskModelDatabaseHelper extends SQLiteOpenHelper {
String tableName;
TaskModelDatabaseHelper(Context context, String databaseName) {
super(context, databaseName, null, VERSION);
this.tableName = databaseName;
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = new StringBuilder().
append("CREATE TABLE ").append(tableName).append(" (").
append(AbstractController.KEY_ROWID).append(" integer primary key autoincrement, ").
append(NAME).append(" text not null,").
append(NOTES).append(" text not null,").
append(PROGRESS_PERCENTAGE).append(" integer not null,").
append(IMPORTANCE).append(" integer not null,").
append(ESTIMATED_SECONDS).append(" integer,").
append(ELAPSED_SECONDS).append(" integer,").
append(TIMER_START).append(" integer,").
append(DEFINITE_DUE_DATE).append(" integer,").
append(PREFERRED_DUE_DATE).append(" integer,").
append(HIDDEN_UNTIL).append(" integer,").
append(BLOCKING_ON).append(" integer,").
append(NOTIFICATIONS).append(" integer,").
append(CREATION_DATE).append(" integer,").
append(COMPLETION_DATE).append(" integer").
append(");").toString();
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(getClass().getSimpleName(), "Upgrading database from version " +
oldVersion + " to " + newVersion + ".");
switch(oldVersion) {
default:
// we don't know how to handle it... do the unfortunate thing
Log.e(getClass().getSimpleName(), "Unsupported migration, table dropped!");
db.execSQL("DROP TABLE IF EXISTS " + tableName);
onCreate(db);
}
}
}
// --- utility methods
/** Gets task color. Requires definiteDueDate and importance */
protected int getTaskColorResource() {
if(getDefiniteDueDate() != null && getDefiniteDueDate().getTime() <
System.currentTimeMillis()) {
return R.color.task_list_overdue;
} else {
return getImportance().getColorResource();
}
}
/** Checks whether task is done. Requires progressPercentage */
protected boolean isTaskCompleted() {
return getProgressPercentage() >= COMPLETE_PERCENTAGE;
}
// --- task identifier
private TaskIdentifier identifier = null;
public TaskIdentifier getTaskIdentifier() {
return identifier;
}
void setTaskIdentifier(TaskIdentifier identifier) {
this.identifier = identifier;
}
// --- constructor pass-through
AbstractTaskModel() {
super();
}
/** Read identifier from database */
AbstractTaskModel(Cursor cursor) {
super(cursor);
Integer id = retrieveInteger(AbstractController.KEY_ROWID);
setTaskIdentifier(new TaskIdentifier(id));
}
/** Get identifier from argument */
AbstractTaskModel(TaskIdentifier identifier, Cursor cursor) {
super(cursor);
setTaskIdentifier(identifier);
}
// --- getters and setters: expose them as you see fit
protected String getName() {
return retrieveString(NAME);
}
protected String getNotes() {
return retrieveString(NOTES);
}
protected int getProgressPercentage() {
return retrieveInteger(PROGRESS_PERCENTAGE);
}
protected Importance getImportance() {
Integer value = retrieveInteger(IMPORTANCE);
if(value == null)
return null;
return Importance.values()[value];
}
protected Integer getEstimatedSeconds() {
return retrieveInteger(ESTIMATED_SECONDS);
}
protected Integer getElapsedSeconds() {
return retrieveInteger(ELAPSED_SECONDS);
}
protected Date getTimerStart() {
return retrieveDate(TIMER_START);
}
protected Date getDefiniteDueDate() {
return retrieveDate(DEFINITE_DUE_DATE);
}
protected Date getPreferredDueDate() {
return retrieveDate(PREFERRED_DUE_DATE);
}
protected Date getHiddenUntil() {
return retrieveDate(HIDDEN_UNTIL);
}
protected Date getCreationDate() {
return retrieveDate(CREATION_DATE);
}
protected Date getCompletionDate() {
return retrieveDate(COMPLETION_DATE);
}
protected TaskIdentifier getBlockingOn() {
Long value = retrieveLong(BLOCKING_ON);
if(value == null)
return null;
return new TaskIdentifier(value);
}
// --- setters
protected void setName(String name) {
setValues.put(NAME, name);
}
protected void setNotes(String notes) {
setValues.put(NOTES, notes);
}
protected void setProgressPercentage(int progressPercentage) {
setValues.put(PROGRESS_PERCENTAGE, progressPercentage);
if(getProgressPercentage() != progressPercentage &&
progressPercentage == COMPLETE_PERCENTAGE)
setCompletionDate(new Date());
}
protected void setImportance(Importance importance) {
setValues.put(IMPORTANCE, importance.ordinal());
}
protected void setEstimatedSeconds(Integer estimatedSeconds) {
setValues.put(ESTIMATED_SECONDS, estimatedSeconds);
}
protected void setElapsedSeconds(int elapsedSeconds) {
setValues.put(ELAPSED_SECONDS, elapsedSeconds);
}
protected void setTimerStart(Date timerStart) {
putDate(setValues, TIMER_START, timerStart);
}
protected void setDefiniteDueDate(Date definiteDueDate) {
putDate(setValues, DEFINITE_DUE_DATE, definiteDueDate);
}
protected void setPreferredDueDate(Date preferredDueDate) {
putDate(setValues, PREFERRED_DUE_DATE, preferredDueDate);
}
protected void setHiddenUntil(Date hiddenUntil) {
putDate(setValues, HIDDEN_UNTIL, hiddenUntil);
}
protected void setBlockingOn(TaskIdentifier blockingOn) {
if(blockingOn == null)
setValues.put(BLOCKING_ON, (Integer)null);
else
setValues.put(BLOCKING_ON, blockingOn.getId());
}
protected void setCreationDate(Date creationDate) {
putDate(setValues, CREATION_DATE, creationDate);
}
protected void setCompletionDate(Date completionDate) {
putDate(setValues, COMPLETION_DATE, completionDate);
}
// --- utility methods
static void putDate(ContentValues cv, String fieldName, Date date) {
if(date == null)
cv.put(fieldName, (Long)null);
else
cv.put(fieldName, date.getTime());
}
}

@ -0,0 +1,154 @@
package com.timsu.astrid.data.task;
import java.util.Date;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteOpenHelper;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.task.AbstractTaskModel.TaskModelDatabaseHelper;
public class TaskController extends AbstractController {
// --- task list operations
/** Return a list of all of the tasks with progress < COMPLETE_PERCENTAGE */
public Cursor getActiveTaskListCursor() {
return database.query(TASK_TABLE_NAME, TaskModelForList.FIELD_LIST,
AbstractTaskModel.PROGRESS_PERCENTAGE + " < " +
AbstractTaskModel.COMPLETE_PERCENTAGE, null, null, null,
null, null);
}
/** Return a list of all tasks */
public Cursor getAllTaskListCursor() {
return database.query(TASK_TABLE_NAME, TaskModelForList.FIELD_LIST,
null, null, null, null, null, null);
}
/** Create a weighted list of tasks from the db cursor given */
public List<TaskModelForList> createTaskListFromCursor(Cursor cursor,
boolean hideHidden) {
return TaskModelForList.createTaskModelList(cursor, hideHidden);
}
// --- single task operations
/** Delete the given task */
public boolean deleteTask(TaskIdentifier taskId) {
if(taskId == null)
throw new UnsupportedOperationException("Cannot delete uncreated task!");
long id = taskId.getId();
return database.delete(TASK_TABLE_NAME, KEY_ROWID + "=" + id, null) > 0;
}
/** Sets the timer start time to the given value. Passing in "null"
* signifies that the timer is not running. */
public boolean startTimer(TaskModelForView task, Date startDate) throws
SQLException {
task.setTimerStart(startDate);
long id = task.getTaskIdentifier().getId();
ContentValues values = new ContentValues();
AbstractTaskModel.putDate(values, AbstractTaskModel.TIMER_START,
startDate);
return database.update(TASK_TABLE_NAME, values, KEY_ROWID + "=" + id,
null) > 0;
}
/** Saves the given task to the database. Returns true on success. */
public boolean saveTask(AbstractTaskModel task) {
boolean saveSucessful;
if(task.getTaskIdentifier() == null) {
saveSucessful = database.insert(TASK_TABLE_NAME, AbstractTaskModel.NAME,
task.getMergedValues()) >= 0;
} else {
long id = task.getTaskIdentifier().getId();
saveSucessful = database.update(TASK_TABLE_NAME, task.getSetValues(),
KEY_ROWID + "=" + id, null) > 0;
}
return saveSucessful;
}
// --- fetching different models
/** Creates a new task and returns the task identifier */
public TaskModelForEdit createNewTaskForEdit() {
TaskModelForEdit task = new TaskModelForEdit();
task.setTaskIdentifier(null);
return task;
}
/** Returns a TaskModelForEdit corresponding to the given TaskIdentifier */
public TaskModelForEdit fetchTaskForEdit(TaskIdentifier taskId) throws SQLException {
long id = taskId.getId();
Cursor cursor = database.query(true, TASK_TABLE_NAME,
TaskModelForEdit.FIELD_LIST,
KEY_ROWID + "=" + id, null, null, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
TaskModelForEdit model = new TaskModelForEdit(taskId, cursor);
return model;
}
throw new SQLException("Returned empty set!");
}
/** Returns a TaskModelForView corresponding to the given TaskIdentifier */
public TaskModelForView fetchTaskForView(TaskIdentifier taskId) throws SQLException {
long id = taskId.getId();
Cursor cursor = database.query(true, TASK_TABLE_NAME,
TaskModelForView.FIELD_LIST,
KEY_ROWID + "=" + id, null, null, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
TaskModelForView model = new TaskModelForView(taskId, cursor);
return model;
}
throw new SQLException("Returned empty set!");
}
// --- boilerplate
/**
* Constructor - takes the context to allow the database to be
* opened/created
*/
public TaskController(Context context) {
this.context = context;
}
/**
* Open the notes database. If it cannot be opened, try to create a new
* instance of the database. If it cannot be created, throw an exception to
* signal the failure
*
* @return this (self reference, allowing this to be chained in an
* initialization call)
* @throws SQLException if the database could be neither opened or created
*/
public TaskController open() throws SQLException {
SQLiteOpenHelper databaseHelper = new TaskModelDatabaseHelper(
context, TASK_TABLE_NAME);
database = databaseHelper.getWritableDatabase();
return this;
}
/** Closes database resource */
public void close() {
database.close();
}
}

@ -0,0 +1,15 @@
package com.timsu.astrid.data.task;
/** A little class that identifies a task. For saving state and passing around */
public class TaskIdentifier {
private long id;
public TaskIdentifier(long id) {
this.id = id;
}
public long getId() {
return id;
}
}

@ -0,0 +1,128 @@
package com.timsu.astrid.data.task;
import java.util.Date;
import com.timsu.astrid.data.enums.Importance;
import android.database.Cursor;
/** Fields that you would want to edit in the TaskModel */
public class TaskModelForEdit extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
NAME,
IMPORTANCE,
ESTIMATED_SECONDS,
ELAPSED_SECONDS,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
HIDDEN_UNTIL,
BLOCKING_ON,
NOTES,
};
// --- constructors
public TaskModelForEdit() {
super();
setCreationDate(new Date());
}
public TaskModelForEdit(TaskIdentifier identifier, Cursor cursor) {
super(identifier, cursor);
}
// --- getters and setters
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
@Override
public Integer getEstimatedSeconds() {
return super.getEstimatedSeconds();
}
@Override
public Integer getElapsedSeconds() {
return super.getElapsedSeconds();
}
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
@Override
public Importance getImportance() {
return super.getImportance();
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getNotes() {
return super.getNotes();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public TaskIdentifier getBlockingOn() {
return super.getBlockingOn();
}
@Override
public void setDefiniteDueDate(Date definiteDueDate) {
super.setDefiniteDueDate(definiteDueDate);
}
@Override
public void setEstimatedSeconds(Integer estimatedSeconds) {
super.setEstimatedSeconds(estimatedSeconds);
}
@Override
public void setElapsedSeconds(int elapsedSeconds) {
super.setElapsedSeconds(elapsedSeconds);
}
@Override
public void setHiddenUntil(Date hiddenUntil) {
super.setHiddenUntil(hiddenUntil);
}
@Override
public void setImportance(Importance importance) {
super.setImportance(importance);
}
@Override
public void setName(String name) {
super.setName(name);
}
@Override
public void setNotes(String notes) {
super.setNotes(notes);
}
@Override
public void setPreferredDueDate(Date preferredDueDate) {
super.setPreferredDueDate(preferredDueDate);
}
@Override
public void setBlockingOn(TaskIdentifier blockingOn) {
super.setBlockingOn(blockingOn);
}
}

@ -0,0 +1,183 @@
package com.timsu.astrid.data.task;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import android.database.Cursor;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.enums.Importance;
/** Fields that you would want to edit in the TaskModel */
public class TaskModelForList extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
NAME,
IMPORTANCE,
ELAPSED_SECONDS,
ESTIMATED_SECONDS,
TIMER_START,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
PROGRESS_PERCENTAGE,
HIDDEN_UNTIL,
};
static List<TaskModelForList> createTaskModelList(Cursor cursor,
boolean hideHidden) {
ArrayList<TaskModelForList> list = new ArrayList<TaskModelForList>();
final HashMap<TaskModelForList, Integer> weights = new
HashMap<TaskModelForList, Integer>();
// first, load everything
for(int i = 0; i < cursor.getCount(); i++) {
cursor.moveToNext();
TaskModelForList task = new TaskModelForList(cursor);
// hide tasks
if(hideHidden) {
if(task.getHiddenUntil() != null &&
task.getHiddenUntil().getTime() > System.currentTimeMillis())
continue;
}
list.add(task);
weights.put(task, task.getWeight());
}
// now sort
Collections.sort(list, new Comparator<TaskModelForList>() {
@Override
public int compare(TaskModelForList a, TaskModelForList b) {
return weights.get(a) - weights.get(b);
}
});
return list;
}
/** Get the weighted score for this task. Smaller is more important */
private int getWeight() {
int weight = 0;
// importance
weight += getImportance().ordinal() * 60;
// estimated time left
int secondsLeft = getEstimatedSeconds() - getElapsedSeconds();
if(secondsLeft > 0)
weight += secondsLeft / 120;
// looming absolute deadline
if(getDefiniteDueDate() != null) {
int hoursLeft = (int) (getDefiniteDueDate().getTime() -
System.currentTimeMillis())/1000/3600;
if(hoursLeft < 5*24)
weight += (hoursLeft - 5*24);
}
// looming preferred deadline
if(getPreferredDueDate() != null) {
int hoursLeft = (int) (getPreferredDueDate().getTime() -
System.currentTimeMillis())/1000/3600;
if(hoursLeft < 5*24)
weight += (hoursLeft - 5*24)/2;
}
// bubble completed tasks to the bottom
if(isTaskCompleted())
weight += 1000;
return weight;
}
// --- constructors
public TaskModelForList(Cursor cursor) {
super(cursor);
// prefetch every field - we can't lazy load with more than 1
getElapsedSeconds();
getDefiniteDueDate();
getEstimatedSeconds();
getHiddenUntil();
getImportance();
getName();
getPreferredDueDate();
getProgressPercentage();
getTimerStart();
}
// --- getters and setters
@Override
public boolean isTaskCompleted() {
return super.isTaskCompleted();
}
@Override
public int getTaskColorResource() {
return super.getTaskColorResource();
}
@Override
public Integer getElapsedSeconds() {
return super.getElapsedSeconds();
}
public static int getCompletedPercentage() {
return COMPLETE_PERCENTAGE;
}
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
@Override
public Integer getEstimatedSeconds() {
return super.getEstimatedSeconds();
}
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
@Override
public Importance getImportance() {
return super.getImportance();
}
@Override
public String getName() {
return super.getName();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public int getProgressPercentage() {
return super.getProgressPercentage();
}
@Override
public Date getTimerStart() {
return super.getTimerStart();
}
@Override
public void setProgressPercentage(int progressPercentage) {
super.setProgressPercentage(progressPercentage);
}
}

@ -0,0 +1,105 @@
package com.timsu.astrid.data.task;
import java.util.Date;
import android.database.Cursor;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.enums.Importance;
/** Fields that you would want to see in the TaskView activity */
public class TaskModelForView extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
NAME,
IMPORTANCE,
PROGRESS_PERCENTAGE,
ESTIMATED_SECONDS,
ELAPSED_SECONDS,
TIMER_START,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
NOTES,
};
// --- constructors
public TaskModelForView(TaskIdentifier identifier, Cursor cursor) {
super(identifier, cursor);
}
// --- getters and setters
@Override
public boolean isTaskCompleted() {
return super.isTaskCompleted();
}
@Override
public int getTaskColorResource() {
return super.getTaskColorResource();
}
@Override
public int getProgressPercentage() {
return super.getProgressPercentage();
}
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
@Override
public Integer getEstimatedSeconds() {
return super.getEstimatedSeconds();
}
@Override
public Integer getElapsedSeconds() {
return super.getElapsedSeconds();
}
@Override
public Importance getImportance() {
return super.getImportance();
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getNotes() {
return super.getNotes();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public Date getTimerStart() {
return super.getTimerStart();
}
@Override
public void setTimerStart(Date timerStart) {
super.setTimerStart(timerStart);
}
@Override
public void setProgressPercentage(int progressPercentage) {
super.setProgressPercentage(progressPercentage);
}
@Override
public void setElapsedSeconds(int elapsedSeconds) {
super.setElapsedSeconds(elapsedSeconds);
}
}

@ -0,0 +1,102 @@
package com.timsu.astrid.utilities;
import android.content.res.Resources;
import com.timsu.astrid.R;
public class DateUtilities {
/**
* Format a time into the format: 5 days, 3 hours, 2 minutes
*
* @param r Resources to get strings from
* @param timeInSeconds
* @param unitsToShow number of units to show (i.e. if 2, then 5 hours
* 3 minutes 2 seconds is truncated to 5 hours 3 minutes)
* @return
*/
public static String getDurationString(Resources r, int timeInSeconds,
int unitsToShow) {
short days, hours, minutes, seconds;
short unitsDisplayed = 0;
if(timeInSeconds == 0)
return r.getQuantityString(R.plurals.Nseconds, 0, 0);
days = (short)(timeInSeconds / 24 / 3600);
timeInSeconds -= days*24*3600;
hours = (short)(timeInSeconds / 3600);
timeInSeconds -= hours * 3600;
minutes = (short)(timeInSeconds / 60);
timeInSeconds -= minutes * 60;
seconds = (short)timeInSeconds;
StringBuilder result = new StringBuilder();
if(days > 0) {
result.append(r.getQuantityString(R.plurals.Ndays, days, days)).
append(" ");
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && hours > 0) {
result.append(r.getQuantityString(R.plurals.Nhours, hours,
hours)).
append(" ");
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && minutes > 0) {
result.append(r.getQuantityString(R.plurals.Nminutes, minutes,
minutes)).append(" ");
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && seconds > 0) {
result.append(r.getQuantityString(R.plurals.Nseconds, seconds,
seconds)).append(" ");
}
return result.toString();
}
/**
* Format a time into the format: 5 d, 3 h, 2 m
*
* @param r Resources to get strings from
* @param timeInSeconds
* @param unitsToShow number of units to show
* @return
*/
public static String getShortDurationString(Resources r, int timeInSeconds,
int unitsToShow) {
short days, hours, minutes, seconds;
short unitsDisplayed = 0;
if(timeInSeconds == 0)
return "0 s";
days = (short)(timeInSeconds / 24 / 3600);
timeInSeconds -= days*24*3600;
hours = (short)(timeInSeconds / 3600);
timeInSeconds -= hours * 3600;
minutes = (short)(timeInSeconds / 60);
timeInSeconds -= minutes * 60;
seconds = (short)timeInSeconds;
StringBuilder result = new StringBuilder();
if(days > 0) {
result.append(days).append(" d ");
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && hours > 0) {
result.append(hours).append(" h ");
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && minutes > 0) {
result.append(minutes).append(" m ");
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && seconds < 0) {
result.append(hours).append(" s ");
}
return result.toString();
}
}

@ -0,0 +1,422 @@
package com.timsu.astrid.widget;
import android.content.Context;
import android.os.Handler;
import android.text.InputFilter;
import android.text.Spanned;
import android.text.method.NumberKeyListener;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnLongClickListener;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.timsu.astrid.R;
public class NumberPicker extends LinearLayout implements OnClickListener,
OnFocusChangeListener, OnLongClickListener {
public interface OnChangedListener {
void onChanged(NumberPicker picker, int oldVal, int newVal);
}
public interface Formatter {
String toString(int value);
}
/*
* Use a custom NumberPicker formatting callback to use two-digit minutes
* strings like "01". Keeping a static formatter etc. is the most efficient
* way to do this; it avoids creating temporary objects on every call to
* format().
*/
public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER = new NumberPicker.Formatter() {
final StringBuilder mBuilder = new StringBuilder();
final java.util.Formatter mFmt = new java.util.Formatter(mBuilder);
final Object[] mArgs = new Object[1];
public String toString(int value) {
mArgs[0] = value;
mBuilder.delete(0, mBuilder.length());
mFmt.format("%02d", mArgs);
return mFmt.toString();
}
};
private int incrementBy;
public void setIncrementBy(int incrementBy) {
this.incrementBy = incrementBy;
}
private final Handler mHandler;
private final Runnable mRunnable = new Runnable() {
public void run() {
if (mIncrement) {
changeCurrent(mCurrent + incrementBy, mSlideUpInAnimation, mSlideUpOutAnimation);
mHandler.postDelayed(this, mSpeed);
} else if (mDecrement) {
changeCurrent(mCurrent - incrementBy, mSlideDownInAnimation, mSlideDownOutAnimation);
mHandler.postDelayed(this, mSpeed);
}
}
};
private final LayoutInflater mInflater;
private final TextView mText;
private final InputFilter mInputFilter;
private final InputFilter mNumberInputFilter;
private final Animation mSlideUpOutAnimation;
private final Animation mSlideUpInAnimation;
private final Animation mSlideDownOutAnimation;
private final Animation mSlideDownInAnimation;
private String[] mDisplayedValues;
private int mStart;
private int mEnd;
private int mCurrent;
private int mPrevious;
private OnChangedListener mListener;
private Formatter mFormatter;
private long mSpeed = 300;
private boolean mIncrement;
private boolean mDecrement;
public NumberPicker(Context context, int incrementBy) {
this(context, null);
}
public NumberPicker(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
setOrientation(VERTICAL);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mInflater.inflate(R.layout.number_picker, this, true);
mHandler = new Handler();
mInputFilter = new NumberPickerInputFilter();
mNumberInputFilter = new NumberRangeKeyListener();
mIncrementButton = (NumberPickerButton) findViewById(R.id.increment);
mIncrementButton.setOnClickListener(this);
mIncrementButton.setOnLongClickListener(this);
mIncrementButton.setNumberPicker(this);
mDecrementButton = (NumberPickerButton) findViewById(R.id.decrement);
mDecrementButton.setOnClickListener(this);
mDecrementButton.setOnLongClickListener(this);
mDecrementButton.setNumberPicker(this);
mText = (TextView) findViewById(R.id.timepicker_input);
mText.setOnFocusChangeListener(this);
mText.setFilters(new InputFilter[] { mInputFilter });
mSlideUpOutAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, -100);
mSlideUpOutAnimation.setDuration(200);
mSlideUpInAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 100, Animation.RELATIVE_TO_SELF, 0);
mSlideUpInAnimation.setDuration(200);
mSlideDownOutAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 100);
mSlideDownOutAnimation.setDuration(200);
mSlideDownInAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, -100, Animation.RELATIVE_TO_SELF, 0);
mSlideDownInAnimation.setDuration(200);
if (!isEnabled()) {
setEnabled(false);
}
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
mIncrementButton.setEnabled(enabled);
mDecrementButton.setEnabled(enabled);
mText.setEnabled(enabled);
}
public void setOnChangeListener(OnChangedListener listener) {
mListener = listener;
}
public void setFormatter(Formatter formatter) {
mFormatter = formatter;
}
/**
* Set the range of numbers allowed for the number picker. The current value
* will be automatically set to the start.
*
* @param start
* the start of the range (inclusive)
* @param end
* the end of the range (inclusive)
*/
public void setRange(int start, int end) {
mStart = start;
mEnd = end;
mCurrent = start;
updateView();
}
/**
* Set the range of numbers allowed for the number picker. The current value
* will be automatically set to the start. Also provide a mapping for values
* used to display to the user.
*
* @param start
* the start of the range (inclusive)
* @param end
* the end of the range (inclusive)
* @param displayedValues
* the values displayed to the user.
*/
public void setRange(int start, int end, String[] displayedValues) {
mDisplayedValues = displayedValues;
mStart = start;
mEnd = end;
mCurrent = start;
updateView();
}
public void setCurrent(int current) {
mCurrent = current;
updateView();
}
/**
* The speed (in milliseconds) at which the numbers will scroll when the the
* +/- buttons are longpressed. Default is 300ms.
*/
public void setSpeed(long speed) {
mSpeed = speed;
}
public void onClick(View v) {
/*
* The text view may still have focus so clear it's focus which will
* trigger the on focus changed and any typed values to be pulled.
*/
mText.clearFocus();
// now perform the increment/decrement
if (R.id.increment == v.getId()) {
changeCurrent(mCurrent + incrementBy, mSlideUpInAnimation,
mSlideUpOutAnimation);
} else if (R.id.decrement == v.getId()) {
changeCurrent(mCurrent - incrementBy, mSlideDownInAnimation,
mSlideDownOutAnimation);
}
}
private String formatNumber(int value) {
return (mFormatter != null) ? mFormatter.toString(value) : String
.valueOf(value);
}
private void changeCurrent(int current, Animation in, Animation out) {
// Wrap around the values if we go past the start or end
if (current > mEnd) {
current = mStart;
} else if (current < mStart) {
current = mEnd;
}
mPrevious = mCurrent;
mCurrent = current;
notifyChange();
updateView();
}
private void notifyChange() {
if (mListener != null) {
mListener.onChanged(this, mPrevious, mCurrent);
}
}
private void updateView() {
/*
* If we don't have displayed values then use the current number else
* find the correct value in the displayed values for the current
* number.
*/
if (mDisplayedValues == null) {
mText.setText(formatNumber(mCurrent));
} else {
mText.setText(mDisplayedValues[mCurrent - mStart]);
}
}
private void validateCurrentView(CharSequence str) {
int val = getSelectedPos(str.toString());
if ((val >= mStart) && (val <= mEnd)) {
mPrevious = mCurrent;
mCurrent = val;
notifyChange();
}
updateView();
}
public void onFocusChange(View v, boolean hasFocus) {
/*
* When focus is lost check that the text field has valid values.
*/
if (!hasFocus) {
String str = String.valueOf(((TextView) v).getText());
if ("".equals(str)) {
// Restore to the old value as we don't allow empty values
updateView();
} else {
// Check the new value and ensure it's in range
validateCurrentView(str);
}
}
}
/**
* We start the long click here but rely on the {@link NumberPickerButton}
* to inform us when the long click has ended.
*/
public boolean onLongClick(View v) {
/*
* The text view may still have focus so clear it's focus which will
* trigger the on focus changed and any typed values to be pulled.
*/
mText.clearFocus();
if (R.id.increment == v.getId()) {
mIncrement = true;
mHandler.post(mRunnable);
} else if (R.id.decrement == v.getId()) {
mDecrement = true;
mHandler.post(mRunnable);
}
return true;
}
public void cancelIncrement() {
mIncrement = false;
}
public void cancelDecrement() {
mDecrement = false;
}
private static final char[] DIGIT_CHARACTERS = new char[] { '0', '1', '2',
'3', '4', '5', '6', '7', '8', '9' };
private NumberPickerButton mIncrementButton;
private NumberPickerButton mDecrementButton;
private class NumberPickerInputFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
if (mDisplayedValues == null) {
return mNumberInputFilter.filter(source, start, end, dest,
dstart, dend);
}
CharSequence filtered = String.valueOf(source.subSequence(start,
end));
String result = String.valueOf(dest.subSequence(0, dstart))
+ filtered + dest.subSequence(dend, dest.length());
String str = String.valueOf(result).toLowerCase();
for (String val : mDisplayedValues) {
val = val.toLowerCase();
if (val.startsWith(str)) {
return filtered;
}
}
return "";
}
}
private class NumberRangeKeyListener extends NumberKeyListener {
@Override
protected char[] getAcceptedChars() {
return DIGIT_CHARACTERS;
}
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
CharSequence filtered = super.filter(source, start, end, dest,
dstart, dend);
if (filtered == null) {
filtered = source.subSequence(start, end);
}
String result = String.valueOf(dest.subSequence(0, dstart))
+ filtered + dest.subSequence(dend, dest.length());
if ("".equals(result)) {
return result;
}
int val = getSelectedPos(result);
/*
* Ensure the user can't type in a value greater than the max
* allowed. We have to allow less than min as the user might want to
* delete some numbers and then type a new number.
*/
if (val > mEnd) {
return "";
} else {
return filtered;
}
}
}
private int getSelectedPos(String str) {
if (mDisplayedValues == null) {
return Integer.parseInt(str);
} else {
for (int i = 0; i < mDisplayedValues.length; i++) {
/* Don't force the user to type in jan when ja will do */
str = str.toLowerCase();
if (mDisplayedValues[i].toLowerCase().startsWith(str)) {
return mStart + i;
}
}
/*
* The user might have typed in a number into the month field i.e.
* 10 instead of OCT so support that too.
*/
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
/* Ignore as if it's not a number we don't care */
}
}
return mStart;
}
/**
* @return the current value.
*/
public int getCurrent() {
return mCurrent;
}
}

@ -0,0 +1,70 @@
package com.timsu.astrid.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.ImageButton;
import com.timsu.astrid.R;
/**
* This class exists purely to cancel long click events.
*/
public class NumberPickerButton extends ImageButton {
private NumberPicker mNumberPicker;
public NumberPickerButton(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public NumberPickerButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NumberPickerButton(Context context) {
super(context);
}
public void setNumberPicker(NumberPicker picker) {
mNumberPicker = picker;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
cancelLongpressIfRequired(event);
return super.onTouchEvent(event);
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
cancelLongpressIfRequired(event);
return super.onTrackballEvent(event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER)
|| (keyCode == KeyEvent.KEYCODE_ENTER)) {
cancelLongpress();
}
return super.onKeyUp(keyCode, event);
}
private void cancelLongpressIfRequired(MotionEvent event) {
if ((event.getAction() == MotionEvent.ACTION_CANCEL)
|| (event.getAction() == MotionEvent.ACTION_UP)) {
cancelLongpress();
}
}
private void cancelLongpress() {
if (R.id.increment == getId()) {
mNumberPicker.cancelIncrement();
} else if (R.id.decrement == getId()) {
mNumberPicker.cancelDecrement();
}
}
}

@ -0,0 +1,51 @@
package com.timsu.astrid.widget;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.view.LayoutInflater;
import android.view.View;
import com.timsu.astrid.R;
public class NumberPickerDialog extends AlertDialog implements OnClickListener {
public interface OnNumberPickedListener {
void onNumberPicked(NumberPicker view, int number);
}
private final NumberPicker mPicker;
private final OnNumberPickedListener mCallback;
public NumberPickerDialog(Context context, OnNumberPickedListener callBack,
String title, int initialValue, int incrementBy, int start, int end) {
super(context);
mCallback = callBack;
setButton(context.getText(android.R.string.ok), this);
setButton2(context.getText(android.R.string.cancel), (OnClickListener) null);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.number_picker_dialog, null);
setView(view);
setTitle(title);
mPicker = (NumberPicker) view.findViewById(R.id.numberPicker);
mPicker.setCurrent(initialValue);
mPicker.setIncrementBy(incrementBy);
mPicker.setRange(start, end);
}
public void setInitialValue(int initialValue) {
mPicker.setCurrent(initialValue);
}
public void onClick(DialogInterface dialog, int which) {
if (mCallback != null) {
mPicker.clearFocus();
mCallback.onNumberPicked(mPicker, mPicker.getCurrent());
}
}
}
Loading…
Cancel
Save