Crazy Astrid 3 activity merged in. Does it work? not really. But it's cool that it's there.
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.filters;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.timsu.astrid.R;
|
||||
import com.todoroo.astrid.activity.FilterListActivity;
|
||||
import com.todoroo.astrid.api.AstridApiConstants;
|
||||
import com.todoroo.astrid.api.Filter;
|
||||
import com.todoroo.astrid.api.FilterListItem;
|
||||
import com.todoroo.astrid.model.Task;
|
||||
|
||||
/**
|
||||
* Exposes Astrid's built in filters to the {@link FilterListActivity}
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public class FilterExposer extends BroadcastReceiver {
|
||||
|
||||
public static Filter buildInboxFilter(Resources r) {
|
||||
return new Filter(r.getString(R.string.BFE_Inbox),
|
||||
r.getString(R.string.BFE_Inbox),
|
||||
/*String.format("WHERE %s AND %s ORDER BY CASE %s WHEN 0 THEN (%d + 1000 * %s) ELSE (%s + 1000 * %s) END ASC", //$NON-NLS-1$
|
||||
TaskSql.isActive(), TaskSql.isVisible(DateUtilities.now()),
|
||||
Task.DUE_DATE, DateUtilities.now() + 60 * 24 * 3600, Task.IMPORTANCE,
|
||||
Task.DUE_DATE, Task.IMPORTANCE)*/ "",
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Resources r = context.getResources();
|
||||
|
||||
// build filters
|
||||
Filter inbox = buildInboxFilter(r);
|
||||
|
||||
Filter all = new Filter(r.getString(R.string.BFE_All),
|
||||
r.getString(R.string.BFE_All),
|
||||
String.format("ORDER BY %s DESC", //$NON-NLS-1$
|
||||
Task.ID.name),
|
||||
null);
|
||||
|
||||
// transmit filter list
|
||||
FilterListItem[] list = new FilterListItem[2];
|
||||
list[0] = inbox;
|
||||
list[1] = all;
|
||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
|
||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, list);
|
||||
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:type="radial"
|
||||
android:startColor="#373737"
|
||||
android:endColor="#000000"
|
||||
android:gradientRadius="300"
|
||||
android:centerX="0.5"
|
||||
android:centerY="0.5" />
|
||||
</shape>
|
||||
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:startColor="#ffffffff"
|
||||
android:centerColor="#ff000000"
|
||||
android:endColor="#ffffffff"
|
||||
android:angle="0" />
|
||||
</shape>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 222 B |
@ -0,0 +1,20 @@
|
||||
<?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:drawable="@drawable/none" />
|
||||
</selector>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 169 B |
@ -0,0 +1,26 @@
|
||||
<?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/ic_tasklist_back" />
|
||||
<item android:state_pressed="true" android:state_enabled="true"
|
||||
android:drawable="@drawable/ic_tasklist_back_pressed" />
|
||||
<item android:state_pressed="false" android:state_enabled="true"
|
||||
android:state_focused="true" android:drawable="@drawable/ic_tasklist_back_selected" />
|
||||
|
||||
</selector>
|
||||
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- See the file "LICENSE" for the full license governing this code. -->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@drawable/background_gradient">
|
||||
|
||||
<!-- Loading Filters label -->
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="@string/FLA_loading"
|
||||
style="@style/TextAppearance.TLA_NoItems"/>
|
||||
|
||||
<!-- List -->
|
||||
<ExpandableListView android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@null"
|
||||
android:scrollbars="vertical"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
ASTRID: Android's Simple Task Recording Dashboard
|
||||
|
||||
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:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@drawable/background_gradient">
|
||||
|
||||
<LinearLayout android:id="@+id/event"
|
||||
android:paddingRight="8px"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<TextView android:id="@+id/name_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/TEA_name_label"
|
||||
style="@style/TextAppearance.GEN_EditLabel"/>
|
||||
|
||||
<EditText android:id="@+id/name"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/TEA_name_hint"
|
||||
android:autoText="true"
|
||||
android:capitalize="sentences"/>
|
||||
|
||||
<TextView android:id="@+id/importance_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/TEA_importance_label"
|
||||
style="@style/TextAppearance.GEN_EditLabel"/>
|
||||
|
||||
<LinearLayout android:id="@+id/importance_container"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
</LinearLayout>
|
||||
|
||||
<TextView android:id="@+id/urgency_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/TEA_urgency_label"
|
||||
style="@style/TextAppearance.GEN_EditLabel"/>
|
||||
|
||||
<Spinner android:id="@+id/urgency"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<Button android:id="@+id/fixedDate"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView android:id="@+id/hiddenUntil_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/TEA_hiddenUntil_label"
|
||||
style="@style/TextAppearance.GEN_EditLabel"/>
|
||||
|
||||
<Button android:id="@+id/hiddenUntil"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView android:id="@+id/plugins_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/TEA_plugins_label"
|
||||
android:visibility="gone"
|
||||
style="@style/TextAppearance.GEN_EditLabel"/>
|
||||
|
||||
<LinearLayout android:id="@+id/plugins_container"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1dip"
|
||||
android:background="@drawable/black_white_gradient"
|
||||
/>
|
||||
|
||||
<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/TEA_save_label"
|
||||
/>
|
||||
|
||||
<Button android:id="@+id/discard"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/TEA_discard_label"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- See the file "LICENSE" for the full license governing this code. -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/background_gradient">
|
||||
|
||||
<!-- Header -->
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/edit_header">
|
||||
|
||||
<!-- Back Button -->
|
||||
<ImageView android:id="@+id/back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:src="@drawable/tasklist_back"
|
||||
android:paddingTop="8px"
|
||||
android:paddingLeft="5px"/>
|
||||
|
||||
<!-- List Label -->
|
||||
<TextView android:id="@+id/listLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="100"
|
||||
android:singleLine="true"
|
||||
android:paddingTop="6px"
|
||||
android:paddingRight="50px"
|
||||
style="@style/TextAppearance.TLA_Header"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Body -->
|
||||
<FrameLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="100">
|
||||
|
||||
<!-- No Tasks label -->
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:visibility="gone"
|
||||
android:text="@string/TLA_no_items"
|
||||
style="@style/TextAppearance.TLA_NoItems"/>
|
||||
|
||||
<!-- Task List -->
|
||||
<ListView android:id="@android:id/list"
|
||||
android:background="@null"
|
||||
android:scrollbars="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Footer -->
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Quick Add Task -->
|
||||
<EditText android:id="@+id/quickAddText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="100"
|
||||
android:hint="@string/TLA_quick_add_hint"
|
||||
android:singleLine="true"
|
||||
android:autoText="true"
|
||||
android:capitalize="sentences"/>
|
||||
|
||||
<!-- Quick Add Button -->
|
||||
<ImageButton android:id="@+id/quickAddButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:src="@android:drawable/ic_input_add"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- See the file "LICENSE" for the full license governing this code. -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/row_layout"
|
||||
android:orientation="horizontal"
|
||||
android:background="@android:drawable/list_selector_background"
|
||||
android:focusable="true"
|
||||
android:paddingTop="2px"
|
||||
android:paddingLeft="6dip"
|
||||
android:paddingRight="6dip"
|
||||
android:paddingBottom="2px"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- completion check-box -->
|
||||
<CheckBox android:id="@+id/completeBox"
|
||||
android:layout_width="40px"
|
||||
android:layout_height="45px"
|
||||
android:layout_weight="1"
|
||||
android:paddingLeft="10px" />
|
||||
|
||||
<LinearLayout android:id="@+id/details"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="100"
|
||||
android:paddingLeft="10px"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- task name -->
|
||||
<TextView android:id="@+id/title"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.TAd_ItemTitle"
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
<!-- due date -->
|
||||
<TextView android:id="@+id/dueDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<!-- other details go here -->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- importance -->
|
||||
<View android:id="@+id/importance"
|
||||
android:layout_width="12px"
|
||||
android:layout_height="fill_parent"/>
|
||||
|
||||
</LinearLayout>
|
||||
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- See the file "LICENSE" for the full license governing this code. -->
|
||||
<resources>
|
||||
|
||||
<!-- Resources for built-in filter plug-in -->
|
||||
|
||||
<!-- Inbox Filter -->
|
||||
<string name="BFE_Inbox">Inbox</string>
|
||||
|
||||
<!-- Completed Filter -->
|
||||
<string name="BFE_Completed">Completed</string>
|
||||
|
||||
<!-- All Tasks Filter -->
|
||||
<string name="BFE_All">All Tasks</string>
|
||||
|
||||
</resources>
|
||||
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- See the file "LICENSE" for the full license governing this code. -->
|
||||
<resources>
|
||||
|
||||
<!-- ============================ General ============================= -->
|
||||
|
||||
<style name="TextAppearance" parent="android:TextAppearance" />
|
||||
|
||||
<!-- generic style for labels above edit boxes -->
|
||||
<style name="TextAppearance.GEN_EditLabel">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme" parent="android:Theme">
|
||||
<item name="android:windowBackground">@null</item>
|
||||
</style>
|
||||
|
||||
<!--========================= TaskListActivity ======================== -->
|
||||
|
||||
<style name="TextAppearance.TLA_NoItems">
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.TLA_Header">
|
||||
<item name="android:textSize">24sp</item>
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
<!-- =========================== TaskAdapter =========================== -->
|
||||
|
||||
<style name="TextAppearance.TAd_ItemTitle">
|
||||
<item name="android:textSize">22sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.TAd_ItemTitle_Completed" parent="TextAppearance.TAd_ItemTitle">
|
||||
<item name="android:textColor">#ff555555</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.TAd_ItemDetails">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:textColor">#ff777777</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.TAd_ItemDueDate" parent="TextAppearance.TAd_ItemDetails">
|
||||
<item name="android:textColor">#ff7777aa</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.TAd_ItemDueDate_Overdue" parent="TextAppearance.TAd_ItemDueDate">
|
||||
<item name="android:textColor">#ffee5555</item>
|
||||
</style>
|
||||
|
||||
<!-- ======================== FilterListAdapter ======================== -->
|
||||
|
||||
<style name="TextAppearance.FLA_Filter" parent="TextAppearance.TAd_ItemTitle">
|
||||
<item name="android:textSize">22sp</item>
|
||||
<item name="android:paddingTop">5px</item>
|
||||
<item name="android:paddingBottom">5px</item>
|
||||
<item name="android:paddingLeft">10px</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.FLA_Header" parent="TextAppearance.TAd_ItemTitle">
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:textColor">#ffdddddd</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.FLA_Category" parent="TextAppearance.TAd_ItemTitle">
|
||||
<item name="android:textColor">#ff7ada24</item>
|
||||
<item name="android:textStyle">bold|italic</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:label="@string/FLA_search_label"
|
||||
android:hint="@string/FLA_search_hint">
|
||||
</searchable>
|
||||
|
||||
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.service.ExceptionService;
|
||||
import com.todoroo.astrid.dao.Database;
|
||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
||||
|
||||
/**
|
||||
* This activity displays a <code>WebView</code> that allows users to log in to the
|
||||
* synchronization provider requested. A callback method determines whether
|
||||
* their login was successful and therefore whether to dismiss the dialog.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
abstract public class AbstractModelActivity<TYPE extends AbstractModel> extends AstridActivity {
|
||||
|
||||
// --- bundle arguments
|
||||
|
||||
/**
|
||||
* Action Item ID
|
||||
*/
|
||||
public static final String ID_TOKEN = "i"; //$NON-NLS-1$
|
||||
|
||||
// --- instance variables
|
||||
|
||||
@Autowired
|
||||
protected ExceptionService exceptionService;
|
||||
|
||||
@Autowired
|
||||
protected Database database;
|
||||
|
||||
protected TYPE model = null;
|
||||
|
||||
// --- abstract methods
|
||||
|
||||
abstract protected TYPE fetchModel(long id);
|
||||
|
||||
/**
|
||||
* Load Bente Dependency Injector
|
||||
*/
|
||||
static {
|
||||
AstridDependencyInjector.initialize();
|
||||
}
|
||||
|
||||
public AbstractModelActivity() {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
loadItem(getIntent());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
loadItem(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads action item from the given intent
|
||||
* @param intent
|
||||
*/
|
||||
protected void loadItem(Intent intent) {
|
||||
long idParam = intent.getLongExtra(ID_TOKEN, -1L);
|
||||
if(idParam == -1) {
|
||||
exceptionService.reportError("AMA-no-token", null); //$NON-NLS-1$
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
database.openForReading();
|
||||
model = fetchModel(idParam);
|
||||
|
||||
if(model == null) {
|
||||
exceptionService.reportError("AMA-no-task", new NullPointerException("model")); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.flurry.android.FlurryAgent;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
||||
import com.todoroo.astrid.utility.Constants;
|
||||
|
||||
/**
|
||||
* General helpers for Astrid activities
|
||||
*/
|
||||
abstract public class AstridActivity extends Activity {
|
||||
|
||||
static {
|
||||
AstridDependencyInjector.initialize();
|
||||
}
|
||||
|
||||
public AstridActivity() {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ContextManager.setContext(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
FlurryAgent.onEndSession(this);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,361 @@
|
||||
/**
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.activity;
|
||||
|
||||
import android.app.ExpandableListActivity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ExpandableListView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
|
||||
|
||||
import com.flurry.android.FlurryAgent;
|
||||
import com.timsu.astrid.R;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.service.ExceptionService;
|
||||
import com.todoroo.andlib.utility.DialogUtilities;
|
||||
import com.todoroo.astrid.adapter.FilterAdapter;
|
||||
import com.todoroo.astrid.api.AstridApiConstants;
|
||||
import com.todoroo.astrid.api.Filter;
|
||||
import com.todoroo.astrid.api.FilterListItem;
|
||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
||||
import com.todoroo.astrid.utility.Constants;
|
||||
|
||||
/**
|
||||
* Activity that displays a user's task lists and allows users
|
||||
* to filter their task list.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public class FilterListActivity extends ExpandableListActivity {
|
||||
|
||||
// --- menu codes
|
||||
|
||||
private static final int MENU_SEARCH_ID = Menu.FIRST + 0;
|
||||
private static final int MENU_HELP_ID = Menu.FIRST + 1;
|
||||
|
||||
private static final int CONTEXT_MENU_SHORTCUT = Menu.FIRST + 2;
|
||||
private static final int CONTEXT_MENU_INTENT = Menu.FIRST + 3;
|
||||
|
||||
// --- instance variables
|
||||
|
||||
@Autowired
|
||||
protected ExceptionService exceptionService;
|
||||
|
||||
@Autowired
|
||||
protected DialogUtilities dialogUtilities;
|
||||
|
||||
FilterAdapter adapter = null;
|
||||
String search = null;
|
||||
FilterReceiver filterReceiver = new FilterReceiver();
|
||||
|
||||
/* ======================================================================
|
||||
* ======================================================= initialization
|
||||
* ====================================================================== */
|
||||
|
||||
static {
|
||||
AstridDependencyInjector.initialize();
|
||||
}
|
||||
|
||||
public FilterListActivity() {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
/** Called when loading up the activity */
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ContextManager.setContext(this);
|
||||
|
||||
setContentView(R.layout.filter_list_activity);
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
|
||||
setTitle(R.string.FLA_title);
|
||||
setUpList();
|
||||
getLists();
|
||||
|
||||
onNewIntent(getIntent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when receiving a new intent. Intents this class handles:
|
||||
* <ul>
|
||||
* <li>ACTION_SEARCH - displays a search bar
|
||||
* <li>ACTION_ADD_LIST - adds new lists to the merge adapter
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
||||
final String intentAction = intent.getAction();
|
||||
if (Intent.ACTION_SEARCH.equals(intentAction)) {
|
||||
search = intent.getStringExtra(SearchManager.QUERY);
|
||||
dialogUtilities.okDialog(this, "TODO!", null); //$NON-NLS-1$
|
||||
} else {
|
||||
search = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create options menu (displayed when user presses menu key)
|
||||
*
|
||||
* @return true if menu should be displayed
|
||||
*/
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
if(menu.size() > 0)
|
||||
return true;
|
||||
|
||||
MenuItem item;
|
||||
|
||||
item = menu.add(Menu.NONE, MENU_SEARCH_ID, Menu.NONE,
|
||||
R.string.FLA_menu_search);
|
||||
item.setIcon(android.R.drawable.ic_menu_search);
|
||||
|
||||
item = menu.add(Menu.NONE, MENU_HELP_ID, Menu.NONE,
|
||||
R.string.FLA_menu_help);
|
||||
item.setIcon(android.R.drawable.ic_menu_help);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ============================================================ lifecycle
|
||||
* ====================================================================== */
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
FlurryAgent.onEndSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
registerReceiver(filterReceiver,
|
||||
new IntentFilter(AstridApiConstants.BROADCAST_SEND_FILTERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
unregisterReceiver(filterReceiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receiver which receives intents to add items to the filter list
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
protected class FilterReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
Parcelable[] filters = intent.getExtras().
|
||||
getParcelableArray(AstridApiConstants.EXTRAS_ITEMS);
|
||||
for (Parcelable item : filters) {
|
||||
adapter.add((FilterListItem)item);
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
} catch (Exception e) {
|
||||
exceptionService.reportError("receive-filter-" + //$NON-NLS-1$
|
||||
intent.getStringExtra(AstridApiConstants.EXTRAS_PLUGIN), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ===================================================== populating lists
|
||||
* ====================================================================== */
|
||||
|
||||
/** Sets up the coach list adapter */
|
||||
protected void setUpList() {
|
||||
adapter = new FilterAdapter(this);
|
||||
setListAdapter(adapter);
|
||||
|
||||
registerForContextMenu(getExpandableListView());
|
||||
getExpandableListView().setGroupIndicator(
|
||||
getResources().getDrawable(R.drawable.expander_group));
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a request for lists. The request is sent to every
|
||||
* application registered to listen for this broadcast. Each application
|
||||
* can then add lists to the
|
||||
*/
|
||||
protected void getLists() {
|
||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_FILTERS);
|
||||
sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ============================================================== actions
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Handles items being clicked. Return true if item is handled.
|
||||
*/
|
||||
protected boolean onItemClicked(FilterListItem item) {
|
||||
if(item instanceof Filter) {
|
||||
Filter filter = (Filter)item;
|
||||
Intent intent = new Intent(FilterListActivity.this, TaskListActivity.class);
|
||||
intent.putExtra(TaskListActivity.TOKEN_FILTER, filter);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onChildClick(ExpandableListView parent, View v,
|
||||
int groupPosition, int childPosition, long id) {
|
||||
FilterListItem item = (FilterListItem) adapter.getChild(groupPosition,
|
||||
childPosition);
|
||||
return onItemClicked(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGroupExpand(int groupPosition) {
|
||||
FilterListItem item = (FilterListItem) adapter.getGroup(groupPosition);
|
||||
onItemClicked(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGroupCollapse(int groupPosition) {
|
||||
FilterListItem item = (FilterListItem) adapter.getGroup(groupPosition);
|
||||
onItemClicked(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo;
|
||||
|
||||
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
|
||||
FilterListItem item;
|
||||
if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
|
||||
int groupPos = ExpandableListView.getPackedPositionGroup(info.packedPosition);
|
||||
int childPos = ExpandableListView.getPackedPositionChild(info.packedPosition);
|
||||
item = (FilterListItem) adapter.getChild(groupPos, childPos);
|
||||
} else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
|
||||
int groupPos = ExpandableListView.getPackedPositionGroup(info.packedPosition);
|
||||
item = (FilterListItem) adapter.getGroup(groupPos);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
MenuItem menuItem;
|
||||
|
||||
if(item instanceof Filter) {
|
||||
Filter filter = (Filter) item;
|
||||
info.targetView.setTag(filter);
|
||||
menuItem = menu.add(0, CONTEXT_MENU_SHORTCUT, 0, R.string.FLA_context_shortcut);
|
||||
Intent shortcutIntent = new Intent(this, TaskListActivity.class);
|
||||
shortcutIntent.setAction(Intent.ACTION_VIEW);
|
||||
shortcutIntent.putExtra(TaskListActivity.TOKEN_SHORTCUT_TITLE, filter.title);
|
||||
shortcutIntent.putExtra(TaskListActivity.TOKEN_SHORTCUT_SQL, filter.sqlQuery);
|
||||
shortcutIntent.putExtra(TaskListActivity.TOKEN_SHORTCUT_NEW_TASK_SQL, filter.sqlForNewTasks);
|
||||
menuItem.setIntent(shortcutIntent);
|
||||
}
|
||||
|
||||
for(int i = 0; i < item.contextMenuLabels.length; i++) {
|
||||
if(item.contextMenuIntents.length <= i)
|
||||
break;
|
||||
menuItem = menu.add(0, CONTEXT_MENU_INTENT, 0, item.contextMenuLabels[i]);
|
||||
menuItem.setIntent(item.contextMenuIntents[i]);
|
||||
}
|
||||
|
||||
if(menu.size() > 0)
|
||||
menu.setHeaderTitle(item.listingTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, final MenuItem item) {
|
||||
|
||||
// handle my own menus
|
||||
switch (item.getItemId()) {
|
||||
case MENU_SEARCH_ID: {
|
||||
onSearchRequested();
|
||||
return true;
|
||||
}
|
||||
|
||||
case MENU_HELP_ID: {
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
case CONTEXT_MENU_SHORTCUT: {
|
||||
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo)item.getMenuInfo();
|
||||
|
||||
final Intent shortcutIntent = item.getIntent();
|
||||
Filter filter = (Filter)info.targetView.getTag();
|
||||
|
||||
String dialogText = getString(R.string.FLA_shortcut_dialog);
|
||||
final EditText editText = new EditText(this);
|
||||
editText.setText(filter.listingTitle);
|
||||
|
||||
dialogUtilities.viewDialog(this, dialogText, editText,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String label = editText.getText().toString();
|
||||
|
||||
Intent createShortcutIntent = new Intent();
|
||||
createShortcutIntent.putExtra(
|
||||
Intent.EXTRA_SHORTCUT_INTENT,
|
||||
shortcutIntent);
|
||||
createShortcutIntent.putExtra(
|
||||
Intent.EXTRA_SHORTCUT_NAME, label);
|
||||
createShortcutIntent.putExtra(
|
||||
Intent.EXTRA_SHORTCUT_ICON,
|
||||
((BitmapDrawable) getResources().getDrawable(
|
||||
R.drawable.icon_tag)).getBitmap());
|
||||
createShortcutIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); //$NON-NLS-1$
|
||||
|
||||
sendBroadcast(createShortcutIntent);
|
||||
Toast.makeText(
|
||||
FilterListActivity.this,
|
||||
getString(
|
||||
R.string.FLA_toast_onCreateShortcut,
|
||||
label), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case CONTEXT_MENU_INTENT: {
|
||||
Intent intent = item.getIntent();
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* ASTRID: Android's Simple Task Recording Dashboard
|
||||
*
|
||||
* 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.todoroo.astrid.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.timsu.astrid.utilities.StartupReceiver;
|
||||
import com.todoroo.andlib.service.ExceptionService.TodorooUncaughtExceptionHandler;
|
||||
import com.todoroo.astrid.filters.FilterExposer;
|
||||
|
||||
/**
|
||||
* HomeActivity is the primary activity for Astrid and determines which activity
|
||||
* to launch next.
|
||||
* <p>
|
||||
* If the user is completely new, it launches {@link IntroductionActivity}.
|
||||
* <p>
|
||||
* If the user needs to sign in, it launches {@link SignInActivity}.
|
||||
* <p>
|
||||
* If the user has no coaches nd no tasks, it launches
|
||||
* {@link CoachSelectorActivity}
|
||||
* <p>
|
||||
* Otherwise, it launches {@link TaskListActivity}.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public class HomeActivity extends AstridActivity {
|
||||
|
||||
public static final int REQUEST_SIGN_IN = 1;
|
||||
public static final int REQUEST_INTRODUCTION = 2;
|
||||
|
||||
/* ======================================================================
|
||||
* ======================================================= initialization
|
||||
* ====================================================================== */
|
||||
|
||||
/** Called when loading up the activity */
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// set uncaught exception handler
|
||||
Thread.setDefaultUncaughtExceptionHandler(new TodorooUncaughtExceptionHandler());
|
||||
|
||||
// open controllers & perform application startup rituals
|
||||
StartupReceiver.onStartupApplication(this);
|
||||
|
||||
performRedirection();
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ======================================================= event handlers
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Perform redirection to next activity
|
||||
*/
|
||||
private void performRedirection() {
|
||||
Intent intent = new Intent(this, TaskListActivity.class);
|
||||
intent.putExtra(TaskListActivity.TOKEN_FILTER, FilterExposer.buildInboxFilter(getResources()));
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,495 @@
|
||||
package com.todoroo.astrid.activity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ListActivity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.AbsListView.OnScrollListener;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
|
||||
import com.flurry.android.FlurryAgent;
|
||||
import com.timsu.astrid.R;
|
||||
import com.timsu.astrid.activities.EditPreferences;
|
||||
import com.timsu.astrid.activities.TaskEdit;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.service.ExceptionService;
|
||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||
import com.todoroo.andlib.utility.DialogUtilities;
|
||||
import com.todoroo.andlib.utility.Pair;
|
||||
import com.todoroo.astrid.adapter.TaskAdapter;
|
||||
import com.todoroo.astrid.adapter.TaskAdapter.ViewHolder;
|
||||
import com.todoroo.astrid.api.AstridApiConstants;
|
||||
import com.todoroo.astrid.api.Filter;
|
||||
import com.todoroo.astrid.api.TaskDetail;
|
||||
import com.todoroo.astrid.dao.Database;
|
||||
import com.todoroo.astrid.filters.FilterExposer;
|
||||
import com.todoroo.astrid.model.Task;
|
||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
||||
import com.todoroo.astrid.service.TaskService;
|
||||
import com.todoroo.astrid.utility.Constants;
|
||||
|
||||
/**
|
||||
* Primary activity for the Bente application. Shows a list of upcoming
|
||||
* tasks and a user's coaches.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public class TaskListActivity extends ListActivity implements OnScrollListener {
|
||||
|
||||
// --- activities
|
||||
|
||||
static final int ACTIVITY_EDIT_TASK = 0;
|
||||
static final int ACTIVITY_SETTINGS = 1;
|
||||
static final int ACTIVITY_PLUGINS = 2;
|
||||
|
||||
// --- menu codes
|
||||
|
||||
private static final int MENU_ADD_TASK_ID = Menu.FIRST + 0;
|
||||
private static final int MENU_PLUGINS_ID = Menu.FIRST + 1;
|
||||
private static final int MENU_SETTINGS_ID = Menu.FIRST + 2;
|
||||
private static final int MENU_HELP_ID = Menu.FIRST + 3;
|
||||
private static final int MENU_PLUGIN_INTENT_ID = Menu.FIRST + 4;
|
||||
|
||||
private static final int CONTEXT_MENU_EDIT_TASK_ID = Menu.FIRST + 5;
|
||||
private static final int CONTEXT_MENU_DELETE_TASK_ID = Menu.FIRST + 6;
|
||||
private static final int CONTEXT_MENU_PLUGIN_INTENT_ID = Menu.FIRST + 7;
|
||||
|
||||
// --- constants
|
||||
|
||||
public static final String TOKEN_FILTER = "filter"; //$NON-NLS-1$
|
||||
public static final String TOKEN_SHORTCUT_SQL = "query"; //$NON-NLS-1$
|
||||
public static final String TOKEN_SHORTCUT_TITLE = "title"; //$NON-NLS-1$
|
||||
public static final String TOKEN_SHORTCUT_NEW_TASK_SQL = "newsql"; //$NON-NLS-1$
|
||||
|
||||
// --- instance variables
|
||||
|
||||
@Autowired
|
||||
protected ExceptionService exceptionService;
|
||||
|
||||
@Autowired
|
||||
protected TaskService taskService;
|
||||
|
||||
@Autowired
|
||||
protected DialogUtilities dialogUtilities;
|
||||
|
||||
@Autowired
|
||||
protected Database database;
|
||||
|
||||
protected TaskAdapter taskAdapter = null;
|
||||
protected DetailReceiver detailReceiver = new DetailReceiver();
|
||||
|
||||
Filter filter;
|
||||
|
||||
/* ======================================================================
|
||||
* ======================================================= initialization
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Load Bente Dependency Injector
|
||||
*/
|
||||
static {
|
||||
AstridDependencyInjector.initialize();
|
||||
}
|
||||
|
||||
public TaskListActivity() {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
/** Called when loading up the activity */
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.task_list_activity);
|
||||
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if(extras.containsKey(TOKEN_FILTER)) {
|
||||
filter = extras.getParcelable(TOKEN_FILTER);
|
||||
} else if(extras.containsKey(TOKEN_SHORTCUT_SQL)) {
|
||||
filter = new Filter();
|
||||
filter.sqlQuery = extras.getString(TOKEN_SHORTCUT_SQL);
|
||||
filter.title = extras.getString(TOKEN_SHORTCUT_TITLE);
|
||||
filter.sqlForNewTasks = extras.getString(TOKEN_SHORTCUT_NEW_TASK_SQL);
|
||||
} else {
|
||||
filter = FilterExposer.buildInboxFilter(getResources());
|
||||
}
|
||||
|
||||
database.openForWriting();
|
||||
setUpUiComponents();
|
||||
setUpTaskList();
|
||||
|
||||
// cache some stuff
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
loadContextMenuIntents();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create options menu (displayed when user presses menu key)
|
||||
*
|
||||
* @return true if menu should be displayed
|
||||
*/
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
if(menu.size() > 0)
|
||||
return true;
|
||||
|
||||
MenuItem item;
|
||||
|
||||
item = menu.add(Menu.NONE, MENU_ADD_TASK_ID, Menu.NONE,
|
||||
R.string.TLA_menu_add);
|
||||
item.setIcon(android.R.drawable.ic_menu_add);
|
||||
|
||||
item = menu.add(Menu.NONE, MENU_PLUGINS_ID, Menu.NONE,
|
||||
R.string.TLA_menu_plugins);
|
||||
item.setIcon(android.R.drawable.ic_menu_set_as);
|
||||
|
||||
item = menu.add(Menu.NONE, MENU_SETTINGS_ID, Menu.NONE,
|
||||
R.string.TLA_menu_settings);
|
||||
item.setIcon(android.R.drawable.ic_menu_preferences);
|
||||
|
||||
item = menu.add(Menu.NONE, MENU_HELP_ID, Menu.NONE,
|
||||
R.string.TLA_menu_help);
|
||||
item.setIcon(android.R.drawable.ic_menu_help);
|
||||
|
||||
// ask about plug-ins
|
||||
Intent queryIntent = new Intent(AstridApiConstants.ACTION_TASK_LIST_MENU);
|
||||
PackageManager pm = getPackageManager();
|
||||
List<ResolveInfo> resolveInfoList = pm.queryIntentActivities(queryIntent, 0);
|
||||
int length = resolveInfoList.size();
|
||||
for(int i = 0; i < length; i++) {
|
||||
ResolveInfo resolveInfo = resolveInfoList.get(i);
|
||||
item = menu.add(Menu.NONE, MENU_PLUGIN_INTENT_ID, Menu.NONE,
|
||||
resolveInfo.loadLabel(pm));
|
||||
item.setIcon(resolveInfo.loadIcon(pm));
|
||||
Intent intent = new Intent(AstridApiConstants.ACTION_TASK_LIST_MENU);
|
||||
intent.setClassName(resolveInfo.activityInfo.packageName,
|
||||
resolveInfo.activityInfo.name);
|
||||
item.setIntent(intent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setUpUiComponents() {
|
||||
((ImageView)findViewById(R.id.back)).setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(TaskListActivity.this,
|
||||
FilterListActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
((TextView)findViewById(R.id.listLabel)).setText(filter.title);
|
||||
|
||||
((ImageButton)findViewById(R.id.quickAddButton)).setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
TextView quickAdd = (TextView)findViewById(R.id.quickAddText);
|
||||
Task task = quickAddTask(quickAdd.getText().toString());
|
||||
if(quickAdd.getText().length() > 0) {
|
||||
quickAdd.setText(""); //$NON-NLS-1$
|
||||
loadTaskListContent(true);
|
||||
} else {
|
||||
Intent intent = new Intent(TaskListActivity.this, TaskEdit.class);
|
||||
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.getId());
|
||||
startActivityForResult(intent, ACTIVITY_EDIT_TASK);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick-add a new task
|
||||
* @param title
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("nls")
|
||||
protected Task quickAddTask(String title) {
|
||||
try {
|
||||
Task task = new Task();
|
||||
task.setValue(Task.TITLE, title);
|
||||
/*task.setValue(Task.DUE_DATE, Task.initializeDueDate(
|
||||
task.getValue(Task.URGENCY)));
|
||||
taskService.save(task, false);
|
||||
if(filter.sqlForNewTasks != null)
|
||||
taskService.invokeSqlForNewTask(filter, task); */ // TODO
|
||||
return task;
|
||||
} catch (Exception e) {
|
||||
exceptionService.displayAndReportError(this, "quick-add-task", e);
|
||||
return new Task();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ============================================================ lifecycle
|
||||
* ====================================================================== */
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
FlurryAgent.onEndSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
registerReceiver(detailReceiver,
|
||||
new IntentFilter(AstridApiConstants.BROADCAST_SEND_DETAILS));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
unregisterReceiver(detailReceiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receiver which receives intents to add items to the filter list
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
protected class DetailReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
Bundle extras = intent.getExtras();
|
||||
long taskId = extras.getLong(AstridApiConstants.EXTRAS_TASK_ID);
|
||||
Parcelable[] details = extras.getParcelableArray(AstridApiConstants.EXTRAS_ITEMS);
|
||||
for(Parcelable detail : details)
|
||||
taskAdapter.addDetails(getListView(), taskId, (TaskDetail)detail);
|
||||
} catch (Exception e) {
|
||||
exceptionService.reportError("receive-detail-" + //$NON-NLS-1$
|
||||
intent.getStringExtra(AstridApiConstants.EXTRAS_PLUGIN), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
// if(requestCode == ACTIVITY_EDIT_TASK && resultCode != TaskEditActivity.RESULT_CODE_DISCARDED)
|
||||
// loadTaskListContent(true);
|
||||
}
|
||||
|
||||
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
|
||||
int totalItemCount) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect when user is flinging the task, disable task adapter loading
|
||||
* when this occurs to save resources and time.
|
||||
*/
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||
switch (scrollState) {
|
||||
case OnScrollListener.SCROLL_STATE_IDLE:
|
||||
if(taskAdapter.isFling)
|
||||
taskAdapter.notifyDataSetChanged();
|
||||
taskAdapter.isFling = false;
|
||||
break;
|
||||
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
|
||||
if(taskAdapter.isFling)
|
||||
taskAdapter.notifyDataSetChanged();
|
||||
taskAdapter.isFling = false;
|
||||
break;
|
||||
case OnScrollListener.SCROLL_STATE_FLING:
|
||||
taskAdapter.isFling = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* =================================================== managing list view
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Load or re-load action items and update views
|
||||
* @param requery
|
||||
*/
|
||||
public void loadTaskListContent(boolean requery) {
|
||||
int oldListItemSelected = getListView().getSelectedItemPosition();
|
||||
Cursor taskCursor = taskAdapter.getCursor();
|
||||
|
||||
if(requery) {
|
||||
taskCursor.requery();
|
||||
taskAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
if(oldListItemSelected != ListView.INVALID_POSITION &&
|
||||
oldListItemSelected < taskCursor.getCount())
|
||||
getListView().setSelection(oldListItemSelected);
|
||||
}
|
||||
|
||||
/** Fill in the Action Item List with current items */
|
||||
protected void setUpTaskList() {
|
||||
// perform query
|
||||
TodorooCursor<Task> currentCursor = taskService.fetchFiltered(
|
||||
TaskAdapter.PROPERTIES, filter);
|
||||
startManagingCursor(currentCursor);
|
||||
|
||||
// set up list adapters
|
||||
taskAdapter = new TaskAdapter(this, R.layout.task_row,
|
||||
currentCursor, false, null);
|
||||
setListAdapter(taskAdapter);
|
||||
getListView().setOnScrollListener(this);
|
||||
registerForContextMenu(getListView());
|
||||
|
||||
loadTaskListContent(false);
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ============================================================== actions
|
||||
* ====================================================================== */
|
||||
|
||||
protected Pair<CharSequence, Intent>[] contextMenuItemCache = null;
|
||||
|
||||
protected void loadContextMenuIntents() {
|
||||
Intent queryIntent = new Intent(AstridApiConstants.ACTION_TASK_CONTEXT_MENU);
|
||||
PackageManager pm = getPackageManager();
|
||||
List<ResolveInfo> resolveInfoList = pm.queryIntentActivities(queryIntent, 0);
|
||||
int length = resolveInfoList.size();
|
||||
contextMenuItemCache = new Pair[length];
|
||||
for(int i = 0; i < length; i++) {
|
||||
ResolveInfo resolveInfo = resolveInfoList.get(i);
|
||||
Intent intent = new Intent(AstridApiConstants.ACTION_TASK_CONTEXT_MENU);
|
||||
intent.setClassName(resolveInfo.activityInfo.packageName,
|
||||
resolveInfo.activityInfo.name);
|
||||
CharSequence title = resolveInfo.loadLabel(pm);
|
||||
contextMenuItemCache[i] = Pair.create(title, intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
AdapterContextMenuInfo adapterInfo = (AdapterContextMenuInfo)menuInfo;
|
||||
Task task = ((ViewHolder)adapterInfo.targetView.getTag()).task;
|
||||
int id = (int)task.getId();
|
||||
menu.setHeaderTitle(task.getValue(Task.TITLE));
|
||||
|
||||
menu.add(id, CONTEXT_MENU_EDIT_TASK_ID, Menu.NONE,
|
||||
R.string.TAd_contextEditTask);
|
||||
|
||||
menu.add(id, CONTEXT_MENU_DELETE_TASK_ID, Menu.NONE,
|
||||
R.string.TAd_contextDeleteTask);
|
||||
|
||||
if(contextMenuItemCache == null)
|
||||
return;
|
||||
|
||||
// ask about plug-ins
|
||||
long taskId = task.getId();
|
||||
for(int i = 0; i < contextMenuItemCache.length; i++) {
|
||||
Intent intent = contextMenuItemCache[i].getRight();
|
||||
MenuItem item = menu.add(id, CONTEXT_MENU_PLUGIN_INTENT_ID, Menu.NONE,
|
||||
contextMenuItemCache[i].getLeft());
|
||||
intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
|
||||
item.setIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
/** Show a dialog box and delete the task specified */
|
||||
private void deleteTask(final long id) {
|
||||
new AlertDialog.Builder(this).setTitle(R.string.DLG_confirm_title)
|
||||
.setMessage(R.string.DLG_delete_this_task_question).setIcon(
|
||||
android.R.drawable.ic_dialog_alert).setPositiveButton(
|
||||
android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
taskService.delete(id);
|
||||
loadTaskListContent(true);
|
||||
}
|
||||
}).setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, final MenuItem item) {
|
||||
Intent intent;
|
||||
long itemId;
|
||||
|
||||
// handle my own menus
|
||||
switch (item.getItemId()) {
|
||||
case MENU_ADD_TASK_ID:
|
||||
intent = new Intent(TaskListActivity.this, TaskEdit.class);
|
||||
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, Task.NO_ID);
|
||||
startActivityForResult(intent, ACTIVITY_EDIT_TASK);
|
||||
return true;
|
||||
case MENU_PLUGINS_ID:
|
||||
dialogUtilities.okDialog(
|
||||
this,
|
||||
"if this were real life, I would display the Android " + //$NON-NLS-1$
|
||||
"market with a search for plugins that you could install.", //$NON-NLS-1$
|
||||
null);
|
||||
return true;
|
||||
case MENU_SETTINGS_ID:
|
||||
intent = new Intent(this, EditPreferences.class);
|
||||
startActivityForResult(intent, ACTIVITY_SETTINGS);
|
||||
return true;
|
||||
case MENU_HELP_ID:
|
||||
// TODO
|
||||
return true;
|
||||
|
||||
|
||||
// context menu items
|
||||
case CONTEXT_MENU_PLUGIN_INTENT_ID: {
|
||||
intent = item.getIntent();
|
||||
AndroidUtilities.startExternalIntent(this, intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
case CONTEXT_MENU_EDIT_TASK_ID: {
|
||||
itemId = item.getGroupId();
|
||||
intent = new Intent(TaskListActivity.this, TaskEdit.class);
|
||||
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, itemId);
|
||||
startActivityForResult(intent, ACTIVITY_EDIT_TASK);
|
||||
return true;
|
||||
}
|
||||
|
||||
case CONTEXT_MENU_DELETE_TASK_ID:
|
||||
itemId = item.getGroupId();
|
||||
deleteTask(itemId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,173 @@
|
||||
/**
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.adapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.BaseExpandableListAdapter;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.timsu.astrid.R;
|
||||
import com.todoroo.astrid.api.Filter;
|
||||
import com.todoroo.astrid.api.FilterCategory;
|
||||
import com.todoroo.astrid.api.FilterListHeader;
|
||||
import com.todoroo.astrid.api.FilterListItem;
|
||||
|
||||
public class FilterAdapter extends BaseExpandableListAdapter {
|
||||
|
||||
private final ArrayList<FilterListItem> items;
|
||||
protected final Activity activity;
|
||||
|
||||
public FilterAdapter(Activity activity) {
|
||||
super();
|
||||
this.activity = activity;
|
||||
this.items = new ArrayList<FilterListItem>();
|
||||
}
|
||||
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void add(FilterListItem item) {
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ========================================================== child nodes
|
||||
* ====================================================================== */
|
||||
|
||||
public Object getChild(int groupPosition, int childPosition) {
|
||||
FilterListItem item = items.get(groupPosition);
|
||||
if(!(item instanceof FilterCategory))
|
||||
return null;
|
||||
|
||||
return ((FilterCategory)item).children[childPosition];
|
||||
}
|
||||
|
||||
public long getChildId(int groupPosition, int childPosition) {
|
||||
return childPosition;
|
||||
}
|
||||
|
||||
public int getChildrenCount(int groupPosition) {
|
||||
FilterListItem item = items.get(groupPosition);
|
||||
if(!(item instanceof FilterCategory))
|
||||
return 0;
|
||||
return ((FilterCategory)item).children.length;
|
||||
}
|
||||
|
||||
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
|
||||
View convertView, ViewGroup parent) {
|
||||
FilterListItem item = (FilterListItem)getChild(groupPosition, childPosition);
|
||||
View textView = getFilterView((Filter)item, true);
|
||||
return textView;
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ========================================================= parent nodes
|
||||
* ====================================================================== */
|
||||
|
||||
public Object getGroup(int groupPosition) {
|
||||
return items.get(groupPosition);
|
||||
}
|
||||
|
||||
public int getGroupCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
public long getGroupId(int groupPosition) {
|
||||
return groupPosition;
|
||||
}
|
||||
|
||||
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
|
||||
ViewGroup parent) {
|
||||
View view = getView((FilterListItem)getGroup(groupPosition), false, isExpanded);
|
||||
return view;
|
||||
}
|
||||
|
||||
public boolean isChildSelectable(int groupPosition, int childPosition) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ================================================================ views
|
||||
* ====================================================================== */
|
||||
|
||||
public View getView(FilterListItem item, boolean isChild, boolean isExpanded) {
|
||||
if(item instanceof FilterListHeader)
|
||||
return getHeaderView((FilterListHeader)item, isChild);
|
||||
else if(item instanceof FilterCategory)
|
||||
return getCategoryView((FilterCategory)item, isExpanded);
|
||||
else if(item instanceof Filter)
|
||||
return getFilterView((Filter)item, isChild);
|
||||
else
|
||||
throw new UnsupportedOperationException("unknown item type"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public View getCategoryView(FilterCategory filter, boolean isExpanded) {
|
||||
AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 64);
|
||||
|
||||
FrameLayout layout = new FrameLayout(activity);
|
||||
layout.setLayoutParams(lp);
|
||||
|
||||
ImageView image = new ImageView(activity);
|
||||
if(isExpanded)
|
||||
image.setImageResource(R.drawable.expander_ic_maximized);
|
||||
else
|
||||
image.setImageResource(R.drawable.expander_ic_minimized);
|
||||
FrameLayout.LayoutParams llp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.FILL_PARENT);
|
||||
llp.gravity = Gravity.CENTER_VERTICAL;
|
||||
image.setLayoutParams(llp);
|
||||
layout.addView(image);
|
||||
|
||||
TextView textView = new TextView(activity);
|
||||
textView.setGravity(Gravity.CENTER_VERTICAL);
|
||||
textView.setLayoutParams(llp);
|
||||
textView.setPadding(40, 0, 0, 0);
|
||||
textView.setText(filter.listingTitle);
|
||||
textView.setTextAppearance(activity, R.style.TextAppearance_FLA_Category);
|
||||
layout.addView(textView);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
public TextView getFilterView(Filter filter, boolean isChild) {
|
||||
AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 64);
|
||||
|
||||
TextView textView = new TextView(activity);
|
||||
textView.setBackgroundDrawable(null);
|
||||
textView.setLayoutParams(lp);
|
||||
textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
|
||||
textView.setPadding(isChild ? 40 : 10, 0, 0, 0);
|
||||
textView.setText(filter.listingTitle);
|
||||
textView.setTextAppearance(activity, R.style.TextAppearance_FLA_Filter);
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
public TextView getHeaderView(FilterListHeader header, boolean isChild) {
|
||||
AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 40);
|
||||
|
||||
TextView textView = new TextView(activity);
|
||||
textView.setLayoutParams(lp);
|
||||
textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
|
||||
textView.setPadding(isChild ? 40 : 10, 0, 0, 0);
|
||||
textView.setTextAppearance(activity, R.style.TextAppearance_FLA_Header);
|
||||
textView.setBackgroundResource(R.drawable.edit_titlebar);
|
||||
textView.setText(header.listingTitle);
|
||||
|
||||
return textView;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,421 @@
|
||||
package com.todoroo.astrid.adapter;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.View.OnCreateContextMenuListener;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.timsu.astrid.R;
|
||||
import com.todoroo.andlib.data.Property;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.service.ExceptionService;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.andlib.utility.DialogUtilities;
|
||||
import com.todoroo.astrid.api.AstridApiConstants;
|
||||
import com.todoroo.astrid.api.TaskDetail;
|
||||
import com.todoroo.astrid.model.Task;
|
||||
import com.todoroo.astrid.service.TaskService;
|
||||
|
||||
/**
|
||||
* Adapter for displaying a user's tasks as a list
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public class TaskAdapter extends CursorAdapter {
|
||||
|
||||
public interface OnCompletedTaskListener {
|
||||
public void onCompletedTask(Task item, boolean newState);
|
||||
}
|
||||
|
||||
// --- other constants
|
||||
|
||||
/** Properties that need to be read from the action item */
|
||||
public static final Property<?>[] PROPERTIES = new Property<?>[] {
|
||||
Task.ID,
|
||||
Task.TITLE,
|
||||
Task.IMPORTANCE,
|
||||
Task.URGENCY,
|
||||
Task.DUE_DATE,
|
||||
Task.COMPLETION_DATE,
|
||||
Task.HIDDEN_UNTIL,
|
||||
};
|
||||
|
||||
private static int[] IMPORTANCE_COLORS = null;
|
||||
|
||||
// --- instance variables
|
||||
|
||||
@Autowired
|
||||
ExceptionService exceptionService;
|
||||
|
||||
@Autowired
|
||||
TaskService taskService;
|
||||
|
||||
@Autowired
|
||||
DialogUtilities dialogUtilities;
|
||||
|
||||
@Autowired
|
||||
Boolean debug;
|
||||
|
||||
protected final Activity activity;
|
||||
protected final HashMap<Long, Boolean> completedItems;
|
||||
protected final HashMap<Long, ArrayList<TaskDetail>> detailCache;
|
||||
public boolean isFling = false;
|
||||
private final int resource;
|
||||
private final LayoutInflater inflater;
|
||||
protected OnCompletedTaskListener onCompletedTaskListener = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param activity
|
||||
* @param resource
|
||||
* layout resource to inflate
|
||||
* @param c
|
||||
* database cursor
|
||||
* @param autoRequery
|
||||
* whether cursor is automatically re-queried on changes
|
||||
* @param onCompletedTaskListener
|
||||
* task listener. can be null
|
||||
*/
|
||||
public TaskAdapter(Activity activity, int resource,
|
||||
TodorooCursor<Task> c, boolean autoRequery,
|
||||
OnCompletedTaskListener onCompletedTaskListener) {
|
||||
super(activity, c, autoRequery);
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
|
||||
inflater = (LayoutInflater) activity.getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
this.resource = resource;
|
||||
this.activity = activity;
|
||||
this.onCompletedTaskListener = onCompletedTaskListener;
|
||||
|
||||
completedItems = new HashMap<Long, Boolean>();
|
||||
detailCache = new HashMap<Long, ArrayList<TaskDetail>>();
|
||||
|
||||
if(IMPORTANCE_COLORS == null) {
|
||||
IMPORTANCE_COLORS = new int[] { Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN }; //Task.getImportanceColors(activity); // TODO
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* =========================================================== view setup
|
||||
* ====================================================================== */
|
||||
|
||||
/** Creates a new view for use in the list view */
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
View view = inflater.inflate(resource, parent, false);
|
||||
|
||||
// create view holder
|
||||
ViewHolder viewHolder = new ViewHolder();
|
||||
viewHolder.task = new Task();
|
||||
viewHolder.nameView = (TextView)view.findViewById(R.id.title);
|
||||
viewHolder.completeBox = (CheckBox)view.findViewById(R.id.completeBox);
|
||||
viewHolder.dueDate = (TextView)view.findViewById(R.id.dueDate);
|
||||
viewHolder.details = (LinearLayout)view.findViewById(R.id.details);
|
||||
viewHolder.importance = (View)view.findViewById(R.id.importance);
|
||||
view.setTag(viewHolder);
|
||||
|
||||
// add UI component listeners
|
||||
addListeners(view);
|
||||
|
||||
// populate view content
|
||||
bindView(view, context, cursor);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/** Populates a view with content */
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor c) {
|
||||
TodorooCursor<Task> cursor = (TodorooCursor<Task>)c;
|
||||
Task actionItem = ((ViewHolder)view.getTag()).task;
|
||||
actionItem.readFromCursor(cursor);
|
||||
|
||||
setFieldContentsAndVisibility(view, actionItem);
|
||||
setTaskAppearance(view, actionItem.isCompleted());
|
||||
}
|
||||
|
||||
/** Helper method to set the visibility based on if there's stuff inside */
|
||||
private static void setVisibility(TextView v) {
|
||||
if(v.getText().length() > 0)
|
||||
v.setVisibility(View.VISIBLE);
|
||||
else
|
||||
v.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* View Holder saves a lot of findViewById lookups.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public static class ViewHolder {
|
||||
public Task task;
|
||||
public TextView nameView;
|
||||
public CheckBox completeBox;
|
||||
public TextView dueDate;
|
||||
public LinearLayout details;
|
||||
public View importance;
|
||||
public TextView loadingDetails;
|
||||
}
|
||||
|
||||
/** Helper method to set the contents and visibility of each field */
|
||||
private void setFieldContentsAndVisibility(View view, Task task) {
|
||||
Resources r = activity.getResources();
|
||||
ViewHolder viewHolder = (ViewHolder)view.getTag();
|
||||
|
||||
// don't bother instantiating views if we're busy
|
||||
if(isFling) {
|
||||
viewHolder.details.removeViews(2, viewHolder.details.getChildCount() - 2);
|
||||
viewHolder.dueDate.setVisibility(View.INVISIBLE);
|
||||
viewHolder.nameView.setText(R.string.TAd_isFling);
|
||||
viewHolder.importance.setVisibility(View.INVISIBLE);
|
||||
viewHolder.completeBox.setVisibility(View.INVISIBLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// name
|
||||
final TextView nameView = viewHolder.nameView; {
|
||||
String nameValue = task.getValue(Task.TITLE);
|
||||
int hiddenUntil = task.getValue(Task.HIDDEN_UNTIL);
|
||||
if(hiddenUntil > DateUtilities.now())
|
||||
nameValue = r.getString(R.string.TAd_hiddenFormat, nameValue);
|
||||
nameView.setText(nameValue);
|
||||
}
|
||||
|
||||
// complete box
|
||||
final CheckBox completeBox = viewHolder.completeBox; {
|
||||
// show item as completed if it was recently checked
|
||||
if(completedItems.containsKey(task.getId()))
|
||||
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
|
||||
completeBox.setChecked(task.isCompleted());
|
||||
completeBox.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// due date / completion date
|
||||
final TextView dueDateView = viewHolder.dueDate; {
|
||||
if(!task.isCompleted() && task.hasDueDate()) {
|
||||
int dueDate = task.getValue(Task.DUE_DATE);
|
||||
int secondsLeft = dueDate - DateUtilities.now();
|
||||
if(secondsLeft > 0) {
|
||||
dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate);
|
||||
} else {
|
||||
dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate_Overdue);
|
||||
}
|
||||
|
||||
String dateValue;
|
||||
Date dueDateAsDate = DateUtilities.unixtimeToDate(dueDate);
|
||||
dateValue = SimpleDateFormat.getDateInstance(
|
||||
SimpleDateFormat.MEDIUM).format(dueDateAsDate);
|
||||
|
||||
if (task.getValue(Task.URGENCY) == Task.URGENCY_SPECIFIC_DAY_TIME) {
|
||||
String timeValue = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(
|
||||
dueDateAsDate);
|
||||
String string = r.getString(R.string.TAd_dueDateTime).replace(
|
||||
"$D", dateValue).replace("$T", timeValue); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
dueDateView.setText(string);
|
||||
} else {
|
||||
String string = r.getString(R.string.TAd_dueDate).replace(
|
||||
"$D", dateValue); //$NON-NLS-1$
|
||||
dueDateView.setText(string);
|
||||
}
|
||||
setVisibility(dueDateView);
|
||||
} else {
|
||||
dueDateView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
// other information - send out a request for it
|
||||
final LinearLayout detailsView = viewHolder.details; {
|
||||
detailsView.removeViews(2, detailsView.getChildCount() - 2);
|
||||
if(detailCache.containsKey(task.getId())) {
|
||||
ArrayList<TaskDetail> details = detailCache.get(task.getId());
|
||||
int length = details.size();
|
||||
for(int i = 0; i < length; i++)
|
||||
detailsView.addView(detailToView(details.get(i)));
|
||||
} else {
|
||||
detailCache.put(task.getId(), new ArrayList<TaskDetail>());
|
||||
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
|
||||
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
|
||||
activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
||||
|
||||
// add loading message
|
||||
if(viewHolder.loadingDetails == null) {
|
||||
viewHolder.loadingDetails = new TextView(activity);
|
||||
viewHolder.loadingDetails.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDetails);
|
||||
viewHolder.loadingDetails.setText(R.string.TAd_loading);
|
||||
detailsView.addView(viewHolder.loadingDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// importance bar - must be set at end when view height is determined
|
||||
final View importanceView = viewHolder.importance; {
|
||||
int value = task.getValue(Task.IMPORTANCE);
|
||||
importanceView.setBackgroundColor(IMPORTANCE_COLORS[value]);
|
||||
importanceView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to a request to add details for a task
|
||||
*
|
||||
* @param taskId
|
||||
*/
|
||||
public synchronized void addDetails(ListView list, long taskId, TaskDetail detail) {
|
||||
if(detail == null)
|
||||
return;
|
||||
|
||||
ArrayList<TaskDetail> details = detailCache.get(taskId);
|
||||
details.add(detail);
|
||||
|
||||
// update view if it is visible
|
||||
int length = list.getChildCount();
|
||||
for(int i = 0; i < length; i++) {
|
||||
ViewHolder viewHolder = (ViewHolder) list.getChildAt(i).getTag();
|
||||
if(viewHolder == null || viewHolder.task.getId() != taskId)
|
||||
continue;
|
||||
|
||||
viewHolder.details.addView(detailToView(detail));
|
||||
viewHolder.details.setVisibility(View.VISIBLE);
|
||||
|
||||
if(viewHolder.loadingDetails != null) {
|
||||
viewHolder.details.removeView(viewHolder.loadingDetails);
|
||||
viewHolder.loadingDetails = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new view for the given detail
|
||||
*
|
||||
* @param detail
|
||||
*/
|
||||
private View detailToView(TaskDetail detail) {
|
||||
TextView textView = new TextView(activity);
|
||||
textView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDetails);
|
||||
textView.setText(detail.text);
|
||||
if(detail.color != 0)
|
||||
textView.setTextColor(detail.color);
|
||||
return textView;
|
||||
}
|
||||
|
||||
private View.OnClickListener completeBoxListener = new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
View container = (View) v.getParent();
|
||||
Task task = ((ViewHolder)container.getTag()).task;
|
||||
|
||||
completeTask(task, ((CheckBox)v).isChecked());
|
||||
// set check box to actual action item state
|
||||
setTaskAppearance(container, task.isCompleted());
|
||||
}
|
||||
};
|
||||
|
||||
protected ContextMenuListener listener = new ContextMenuListener();
|
||||
/**
|
||||
* Set listeners for this view. This is called once per view when it is
|
||||
* created.
|
||||
*/
|
||||
private void addListeners(final View container) {
|
||||
final CheckBox completeBox = ((CheckBox)container.findViewById(R.id.completeBox));
|
||||
completeBox.setOnClickListener(completeBoxListener);
|
||||
|
||||
container.setOnCreateContextMenuListener(listener);
|
||||
}
|
||||
|
||||
class ContextMenuListener implements OnCreateContextMenuListener {
|
||||
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
// this is all a big sham. it's actually handled in Task List Activity
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ======================================================= event handlers
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Call me when the parent presses trackpad
|
||||
*/
|
||||
public void onTrackpadPressed(View container) {
|
||||
if(container == null)
|
||||
return;
|
||||
|
||||
final CheckBox completeBox = ((CheckBox)container.findViewById(R.id.completeBox));
|
||||
completeBox.performClick();
|
||||
}
|
||||
|
||||
/** Helper method to adjust a tasks' appearance if the task is completed or
|
||||
* uncompleted.
|
||||
*
|
||||
* @param actionItem
|
||||
* @param name
|
||||
* @param progress
|
||||
*/
|
||||
void setTaskAppearance(View container, boolean state) {
|
||||
CheckBox completed = (CheckBox)container.findViewById(R.id.completeBox);
|
||||
TextView name = (TextView)container.findViewById(R.id.title);
|
||||
|
||||
completed.setChecked(state);
|
||||
|
||||
if(state) {
|
||||
name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
name.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemTitle_Completed);
|
||||
} else {
|
||||
name.setPaintFlags(name.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
name.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemTitle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when user completes a task via check box or other
|
||||
* means
|
||||
*
|
||||
* @param container
|
||||
* container for the action item
|
||||
* @param newState
|
||||
* state that this task should be set to
|
||||
* @param completeBox
|
||||
* the box that was clicked. can be null
|
||||
*/
|
||||
protected void completeTask(final Task actionItem, final boolean newState) {
|
||||
if(actionItem == null)
|
||||
return;
|
||||
|
||||
if (newState != actionItem.isCompleted()) {
|
||||
completedItems.put(actionItem.getId(), newState);
|
||||
taskService.setComplete(actionItem, newState);
|
||||
|
||||
if(onCompletedTaskListener != null)
|
||||
onCompletedTaskListener.onCompletedTask(actionItem, newState);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||