From 8692891464a4c48782b3831d27442b6197e0b6e9 Mon Sep 17 00:00:00 2001 From: Gabor Keszthelyi Date: Tue, 17 Oct 2017 21:23:08 +0200 Subject: [PATCH 1/2] Show subtasks on details view. #442 --- .idea/dictionaries/dictionary.xml | 1 + opentasks/build.gradle | 7 ++ opentasks/proguard.cfg | 5 +- .../java/org/dmfs/tasks/ViewTaskFragment.java | 16 +++ .../RowDataSubtaskViewParams.java | 94 ++++++++++++++++++ .../RowDataSubtasksViewParams.java | 55 +++++++++++ .../dmfs/tasks/detailsscreen/SubtaskView.java | 96 ++++++++++++++++++ .../tasks/detailsscreen/SubtasksSource.java | 54 +++++++++++ .../tasks/detailsscreen/SubtasksView.java | 97 +++++++++++++++++++ .../ContentProviderClientDisposable.java | 50 ++++++++++ .../readdata/ContentProviderClientSource.java | 56 +++++++++++ .../java/org/dmfs/tasks/readdata/CpQuery.java | 38 ++++++++ .../dmfs/tasks/readdata/CpQuerySource.java | 52 ++++++++++ .../dmfs/tasks/readdata/TaskContentUri.java | 51 ++++++++++ .../utils/DatabaseInitializedReceiver.java | 95 +++++++++++++++--- .../utils/rxjava/DelegatingDisposable.java | 50 ++++++++++ .../tasks/utils/rxjava/DelegatingSingle.java | 45 +++++++++ .../dmfs/tasks/utils/rxjava/Offloading.java | 42 ++++++++ .../dmfs/tasks/widget/PopulateableView.java | 33 +++++++ .../tasks/widget/PopulateableViewGroup.java | 48 +++++++++ .../dmfs/tasks/widget/UpdatedSmartViews.java | 48 +++++++++ .../res/layout/fragment_task_view_detail.xml | 1 + .../layout/opentasks_view_item_divider.xml | 12 +++ ...entasks_view_item_task_details_subtask.xml | 59 +++++++++++ ..._task_details_subtitles_section_header.xml | 14 +++ opentasks/src/main/res/values/strings.xml | 4 + .../dmfs/opentaskspal/readdata/TaskUri.java | 58 ----------- 27 files changed, 1108 insertions(+), 73 deletions(-) create mode 100644 opentasks/src/main/java/org/dmfs/tasks/detailsscreen/RowDataSubtaskViewParams.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/detailsscreen/RowDataSubtasksViewParams.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtaskView.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtasksSource.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtasksView.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/readdata/ContentProviderClientDisposable.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/readdata/ContentProviderClientSource.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/readdata/CpQuery.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/readdata/CpQuerySource.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/readdata/TaskContentUri.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/DelegatingDisposable.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/DelegatingSingle.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/Offloading.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/widget/PopulateableView.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/widget/PopulateableViewGroup.java create mode 100644 opentasks/src/main/java/org/dmfs/tasks/widget/UpdatedSmartViews.java create mode 100644 opentasks/src/main/res/layout/opentasks_view_item_divider.xml create mode 100644 opentasks/src/main/res/layout/opentasks_view_item_task_details_subtask.xml create mode 100644 opentasks/src/main/res/layout/opentasks_view_item_task_details_subtitles_section_header.xml delete mode 100644 opentaskspal/src/main/java/org/dmfs/opentaskspal/readdata/TaskUri.java diff --git a/.idea/dictionaries/dictionary.xml b/.idea/dictionaries/dictionary.xml index 7db7138b1..4a32d902f 100644 --- a/.idea/dictionaries/dictionary.xml +++ b/.idea/dictionaries/dictionary.xml @@ -2,6 +2,7 @@ opentasks + populateable subtask subtasks diff --git a/opentasks/build.gradle b/opentasks/build.gradle index 67d52e981..7dbd08dc7 100644 --- a/opentasks/build.gradle +++ b/opentasks/build.gradle @@ -63,11 +63,15 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + dataBinding { + enabled = true + } } dependencies { implementation project(':opentasks-theme') implementation project(':opentasks-provider') + implementation project(':opentaskspal') implementation deps.support_appcompat implementation deps.support_design implementation(deps.xml_magic) { @@ -88,6 +92,9 @@ dependencies { implementation deps.datetime implementation deps.bolts_color implementation deps.retention_magic + implementation deps.contentpal + implementation 'io.reactivex.rxjava2:rxjava:2.1.5' + implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' testImplementation deps.junit testImplementation deps.robolectric diff --git a/opentasks/proguard.cfg b/opentasks/proguard.cfg index bd574eba9..5115fbfda 100644 --- a/opentasks/proguard.cfg +++ b/opentasks/proguard.cfg @@ -80,4 +80,7 @@ java.lang.String TAG; @org.dmfs.android.retentionmagic.annotations.* ; private long mId; -} \ No newline at end of file +} + +-dontwarn android.databinding.** +-keep class android.databinding.** { *; } diff --git a/opentasks/src/main/java/org/dmfs/tasks/ViewTaskFragment.java b/opentasks/src/main/java/org/dmfs/tasks/ViewTaskFragment.java index 8b3539c79..e35b787e5 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/ViewTaskFragment.java +++ b/opentasks/src/main/java/org/dmfs/tasks/ViewTaskFragment.java @@ -57,6 +57,10 @@ import org.dmfs.android.retentionmagic.annotations.Parameter; import org.dmfs.android.retentionmagic.annotations.Retain; import org.dmfs.tasks.contract.TaskContract.Tasks; +import org.dmfs.tasks.detailsscreen.RowDataSubtaskViewParams; +import org.dmfs.tasks.detailsscreen.RowDataSubtasksViewParams; +import org.dmfs.tasks.detailsscreen.SubtasksSource; +import org.dmfs.tasks.detailsscreen.SubtasksView; import org.dmfs.tasks.model.ContentSet; import org.dmfs.tasks.model.Model; import org.dmfs.tasks.model.OnContentChangeListener; @@ -74,6 +78,8 @@ import java.util.HashSet; import java.util.Set; +import io.reactivex.disposables.CompositeDisposable; + /** * 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 @@ -135,6 +141,8 @@ public class ViewTaskFragment extends SupportFragment */ private TaskView mDetailView; + private CompositeDisposable mDisposables; + private int mListColor; private int mOldStatus = -1; private boolean mPinned = false; @@ -252,12 +260,14 @@ public void onDestroyView() mDetailView.setValues(null); } + mDisposables.dispose(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + mDisposables = new CompositeDisposable(); mShowFloatingActionButton = !getResources().getBoolean(R.bool.has_two_panes); mRootView = inflater.inflate(R.layout.fragment_task_view_detail, container, false); @@ -442,6 +452,12 @@ private void updateView() ((TextView) mToolBar.findViewById(R.id.toolbar_title)).setText(TaskFieldAdapters.TITLE.get(mContentSet)); } } + + mDisposables.add(new SubtasksSource(mAppContext, mTaskUri, RowDataSubtaskViewParams.SUBTASK_PROJECTION) + .subscribe(subtasks -> + { + new SubtasksView(mContent).update(new RowDataSubtasksViewParams(new ValueColor(mListColor), subtasks)); + })); } diff --git a/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/RowDataSubtaskViewParams.java b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/RowDataSubtaskViewParams.java new file mode 100644 index 000000000..4df4afc74 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/RowDataSubtaskViewParams.java @@ -0,0 +1,94 @@ +/* + * Copyright 2018 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.detailsscreen; + +import org.dmfs.android.bolts.color.Color; +import org.dmfs.android.contentpal.Projection; +import org.dmfs.android.contentpal.RowDataSnapshot; +import org.dmfs.android.contentpal.projections.Composite; +import org.dmfs.jems.optional.Optional; +import org.dmfs.opentaskspal.readdata.EffectiveDueDate; +import org.dmfs.opentaskspal.readdata.EffectiveTaskColor; +import org.dmfs.opentaskspal.readdata.Id; +import org.dmfs.opentaskspal.readdata.PercentComplete; +import org.dmfs.opentaskspal.readdata.TaskTitle; +import org.dmfs.rfc5545.DateTime; +import org.dmfs.tasks.contract.TaskContract; + + +/** + * {@link SubtasksView.Params} that reads the data from the given {@link RowDataSnapshot}. + * + * @author Gabor Keszthelyi + */ +public final class RowDataSubtaskViewParams implements SubtaskView.Params +{ + + /** + * The projection required for this adapter to work. + */ + public static final Projection SUBTASK_PROJECTION = new Composite<>( + Id.PROJECTION, + TaskTitle.PROJECTION, + EffectiveDueDate.PROJECTION, + EffectiveTaskColor.PROJECTION, + PercentComplete.PROJECTION + ); + + private final RowDataSnapshot mRowDataSnapshot; + + + public RowDataSubtaskViewParams(RowDataSnapshot rowDataSnapshot) + { + mRowDataSnapshot = rowDataSnapshot; + } + + + @Override + public Long id() + { + return new Id(mRowDataSnapshot).value(); + } + + + @Override + public Optional title() + { + return new TaskTitle(mRowDataSnapshot); + } + + + @Override + public Optional due() + { + return new EffectiveDueDate(mRowDataSnapshot); + } + + + @Override + public Color color() + { + return new EffectiveTaskColor(mRowDataSnapshot); + } + + + @Override + public Optional percentComplete() + { + return new PercentComplete(mRowDataSnapshot); + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/RowDataSubtasksViewParams.java b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/RowDataSubtasksViewParams.java new file mode 100644 index 000000000..edb2d11a8 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/RowDataSubtasksViewParams.java @@ -0,0 +1,55 @@ +/* + * Copyright 2018 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.detailsscreen; + +import org.dmfs.android.bolts.color.Color; +import org.dmfs.android.contentpal.RowDataSnapshot; +import org.dmfs.jems.iterable.decorators.Mapped; +import org.dmfs.tasks.contract.TaskContract; + + +/** + * {@link SubtasksView.Params} that adapts the given {@link RowDataSnapshot}s (and takes the list color). + * + * @author Gabor Keszthelyi + */ +public final class RowDataSubtasksViewParams implements SubtasksView.Params +{ + private final Color mTaskListColor; + private final Iterable> mSubtaskRows; + + + public RowDataSubtasksViewParams(Color taskListColor, Iterable> subtaskRows) + { + mTaskListColor = taskListColor; + mSubtaskRows = subtaskRows; + } + + + @Override + public Color taskListColor() + { + return mTaskListColor; + } + + + @Override + public Iterable subtasks() + { + return new Mapped<>(RowDataSubtaskViewParams::new, mSubtaskRows); + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtaskView.java b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtaskView.java new file mode 100644 index 000000000..7e77ce9c3 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtaskView.java @@ -0,0 +1,96 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.detailsscreen; + +import android.content.Context; +import android.content.Intent; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import org.dmfs.android.bolts.color.Color; +import org.dmfs.jems.optional.Optional; +import org.dmfs.jems.single.combined.Backed; +import org.dmfs.rfc5545.DateTime; +import org.dmfs.tasks.R; +import org.dmfs.tasks.databinding.OpentasksViewItemTaskDetailsSubtaskBinding; +import org.dmfs.tasks.readdata.TaskContentUri; +import org.dmfs.tasks.utils.DateFormatter; +import org.dmfs.tasks.utils.DateFormatter.DateFormatContext; +import org.dmfs.tasks.widget.ProgressBackgroundView; +import org.dmfs.tasks.widget.SmartView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.databinding.DataBindingUtil; + + +/** + * {@link View} for showing a subtask on the details screen. + * + * @author Gabor Keszthelyi + */ +public final class SubtaskView extends FrameLayout implements SmartView +{ + + public interface Params // i.e. fields of the subtask + { + Long id(); + + Optional title(); + + Optional due(); + + Color color(); + + Optional percentComplete(); + } + + + public SubtaskView(@NonNull Context context, @Nullable AttributeSet attrs) + { + super(context, attrs); + } + + + @Override + public void update(Params subtask) + { + OpentasksViewItemTaskDetailsSubtaskBinding views = DataBindingUtil.bind(this); + + views.opentasksTaskDetailsSubtaskTitle.setText( + new Backed<>(subtask.title(), getContext().getString(R.string.opentasks_task_details_subtask_untitled)).value()); + + if (subtask.due().isPresent()) + { + views.opentasksTaskDetailsSubtaskDue.setText( + new DateFormatter(getContext()).format(subtask.due().value(), DateTime.now(), DateFormatContext.LIST_VIEW)); + } + + views.opentasksTaskDetailsSubtaskListRibbon.setBackgroundColor(subtask.color().argb()); + + new ProgressBackgroundView(views.opentasksTaskDetailsSubtaskProgressBackground) + .update(subtask.percentComplete()); + + views.getRoot().setOnClickListener((v) -> + { + Context ctx = v.getContext(); + // TODO Use BasicTaskDetailsUi class when #589 is merged + ctx.startActivity(new Intent(Intent.ACTION_VIEW, new TaskContentUri(subtask.id(), ctx).value())); + }); + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtasksSource.java b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtasksSource.java new file mode 100644 index 000000000..822a5aa78 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtasksSource.java @@ -0,0 +1,54 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.detailsscreen; + +import android.content.Context; +import android.net.Uri; + +import org.dmfs.android.contentpal.Projection; +import org.dmfs.android.contentpal.RowDataSnapshot; +import org.dmfs.android.contentpal.references.RowUriReference; +import org.dmfs.opentaskspal.rowsets.Subtasks; +import org.dmfs.opentaskspal.views.TasksView; +import org.dmfs.tasks.contract.TaskContract; +import org.dmfs.tasks.contract.TaskContract.Tasks; +import org.dmfs.tasks.readdata.CpQuerySource; +import org.dmfs.tasks.utils.rxjava.DelegatingSingle; +import org.dmfs.tasks.utils.rxjava.Offloading; + +import io.reactivex.Single; + +import static org.dmfs.provider.tasks.AuthorityUtil.taskAuthority; + + +/** + * {@link Single} to get the subtasks of a task. + * + * @author Gabor Keszthelyi + */ +public final class SubtasksSource extends DelegatingSingle>> +{ + public SubtasksSource(Context context, Uri taskUri, Projection projection) + { + super(new Offloading<>( + new CpQuerySource<>( + context.getApplicationContext(), + (client, ctx) -> new Subtasks(new TasksView(taskAuthority(context), client), projection, new RowUriReference<>(taskUri))) + ) + ); + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtasksView.java b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtasksView.java new file mode 100644 index 000000000..80eede147 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtasksView.java @@ -0,0 +1,97 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.detailsscreen; + +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.dmfs.android.bolts.color.Color; +import org.dmfs.tasks.R; +import org.dmfs.tasks.widget.PopulateableViewGroup; +import org.dmfs.tasks.widget.SmartView; +import org.dmfs.tasks.widget.UpdatedSmartViews; + + +/** + * {@link SmartView} for the subtasks section of the task details screen. + * + * @author Gabor Keszthelyi + */ +public final class SubtasksView implements SmartView +{ + public interface Params + { + Color taskListColor(); + + Iterable subtasks(); + } + + + private final ViewGroup mContentView; + + + public SubtasksView(ViewGroup contentView) + { + mContentView = contentView; + } + + + @Override + public void update(SubtasksView.Params params) + { + if (!params.subtasks().iterator().hasNext()) + { + // Don't show the subtasks UI section if there are no subtasks + return; + } + + LayoutInflater inflater = LayoutInflater.from(mContentView.getContext()); + + inflater.inflate(R.layout.opentasks_view_item_divider, mContentView); + + TextView sectionHeader = (TextView) inflater.inflate(R.layout.opentasks_view_item_task_details_subtitles_section_header, null); + sectionHeader.setTextColor(new Darkened(params.taskListColor()).argb()); + mContentView.addView(sectionHeader); + + new PopulateableViewGroup(mContentView) + .populate(new UpdatedSmartViews<>(params.subtasks(), inflater, R.layout.opentasks_view_item_task_details_subtask)); + } + + + // TODO Remove when #522 is merged, use the version from there + private static final class Darkened implements Color + { + private final Color mOriginal; + + + private Darkened(Color original) + { + mOriginal = original; + } + + + @Override + public int argb() + { + float[] hsv = new float[3]; + android.graphics.Color.colorToHSV(mOriginal.argb(), hsv); + hsv[2] = hsv[2] * 0.75f; + return android.graphics.Color.HSVToColor(hsv); + } + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/readdata/ContentProviderClientDisposable.java b/opentasks/src/main/java/org/dmfs/tasks/readdata/ContentProviderClientDisposable.java new file mode 100644 index 000000000..de8c9bfc7 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/readdata/ContentProviderClientDisposable.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.readdata; + +import android.content.ContentProviderClient; +import android.os.Build; + +import org.dmfs.tasks.utils.rxjava.DelegatingDisposable; + +import io.reactivex.disposables.Disposable; +import io.reactivex.disposables.Disposables; + + +/** + * {@link Disposable} for {@link ContentProviderClient}. + * + * @author Gabor Keszthelyi + */ +public final class ContentProviderClientDisposable extends DelegatingDisposable +{ + public ContentProviderClientDisposable(final ContentProviderClient client) + { + super(Disposables.fromRunnable(() -> + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + { + client.close(); + } + else + { + client.release(); + } + })); + } + +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/readdata/ContentProviderClientSource.java b/opentasks/src/main/java/org/dmfs/tasks/readdata/ContentProviderClientSource.java new file mode 100644 index 000000000..26a933864 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/readdata/ContentProviderClientSource.java @@ -0,0 +1,56 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.readdata; + +import android.content.ContentProviderClient; +import android.content.Context; +import android.net.Uri; + +import org.dmfs.provider.tasks.AuthorityUtil; +import org.dmfs.tasks.contract.TaskContract; +import org.dmfs.tasks.utils.rxjava.DelegatingSingle; + +import io.reactivex.Single; +import io.reactivex.SingleEmitter; + + +/** + * {@link Single} for accessing a {@link ContentProviderClient} for the given {@link Uri}. + * Takes care of closing the client upon disposal. + * + * @author Gabor Keszthelyi + */ +public class ContentProviderClientSource extends DelegatingSingle +{ + + public ContentProviderClientSource(Context context, Uri uri) + { + super(Single.create((SingleEmitter emitter) -> + { + ContentProviderClient client = context.getContentResolver().acquireContentProviderClient(uri); + emitter.setDisposable(new ContentProviderClientDisposable(client)); + emitter.onSuccess(client); + })); + } + + + public ContentProviderClientSource(Context context) + { + this(context, TaskContract.getContentUri(AuthorityUtil.taskAuthority(context))); + } + +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/readdata/CpQuery.java b/opentasks/src/main/java/org/dmfs/tasks/readdata/CpQuery.java new file mode 100644 index 000000000..b442cd2bb --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/readdata/CpQuery.java @@ -0,0 +1,38 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.readdata; + +import android.content.ContentProviderClient; +import android.content.Context; + +import org.dmfs.android.contentpal.RowSet; + + +/** + * Represents a ContentProvider query resulting in ContentPal's {@link RowSet}. + * + * @author Gabor Keszthelyi + */ +public interface CpQuery +{ + + /** + * Returns the {@link RowSet} that represent the result of this query. + */ + RowSet rowSet(ContentProviderClient client, Context appContext); + +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/readdata/CpQuerySource.java b/opentasks/src/main/java/org/dmfs/tasks/readdata/CpQuerySource.java new file mode 100644 index 000000000..bbbabbac8 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/readdata/CpQuerySource.java @@ -0,0 +1,52 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.readdata; + +import android.content.Context; + +import org.dmfs.android.contentpal.RowDataSnapshot; +import org.dmfs.android.contentpal.RowSet; +import org.dmfs.android.contentpal.RowSnapshot; +import org.dmfs.android.contentpal.rowsets.Frozen; +import org.dmfs.iterables.decorators.Mapped; +import org.dmfs.tasks.utils.rxjava.DelegatingSingle; + +import io.reactivex.Single; + + +/** + * {@link Single} that accesses the Tasks provider, runs the given {@link CpQuery} + * and delivers the the result {@link Iterable} of {@link RowDataSnapshot}s. + * + * @author Gabor Keszthelyi + */ +public final class CpQuerySource extends DelegatingSingle>> +{ + + public CpQuerySource(Context context, CpQuery cpQuery) + { + super(new ContentProviderClientSource(context) + .map(client -> + { + RowSet frozen = new Frozen<>(cpQuery.rowSet(client, context)); + frozen.iterator(); // To actually freeze it + + return new Mapped<>(frozen, RowSnapshot::values); + })); + } + +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/readdata/TaskContentUri.java b/opentasks/src/main/java/org/dmfs/tasks/readdata/TaskContentUri.java new file mode 100644 index 000000000..29ba29d07 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/readdata/TaskContentUri.java @@ -0,0 +1,51 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.readdata; + +import android.content.ContentUris; +import android.content.Context; +import android.net.Uri; + +import org.dmfs.jems.single.Single; +import org.dmfs.provider.tasks.AuthorityUtil; +import org.dmfs.tasks.contract.TaskContract; + + +/** + * Content Uri for a given task id. + * + * @author Gabor Keszthelyi + */ +public final class TaskContentUri implements Single +{ + private final Long mTaskId; + private final Context mAppContext; + + + public TaskContentUri(Long taskId, Context context) + { + mTaskId = taskId; + mAppContext = context.getApplicationContext(); + } + + + @Override + public Uri value() + { + return ContentUris.withAppendedId(TaskContract.Tasks.getContentUri(AuthorityUtil.taskAuthority(mAppContext)), mTaskId); + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/utils/DatabaseInitializedReceiver.java b/opentasks/src/main/java/org/dmfs/tasks/utils/DatabaseInitializedReceiver.java index 9db6b5021..5bf43e7d0 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/utils/DatabaseInitializedReceiver.java +++ b/opentasks/src/main/java/org/dmfs/tasks/utils/DatabaseInitializedReceiver.java @@ -16,16 +16,29 @@ package org.dmfs.tasks.utils; +import android.accounts.Account; import android.content.BroadcastReceiver; -import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.OperationApplicationException; import android.graphics.Color; +import android.os.RemoteException; +import org.dmfs.android.bolts.color.elementary.ValueColor; +import org.dmfs.android.contentpal.operations.Insert; +import org.dmfs.android.contentpal.rowdata.Composite; +import org.dmfs.android.contentpal.tables.Synced; +import org.dmfs.android.contentpal.transactions.BaseTransaction; +import org.dmfs.iterables.SingletonIterable; +import org.dmfs.opentaskspal.tables.TaskListsTable; +import org.dmfs.opentaskspal.tasklists.ColorData; +import org.dmfs.opentaskspal.tasklists.NameData; +import org.dmfs.opentaskspal.tasklists.OwnerData; +import org.dmfs.opentaskspal.tasklists.SyncStatusData; +import org.dmfs.opentaskspal.tasklists.VisibilityData; import org.dmfs.provider.tasks.AuthorityUtil; import org.dmfs.tasks.R; import org.dmfs.tasks.contract.TaskContract; -import org.dmfs.tasks.contract.TaskContract.TaskLists; public class DatabaseInitializedReceiver extends BroadcastReceiver @@ -36,18 +49,72 @@ public void onReceive(Context context, Intent intent) if (context.getResources().getBoolean(R.bool.opentasks_support_local_lists)) { // The database was just created, insert a local task list - ContentValues listValues = new ContentValues(5); - listValues.put(TaskLists.LIST_NAME, context.getString(R.string.initial_local_task_list_name)); - listValues.put(TaskLists.LIST_COLOR, Color.rgb(30, 136, 229) /* material blue 600 */); - listValues.put(TaskLists.VISIBLE, 1); - listValues.put(TaskLists.SYNC_ENABLED, 1); - listValues.put(TaskLists.OWNER, ""); - - context.getContentResolver().insert( - TaskContract.TaskLists.getContentUri(AuthorityUtil.taskAuthority(context)).buildUpon() - .appendQueryParameter(TaskContract.CALLER_IS_SYNCADAPTER, "true") - .appendQueryParameter(TaskContract.ACCOUNT_NAME, TaskContract.LOCAL_ACCOUNT_NAME) - .appendQueryParameter(TaskContract.ACCOUNT_TYPE, TaskContract.LOCAL_ACCOUNT_TYPE).build(), listValues); + try + { + new BaseTransaction().with(new SingletonIterable<>( + new Insert<>( + // the table to insert into + new Synced<>(new Account(TaskContract.LOCAL_ACCOUNT_NAME, TaskContract.LOCAL_ACCOUNT_TYPE), + new TaskListsTable(AuthorityUtil.taskAuthority(context))), + // the data to insert + new Composite<>( + new NameData(context.getString(R.string.initial_local_task_list_name)), + new VisibilityData(true), + new OwnerData(""), + new SyncStatusData(true), + new ColorData(new ValueColor(Color.rgb(30, 136, 229))))))) + .commit(context.getContentResolver().acquireContentProviderClient(AuthorityUtil.taskAuthority(context))); + } + catch (RemoteException | OperationApplicationException e) + { + throw new Error("Unable to create initial task list. Something seems to be broken badly.", e); + } + + + /* + + Table tasklistTable = new Synced<>(new AccountScoped<>(new Account(TaskContract.LOCAL_ACCOUNT_NAME, TaskContract.LOCAL_ACCOUNT_TYPE), + new TaskListsTable(AuthorityUtil.taskAuthority(context)))); + + RowSnapshot taskList = new VirtualRowSnapshot<>(tasklistTable); + + Table taskTable = new Synced<>(new TaskListScoped(taskList, new TasksTable(AuthorityUtil.taskAuthority(context)))); + Table taskProperties = new Synced<>(new PropertiesTable(AuthorityUtil.taskAuthority(context))); + + RowSnapshot task1 = new VirtualRowSnapshot<>(taskTable); + RowSnapshot task2 = new VirtualRowSnapshot<>(taskTable); + RowSnapshot task3 = new VirtualRowSnapshot<>(taskTable); + RowSnapshot task4 = new VirtualRowSnapshot<>(taskTable); + try + { + new BaseTransaction().with(new Seq<>( + new Put<>(taskList, new Composite<>( + new NameData(context.getString(R.string.initial_local_task_list_name)), + new VisibilityData(true), + new OwnerData(""), + new SyncStatusData(true), + new ColorData(new ValueColor(Color.rgb(30, 136, 229))))), + new Put<>(task1, new Composite<>( + new TitleData("Task1"))), + new Put<>(task2, new Composite<>( + new TitleData("Task2"))), + new Put<>(task3, new Composite<>( + new TitleData("Task3"))), + new Put<>(task4, new Composite<>( + new TitleData("Task4"))), + new Insert<>(taskProperties, new RelationData(task2, TaskContract.Property.Relation.RELTYPE_PARENT, task1)), + new Insert<>(taskProperties, new RelationData(task3, TaskContract.Property.Relation.RELTYPE_PARENT, task1)), + new Insert<>(taskProperties, new RelationData(task4, TaskContract.Property.Relation.RELTYPE_PARENT, task2)) + )).commit(context.getContentResolver().acquireContentProviderClient(AuthorityUtil.taskAuthority(context))); + } + catch (RemoteException e) + { + e.printStackTrace(); + } + catch (OperationApplicationException e) + { + e.printStackTrace(); + }*/ } } } diff --git a/opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/DelegatingDisposable.java b/opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/DelegatingDisposable.java new file mode 100644 index 000000000..cea7b8b07 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/DelegatingDisposable.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.utils.rxjava; + +import io.reactivex.disposables.Disposable; + + +/** + * {@link Disposable} that simply delegates to the given {@link Disposable}. + * + * @author Gabor Keszthelyi + */ +public abstract class DelegatingDisposable implements Disposable +{ + private final Disposable mDelegate; + + + protected DelegatingDisposable(Disposable delegate) + { + mDelegate = delegate; + } + + + @Override + public final void dispose() + { + mDelegate.dispose(); + } + + + @Override + public final boolean isDisposed() + { + return mDelegate.isDisposed(); + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/DelegatingSingle.java b/opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/DelegatingSingle.java new file mode 100644 index 000000000..7311c28db --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/DelegatingSingle.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.utils.rxjava; + +import io.reactivex.Single; +import io.reactivex.SingleObserver; +import io.reactivex.SingleSource; + + +/** + * Base class for {@link Single} that delegates to a {@link SingleSource}. + * + * @author Gabor Keszthelyi + */ +public abstract class DelegatingSingle extends Single +{ + private final SingleSource mDelegate; + + + protected DelegatingSingle(SingleSource delegate) + { + mDelegate = delegate; + } + + + @Override + protected final void subscribeActual(SingleObserver observer) + { + mDelegate.subscribe(observer); + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/Offloading.java b/opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/Offloading.java new file mode 100644 index 000000000..ae079dc75 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/utils/rxjava/Offloading.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.utils.rxjava; + +import io.reactivex.Single; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + + +/** + * {@link Single} decorator that sets the threading so that + * the work is on {@link Schedulers#io()} and delivery is on main thread. + *

+ * Important: it has to be applied last normally, since .observeOn(mainThread) results in + * doing everything on main thread after this decorator is applied. + * + * @author Gabor Keszthelyi + */ +public final class Offloading extends DelegatingSingle +{ + + public Offloading(Single delegate) + { + super(delegate + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread())); + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/widget/PopulateableView.java b/opentasks/src/main/java/org/dmfs/tasks/widget/PopulateableView.java new file mode 100644 index 000000000..de72ccaea --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/widget/PopulateableView.java @@ -0,0 +1,33 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.widget; + +import android.view.View; + + +/** + * Represents a View that can be populated with other Views, i.e. views can be added to it. + * + * @author Gabor Keszthelyi + */ +public interface PopulateableView +{ + /** + * Adds the given views to this view. + */ + void populate(Iterable views); +} \ No newline at end of file diff --git a/opentasks/src/main/java/org/dmfs/tasks/widget/PopulateableViewGroup.java b/opentasks/src/main/java/org/dmfs/tasks/widget/PopulateableViewGroup.java new file mode 100644 index 000000000..8a2dfc4b9 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/widget/PopulateableViewGroup.java @@ -0,0 +1,48 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.widget; + +import android.view.View; +import android.view.ViewGroup; + + +/** + * {@link PopulateableView} that simply adds the views to the provided {@link ViewGroup} as child views. + * + * @author Gabor Keszthelyi + */ +public final class PopulateableViewGroup implements PopulateableView +{ + private final ViewGroup mViewGroup; + + + public PopulateableViewGroup(ViewGroup viewGroup) + { + mViewGroup = viewGroup; + } + + + @Override + public void populate(Iterable views) + { + for (V view : views) + { + mViewGroup.addView(view); + } + mViewGroup.requestLayout(); + } +} diff --git a/opentasks/src/main/java/org/dmfs/tasks/widget/UpdatedSmartViews.java b/opentasks/src/main/java/org/dmfs/tasks/widget/UpdatedSmartViews.java new file mode 100644 index 000000000..ac3f2b310 --- /dev/null +++ b/opentasks/src/main/java/org/dmfs/tasks/widget/UpdatedSmartViews.java @@ -0,0 +1,48 @@ +/* + * Copyright 2017 dmfs GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dmfs.tasks.widget; + +import android.view.LayoutInflater; +import android.view.View; + +import org.dmfs.iterables.decorators.DelegatingIterable; +import org.dmfs.jems.iterable.decorators.Mapped; + +import androidx.annotation.LayoutRes; + + +/** + * {@link Iterable} of {@link SmartView}s that are updated with the corresponding data items + * from the given {@link Iterable} of D. + * + * @author Gabor Keszthelyi + */ +public final class UpdatedSmartViews> extends DelegatingIterable +{ + + public UpdatedSmartViews(Iterable dataIterable, LayoutInflater inflater, @LayoutRes int layout) + { + super(new Mapped<>((dataItem) -> + { + //noinspection unchecked + V view = (V) inflater.inflate(layout, null); + view.update(dataItem); + return view; + }, dataIterable)); + } + +} diff --git a/opentasks/src/main/res/layout/fragment_task_view_detail.xml b/opentasks/src/main/res/layout/fragment_task_view_detail.xml index 0172b8870..6fd7bde9b 100644 --- a/opentasks/src/main/res/layout/fragment_task_view_detail.xml +++ b/opentasks/src/main/res/layout/fragment_task_view_detail.xml @@ -129,6 +129,7 @@ android:layout_width="match_parent" android:background="@android:color/white" android:layout_height="wrap_content" + android:paddingBottom="8dp" android:orientation="vertical"/> diff --git a/opentasks/src/main/res/layout/opentasks_view_item_divider.xml b/opentasks/src/main/res/layout/opentasks_view_item_divider.xml new file mode 100644 index 000000000..702bbf3b5 --- /dev/null +++ b/opentasks/src/main/res/layout/opentasks_view_item_divider.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/opentasks/src/main/res/layout/opentasks_view_item_task_details_subtask.xml b/opentasks/src/main/res/layout/opentasks_view_item_task_details_subtask.xml new file mode 100644 index 000000000..f396b0c0c --- /dev/null +++ b/opentasks/src/main/res/layout/opentasks_view_item_task_details_subtask.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/opentasks/src/main/res/layout/opentasks_view_item_task_details_subtitles_section_header.xml b/opentasks/src/main/res/layout/opentasks_view_item_task_details_subtitles_section_header.xml new file mode 100644 index 000000000..130993c3f --- /dev/null +++ b/opentasks/src/main/res/layout/opentasks_view_item_task_details_subtitles_section_header.xml @@ -0,0 +1,14 @@ + + \ No newline at end of file diff --git a/opentasks/src/main/res/values/strings.xml b/opentasks/src/main/res/values/strings.xml index 0d52b7b4b..8af6e4716 100644 --- a/opentasks/src/main/res/values/strings.xml +++ b/opentasks/src/main/res/values/strings.xml @@ -46,6 +46,10 @@ No task selected Send Send to + + Untitled + + Subtasks Text diff --git a/opentaskspal/src/main/java/org/dmfs/opentaskspal/readdata/TaskUri.java b/opentaskspal/src/main/java/org/dmfs/opentaskspal/readdata/TaskUri.java deleted file mode 100644 index 84ecb209c..000000000 --- a/opentaskspal/src/main/java/org/dmfs/opentaskspal/readdata/TaskUri.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 dmfs GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.dmfs.opentaskspal.readdata; - -import android.content.ContentUris; -import android.net.Uri; -import android.provider.BaseColumns; - -import org.dmfs.android.contentpal.Projection; -import org.dmfs.android.contentpal.RowDataSnapshot; -import org.dmfs.jems.single.Single; -import org.dmfs.tasks.contract.TaskContract; -import org.dmfs.tasks.contract.TaskContract.Tasks; - -import androidx.annotation.NonNull; - - -/** - * {@link Single} for the content {@link Uri} that refers to the given {@link RowDataSnapshot}. - * - * @author Gabor Keszthelyi - */ -public final class TaskUri implements Single -{ - public static final Projection PROJECTION = Id.PROJECTION; - - private final RowDataSnapshot mRowDataSnapshot; - private final String mAuthority; - - - public TaskUri(@NonNull String authority, @NonNull RowDataSnapshot rowDataSnapshot) - { - mAuthority = authority; - mRowDataSnapshot = rowDataSnapshot; - } - - - @Override - public Uri value() - { - // TODO: use the instance URI one we support recurrence - return ContentUris.withAppendedId(Tasks.getContentUri(mAuthority), new Id(mRowDataSnapshot).value()); - } -} From bd363859be8c595330de5fd8d82b2c5e6db0952d Mon Sep 17 00:00:00 2001 From: Marten Gajda Date: Thu, 23 Apr 2020 17:03:41 +0200 Subject: [PATCH 2/2] remove some unused code --- .idea/codeStyles/Project.xml | 117 ++++++++++++++++-- .../dmfs/tasks/detailsscreen/SubtaskView.java | 4 - .../org/dmfs/tasks/model/DefaultModel.java | 3 - .../org/dmfs/tasks/model/MinimalModel.java | 3 - .../main/java/org/dmfs/tasks/model/Model.java | 41 ++---- 5 files changed, 117 insertions(+), 51 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index a20ef393e..6210db6bf 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -5,7 +5,6 @@

+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+ + + \ No newline at end of file diff --git a/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtaskView.java b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtaskView.java index 7e77ce9c3..9c655b785 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtaskView.java +++ b/opentasks/src/main/java/org/dmfs/tasks/detailsscreen/SubtaskView.java @@ -31,7 +31,6 @@ import org.dmfs.tasks.readdata.TaskContentUri; import org.dmfs.tasks.utils.DateFormatter; import org.dmfs.tasks.utils.DateFormatter.DateFormatContext; -import org.dmfs.tasks.widget.ProgressBackgroundView; import org.dmfs.tasks.widget.SmartView; import androidx.annotation.NonNull; @@ -83,9 +82,6 @@ public void update(Params subtask) views.opentasksTaskDetailsSubtaskListRibbon.setBackgroundColor(subtask.color().argb()); - new ProgressBackgroundView(views.opentasksTaskDetailsSubtaskProgressBackground) - .update(subtask.percentComplete()); - views.getRoot().setOnClickListener((v) -> { Context ctx = v.getContext(); diff --git a/opentasks/src/main/java/org/dmfs/tasks/model/DefaultModel.java b/opentasks/src/main/java/org/dmfs/tasks/model/DefaultModel.java index 23cccf4e3..ffe7a7cb3 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/model/DefaultModel.java +++ b/opentasks/src/main/java/org/dmfs/tasks/model/DefaultModel.java @@ -170,9 +170,6 @@ public void inflate() addField(new FieldDescriptor(context, R.id.task_field_list_and_account_name, R.string.task_list, null, TaskFieldAdapters.LIST_AND_ACCOUNT_NAME) .setViewLayout(TEXT_VIEW_NO_LINKS).setIcon(R.drawable.ic_detail_list)); - setAllowRecurrence(false); - setAllowExceptions(false); - mInflated = true; } } diff --git a/opentasks/src/main/java/org/dmfs/tasks/model/MinimalModel.java b/opentasks/src/main/java/org/dmfs/tasks/model/MinimalModel.java index 417ec55c5..acf2c14fe 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/model/MinimalModel.java +++ b/opentasks/src/main/java/org/dmfs/tasks/model/MinimalModel.java @@ -62,9 +62,6 @@ public void inflate() addField(new FieldDescriptor(context, R.id.task_field_due, R.string.task_due, TaskFieldAdapters.DUE).setViewLayout(TIME_VIEW_ADD_BUTTON) .setEditorLayout(TIME_EDIT).setIcon(R.drawable.ic_detail_due)); - setAllowRecurrence(false); - setAllowExceptions(false); - mInflated = true; } } diff --git a/opentasks/src/main/java/org/dmfs/tasks/model/Model.java b/opentasks/src/main/java/org/dmfs/tasks/model/Model.java index 2899c85c2..8542523be 100644 --- a/opentasks/src/main/java/org/dmfs/tasks/model/Model.java +++ b/opentasks/src/main/java/org/dmfs/tasks/model/Model.java @@ -22,7 +22,6 @@ import android.content.ContentUris; import android.content.Context; import android.content.Intent; -import androidx.collection.SparseArrayCompat; import android.text.TextUtils; import org.dmfs.provider.tasks.AuthorityUtil; @@ -32,6 +31,8 @@ import java.util.ArrayList; import java.util.List; +import androidx.collection.SparseArrayCompat; + /** * An abstract model class. @@ -55,8 +56,6 @@ public abstract class Model boolean mInflated = false; - private boolean mAllowRecurrence = false; - private boolean mAllowExceptions = false; private int mIconId = -1; private int mLabelId = -1; private String mAccountType; @@ -112,31 +111,7 @@ public List getFields() // TODO Auto-generated catch block e.printStackTrace(); } - return new ArrayList(mFields); - } - - - public boolean getAllowRecurrence() - { - return mAllowRecurrence; - } - - - void setAllowRecurrence(boolean allowRecurrence) - { - mAllowRecurrence = allowRecurrence; - } - - - public boolean getAllowExceptions() - { - return mAllowExceptions; - } - - - void setAllowExceptions(boolean allowExceptions) - { - mAllowExceptions = allowExceptions; + return new ArrayList<>(mFields); } @@ -183,7 +158,7 @@ public void startInsertIntent(Activity activity, Account account) throw new IllegalStateException("Syncadapter for " + mAccountType + " does not support inserting lists."); } - activity.startActivity(getListIntent(mContext, Intent.ACTION_INSERT, account)); + activity.startActivity(getListIntent(Intent.ACTION_INSERT, account)); } @@ -194,7 +169,7 @@ public void startEditIntent(Activity activity, Account account, long listId, Str throw new IllegalStateException("Syncadapter for " + mAccountType + " does not support editing lists."); } - Intent intent = getListIntent(mContext, Intent.ACTION_EDIT, account); + Intent intent = getListIntent(Intent.ACTION_EDIT, account); intent.setData(ContentUris.withAppendedId(TaskLists.getContentUri(mAuthority), listId)); if (nameHint != null) { @@ -212,7 +187,7 @@ public boolean hasEditActivity() { if (mSupportsEditListIntent == null) { - ComponentName editComponent = getListIntent(mContext, Intent.ACTION_EDIT, null).setData( + ComponentName editComponent = getListIntent(Intent.ACTION_EDIT, null).setData( ContentUris.withAppendedId(TaskLists.getContentUri(mAuthority), 0 /* for pure intent resolution it doesn't matter which id we append */)) .resolveActivity(mContext.getPackageManager()); mSupportsEditListIntent = editComponent != null; @@ -226,7 +201,7 @@ public boolean hasInsertActivity() { if (mSupportsInsertListIntent == null) { - ComponentName insertComponent = getListIntent(mContext, Intent.ACTION_INSERT, null).resolveActivity(mContext.getPackageManager()); + ComponentName insertComponent = getListIntent(Intent.ACTION_INSERT, null).resolveActivity(mContext.getPackageManager()); mSupportsInsertListIntent = insertComponent != null; } @@ -234,7 +209,7 @@ public boolean hasInsertActivity() } - private Intent getListIntent(Context context, String action, Account account) + private Intent getListIntent(String action, Account account) { // insert action Intent insertIntent = new Intent();