Update to API level 19 KitKat

DataAccess code refactor
Include JodaTime to have correct Date implementation
WIP Task due dates
This commit is contained in:
bg45
2017-03-15 17:34:27 -04:00
parent 9df847a01a
commit 9e90715e65
17 changed files with 189 additions and 79 deletions

View File

@@ -2,11 +2,11 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "23.0.2"
buildToolsVersion '25.0.2'
defaultConfig {
applicationId "com.wismna.geoffroy.donext"
minSdkVersion 15
minSdkVersion 19
targetSdkVersion 25
versionCode 14
versionName "1.3.0"
@@ -22,10 +22,11 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.2.0'
compile 'com.android.support:design:25.2.0'
compile 'com.android.support:support-v4:25.2.0'
compile 'com.android.support:percent:25.2.0'
compile 'com.android.support:recyclerview-v7:25.2.0'
compile 'com.android.support:appcompat-v7:25.3.0'
compile 'com.android.support:design:25.3.0'
compile 'com.android.support:support-v4:25.3.0'
compile 'com.android.support:percent:25.3.0'
compile 'com.android.support:recyclerview-v7:25.3.0'
compile 'com.google.android.gms:play-services-ads:10.2.0'
compile 'net.danlew:android.joda:2.9.7'
}

View File

@@ -1,17 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wismna.geoffroy.donext">
<supports-screens
android:largeScreens="true"
android:smallScreens="true">
</supports-screens>
<application
android:allowBackup="true"
android:name=".DoNext"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
android:allowBackup="true"
android:fullBackupContent="true">
<activity
android:name=".activities.MainActivity"
android:label="@string/app_name"
@@ -47,5 +48,4 @@
android:value=".activities.MainActivity" />
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,17 @@
package com.wismna.geoffroy.donext;
import android.app.Application;
import net.danlew.android.joda.JodaTimeAndroid;
/**
* Created by bg45 on 2017-03-15.
*/
public class DoNext extends Application {
@Override
public void onCreate() {
super.onCreate();
JodaTimeAndroid.init(this);
}
}

View File

@@ -27,6 +27,7 @@ import com.wismna.geoffroy.donext.adapters.SmartFragmentStatePagerAdapter;
import com.wismna.geoffroy.donext.adapters.TaskRecyclerViewAdapter;
import com.wismna.geoffroy.donext.dao.Task;
import com.wismna.geoffroy.donext.dao.TaskList;
import com.wismna.geoffroy.donext.database.TaskDataAccess;
import com.wismna.geoffroy.donext.database.TaskListDataAccess;
import com.wismna.geoffroy.donext.fragments.TaskDialogFragment;
import com.wismna.geoffroy.donext.fragments.TasksFragment;
@@ -66,17 +67,14 @@ public class MainActivity extends AppCompatActivity implements TasksFragment.Tas
SharedPreferences sharedPref =
PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
// Access database to retrieve Tabs
TaskListDataAccess taskListDataAccess = new TaskListDataAccess(this);
taskListDataAccess.open();
// Handle Today list
handleTodayList(sharedPref, taskListDataAccess);
taskLists = taskListDataAccess.getAllTaskLists();
mSectionsPagerAdapter.notifyDataSetChanged();
taskListDataAccess.close();
handleTodayList(sharedPref);
// Access database to retrieve Tabs
try (TaskListDataAccess taskListDataAccess = new TaskListDataAccess(this)) {
taskLists = taskListDataAccess.getAllTaskLists();
mSectionsPagerAdapter.notifyDataSetChanged();
}
if (taskLists.size() == 0) {
Intent intent = new Intent(this, TaskListActivity.class);
startActivity(intent);
@@ -260,21 +258,28 @@ public class MainActivity extends AppCompatActivity implements TasksFragment.Tas
}
}
private void handleTodayList(SharedPreferences sharedPref, TaskListDataAccess taskListDataAccess) {
private void handleTodayList(SharedPreferences sharedPref) {
String todayListName = getString(R.string.task_list_today);
TaskList todayList = taskListDataAccess.getTaskListByName(todayListName);
if (sharedPref.getBoolean("pref_conf_today_enable", false)) {
// Get or create the Today list
if (todayList == null) {
// TODO: set order correctly
todayList = taskListDataAccess.createTaskList(todayListName, 0);
}
if (!todayList.isVisible()) taskListDataAccess.updateVisibility(todayList.getId(), true);
// Mark all tasks with an earlier do date as done
}
else {
if (todayList != null){
taskListDataAccess.updateVisibility(todayList.getId(), false);
try (TaskListDataAccess taskListDataAccess = new TaskListDataAccess(this, TaskListDataAccess.MODE.WRITE)) {
TaskList todayList = taskListDataAccess.getTaskListByName(todayListName);
if (sharedPref.getBoolean("pref_conf_today_enable", false)) {
// Get or create the Today list
if (todayList == null) {
// TODO: set order correctly
todayList = taskListDataAccess.createTaskList(todayListName, 0);
}
if (!todayList.isVisible())
taskListDataAccess.updateVisibility(todayList.getId(), true);
// Mark all tasks with an earlier do date as done
try (TaskDataAccess taskDataAccess = new TaskDataAccess(this, TaskDataAccess.MODE.WRITE)) {
taskDataAccess.updateExpiredTasks(
Integer.valueOf(sharedPref.getString("pref_conf_today_action", "2")), todayList.getId());
}
} else {
// Hide the today list if it exists
if (todayList != null) {
taskListDataAccess.updateVisibility(todayList.getId(), false);
}
}
}
}

View File

@@ -1,7 +1,6 @@
package com.wismna.geoffroy.donext.dao;
import java.sql.Date;
import java.text.SimpleDateFormat;
import org.joda.time.LocalDate;
/**
* Created by geoffroy on 15-11-25.
@@ -17,7 +16,7 @@ public class Task {
private int deleted;
private long taskList;
private String taskListName;
private Date dueDate;
private LocalDate dueDate;
public long getId() {
return id;
@@ -92,10 +91,15 @@ public class Task {
}
public void setDueDate(String dueDate) {
this.dueDate = Date.valueOf(dueDate);
try {
this.dueDate = LocalDate.parse(dueDate);
}
catch (Exception e){
this.dueDate = LocalDate.now();
}
}
public Date getDueDate() {
public LocalDate getDueDate() {
return dueDate;
}

View File

@@ -9,9 +9,10 @@ import android.database.sqlite.SQLiteDatabase;
import com.wismna.geoffroy.donext.R;
import com.wismna.geoffroy.donext.dao.Task;
import org.joda.time.LocalDate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ArrayList;
import java.util.List;
@@ -31,7 +32,8 @@ public class TaskDataAccess implements AutoCloseable {
DatabaseHelper.COLUMN_ID, DatabaseHelper.TASKS_COLUMN_NAME,
DatabaseHelper.TASKS_COLUMN_DESC, DatabaseHelper.TASKS_COLUMN_PRIORITY,
DatabaseHelper.TASKS_COLUMN_CYCLE, DatabaseHelper.TASKS_COLUMN_DONE,
DatabaseHelper.TASKS_COLUMN_DELETED, DatabaseHelper.TASKS_COLUMN_LIST};
DatabaseHelper.TASKS_COLUMN_DELETED, DatabaseHelper.TASKS_COLUMN_LIST,
DatabaseHelper.TASKS_COLUMN_DUEDATE};
private List<String> priorities = new ArrayList<>();
public TaskDataAccess(Context context) {
@@ -58,15 +60,16 @@ public class TaskDataAccess implements AutoCloseable {
/** Adds or update a task in the database */
public Task createOrUpdateTask(long id, String name, String description, String priority, long taskList) {
return createOrUpdateTask(id, name, description, priority, taskList, new Date());
return createOrUpdateTask(id, name, description, priority, taskList, LocalDate.now());
}
public Task createOrUpdateTask(long id, String name, String description, String priority, long taskList, Date date) {
public Task createOrUpdateTask(long id, String name, String description, String priority, long taskList, LocalDate date) {
ContentValues values = new ContentValues();
values.put(DatabaseHelper.TASKS_COLUMN_NAME, name);
values.put(DatabaseHelper.TASKS_COLUMN_DESC, description);
values.put(DatabaseHelper.TASKS_COLUMN_PRIORITY, priorities.indexOf(priority));
values.put(DatabaseHelper.TASKS_COLUMN_LIST, taskList);
DateFormat sdf = SimpleDateFormat.getDateInstance();
//SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD", Locale.US);
String dateString = sdf.format(date);
values.put(DatabaseHelper.TASKS_COLUMN_DUEDATE, dateString);
long insertId;
@@ -85,6 +88,20 @@ public class TaskDataAccess implements AutoCloseable {
return newTask;
}
public int updateExpiredTasks(int action, long taskListId){
String column = DatabaseHelper.TASKS_COLUMN_DELETED;
if (action == 1)
column = DatabaseHelper.TASKS_COLUMN_DONE;
else if (action == 2)
column = DatabaseHelper.TASKS_COLUMN_DELETED;
ContentValues contentValues = new ContentValues();
contentValues.put(column, 1);
return database.update(DatabaseHelper.TASKS_TABLE_NAME, contentValues,
DatabaseHelper.TASKS_COLUMN_DUEDATE + " < date('now','-1 day') " +
"AND " + DatabaseHelper.TASKS_COLUMN_LIST + " = " + taskListId, null);
}
public List<Task> getAllTasks(long id) {
List<Task> tasks = new ArrayList<>();

View File

@@ -15,7 +15,11 @@ import java.util.List;
* Created by geoffroy on 15-11-25.
* Data access class that handles Task Lists
*/
public class TaskListDataAccess {
public class TaskListDataAccess implements AutoCloseable {
public enum MODE {
READ,
WRITE
}
// Database fields
private SQLiteDatabase database;
private DatabaseHelper dbHelper;
@@ -24,11 +28,16 @@ public class TaskListDataAccess {
DatabaseHelper.COLUMN_ORDER, DatabaseHelper.TASKLIST_COLUMN_VISIBLE};
public TaskListDataAccess(Context context) {
this(context, MODE.READ);
}
public TaskListDataAccess(Context context, MODE writeMode) {
dbHelper = new DatabaseHelper(context);
open(writeMode);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
public void open(MODE writeMode) throws SQLException {
if (writeMode == MODE.WRITE) database = dbHelper.getWritableDatabase();
else database = dbHelper.getReadableDatabase();
}
public void close() {
@@ -114,7 +123,7 @@ public class TaskListDataAccess {
private Cursor getTaskListByNameCursor(String name) {
return database.query(true, DatabaseHelper.TASKLIST_TABLE_NAME, taskListColumns,
DatabaseHelper.TASKLIST_COLUMN_NAME + " = '" + name + "'", null, null, null, null, null);
DatabaseHelper.TASKLIST_COLUMN_NAME + " = '" + name.replace("'", "''") + "'", null, null, null, null, null);
}
private Cursor getAllTaskListsCursor() {
return database.rawQuery("SELECT *," +

View File

@@ -8,8 +8,10 @@ import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Spinner;
@@ -18,6 +20,8 @@ import com.wismna.geoffroy.donext.R;
import com.wismna.geoffroy.donext.dao.Task;
import com.wismna.geoffroy.donext.dao.TaskList;
import org.joda.time.LocalDate;
import java.util.List;
/**
@@ -33,7 +37,7 @@ public class TaskDialogFragment extends DialogFragment {
/** The activity that creates an instance of this dialog fragment must
* implement this interface in order to receive event callbacks.
* Each method passes the DialogFragment in case the host needs to query it. */
public interface NewTaskListener {
interface NewTaskListener {
void onNewTaskDialogPositiveClick(DialogFragment dialog);
void onNewTaskDialogNeutralClick(DialogFragment dialog);
}
@@ -76,6 +80,14 @@ public class TaskDialogFragment extends DialogFragment {
}
});
// Get date picker
final DatePicker dueDatePicker = (DatePicker) view.findViewById(R.id.new_task_due_date);
// Disallow past dates
dueDatePicker.setMinDate(LocalDate.now().toDate().getTime());
// TODO: set task due date
LocalDate dueDate = task.getDueDate();
dueDatePicker.updateDate(dueDate.getYear(), dueDate.getMonthOfYear(), dueDate.getDayOfMonth());
// Populate spinner with task lists
Spinner spinner = (Spinner) view.findViewById(R.id.new_task_list);
// Create an ArrayAdapter using the string array and a default spinner layout
@@ -84,6 +96,19 @@ public class TaskDialogFragment extends DialogFragment {
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// Set due date
dueDatePicker.setEnabled(!taskLists.get(position).getName()
.equals(getString(R.string.task_list_today)));
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
// Auto set list value to current tab
Bundle args = getArguments();

View File

@@ -50,7 +50,7 @@ public class TaskListsFragment extends Fragment implements
super.onCreate(savedInstanceState);
taskListDataAccess = new TaskListDataAccess(getContext());
taskListDataAccess.open();
taskListDataAccess.open(TaskListDataAccess.MODE.WRITE);
new GetTaskListsTask().execute(taskListDataAccess);
}
@@ -98,7 +98,7 @@ public class TaskListsFragment extends Fragment implements
public void onResume() {
super.onResume();
clearFocus();
taskListDataAccess.open();
taskListDataAccess.open(TaskListDataAccess.MODE.WRITE);
}
private void toggleVisibleCreateNewTaskListLayout(View view) {

View File

@@ -1,6 +1,5 @@
package com.wismna.geoffroy.donext.fragments;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.content.SharedPreferences;
@@ -19,6 +18,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.CheckBox;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
@@ -36,6 +36,8 @@ import com.wismna.geoffroy.donext.listeners.RecyclerItemClickListener;
import com.wismna.geoffroy.donext.widgets.DividerItemDecoration;
import com.wismna.geoffroy.donext.widgets.NoScrollingLayoutManager;
import org.joda.time.LocalDate;
/**
* A fragment representing a list of Items.
*/
@@ -82,7 +84,6 @@ public class TasksFragment extends Fragment implements
}
}
@SuppressLint("NewApi")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -225,7 +226,6 @@ public class TasksFragment extends Fragment implements
}
});
snackbar.addCallback(new Snackbar.Callback() {
@SuppressLint("NewApi")
@Override
public void onDismissed(Snackbar snackbar, int event) {
super.onDismissed(snackbar, event);
@@ -288,7 +288,6 @@ public class TasksFragment extends Fragment implements
}
}
@SuppressLint("NewApi")
@Override
public void onNewTaskDialogPositiveClick(DialogFragment dialog) {
// Get the dialog fragment
@@ -303,6 +302,7 @@ public class TasksFragment extends Fragment implements
EditText descText = (EditText) dialogView.findViewById(R.id.new_task_description);
RadioGroup priorityGroup = (RadioGroup) dialogView.findViewById(R.id.new_task_priority);
RadioButton priorityRadio = (RadioButton) dialogView.findViewById(priorityGroup.getCheckedRadioButtonId());
DatePicker dueDatePicker = (DatePicker) dialogView.findViewById(R.id.new_task_due_date);
TaskList taskList = (TaskList) listSpinner.getSelectedItem();
// Add the task to the database
@@ -311,7 +311,8 @@ public class TasksFragment extends Fragment implements
nameText.getText().toString(),
descText.getText().toString(),
priorityRadio.getText().toString(),
taskList.getId());
taskList.getId(),
new LocalDate(dueDatePicker.getYear(), dueDatePicker.getMonth(), dueDatePicker.getDayOfMonth()));
Bundle args = dialog.getArguments();
// Should never happen because we will have to be on this tab to open the dialog

View File

@@ -4,13 +4,14 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="@dimen/text_margin"
android:orientation="vertical"
tools:context=".activities.MainActivity">
<TextView
android:id="@+id/new_task_list_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/new_task_list"
@@ -18,41 +19,53 @@
<Spinner
android:id="@+id/new_task_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginStart="5dp"
android:layout_toEndOf="@id/new_task_list_label">
</Spinner>
<TextView
android:id="@+id/new_task_name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/new_task_name"
android:textAppearance="?android:attr/textAppearanceLarge" />
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_below="@id/new_task_list_label"/>
<EditText
android:id="@+id/new_task_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/new_task_name_hint"
android:maxLines="1"
android:inputType="text" />
android:inputType="text"
android:layout_below="@id/new_task_name_label"/>
<TextView
android:id="@+id/new_task_description_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/new_task_description"
android:textAppearance="?android:attr/textAppearanceLarge" />
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_below="@id/new_task_name" />
<EditText
android:id="@+id/new_task_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/new_task_description_hint"
android:gravity="top|start"
android:lines="3"/>
android:lines="3"
android:layout_below="@id/new_task_description_label" />
<TextView
android:id="@+id/new_task_priority_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/new_task_priority"
android:textAppearance="?android:attr/textAppearanceLarge" />
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_below="@id/new_task_description" />
<RadioGroup
android:id="@+id/new_task_priority"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_below="@id/new_task_priority_label" >
<RadioButton
android:id="@+id/new_task_priority_low"
android:text="@string/new_task_priority_low"
@@ -73,5 +86,21 @@
android:layout_height="wrap_content" >
</RadioButton>
</RadioGroup>
</LinearLayout>
<TextView
android:id="@+id/new_task_due_date_label"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="@string/new_task_due_date"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_toEndOf="@id/new_task_priority"
android:layout_below="@id/new_task_description" />
<DatePicker
android:id="@+id/new_task_due_date"
android:calendarViewShown="true"
android:spinnersShown="false"
android:layout_width="800dp"
android:layout_height="800dp"
android:layout_toEndOf="@id/new_task_priority"
android:layout_below="@id/new_task_due_date_label" />
</RelativeLayout>
</ScrollView>

View File

@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="settings_task_layouts">
<item>Simple</item>
<item>Détaillée</item>
</string-array>
<string-array name="settings_today_actions">
<item>Terminé</item>
<item>Supprimé</item>
<item>Terminer</item>
<item>Supprimer</item>
</string-array>
</resources>

View File

@@ -29,12 +29,8 @@
<string name="settings_confirm_donext">Confirmation sur suivant</string>
<string name="settings_confirm_markdone">Confirmation sur terminé</string>
<string name="settings_confirm_message">Changer l\'état de la tâche en</string>
<string name="settings_max_lists_label">Nombre de listes maximum:</string>
<string name="settings_task_layout">Apparence des tâches:</string>
<string-array name="settings_task_layouts">
<item>Simple</item>
<item>Détaillée</item>
</string-array>
<string name="settings_max_lists_label">Nombre de listes maximum</string>
<string name="settings_task_layout">Apparence des tâches</string>
<string name="snackabar_action_deleted">supprimée</string>
<string name="snackabar_action_done">terminée</string>
<string name="snackabar_action_next">suivante</string>
@@ -69,4 +65,5 @@
<string name="task_list_today">Aujourd\'hui</string>
<string name="task_list_today_list_error">Le nom \"Aujourd\'hui\" est réservé. Vous pouvez activer la liste Aujourd\'hui dans les paramètres.</string>
<string name="settings_today_action_title">Action à entreprendre à la fin de la journée:</string>
<string name="new_task_due_date">Date de fin</string>
</resources>

View File

@@ -68,9 +68,9 @@
<string name="settings_confirm_donext">Confirm on next?</string>
<string name="settings_confirm_markdone">Confirm on done?</string>
<string name="settings_confirm_delete">Confirm on delete?</string>
<string name="settings_task_layout">Task layout:</string>
<string name="settings_task_layout">Task layout</string>
<string name="settings_max_lists_label">Maximum number of lists:</string>
<string name="settings_max_lists_label">Maximum number of lists</string>
<string name="title_activity_task_list">TaskListActivity</string>
<!-- Strings related to About -->
@@ -83,4 +83,5 @@
<string name="task_list_today">Today</string>
<string name="task_list_today_list_error">Name \"Today\" is reserved. You may activate the Today list from the Settings.</string>
<string name="settings_today_action_title">Action at the end of the day</string>
<string name="new_task_due_date">Due date</string>
</resources>

View File

@@ -50,7 +50,7 @@
android:entries="@array/settings_today_actions"
android:entryValues="@array/settings_today_action_values"
android:summary="%s"
android:defaultValue="0" />
android:defaultValue="2" />
</PreferenceScreen>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.android.tools.build:gradle:2.3.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@@ -1,6 +1,6 @@
#Fri Sep 09 18:19:32 EDT 2016
#Wed Mar 15 12:45:45 EDT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip