Skip to content

Commit a171f54

Browse files
author
Gabor Keszthelyi
committed
Show subtasks on details view. #442
1 parent 62e6af8 commit a171f54

27 files changed

+1004
-70
lines changed

.idea/dictionaries/dictionary.xml

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

opentasks-provider/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ dependencies {
3939
exclude module: 'jems'
4040
}
4141
androidTestImplementation 'com.android.support:support-annotations:' + SUPPORT_LIBRARY_VERSION
42-
androidTestImplementation 'com.android.support.test:runner:0.5'
43-
androidTestImplementation 'com.android.support.test:rules:0.5'
42+
androidTestImplementation 'com.android.support.test:runner:' + ANDROID_TEST_RUNNER_VERSION
43+
androidTestImplementation 'com.android.support.test:rules:' + ANDROID_TEST_RUNNER_VERSION
4444
testImplementation 'org.robolectric:robolectric:' + ROBOLECTRIC_VERSION
4545
testImplementation 'junit:junit:4.12'
4646
testImplementation 'org.mockito:mockito-core:2.10.0'

opentasks/build.gradle

+9
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,14 @@ android {
4242
sourceCompatibility JavaVersion.VERSION_1_8
4343
targetCompatibility JavaVersion.VERSION_1_8
4444
}
45+
dataBinding {
46+
enabled = true
47+
}
4548
}
4649

4750
dependencies {
4851
implementation project(':opentasks-provider')
52+
implementation project(':opentaskspal')
4953
implementation 'com.android.support:appcompat-v7:' + SUPPORT_LIBRARY_VERSION
5054
implementation 'com.android.support:design:' + SUPPORT_LIBRARY_VERSION
5155
implementation('org.dmfs:android-xml-magic:0.1.1') {
@@ -66,6 +70,11 @@ dependencies {
6670
implementation 'org.dmfs:jems:' + JEMS_VERSION
6771
implementation 'org.dmfs:rfc5545-datetime:' + RFC5545_DATETIME_VERSION
6872
implementation 'com.github.dmfs.bolts:color-bolts:' + BOLTS_VERSION
73+
implementation('com.github.dmfs.contentpal:contentpal:' + CONTENTPAL_VERSION) {
74+
exclude module: 'jems'
75+
}
76+
implementation 'io.reactivex.rxjava2:rxjava:2.1.5'
77+
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
6978

7079
testImplementation 'junit:junit:4.12'
7180
testImplementation 'org.robolectric:robolectric:3.5.1'

opentasks/proguard.cfg

+4-1
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,7 @@
7777
java.lang.String TAG;
7878
@org.dmfs.android.retentionmagic.annotations.* <fields>;
7979
private long mId;
80-
}
80+
}
81+
82+
-dontwarn android.databinding.**
83+
-keep class android.databinding.** { *; }

opentasks/src/main/java/org/dmfs/tasks/ViewTaskFragment.java

+16-9
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,13 @@
5050
import android.view.animation.AlphaAnimation;
5151
import android.widget.TextView;
5252

53-
import org.dmfs.android.bolts.color.colors.ValueColor;
53+
import org.dmfs.android.bolts.color.elementary.ValueColor;
5454
import org.dmfs.android.retentionmagic.SupportFragment;
5555
import org.dmfs.android.retentionmagic.annotations.Parameter;
5656
import org.dmfs.android.retentionmagic.annotations.Retain;
5757
import org.dmfs.tasks.contract.TaskContract.Tasks;
58+
import org.dmfs.tasks.detailsscreen.SubtasksView;
59+
import org.dmfs.tasks.detailsscreen.SubtasksViewParamsSource;
5860
import org.dmfs.tasks.model.ContentSet;
5961
import org.dmfs.tasks.model.Model;
6062
import org.dmfs.tasks.model.OnContentChangeListener;
@@ -70,6 +72,8 @@
7072
import java.util.HashSet;
7173
import java.util.Set;
7274

75+
import io.reactivex.disposables.CompositeDisposable;
76+
7377

7478
/**
7579
* A fragment representing a single Task detail screen. This fragment is either contained in a {@link TaskListActivity} in two-pane mode (on tablets) or in a
@@ -130,6 +134,8 @@ public class ViewTaskFragment extends SupportFragment
130134
*/
131135
private TaskView mDetailView;
132136

137+
private CompositeDisposable mDisposables;
138+
133139
private int mListColor;
134140
private int mOldStatus = -1;
135141
private boolean mPinned = false;
@@ -207,14 +213,6 @@ public static ViewTaskFragment newInstance(Uri uri)
207213
}
208214

209215

210-
/**
211-
* Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon screen orientation changes).
212-
*/
213-
public ViewTaskFragment()
214-
{
215-
}
216-
217-
218216
@Override
219217
public void onCreate(Bundle savedInstanceState)
220218
{
@@ -265,6 +263,7 @@ public void onDestroyView()
265263
mDetailView.setValues(null);
266264
}
267265

266+
mDisposables.dispose();
268267
}
269268

270269

@@ -319,6 +318,8 @@ else if (mTaskUri != null)
319318
loadUri(uri);
320319
}
321320

321+
mDisposables = new CompositeDisposable();
322+
322323
return mRootView;
323324
}
324325

@@ -445,6 +446,12 @@ private void updateView()
445446
((TextView) mToolBar.findViewById(R.id.toolbar_title)).setText(TaskFieldAdapters.TITLE.get(mContentSet));
446447
}
447448
}
449+
450+
mDisposables.add(new SubtasksViewParamsSource(mAppContext, mTaskUri, new ValueColor(mListColor))
451+
.subscribe(subtasksViewParams ->
452+
{
453+
new SubtasksView(mContent).update(subtasksViewParams);
454+
}));
448455
}
449456

450457

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2017 dmfs GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.dmfs.tasks.detailsscreen;
18+
19+
import android.content.Context;
20+
import android.content.Intent;
21+
import android.databinding.DataBindingUtil;
22+
import android.support.annotation.NonNull;
23+
import android.support.annotation.Nullable;
24+
import android.util.AttributeSet;
25+
import android.view.View;
26+
import android.widget.FrameLayout;
27+
28+
import org.dmfs.android.bolts.color.Color;
29+
import org.dmfs.optional.Optional;
30+
import org.dmfs.rfc5545.DateTime;
31+
import org.dmfs.tasks.R;
32+
import org.dmfs.tasks.databinding.OpentasksViewItemTaskDetailsSubtaskBinding;
33+
import org.dmfs.tasks.readdata.TaskContentUri;
34+
import org.dmfs.tasks.utils.DateFormatter;
35+
import org.dmfs.tasks.utils.DateFormatter.DateFormatContext;
36+
import org.dmfs.tasks.widget.ProgressBackgroundView;
37+
import org.dmfs.tasks.widget.SmartView;
38+
39+
40+
/**
41+
* {@link View} for showing a subtask on the details screen.
42+
*
43+
* @author Gabor Keszthelyi
44+
*/
45+
public final class SubtaskView extends FrameLayout implements SmartView<SubtaskView.Params>
46+
{
47+
48+
public interface Params // i.e. fields of the subtask
49+
{
50+
Long id();
51+
52+
Optional<CharSequence> title();
53+
54+
Optional<DateTime> due();
55+
56+
Color color();
57+
58+
Optional<Integer> percentComplete();
59+
}
60+
61+
62+
public SubtaskView(@NonNull Context context, @Nullable AttributeSet attrs)
63+
{
64+
super(context, attrs);
65+
}
66+
67+
68+
@Override
69+
public void update(Params subtask)
70+
{
71+
OpentasksViewItemTaskDetailsSubtaskBinding views = DataBindingUtil.bind(this);
72+
73+
views.opentasksTaskDetailsSubtaskTitle.setText(subtask.title().value(getContext().getString(R.string.opentasks_task_details_subtask_untitled)));
74+
75+
if (subtask.due().isPresent())
76+
{
77+
views.opentasksTaskDetailsSubtaskDue.setText(
78+
new DateFormatter(getContext()).format(subtask.due().value(), DateTime.now(), DateFormatContext.LIST_VIEW));
79+
}
80+
81+
views.opentasksTaskDetailsSubtaskListRibbon.setBackgroundColor(subtask.color().argb());
82+
83+
new ProgressBackgroundView(views.opentasksTaskDetailsSubtaskProgressBackground)
84+
.update(subtask.percentComplete());
85+
86+
views.getRoot().setOnClickListener((v) ->
87+
{
88+
Context ctx = v.getContext();
89+
// TODO Use BasicTaskDetailsUi class when #589 is merged
90+
ctx.startActivity(new Intent(Intent.ACTION_VIEW, new TaskContentUri(subtask.id(), ctx).value()));
91+
});
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2017 dmfs GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.dmfs.tasks.detailsscreen;
18+
19+
import android.view.LayoutInflater;
20+
import android.view.ViewGroup;
21+
import android.widget.TextView;
22+
23+
import org.dmfs.android.bolts.color.Color;
24+
import org.dmfs.tasks.R;
25+
import org.dmfs.tasks.widget.PopulateableViewGroup;
26+
import org.dmfs.tasks.widget.SmartView;
27+
import org.dmfs.tasks.widget.UpdatedSmartViews;
28+
29+
30+
/**
31+
* {@link SmartView} for the subtasks section of the task details screen.
32+
*
33+
* @author Gabor Keszthelyi
34+
*/
35+
public final class SubtasksView implements SmartView<SubtasksView.Params>
36+
{
37+
public interface Params
38+
{
39+
Color taskListColor();
40+
41+
Iterable<SubtaskView.Params> subtasks();
42+
}
43+
44+
45+
private final ViewGroup mContentView;
46+
47+
48+
public SubtasksView(ViewGroup contentView)
49+
{
50+
mContentView = contentView;
51+
}
52+
53+
54+
@Override
55+
public void update(SubtasksView.Params params)
56+
{
57+
if (!params.subtasks().iterator().hasNext())
58+
{
59+
// Don't show the subtasks UI section if there are no subtasks
60+
return;
61+
}
62+
63+
LayoutInflater inflater = LayoutInflater.from(mContentView.getContext());
64+
65+
inflater.inflate(R.layout.opentasks_view_item_divider, mContentView);
66+
67+
TextView sectionHeader = (TextView) inflater.inflate(R.layout.opentasks_view_item_task_details_subtitles_section_header, null);
68+
sectionHeader.setTextColor(new Darkened(params.taskListColor()).argb());
69+
mContentView.addView(sectionHeader);
70+
71+
new PopulateableViewGroup<SubtaskView>(mContentView)
72+
.populate(new UpdatedSmartViews<>(params.subtasks(), inflater, R.layout.opentasks_view_item_task_details_subtask));
73+
}
74+
75+
76+
// TODO Remove when #522 is merged, use the version from there
77+
private static final class Darkened implements Color
78+
{
79+
private final Color mOriginal;
80+
81+
82+
private Darkened(Color original)
83+
{
84+
mOriginal = original;
85+
}
86+
87+
88+
@Override
89+
public int argb()
90+
{
91+
float[] hsv = new float[3];
92+
android.graphics.Color.colorToHSV(mOriginal.argb(), hsv);
93+
hsv[2] = hsv[2] * 0.75f;
94+
return android.graphics.Color.HSVToColor(hsv);
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)