list = new ArrayList();
28 | }
29 |
30 | //we provide a way of accessing the data which is secure
31 | public HOUR_MODE getMode() {
32 | return mode;
33 | }
34 |
35 | public Hour[] getHours() {
36 | return hours;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/dayview/DayListItemView.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.dayview;
2 |
3 | import androidx.annotation.IntegerRes;
4 | import androidx.annotation.VisibleForTesting;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * In order to isolate the logic of generating appropriate data for the view and the adapter,
10 | * I will create a special kind of ViewModel which will contain the data in a form which simplifies
11 | * the View binding for the Adapter.
12 | *
13 | *
14 | *
15 | *
16 | * See the DayListViewItemMaker class for more details
17 | */
18 | public
19 | class DayListItemView {
20 | private final String hourBlockText;
21 |
22 | @IntegerRes
23 | private final int[] iconResId;
24 |
25 | @IntegerRes
26 | private final int[] backgroundResId;
27 |
28 | private final String[] taskNames;
29 |
30 | private final LIST_ITEM_TYPE type;
31 |
32 | public String getHourBlockText() {
33 | return hourBlockText;
34 | }
35 |
36 | public int[] getIconResId() {
37 | return iconResId;
38 | }
39 |
40 | public int[] getBackgroundResId() {
41 | return backgroundResId;
42 | }
43 |
44 | public String[] getTaskNames() {
45 | return taskNames;
46 | }
47 |
48 | public LIST_ITEM_TYPE getType() {
49 | return type;
50 | }
51 |
52 | public DayListItemView(String hourBlockText, int[] iconResId, int[] backgroundResId, String[] taskNames, LIST_ITEM_TYPE type) {
53 | this.hourBlockText = hourBlockText;
54 | this.backgroundResId = backgroundResId;
55 | this.iconResId = iconResId;
56 | this.taskNames = taskNames;
57 | this.type = type;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/common/IconUtility.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.common;
2 |
3 | import android.content.Context;
4 |
5 | import com.wiseassblog.samsaradayplanner.domain.constants.ICON;
6 |
7 | /**
8 | * Helps to map to and from the ICON enum and dynamically generated resource ids
9 | */
10 | public class IconUtility {
11 |
12 | public static final String[] iconNames = {
13 | "Free Time",
14 | "Break",
15 | "Study",
16 | "Work",
17 | "Exercise",
18 | "Mental Cultivation",
19 | "Eat",
20 | "Rest",
21 | "Shop"
22 | };
23 |
24 | public static int getResIdFromEnum(Context context, ICON icon) {
25 | switch (icon) {
26 | case FREE_TIME: return getId("ic_free_time", context);
27 | case BREAK: return getId("ic_break", context);
28 | case STUDY: return getId("ic_study", context);
29 | case WORK: return getId("ic_work", context);
30 | case EXERCISE: return getId("ic_exercise", context);
31 | case MENTAL_CULTIVATION: return getId("ic_bhavana", context);
32 | case EAT: return getId("ic_eat", context);
33 | case SLEEP: return getId("ic_rest", context);
34 | case SHOP: return getId("ic_shopping", context);
35 | default: FREE_TIME: return getId("ic_free_time", context);
36 | }
37 | }
38 |
39 | private static int getId(String s, Context context) {
40 | return context.getResources()
41 | .getIdentifier(
42 | s,
43 | "drawable",
44 | context.getPackageName()
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_tasks.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
26 |
27 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/managetaskview/IconSpinnerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.managetaskview;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ArrayAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 |
14 | import com.wiseassblog.samsaradayplanner.R;
15 |
16 | import java.util.List;
17 |
18 | class IconSpinnerAdapter extends ArrayAdapter {
19 |
20 | public IconSpinnerAdapter(Context context, List items) {
21 | super(context, 0, items);
22 | }
23 |
24 | @NonNull
25 | @Override
26 | public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
27 | if (convertView == null) convertView = LayoutInflater.from(getContext())
28 | .inflate(android.R.layout.simple_spinner_item, parent, false);
29 |
30 | IconSpinnerItem item = getItem(position);
31 | TextView itemText = convertView.findViewById(android.R.id.text1);
32 |
33 | if (item != null) {
34 | itemText.setText(item.getItemText());
35 | }
36 |
37 | return convertView;
38 | }
39 |
40 | @NonNull
41 | @Override
42 | public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
43 | if (convertView == null) convertView = LayoutInflater.from(getContext())
44 | .inflate(R.layout.spinner_icon_dropdown_item, parent, false);
45 |
46 | IconSpinnerItem item = getItem(position);
47 |
48 | ImageView itemIcon = convertView.findViewById(R.id.imv_spinner_item_icon);
49 | TextView itemText = convertView.findViewById(R.id.lbl_spinner_item);
50 |
51 | if (item != null) {
52 | itemIcon.setImageResource(item.getItemIconResId());
53 | itemText.setText(item.getItemText());
54 | }
55 |
56 | return convertView;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_half_hour_task.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
33 |
34 |
44 |
45 |
50 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_full_hour_task.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
33 |
34 |
44 |
45 |
50 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_quarter_hour_task.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
33 |
34 |
44 |
45 |
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/tasklistview/TaskListViewLogic.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.tasklistview;
2 |
3 | import com.wiseassblog.samsaradayplanner.common.BaseViewLogic;
4 | import com.wiseassblog.samsaradayplanner.common.Continuation;
5 | import com.wiseassblog.samsaradayplanner.domain.ITaskStorage;
6 | import com.wiseassblog.samsaradayplanner.domain.Tasks;
7 |
8 | public class TaskListViewLogic extends BaseViewLogic {
9 | private final ITaskListViewContract.View view;
10 | private final ITaskListViewContract.ViewModel vm;
11 | private final ITaskStorage storage;
12 |
13 | public TaskListViewLogic(ITaskListViewContract.View view, ITaskListViewContract.ViewModel vm, ITaskStorage storage) {
14 | this.view = view;
15 | this.vm = vm;
16 | this.storage = storage;
17 | }
18 |
19 |
20 | @Override
21 | public void onViewEvent(TasksListViewEvent event) {
22 | switch (event.getEvent()) {
23 | case ON_START:
24 | onStart();
25 | break;
26 | case ON_LIST_ITEM_SELECTED:
27 | onItemSelected((int)event.getValue());
28 | break;
29 | case ON_BACK_PRESSED:
30 | onBackPressed();
31 | break;
32 | }
33 | }
34 |
35 | private void onBackPressed() {
36 | view.navigateToDayView();
37 | }
38 |
39 | private void onItemSelected(int position) {
40 | int id = vm.getTasks().get()[position].getTaskId();
41 |
42 | view.navigateToTaskView(id);
43 | }
44 |
45 | private void onDone() {
46 |
47 | }
48 |
49 | private void onStart() {
50 | storage.getTasks(
51 | new Continuation() {
52 | @Override
53 | public void onSuccess(Tasks result) {
54 | vm.setTasks(result);
55 | view.setTasks(result);
56 | }
57 |
58 | @Override
59 | public void onException(Exception e) {
60 | view.showMessage(e.getMessage());
61 | view.navigateToDayView();
62 | }
63 | }
64 | );
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/managetaskview/TaskActivity.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.managetaskview;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 |
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.widget.Toast;
8 |
9 | import com.wiseassblog.samsaradayplanner.R;
10 | import com.wiseassblog.samsaradayplanner.SamsaraApp;
11 | import com.wiseassblog.samsaradayplanner.domain.constants.Extras;
12 | import com.wiseassblog.samsaradayplanner.ui.dayview.DayActivity;
13 | import com.wiseassblog.samsaradayplanner.ui.dayview.DayView;
14 | import com.wiseassblog.samsaradayplanner.ui.dayview.buildlogic.DayViewInjector;
15 | import com.wiseassblog.samsaradayplanner.ui.managetaskview.buildlogic.TaskViewInjector;
16 |
17 | import static com.wiseassblog.samsaradayplanner.common.Messages.GENERIC_ERROR_MESSAGE;
18 |
19 | public class TaskActivity extends AppCompatActivity {
20 |
21 | private static final String TASK_VIEW = "TASK_VIEW";
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_manage_task);
27 |
28 |
29 | final Intent i = this.getIntent();
30 |
31 | if (i == null) {
32 | Toast.makeText(this,
33 | GENERIC_ERROR_MESSAGE,
34 | Toast.LENGTH_SHORT
35 | ).show();
36 |
37 | startActivity(
38 | new Intent(this, DayActivity.class)
39 | );
40 | }
41 |
42 | int taskId = i.getIntExtra(
43 | Extras.EXTRA_TASK_ID,
44 | 0
45 | );
46 |
47 | TaskView fragment = (TaskView) this.getSupportFragmentManager().findFragmentByTag(
48 | TASK_VIEW
49 | );
50 |
51 | if (fragment == null) {
52 | fragment = TaskView.newInstance();
53 | }
54 |
55 | this.getSupportFragmentManager().beginTransaction().replace(
56 | R.id.root_manage_task_view,
57 | fragment,
58 | TASK_VIEW
59 | ).commit();
60 |
61 | TaskViewInjector.build(
62 | fragment,
63 | ((SamsaraApp) getApplication()).getServiceLocator(),
64 | taskId
65 | );
66 | }
67 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_quarter_hour_toggle.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
29 |
30 |
39 |
40 |
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/managehourview/HourActivity.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.managehourview;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 |
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.widget.Toast;
8 |
9 | import com.wiseassblog.samsaradayplanner.R;
10 | import com.wiseassblog.samsaradayplanner.SamsaraApp;
11 | import com.wiseassblog.samsaradayplanner.domain.constants.Extras;
12 | import com.wiseassblog.samsaradayplanner.ui.dayview.DayActivity;
13 | import com.wiseassblog.samsaradayplanner.ui.managehourview.buildlogic.HourViewInjector;
14 | import com.wiseassblog.samsaradayplanner.ui.tasklistview.TaskListView;
15 | import com.wiseassblog.samsaradayplanner.ui.tasklistview.buildlogic.TaskListViewInjector;
16 |
17 | import static com.wiseassblog.samsaradayplanner.common.Messages.GENERIC_ERROR_MESSAGE;
18 |
19 | public class HourActivity extends AppCompatActivity {
20 |
21 | private static final String HOUR_VIEW = "HOUR_VIEW";
22 |
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_manage_hour);
28 |
29 | final Intent i = this.getIntent();
30 |
31 | if (i == null) {
32 | Toast.makeText(this,
33 | GENERIC_ERROR_MESSAGE,
34 | Toast.LENGTH_SHORT
35 | ).show();
36 |
37 | startActivity(
38 | new Intent(this, DayActivity.class)
39 | );
40 | }
41 |
42 | int hourInteger = i.getIntExtra(
43 | Extras.EXTRA_HOUR_INTEGER,
44 | 0
45 | );
46 |
47 | HourView fragment = (HourView) this.getSupportFragmentManager().findFragmentByTag(
48 | HOUR_VIEW
49 | );
50 |
51 | if (fragment == null) {
52 | fragment = HourView.newInstance();
53 | }
54 |
55 | this.getSupportFragmentManager().beginTransaction().replace(
56 | R.id.root_manage_hour_view,
57 | fragment,
58 | HOUR_VIEW
59 | ).commit();
60 |
61 |
62 | HourViewInjector.build(
63 | hourInteger,
64 | fragment,
65 | ((SamsaraApp) getApplication()).getServiceLocator()
66 | );
67 | }
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/common/ColorUtility.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.common;
2 |
3 | import android.content.Context;
4 | import android.content.res.ColorStateList;
5 |
6 | import androidx.core.content.ContextCompat;
7 |
8 | import com.wiseassblog.samsaradayplanner.R;
9 | import com.wiseassblog.samsaradayplanner.domain.constants.COLOR;
10 |
11 | public class ColorUtility {
12 |
13 | public static int getColorResId(Context context, COLOR color) {
14 | switch (color) {
15 | case DARK_BLUE:
16 | return ContextCompat.getColor(
17 | context,
18 | R.color.darkBlue
19 | );
20 | case BURNT_ORANGE:
21 | return ContextCompat.getColor(
22 | context,
23 | R.color.burntOrange
24 | );
25 | case GREEN:
26 | return ContextCompat.getColor(
27 | context,
28 | R.color.green
29 | );
30 | case DARK_RED:
31 | return ContextCompat.getColor(
32 | context,
33 | R.color.red
34 | );
35 | case DARK_LIME:
36 | return ContextCompat.getColor(
37 | context,
38 | R.color.lime
39 | );
40 | case LIGHT_BLUE:
41 | return ContextCompat.getColor(
42 | context,
43 | R.color.lightBlue
44 | );
45 | case MAUVE:
46 | return ContextCompat.getColor(
47 | context,
48 | R.color.mauve
49 | );
50 | case BROWN:
51 | return ContextCompat.getColor(
52 | context,
53 | R.color.brown
54 | );
55 | case TEAL:
56 | return ContextCompat.getColor(
57 | context,
58 | R.color.teal
59 | );
60 | default:
61 | FREE_TIME:
62 | return ContextCompat.getColor(
63 | context,
64 | R.color.lightBlue
65 | );
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item_task.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
21 |
22 |
37 |
38 |
44 |
45 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_hour_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
26 |
27 |
36 |
37 |
46 |
47 |
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/tasklistview/TaskGridItemViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.tasklistview;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.AdapterView;
7 | import android.widget.BaseAdapter;
8 |
9 | import androidx.annotation.NonNull;
10 |
11 | import com.wiseassblog.samsaradayplanner.common.ColorUtility;
12 | import com.wiseassblog.samsaradayplanner.common.IconUtility;
13 | import com.wiseassblog.samsaradayplanner.domain.Task;
14 |
15 | class TaskGridItemViewAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
16 |
17 | private final Task[] taskItems;
18 | private TaskGridAdapterCallback callback;
19 |
20 | @Override
21 | public void onItemClick(AdapterView> adapterView, View view, int position, long l) {
22 | if (callback != null) callback.onItemClick(taskItems[position].getTaskId());
23 | }
24 |
25 |
26 | interface TaskGridAdapterCallback {
27 | public void onItemClick(int taskId);
28 | }
29 |
30 | public void setCallback(TaskGridAdapterCallback callback) {
31 | this.callback = callback;
32 | }
33 |
34 | public TaskGridItemViewAdapter(@NonNull Context context, Task[] taskItems) {
35 | this.taskItems = taskItems;
36 | }
37 |
38 | @Override
39 | public int getCount() {
40 | return taskItems.length;
41 | }
42 |
43 | @Override
44 | public Object getItem(int i) {
45 | return taskItems[i];
46 | }
47 |
48 | @Override
49 | public long getItemId(int i) {
50 | return taskItems[i].getTaskId();
51 | }
52 |
53 | @Override
54 | public View getView(int position, View view, ViewGroup parent) {
55 | TaskGridItemView gridItem = (TaskGridItemView) view;
56 | if (gridItem == null) {
57 | gridItem = new TaskGridItemView(parent.getContext());
58 | }
59 |
60 | gridItem.setListItemBackground(
61 | ColorUtility.getColorResId(
62 | parent.getContext(),
63 | taskItems[position].getTaskColor()
64 | )
65 | );
66 |
67 | gridItem.setListItemIcon(
68 | IconUtility.getResIdFromEnum(
69 | parent.getContext(),
70 | taskItems[position].getTaskIcon()
71 | )
72 | );
73 |
74 | gridItem.setListItemText(taskItems[position].getTaskName());
75 |
76 | return gridItem;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/managehourview/HourViewEvent.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.managehourview;
2 |
3 | import com.wiseassblog.samsaradayplanner.domain.constants.QUARTER;
4 | import com.wiseassblog.samsaradayplanner.ui.managetaskview.TaskViewEvent;
5 |
6 | public class HourViewEvent {
7 | private final HourViewEvent.Event event;
8 | private final Object value;
9 | private QUARTER quarter;
10 | private int position;
11 | private boolean isActive;
12 |
13 | public QUARTER getQuarter() {
14 | return quarter;
15 | }
16 |
17 | public int getPosition() {
18 | return position;
19 | }
20 |
21 | public boolean isActive() {
22 | return isActive;
23 | }
24 |
25 | public HourViewEvent(HourViewEvent.Event event, Object value){
26 | this.event = event;
27 | this.value = value;
28 | }
29 |
30 | private HourViewEvent(
31 | HourViewEvent.Event event,
32 | Object value,
33 | QUARTER quarter,
34 | int position){
35 | this.event = event;
36 | this.value = value;
37 | this.quarter = quarter;
38 | this.position = position;
39 | this.isActive = false;
40 | }
41 |
42 | private HourViewEvent(
43 | HourViewEvent.Event event,
44 | Object value,
45 | QUARTER quarter,
46 | boolean isActive){
47 | this.event = event;
48 | this.value = value;
49 | this.quarter = quarter;
50 | this.isActive = isActive;
51 | this.position = 0;
52 | }
53 |
54 | /**
55 | * Convenience method for creating events with multiple parameters safely
56 | * @param quarter
57 | * @param position
58 | * @return
59 | */
60 | public static HourViewEvent getOnTaskSelectedEvent(
61 | QUARTER quarter,
62 | int position
63 | ){
64 |
65 | return new HourViewEvent(Event.ON_TASK_SELECTED, null, quarter, position);
66 | }
67 |
68 | /**
69 | * Convenience method for creating events with multiple parameters safely
70 | * @param quarter
71 | * @param isActive
72 | * @return
73 | */
74 | public static HourViewEvent getOnQuarterToggleEvent(
75 | QUARTER quarter,
76 | boolean isActive
77 | ){
78 |
79 | return new HourViewEvent(Event.ON_QUARTER_TOGGLE, null, quarter, isActive);
80 | }
81 |
82 |
83 | public HourViewEvent.Event getEvent() {
84 | return event;
85 | }
86 |
87 | public Object getValue() {
88 | return value;
89 | }
90 |
91 | public enum Event {
92 | ON_QUARTER_TOGGLE,
93 | ON_DONE_CLICK,
94 | ON_TASK_SELECTED,
95 | ON_START
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/common/TimeFormatUtility.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.common;
2 |
3 | import com.wiseassblog.samsaradayplanner.domain.Hour;
4 | import com.wiseassblog.samsaradayplanner.domain.constants.HOUR_MODE;
5 | import com.wiseassblog.samsaradayplanner.domain.constants.QUARTER;
6 |
7 | public class TimeFormatUtility {
8 | public static String getHourBlockText(int hour, HOUR_MODE mode) {
9 | String suffix = "";
10 | String hourText = Integer.toString(hour);
11 |
12 | //Twelve hour clock is stupid
13 | if (mode == HOUR_MODE.TWELVE_HOUR) {
14 | if (hour == 0) {
15 | suffix = "AM";
16 | hourText = "12";
17 | }
18 | if (hour == 12) suffix = "PM";
19 | else if (hour > 12) {
20 | //hour is 13-23 (1PM - 11PM)
21 | suffix = "PM";
22 | hourText = Integer.toString(hour - 12);
23 | } else {
24 | //hour is 01-11 (1PM - 11PM)
25 | suffix = "AM";
26 | }
27 | } else if (mode == HOUR_MODE.TWENTY_FOUR_HOUR) {
28 | //else if included for legibility, in case you were wondering
29 | if (hour == 0) {
30 | //military clock uses 00, not 0 for 12:00AM equivalent
31 | hourText = "00";
32 | }
33 | //either way suffix is the same
34 | suffix = ":00";
35 | }
36 |
37 | return hourText + suffix;
38 | }
39 |
40 | public static String getHourToggleViewFormattedText(QUARTER q, int hour, HOUR_MODE mode) {
41 | String suffix = "";
42 | String quarterText = getQuarterText(q);
43 | String hourText = Integer.toString(hour);
44 |
45 | //Twelve hour clock is stupid
46 | if (mode == HOUR_MODE.TWELVE_HOUR) {
47 | if (hour == 0) {
48 | suffix = "AM";
49 | hourText = "12";
50 | } else if (hour == 12) suffix = "PM";
51 | else if (hour > 12) {
52 | //hour is 13-23 (1PM - 11PM)
53 | suffix = "PM";
54 | hourText = Integer.toString(hour - 12);
55 | } else {
56 | //hour is 01-11 (1PM - 11PM)
57 | suffix = "AM";
58 | }
59 | } else if (mode == HOUR_MODE.TWENTY_FOUR_HOUR) {
60 | //else if included for legibility, in case you were wondering
61 | if (hour == 0) {
62 | //military clock uses 00, not 0 for 12:00AM equivalent
63 | hourText = "00";
64 | }
65 | }
66 |
67 | return hourText + quarterText + suffix;
68 | }
69 |
70 | private static String getQuarterText(QUARTER q) {
71 | switch (q) {
72 | case FIFTEEN: return ":15";
73 | case THIRTY: return ":30";
74 | case FOURTY_FIVE: return ":45";
75 | default: return ":00";
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item_day_view_full_hour.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
18 |
27 |
28 |
34 |
35 |
56 |
57 |
67 |
68 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/managetaskview/ColorPickerView.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.managetaskview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.ImageButton;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.annotation.Nullable;
10 | import androidx.constraintlayout.widget.ConstraintLayout;
11 |
12 | import com.wiseassblog.samsaradayplanner.R;
13 | import com.wiseassblog.samsaradayplanner.domain.constants.COLOR;
14 |
15 | public class ColorPickerView extends ConstraintLayout implements View.OnClickListener {
16 |
17 | ImageButton darkBlue, burntOrange, green, red, darkLime, lightBlue, mauve, brown, teal;
18 | private ColorPickerViewClickHandler callback;
19 |
20 | @Override
21 | public void onClick(View view) {
22 | int id = view.getId();
23 |
24 | switch (id) {
25 | case R.id.imb_dark_blue:
26 | callback.onColorSelected(COLOR.DARK_BLUE);
27 | break;
28 | case R.id.imb_burnt_orange:
29 | callback.onColorSelected(COLOR.BURNT_ORANGE);
30 | break;
31 | case R.id.imb_green:
32 | callback.onColorSelected(COLOR.GREEN);
33 | break;
34 | case R.id.imb_red:
35 | callback.onColorSelected(COLOR.DARK_RED);
36 | break;
37 | case R.id.imb_dark_lime:
38 | callback.onColorSelected(COLOR.DARK_LIME);
39 | break;
40 | case R.id.imb_light_blue:
41 | callback.onColorSelected(COLOR.LIGHT_BLUE);
42 | break;
43 | case R.id.imb_mauve:
44 | callback.onColorSelected(COLOR.MAUVE);
45 | break;
46 | case R.id.imb_brown:
47 | callback.onColorSelected(COLOR.BROWN);
48 | break;
49 | case R.id.imb_teal:
50 | callback.onColorSelected(COLOR.TEAL);
51 | break;
52 | }
53 | }
54 |
55 | interface ColorPickerViewClickHandler {
56 | void onColorSelected(COLOR color);
57 | }
58 |
59 | public void setCallback(ColorPickerViewClickHandler callback) {
60 | this.callback = callback;
61 | }
62 |
63 |
64 | public ColorPickerView(@NonNull Context context, @Nullable AttributeSet attrs) {
65 | super(context, attrs);
66 |
67 | build();
68 | }
69 |
70 | private void build() {
71 | inflate(getContext(), R.layout.view_color_picker_sheet, this);
72 |
73 | darkBlue = findViewById(R.id.imb_dark_blue);
74 | darkBlue.setOnClickListener(this);
75 |
76 | burntOrange = findViewById(R.id.imb_burnt_orange);
77 | burntOrange.setOnClickListener(this);
78 |
79 | green = findViewById(R.id.imb_green);
80 | green.setOnClickListener(this);
81 |
82 | red = findViewById(R.id.imb_red);
83 | red.setOnClickListener(this);
84 |
85 | darkLime = findViewById(R.id.imb_dark_lime);
86 | darkLime.setOnClickListener(this);
87 |
88 | lightBlue = findViewById(R.id.imb_light_blue);
89 | lightBlue.setOnClickListener(this);
90 |
91 | mauve = findViewById(R.id.imb_mauve);
92 | mauve.setOnClickListener(this);
93 |
94 | brown = findViewById(R.id.imb_brown);
95 | brown.setOnClickListener(this);
96 |
97 | teal = findViewById(R.id.imb_teal);
98 | teal.setOnClickListener(this);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/dayview/DayViewLogic.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.dayview;
2 |
3 | import com.wiseassblog.samsaradayplanner.common.BaseViewLogic;
4 | import com.wiseassblog.samsaradayplanner.common.Continuation;
5 | import com.wiseassblog.samsaradayplanner.common.Messages;
6 | import com.wiseassblog.samsaradayplanner.domain.Day;
7 | import com.wiseassblog.samsaradayplanner.domain.IDayStorage;
8 | import com.wiseassblog.samsaradayplanner.domain.ITaskStorage;
9 | import com.wiseassblog.samsaradayplanner.domain.Tasks;
10 |
11 | //Decision maker (logic) class for the Front End
12 | public class DayViewLogic extends BaseViewLogic {
13 |
14 | private final IDayViewContract.View view;
15 | private final IDayViewContract.ViewModel vm;
16 |
17 | //backend IO devices
18 | //this is an example of the Facade Pattern
19 | //Hide the details of a sub-system
20 | //A more useful advanced pattern is the Observer Pattern/Publisher Subscriber (RxJava well)
21 | //I learned patterns from a book called
22 | // Design Patterns Explained James R. Trott & Alan Shalloway
23 | private final IDayStorage dayStorage;
24 | private final ITaskStorage taskStorage;
25 |
26 | public DayViewLogic(IDayViewContract.View view, IDayViewContract.ViewModel vm, IDayStorage dayStorage, ITaskStorage taskStorage) {
27 | this.view = view;
28 | this.vm = vm;
29 | this.dayStorage = dayStorage;
30 | this.taskStorage = taskStorage;
31 | }
32 |
33 | @Override
34 | public void onViewEvent(DayViewEvent event) {
35 | switch (event.getEvent()) {
36 | case ON_START:
37 | onStart();
38 | break;
39 | case ON_HOUR_SELECTED:
40 | onHourSelected((int)event.getValue());
41 | break;
42 | case ON_MANAGE_TASKS_SELECTED:
43 | onManageTaskSelected();
44 | break;
45 | }
46 | }
47 |
48 | private void onManageTaskSelected() {
49 | view.navigateToTasksView();
50 | }
51 |
52 | private void onHourSelected(int hourInteger) {
53 | view.navigateToHourView(hourInteger);
54 | }
55 |
56 | /**
57 | * 1. Get data from both data sources
58 | * 2. Give data to the VMs
59 | * 3. Give data to the Views
60 | *
61 | *
62 | *
63 | */
64 | private void onStart() {
65 | dayStorage.getDay(new Continuation() {
66 | @Override
67 | public void onSuccess(Day result) {
68 | getTasks(result);
69 | }
70 |
71 | @Override
72 | public void onException(Exception e) {
73 | view.showMessage(Messages.GENERIC_ERROR_MESSAGE);
74 | view.restartFeature();
75 | }
76 | });
77 | }
78 |
79 | private void getTasks(Day dayResult) {
80 | taskStorage.getTasks(new Continuation() {
81 | @Override
82 | public void onSuccess(Tasks taskResult) {
83 | bindData(dayResult, taskResult);
84 | }
85 |
86 | @Override
87 | public void onException(Exception e) {
88 | view.showMessage(Messages.GENERIC_ERROR_MESSAGE);
89 | view.restartFeature();
90 | }
91 | });
92 | }
93 |
94 | private void bindData(Day dayResult, Tasks taskResult) {
95 | vm.setDay(dayResult);
96 | vm.setTasks(taskResult);
97 | view.setData(dayResult, taskResult);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_manage_task.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
47 |
48 |
59 |
60 |
61 |
72 |
73 |
81 |
82 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/app/src/test/java/com/wiseassblog/samsaradayplanner/TestData.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner;
2 |
3 | import com.wiseassblog.samsaradayplanner.domain.Day;
4 | import com.wiseassblog.samsaradayplanner.domain.Hour;
5 | import com.wiseassblog.samsaradayplanner.domain.QuarterHour;
6 | import com.wiseassblog.samsaradayplanner.domain.Task;
7 | import com.wiseassblog.samsaradayplanner.domain.Tasks;
8 | import com.wiseassblog.samsaradayplanner.domain.constants.COLOR;
9 | import com.wiseassblog.samsaradayplanner.domain.constants.HOUR_MODE;
10 | import com.wiseassblog.samsaradayplanner.domain.constants.ICON;
11 |
12 | import static com.wiseassblog.samsaradayplanner.domain.constants.ICON.WORK;
13 | import static com.wiseassblog.samsaradayplanner.domain.constants.QUARTER.FIFTEEN;
14 | import static com.wiseassblog.samsaradayplanner.domain.constants.QUARTER.FOURTY_FIVE;
15 | import static com.wiseassblog.samsaradayplanner.domain.constants.QUARTER.THIRTY;
16 | import static com.wiseassblog.samsaradayplanner.domain.constants.QUARTER.ZERO;
17 |
18 | /**
19 | * Contains data use in various tests
20 | */
21 | class TestData {
22 |
23 | static final int TASK_ID = 123456;
24 | static final String TASK_NAME = "Work";
25 | static final ICON TASK_ICON = WORK;
26 | static final COLOR TASK_COLOR = COLOR.DARK_BLUE;
27 | static final HOUR_MODE MODE = HOUR_MODE.TWELVE_HOUR;
28 | static final int HOUR_INTEGER = 12;
29 |
30 |
31 |
32 | static Tasks getTestTasks() {
33 | return new Tasks(
34 | new Task[]{
35 | getTestTask(),
36 | getTestTask(),
37 | getTestTask()
38 | }
39 | );
40 | }
41 |
42 | static Task getTestTask() {
43 | return new Task(
44 | TASK_ID,
45 | TASK_NAME,
46 | TASK_ICON,
47 | TASK_COLOR
48 | );
49 | }
50 |
51 | static Day getTestDay() {
52 | return new Day(
53 | MODE,
54 | new Hour[]{
55 | getTestHour(),
56 | getTestHour(),
57 | getTestHour(),
58 | getTestHour(),
59 | getTestHour(),
60 | getTestHour(),
61 | getTestHour(),
62 | getTestHour(),
63 | getTestHour(),
64 | getTestHour(),
65 | getTestHour(),
66 | getTestHour(),
67 | getTestHour(),
68 | getTestHour(),
69 | getTestHour(),
70 | getTestHour(),
71 | getTestHour(),
72 | getTestHour(),
73 | getTestHour(),
74 | getTestHour(),
75 | getTestHour(),
76 | getTestHour(),
77 | getTestHour(),
78 | getTestHour()
79 | }
80 | );
81 | }
82 |
83 | static Hour getTestHour() {
84 | return new Hour(
85 | new QuarterHour[]{
86 | getQuarterHour(),
87 | new QuarterHour(
88 | getQuarterHour().getTaskId(),
89 | FIFTEEN,
90 | false
91 | ),
92 | new QuarterHour(
93 | getQuarterHour().getTaskId(),
94 | THIRTY,
95 | true
96 | ),
97 | new QuarterHour(
98 | getQuarterHour().getTaskId(),
99 | FOURTY_FIVE,
100 | false
101 | )
102 | },
103 | HOUR_INTEGER
104 | );
105 | }
106 |
107 | public static QuarterHour getQuarterHour() {
108 | return new QuarterHour(
109 | TASK_ID,
110 | ZERO,
111 | true
112 | );
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/app/src/test/java/com/wiseassblog/samsaradayplanner/TaskListTests.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner;
2 |
3 | import com.wiseassblog.samsaradayplanner.domain.Tasks;
4 | import com.wiseassblog.samsaradayplanner.ui.tasklistview.ITaskListViewContract;
5 | import com.wiseassblog.samsaradayplanner.ui.tasklistview.TaskListViewLogic;
6 | import com.wiseassblog.samsaradayplanner.ui.tasklistview.TasksListViewEvent;
7 |
8 | import org.junit.jupiter.api.BeforeEach;
9 | import org.junit.jupiter.api.Test;
10 |
11 | class TaskListTests {
12 |
13 | private TaskListViewLogic logic;
14 | private FakeTaskListView view;
15 | private FakeTaskListViewModel vm;
16 | private FakeTaskStorage taskStorage;
17 |
18 | @BeforeEach
19 | void setUp(){
20 | view = new TaskListTests.FakeTaskListView();
21 | vm = new TaskListTests.FakeTaskListViewModel();
22 | vm.setTasks(TestData.getTestTasks());
23 | vm.setTasksCalled = false;
24 | taskStorage = new FakeTaskStorage();
25 | logic = new TaskListViewLogic(view, vm, taskStorage);
26 | }
27 |
28 | /**
29 | * Retrieve the list of tasks from storage, update VM, and update View
30 | */
31 | @Test
32 | public void onStartSuccessful() {
33 | TasksListViewEvent event = new TasksListViewEvent(TasksListViewEvent.Event.ON_START, 0);
34 |
35 | logic.onViewEvent(event);
36 |
37 | assert (vm.setTasksCalled);
38 | assert (view.setTasksCalled);
39 | }
40 |
41 | @Test
42 | public void onStartUnsuccessful() {
43 | TasksListViewEvent event = new TasksListViewEvent(TasksListViewEvent.Event.ON_START, 0);
44 |
45 | taskStorage.setWillFail(true);
46 |
47 | logic.onViewEvent(event);
48 |
49 | assert (view.showMessageCalled);
50 | assert (view.navigateDayViewCalled);
51 | }
52 |
53 | /**
54 | * User has selected a task and therefore wants to edit it.
55 | *
56 | * 1. Retrieve position from selected item
57 | * 2. Retrieve id from VM relative to position
58 | * 3. Tell view to start manage task view with id as bundle extra
59 | *
60 | */
61 | @Test
62 | public void onTaskSelectedSuccess() {
63 | TasksListViewEvent event = new TasksListViewEvent(
64 | TasksListViewEvent.Event.ON_LIST_ITEM_SELECTED,
65 | 0);
66 |
67 | logic.onViewEvent(event);
68 |
69 | assert (vm.getTasksCalled);
70 | assert (view.navigateManageTaskViewCalled);
71 |
72 | }
73 |
74 | @Test
75 | public void onBackPressed() {
76 | TasksListViewEvent event = new TasksListViewEvent(
77 | TasksListViewEvent.Event.ON_BACK_PRESSED,
78 | 0);
79 |
80 | logic.onViewEvent(event);
81 |
82 | assert (view.navigateDayViewCalled);
83 | }
84 |
85 | private class FakeTaskListView implements ITaskListViewContract.View {
86 |
87 | boolean setTasksCalled = false;
88 | boolean showMessageCalled = false;
89 | boolean navigateDayViewCalled = false;
90 | boolean navigateManageTaskViewCalled = false;
91 |
92 |
93 | @Override
94 | public void setTasks(Tasks tasks) {
95 | this.setTasksCalled = true;
96 | }
97 |
98 | @Override
99 | public void showMessage(String message) {
100 | showMessageCalled = true;
101 | }
102 |
103 | @Override
104 | public void navigateToDayView() {
105 | navigateDayViewCalled = true;
106 | }
107 |
108 | @Override
109 | public void navigateToTaskView(int taskId) {
110 | navigateManageTaskViewCalled = true;
111 | }
112 | }
113 |
114 | private class FakeTaskListViewModel implements ITaskListViewContract.ViewModel {
115 | private Tasks tasks = null;
116 |
117 | private boolean setTasksCalled = false;
118 | private boolean getTasksCalled = false;
119 | @Override
120 | public void setTasks(Tasks tasks) {
121 | this.tasks = tasks;
122 | setTasksCalled = true;
123 | }
124 |
125 | @Override
126 | public Tasks getTasks() {
127 | getTasksCalled = true;
128 | return this.tasks;
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/tasklistview/TaskListView.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.tasklistview;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.AdapterView;
9 | import android.widget.ArrayAdapter;
10 | import android.widget.GridView;
11 | import android.widget.ImageButton;
12 | import android.widget.Toast;
13 |
14 | import androidx.annotation.NonNull;
15 | import androidx.annotation.Nullable;
16 | import androidx.fragment.app.Fragment;
17 |
18 | import com.wiseassblog.samsaradayplanner.R;
19 | import com.wiseassblog.samsaradayplanner.common.BaseViewLogic;
20 | import com.wiseassblog.samsaradayplanner.domain.Tasks;
21 | import com.wiseassblog.samsaradayplanner.ui.dayview.DayActivity;
22 | import com.wiseassblog.samsaradayplanner.ui.dayview.DayViewEvent;
23 | import com.wiseassblog.samsaradayplanner.ui.managetaskview.TaskActivity;
24 |
25 | import static com.wiseassblog.samsaradayplanner.domain.constants.Extras.EXTRA_TASK_ID;
26 | import static com.wiseassblog.samsaradayplanner.ui.tasklistview.TasksListViewEvent.Event.ON_BACK_PRESSED;
27 | import static com.wiseassblog.samsaradayplanner.ui.tasklistview.TasksListViewEvent.Event.ON_LIST_ITEM_SELECTED;
28 | import static com.wiseassblog.samsaradayplanner.ui.tasklistview.TasksListViewEvent.Event.ON_START;
29 |
30 | public class TaskListView extends Fragment implements ITaskListViewContract.View {
31 | private BaseViewLogic logic;
32 | private ImageButton back;
33 | private GridView taskGrid;
34 | private View view;
35 |
36 |
37 | public static TaskListView newInstance() {
38 | return new TaskListView();
39 | }
40 |
41 | public void setLogic(BaseViewLogic logic) {
42 | this.logic = logic;
43 | }
44 |
45 | @Nullable
46 | @Override
47 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
48 | view = inflater.inflate(R.layout.fragment_tasks, container, false);
49 |
50 | return view;
51 | }
52 |
53 | @Override
54 | public void onStart() {
55 | super.onStart();
56 |
57 | back = view.findViewById(R.id.tlb_icon);
58 | back.setOnClickListener(
59 | v -> {
60 | logic.onViewEvent(
61 | new TasksListViewEvent(
62 | ON_BACK_PRESSED,
63 | null
64 | )
65 | );
66 | }
67 | );
68 |
69 | taskGrid = view.findViewById(R.id.gdl_list_item_task);
70 | }
71 |
72 | @Override
73 | public void onResume() {
74 | super.onResume();
75 | logic.onViewEvent(new TasksListViewEvent(ON_START, ""));
76 | }
77 |
78 | @Override
79 | public void setTasks(Tasks tasks) {
80 | TaskGridItemViewAdapter adapter = new TaskGridItemViewAdapter(
81 | requireContext(),
82 | tasks.get()
83 | );
84 |
85 | taskGrid.setAdapter(adapter);
86 |
87 | taskGrid.setOnItemClickListener(
88 | //Note: position in adapter and TaskId are equivalent values
89 | (adapterView, clickView, position, id) -> logic.onViewEvent(
90 | new TasksListViewEvent(
91 | ON_LIST_ITEM_SELECTED,
92 | position
93 | )
94 | )
95 | );
96 | }
97 |
98 | @Override
99 | public void showMessage(String message) {
100 | Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
101 | }
102 |
103 | @Override
104 | public void navigateToDayView() {
105 | startActivity(
106 | new Intent(
107 | requireActivity(),
108 | DayActivity.class
109 | )
110 | );
111 | }
112 |
113 | @Override
114 | public void navigateToTaskView(int taskId) {
115 | startActivity(
116 | new Intent(
117 | requireActivity(),
118 | TaskActivity.class
119 | ).putExtra(
120 | EXTRA_TASK_ID,
121 | taskId
122 | )
123 | );
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_color_picker_sheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
30 |
31 |
41 |
42 |
51 |
52 |
61 |
62 |
63 |
72 |
73 |
82 |
83 |
92 |
93 |
94 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/managetaskview/TaskViewLogic.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.managetaskview;
2 |
3 | import com.wiseassblog.samsaradayplanner.common.BaseViewLogic;
4 | import com.wiseassblog.samsaradayplanner.common.Continuation;
5 | import com.wiseassblog.samsaradayplanner.domain.ITaskStorage;
6 | import com.wiseassblog.samsaradayplanner.domain.Task;
7 | import com.wiseassblog.samsaradayplanner.domain.constants.COLOR;
8 | import com.wiseassblog.samsaradayplanner.domain.constants.ICON;
9 |
10 | public class TaskViewLogic extends BaseViewLogic {
11 |
12 | private ITaskViewContract.View view;
13 | private ITaskViewContract.ViewModel vm;
14 | private ITaskStorage storage;
15 |
16 | /**
17 | * Why skip Use Cases/Interactors/Transaction Scripts and just call the Repository directly?
18 | * Because this feature is so simple that adding in the domain layer (Use Case...etc.) is
19 | * not worth the effort.
20 | *
21 | * @param view
22 | * @param vm
23 | * @param storage
24 | */
25 | public TaskViewLogic(ITaskViewContract.View view, ITaskViewContract.ViewModel vm, ITaskStorage storage) {
26 | this.view = view;
27 | this.vm = vm;
28 | this.storage = storage;
29 | }
30 |
31 | @Override
32 | public void onViewEvent(TaskViewEvent event) {
33 | //code like this make me miss Kotlin syntax...
34 | switch (event.getEvent()) {
35 | case ON_START:
36 | onStart();
37 | break;
38 | case ON_COLOR_BUTTON_CLICK:
39 | view.showColorPickerSheet();
40 | break;
41 | case ON_COLOR_SELECTED:
42 | onColorSelected((COLOR) event.getValue());
43 | break;
44 | case ON_DONE_CLICK:
45 | updateStorage();
46 | break;
47 | case ON_ICON_SELECTED:
48 | onIconSelected(
49 | (ICON) event.getValue()
50 | );
51 | break;
52 | }
53 | }
54 |
55 |
56 | private void onStart() {
57 | storage.getTask(vm.getTask().getTaskId(),
58 | new Continuation() {
59 | @Override
60 | public void onSuccess(Task result) {
61 | vm.setTask(result);
62 | view.setButtonColor(result.getTaskColor());
63 | view.setName(result.getTaskName());
64 | view.setIconSelection(result.getTaskIcon());
65 | view.setSelectedSpinnerItem(result.getTaskId());
66 | }
67 |
68 | @Override
69 | public void onException(Exception e) {
70 | view.showMessage(e.getMessage());
71 | view.goToTaskListActivity();
72 | }
73 | }
74 | );
75 | }
76 |
77 | private void onIconSelected(ICON icon) {
78 | Task oldTask = vm.getTask();
79 | Task update = new Task(
80 | oldTask.getTaskId(),
81 | oldTask.getTaskName(),
82 | icon,
83 | oldTask.getTaskColor()
84 | );
85 |
86 | vm.setTask(update);
87 | view.setIconSelection(update.getTaskIcon());
88 | }
89 |
90 | private void updateStorage() {
91 | Task oldTask = vm.getTask();
92 | Task update = new Task(
93 | oldTask.getTaskId(),
94 | view.getName(),
95 | oldTask.getTaskIcon(),
96 | oldTask.getTaskColor()
97 | );
98 |
99 | storage.updateTask(update,
100 | new Continuation() {
101 | @Override
102 | public void onSuccess(Void result) {
103 | view.goToTaskListActivity();
104 | }
105 |
106 | @Override
107 | public void onException(Exception e) {
108 | view.showMessage(e.getMessage());
109 | view.goToTaskListActivity();
110 | }
111 | }
112 | );
113 | }
114 |
115 | private void onColorSelected(COLOR color) {
116 | Task oldTask = vm.getTask();
117 | Task newTask = new Task(
118 | oldTask.getTaskId(),
119 | oldTask.getTaskName(),
120 | oldTask.getTaskIcon(),
121 | color
122 | );
123 | vm.setTask(
124 | newTask
125 | );
126 |
127 | view.setButtonColor(newTask.getTaskColor());
128 | view.hideColorPickerSheet();
129 | }
130 |
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/dayview/DayView.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.dayview;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageButton;
9 | import android.widget.Toast;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.annotation.Nullable;
13 | import androidx.fragment.app.Fragment;
14 | import androidx.recyclerview.widget.RecyclerView;
15 |
16 | import com.wiseassblog.samsaradayplanner.R;
17 | import com.wiseassblog.samsaradayplanner.common.BaseViewLogic;
18 | import com.wiseassblog.samsaradayplanner.domain.Day;
19 | import com.wiseassblog.samsaradayplanner.domain.Tasks;
20 | import com.wiseassblog.samsaradayplanner.ui.managehourview.HourActivity;
21 | import com.wiseassblog.samsaradayplanner.ui.tasklistview.TaskListActivity;
22 |
23 | import static com.wiseassblog.samsaradayplanner.domain.constants.Extras.EXTRA_HOUR_INTEGER;
24 | import static com.wiseassblog.samsaradayplanner.ui.dayview.DayViewEvent.Event.ON_HOUR_SELECTED;
25 | import static com.wiseassblog.samsaradayplanner.ui.dayview.DayViewEvent.Event.ON_START;
26 |
27 | /**
28 | * I did not bother using the Pub/Sub pattern here because ultimately the presentation logic is
29 | * properly decoupled via DayListItemViewMaker.java and the Adapter itself
30 | */
31 | public class DayView extends Fragment
32 | implements IDayViewContract.View {
33 |
34 | private BaseViewLogic logic;
35 |
36 | private RecyclerView rec;
37 | private ImageButton toolbarManageTasks;
38 | private DayAdapter adapter;
39 | private View view;
40 |
41 | public void setLogic(BaseViewLogic logic) {
42 | this.logic = logic;
43 | }
44 |
45 | public static DayView newInstance() {
46 | return new DayView();
47 | }
48 |
49 | @Nullable
50 | @Override
51 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
52 | view = inflater.inflate(R.layout.fragment_day_view, container, false);
53 | return view;
54 | }
55 |
56 | @Override
57 | public void onStart() {
58 | super.onStart();
59 |
60 | toolbarManageTasks = view.findViewById(R.id.imb_toolbar_manage_tasks);
61 | toolbarManageTasks.setOnClickListener(
62 | view -> {
63 | logic.onViewEvent(
64 | new DayViewEvent(
65 | DayViewEvent.Event.ON_MANAGE_TASKS_SELECTED,
66 | 0
67 | )
68 | );
69 | }
70 | );
71 |
72 | rec = view.findViewById(R.id.rec_day_list);
73 | }
74 |
75 | @Override
76 | public void onResume() {
77 | super.onResume();
78 | logic.onViewEvent(new DayViewEvent(ON_START, ""));
79 | }
80 |
81 |
82 |
83 | @Override
84 | public void setData(Day day, Tasks tasks) {
85 | adapter = new DayAdapter(
86 | DayListItemViewMaker.getItemList(
87 | this.requireContext(),
88 | day,
89 | tasks
90 | ),
91 | position -> logic.onViewEvent(
92 | new DayViewEvent(
93 | ON_HOUR_SELECTED,
94 | position
95 | )
96 | )
97 | );
98 |
99 | rec.setAdapter(adapter);
100 | }
101 |
102 |
103 | @Override
104 | public void onDestroyView() {
105 | super.onDestroyView();
106 |
107 | rec.setAdapter(null);
108 | }
109 |
110 | @Override
111 | public void navigateToHourView(int hourInteger) {
112 | Intent i = new Intent(
113 | requireContext(),
114 | HourActivity.class
115 | );
116 |
117 | i.putExtra(EXTRA_HOUR_INTEGER, hourInteger);
118 |
119 | startActivity(i);
120 | }
121 |
122 | @Override
123 | public void navigateToTasksView() {
124 | startActivity(
125 | new Intent(requireContext(), TaskListActivity.class)
126 | );
127 | }
128 |
129 | @Override
130 | public void showMessage(String message) {
131 | Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
132 | }
133 |
134 | @Override
135 | public void restartFeature() {
136 | requireActivity().recreate();
137 | startActivity(
138 | new Intent(
139 | requireActivity(),
140 | DayActivity.class
141 | )
142 | );
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/managehourview/HourToggleView.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.managehourview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.AdapterView;
7 | import android.widget.ArrayAdapter;
8 | import android.widget.Spinner;
9 | import android.widget.Switch;
10 | import android.widget.TextView;
11 |
12 | import androidx.annotation.NonNull;
13 | import androidx.annotation.Nullable;
14 | import androidx.constraintlayout.widget.ConstraintLayout;
15 | import androidx.core.content.ContextCompat;
16 |
17 | import com.wiseassblog.samsaradayplanner.R;
18 | import com.wiseassblog.samsaradayplanner.domain.constants.QUARTER;
19 |
20 | public class HourToggleView extends ConstraintLayout {
21 | private Switch quarterHourIsActive;
22 | private TextView timeLabel;
23 | private Spinner selectTask;
24 | private View bottomBorder;
25 | private QUARTER quarter;
26 | private ToggleViewClickHandler callback;
27 |
28 | public void setToggleState(QUARTER quarter, boolean isActive) {
29 | if (quarter != QUARTER.ZERO) {
30 | quarterHourIsActive.setChecked(isActive);
31 | setTimeLabelColor(isActive);
32 | } else {
33 | quarterHourIsActive.setVisibility(INVISIBLE);
34 | setTimeLabelColor(true);
35 | }
36 |
37 | if (quarter == QUARTER.FOURTY_FIVE) hideBottomDivider();
38 | }
39 |
40 | private void setTimeLabelColor(boolean isAccentColor) {
41 | if (isAccentColor) timeLabel.setTextColor(
42 | ContextCompat.getColor(
43 | getContext(),
44 | R.color.colorAccent
45 | )
46 | );
47 | else {
48 | timeLabel.setTextColor(
49 | ContextCompat.getColor(
50 | getContext(),
51 | android.R.color.white
52 | )
53 | );
54 | }
55 | }
56 |
57 | interface ToggleViewClickHandler {
58 | void onQuarterToggle(QUARTER quarter, boolean activate);
59 |
60 | /**
61 | * @param quarter the Quarter which represents one of the four HourToggleViews
62 | * @param position the position of the task which was selected from the ArrayAdapter's data.
63 | * From there we can determine how to update the UI and storage
64 | */
65 | void onTaskSelected(QUARTER quarter, int position);
66 | }
67 |
68 |
69 | public HourToggleView(@NonNull Context context, @Nullable AttributeSet attrs) {
70 | super(context, attrs);
71 | build();
72 | }
73 |
74 | private void build() {
75 | inflate(getContext(), R.layout.view_quarter_hour_toggle, this);
76 |
77 | quarterHourIsActive = findViewById(R.id.swi_activate_quarter_hour);
78 | quarterHourIsActive.setOnCheckedChangeListener(
79 | (view, isChecked) -> {
80 | if (callback != null) callback.onQuarterToggle(quarter, isChecked);
81 | setTimeLabelColor(isChecked);
82 | }
83 | );
84 |
85 | timeLabel = findViewById(R.id.lbl_quarter_hour);
86 |
87 | selectTask = findViewById(R.id.spn_quarter_hour);
88 | bottomBorder = findViewById(R.id.view_bottom_border);
89 | }
90 |
91 | public void setCallback(ToggleViewClickHandler callback) {
92 | this.callback = callback;
93 |
94 | selectTask.setOnItemSelectedListener(
95 | new AdapterView.OnItemSelectedListener() {
96 | @Override
97 | public void onItemSelected(AdapterView> adapterView,
98 | View view,
99 | int position,
100 | long l) {
101 | if (callback != null) callback.onTaskSelected(quarter, position);
102 | }
103 |
104 | @Override
105 | public void onNothingSelected(AdapterView> adapterView) {}
106 | }
107 | );
108 | }
109 |
110 | /**
111 | * @param text Expected to be a properly formatted time display like 1:00PM or 13:00
112 | */
113 | public void setTimeLabel(String text) { timeLabel.setText(text);}
114 |
115 | public void hideBottomDivider() { bottomBorder.setVisibility(INVISIBLE); }
116 |
117 | public QUARTER getQuarter() { return quarter;}
118 |
119 | public void setQuarter(QUARTER quarter) { this.quarter = quarter;}
120 |
121 | public void createTaskAdapter(String[] tasks, int startingPosition) {
122 | ArrayAdapter adapter = new ArrayAdapter(getContext(), android.R.layout.simple_spinner_dropdown_item, tasks);
123 |
124 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
125 | selectTask.setAdapter(adapter);
126 |
127 | selectTask.setSelection(startingPosition, false);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wiseassblog/samsaradayplanner/ui/managehourview/HourView.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner.ui.managehourview;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.Toast;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.annotation.Nullable;
12 | import androidx.fragment.app.Fragment;
13 |
14 | import com.wiseassblog.samsaradayplanner.R;
15 | import com.wiseassblog.samsaradayplanner.common.BaseViewLogic;
16 | import com.wiseassblog.samsaradayplanner.domain.constants.QUARTER;
17 | import com.wiseassblog.samsaradayplanner.ui.dayview.DayActivity;
18 |
19 | public class HourView extends Fragment implements IHourContract.View, HourToggleView.ToggleViewClickHandler {
20 | private BaseViewLogic logic;
21 | private HourToggleView first, second, third, fourth;
22 |
23 | public static HourView newInstance() {
24 | return new HourView();
25 | }
26 |
27 | @Nullable
28 | @Override
29 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
30 | View v = inflater.inflate(R.layout.fragment_hour_view, container, false);
31 | first = v.findViewById(R.id.vqht_one);
32 |
33 | second = v.findViewById(R.id.vqht_two);
34 |
35 | third = v.findViewById(R.id.vqht_three);
36 |
37 | fourth = v.findViewById(R.id.vqht_four);
38 |
39 | //toolbar action button
40 | v.findViewById(R.id.tlb_icon).setOnClickListener(
41 | view -> logic.onViewEvent(
42 | new HourViewEvent(
43 | HourViewEvent.Event.ON_DONE_CLICK,
44 | null
45 | )
46 | )
47 | );
48 |
49 | return v;
50 | }
51 |
52 | @Override
53 | public void onQuarterToggle(QUARTER quarter, boolean activate) {
54 | logic.onViewEvent(
55 | HourViewEvent.getOnQuarterToggleEvent(
56 | quarter,
57 | activate
58 | )
59 | );
60 | }
61 |
62 | @Override
63 | public void onTaskSelected(QUARTER quarter, int position) {
64 | logic.onViewEvent(
65 | HourViewEvent.getOnTaskSelectedEvent(
66 | quarter,
67 | position
68 | )
69 | );
70 | }
71 |
72 | /**
73 | * Signal onStart
74 | *
75 | * @param logic
76 | */
77 | public void setLogic(BaseViewLogic logic) {
78 | this.logic = logic;
79 | }
80 |
81 | @Override
82 | public void onResume() {
83 | super.onResume();
84 | this.logic.onViewEvent(
85 | new HourViewEvent(
86 | HourViewEvent.Event.ON_START,
87 | null
88 | )
89 | );
90 | }
91 |
92 | @Override
93 | public void setListeners() {
94 | first.setCallback(this);
95 | second.setCallback(this);
96 | third.setCallback(this);
97 | fourth.setCallback(this);
98 | }
99 |
100 | @Override
101 | public void setQuarterHourText(QUARTER quarter, String hour) {
102 | HourToggleView toggle = findToggleView(quarter);
103 | toggle.setTimeLabel(hour);
104 | }
105 |
106 | private HourToggleView findToggleView(QUARTER quarter) {
107 | switch (quarter) {
108 | case FIFTEEN:
109 | return second;
110 | case THIRTY:
111 | return third;
112 | case FOURTY_FIVE:
113 | return fourth;
114 | default:
115 | return first;
116 | }
117 | }
118 |
119 | @Override
120 | public void setQuarterHourAdapterData(QUARTER quarter, String[] taskNames, int position) {
121 | HourToggleView toggle = findToggleView(quarter);
122 | toggle.createTaskAdapter(taskNames, position);
123 | }
124 |
125 | @Override
126 | public void setQuarterHourActive(QUARTER quarter, boolean isActive) {
127 | HourToggleView toggle = findToggleView(quarter);
128 | toggle.setToggleState(quarter, isActive);
129 | }
130 |
131 | @Override
132 | public void navigateToDayView() {
133 | startActivity(
134 | new Intent(
135 | requireContext(),
136 | DayActivity.class
137 | )
138 | );
139 | }
140 |
141 | @Override
142 | public void showMessage(String message) {
143 | Toast.makeText(requireActivity(), message, Toast.LENGTH_SHORT).show();
144 | }
145 |
146 | /**
147 | * Ok, the thing is that we can determine which HourToggleView by position, but we still
148 | * need to set the field in each HourToggleView which holds the appropriate QUARTER
149 | * @param q
150 | */
151 | @Override
152 | public void setQuarterHour(QUARTER q) {
153 | findToggleView(q).setQuarter(q);
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item_day_view_quarter_three_quarter.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
26 |
27 |
33 |
34 |
54 |
55 |
65 |
66 |
71 |
72 |
73 |
83 |
84 |
90 |
91 |
115 |
116 |
126 |
127 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item_day_view_half_and_half.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
29 |
30 |
36 |
37 |
58 |
59 |
69 |
70 |
75 |
76 |
77 |
88 |
89 |
95 |
96 |
117 |
118 |
128 |
129 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item_day_view_three_quarter_quarter.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
27 |
28 |
34 |
35 |
59 |
60 |
70 |
71 |
76 |
77 |
78 |
89 |
90 |
96 |
97 |
117 |
118 |
128 |
129 |
134 |
135 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/test/java/com/wiseassblog/samsaradayplanner/DayViewTests.java:
--------------------------------------------------------------------------------
1 | package com.wiseassblog.samsaradayplanner;
2 |
3 | import com.wiseassblog.samsaradayplanner.domain.Day;
4 | import com.wiseassblog.samsaradayplanner.domain.Tasks;
5 | import com.wiseassblog.samsaradayplanner.ui.dayview.DayViewEvent;
6 | import com.wiseassblog.samsaradayplanner.ui.dayview.DayViewLogic;
7 | import com.wiseassblog.samsaradayplanner.ui.dayview.IDayViewContract;
8 | import com.wiseassblog.samsaradayplanner.ui.managehourview.HourViewEvent;
9 |
10 | import org.junit.jupiter.api.BeforeEach;
11 | import org.junit.jupiter.api.Test;
12 |
13 | import java.util.concurrent.Semaphore;
14 |
15 | import static com.wiseassblog.samsaradayplanner.TestData.HOUR_INTEGER;
16 |
17 | class DayViewTests {
18 | private DayViewLogic logic;
19 | private DayViewTests.FakeDayView view;
20 | private DayViewTests.FakeDayViewModel vm;
21 | private FakeDayStorage dayStorage;
22 | private FakeTaskStorage taskStorage;
23 |
24 | @BeforeEach
25 | void setUp() {
26 | view = new DayViewTests.FakeDayView();
27 | vm = new DayViewTests.FakeDayViewModel();
28 | vm.setDay(TestData.getTestDay());
29 | vm.setDayCalled = false;
30 | dayStorage = new FakeDayStorage();
31 | taskStorage = new FakeTaskStorage();
32 | logic = new DayViewLogic(view, vm, dayStorage, taskStorage);
33 | }
34 |
35 | /**
36 | * Designing ahead of time without worrying about implementation details
37 | * - Design of code, and implementation present different (often related) problems which
38 | * ought to be solved separately
39 | *
40 | * Retrieve current day from storage, as well as the list of tasks, and render that
41 | * * appropriately
42 | * *
43 | * * 1. Get Day
44 | * * 2. Get Tasks
45 | * * 3. Give to View and ViewModel
46 | * *
47 | * * Note, if either of the IO devices fail, we cannot render the view properly, and therefore
48 | * * a reboot is the only recourse.
49 | */
50 | @Test
51 | public void onStartSuccess() {
52 | DayViewEvent event = new DayViewEvent(
53 | DayViewEvent.Event.ON_START,
54 | ""
55 | );
56 |
57 | logic.onViewEvent(event);
58 |
59 | assert (vm.setDayCalled);
60 | assert (vm.setTasksCalled);
61 | assert (view.setDataCalled);
62 | }
63 |
64 | @Test
65 | public void onStartDayStorageException() {
66 | dayStorage.setWillFail(true);
67 |
68 | DayViewEvent event = new DayViewEvent(
69 | DayViewEvent.Event.ON_START,
70 | ""
71 | );
72 |
73 | logic.onViewEvent(event);
74 |
75 | assert (view.showMessageCalled);
76 | assert (view.restartFeatureCalled);
77 | }
78 |
79 | @Test
80 | public void onStartTaskStorageException() {
81 | taskStorage.setWillFail(true);
82 |
83 | DayViewEvent event = new DayViewEvent(
84 | DayViewEvent.Event.ON_START,
85 | ""
86 | );
87 |
88 | logic.onViewEvent(event);
89 |
90 | assert (view.showMessageCalled);
91 | assert (view.restartFeatureCalled);
92 | }
93 |
94 |
95 | /**
96 | * Tell the View to start the HourViewFeature and give it the appropriate data
97 | *
98 | *
99 | */
100 | @Test
101 | public void onHourSelected() {
102 | DayViewEvent event = new DayViewEvent(
103 | DayViewEvent.Event.ON_HOUR_SELECTED,
104 | 12
105 | );
106 |
107 | logic.onViewEvent(event);
108 |
109 | assert (view.navigateHourCalled);
110 | }
111 |
112 | /**
113 | *
114 | * 1. Tell view to start manage task feature
115 | *
116 | */
117 | @Test
118 | public void onManageTasksSelected() {
119 | DayViewEvent event = new DayViewEvent(
120 | DayViewEvent.Event.ON_MANAGE_TASKS_SELECTED,
121 | null
122 | );
123 |
124 | logic.onViewEvent(event);
125 |
126 | assert (view.navigateToTasksViewCalled);
127 | }
128 |
129 |
130 |
131 | private class FakeDayView implements IDayViewContract.View {
132 |
133 | boolean setDataCalled = false;
134 |
135 | boolean navigateHourCalled = false;
136 |
137 | boolean restartFeatureCalled = false;
138 |
139 | boolean navigateToTasksViewCalled = false;
140 |
141 | boolean showMessageCalled = false;
142 |
143 | @Override
144 | public void setData(Day day, Tasks tasks) {
145 | this.setDataCalled = true;
146 | }
147 |
148 | @Override
149 | public void navigateToHourView(int hourInteger) {
150 | this.navigateHourCalled = true;
151 | }
152 |
153 | @Override
154 | public void navigateToTasksView() {
155 | this.navigateToTasksViewCalled = true;
156 | }
157 |
158 | @Override
159 | public void showMessage(String message) {
160 | showMessageCalled = true;
161 | }
162 |
163 | @Override
164 | public void restartFeature() {
165 | restartFeatureCalled = true;
166 | }
167 | }
168 |
169 | private class FakeDayViewModel implements IDayViewContract.ViewModel {
170 | private Day day = null;
171 | private Tasks tasks = null;
172 |
173 | boolean setDayCalled = false;
174 | boolean getDayCalled = false;
175 |
176 | boolean setTasksCalled = false;
177 | boolean getTasksCalled = false;
178 |
179 | @Override
180 | public void setDay(Day day) {
181 | this.day = day;
182 | this.setDayCalled = true;
183 | }
184 |
185 | @Override
186 | public Day getDay() {
187 | this.getDayCalled = true;
188 | return this.day;
189 | }
190 |
191 | @Override
192 | public void setTasks(Tasks tasks) {
193 | this.setTasksCalled = true;
194 | this.tasks = tasks;
195 | }
196 |
197 | @Override
198 | public Tasks getTasks() {
199 | this.getTasksCalled = true;
200 | return this.tasks;
201 | }
202 | }
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------