From 13bb2ea8ae47f83b67ce67becc76df2c98a71a07 Mon Sep 17 00:00:00 2001 From: Fabian No ID Date: Fri, 17 Nov 2023 21:46:13 +0100 Subject: [PATCH 1/2] Functionality and option in Triggers' Task-dropdown to run all Tasks with only one Trigger. The Tasks will run one by one in alphabetical order. --- .../Activities/TriggerActivity.kt | 14 +++++++-- .../TriggerRecyclerViewAdapter.java | 9 ++++-- .../rcloneexplorer/workmanager/SyncManager.kt | 29 +++++++++++++++++-- app/src/main/res/values/strings.xml | 2 ++ 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TriggerActivity.kt b/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TriggerActivity.kt index 25fc1713..4d0442ca 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TriggerActivity.kt +++ b/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TriggerActivity.kt @@ -29,6 +29,7 @@ class TriggerActivity : AppCompatActivity() { companion object { const val ID_EXTRA = "TRIGGER_EDIT_ID" + const val ID_ALL_TASKS = -1000L } private lateinit var mTrigger: Trigger @@ -195,7 +196,11 @@ class TriggerActivity : AppCompatActivity() { mTargetDropdown.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View, pos: Int, id: Long) { - mTrigger.triggerTarget = mTaskList[pos].id + if (pos == (mTaskList.size)) { + mTrigger.triggerTarget = ID_ALL_TASKS + } else { + mTrigger.triggerTarget = mTaskList[pos].id + } } override fun onNothingSelected(parent: AdapterView<*>?) {} @@ -213,10 +218,11 @@ class TriggerActivity : AppCompatActivity() { * Set up Task-Target Dropdown */ private fun setUpTargetsDropdown() { - val items = arrayOfNulls(this.mTaskList.size) + val items = arrayOfNulls(if (this.mTaskList.size == 0) 0 else (this.mTaskList.size + 1)) for (i in this.mTaskList.indices) { items[i] = this.mTaskList[i].title } + if (this.mTaskList.size > 0) items[this.mTaskList.size] = this.resources.getString(R.string.sync_option_all_tasks_asc) val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, items) mTargetDropdown.adapter = adapter } @@ -258,7 +264,9 @@ class TriggerActivity : AppCompatActivity() { //Todo properly populate the fields for (task in mTaskList) { - if (task.id == mTrigger.triggerTarget) { + if (mTrigger.triggerTarget == ID_ALL_TASKS) { + mTargetDropdown.setSelection(mTaskList.size) + } else if (task.id == mTrigger.triggerTarget) { mTargetDropdown.setSelection(mTaskList.indexOf(task)) } } diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/RecyclerViewAdapters/TriggerRecyclerViewAdapter.java b/app/src/main/java/ca/pkay/rcloneexplorer/RecyclerViewAdapters/TriggerRecyclerViewAdapter.java index 09d558f3..be0579f2 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/RecyclerViewAdapters/TriggerRecyclerViewAdapter.java +++ b/app/src/main/java/ca/pkay/rcloneexplorer/RecyclerViewAdapters/TriggerRecyclerViewAdapter.java @@ -1,6 +1,7 @@ package ca.pkay.rcloneexplorer.RecyclerViewAdapters; +import static ca.pkay.rcloneexplorer.Activities.TriggerActivity.ID_ALL_TASKS; import static ca.pkay.rcloneexplorer.Items.Trigger.TRIGGER_DAY_FRI; import static ca.pkay.rcloneexplorer.Items.Trigger.TRIGGER_DAY_MON; import static ca.pkay.rcloneexplorer.Items.Trigger.TRIGGER_DAY_SAT; @@ -82,9 +83,13 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) { final Trigger selectedTrigger = triggers.get(position); - Task task = (new DatabaseHandler(context)).getTask(selectedTrigger.getTriggerTarget()); String targetTaskTitle = "ERR: NOTFOUND"; - if(task != null){ targetTaskTitle = task.getTitle(); } + if (selectedTrigger.getTriggerTarget() == ID_ALL_TASKS) { + targetTaskTitle = context.getResources().getString(R.string.sync_title_all_tasks_asc); + } else { + Task task = (new DatabaseHandler(context)).getTask(selectedTrigger.getTriggerTarget()); + if (task != null) { targetTaskTitle = task.getTitle(); } + } switch (holder.getItemViewType()) { case VIEW_TYPE_SCHEDULE: diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncManager.kt b/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncManager.kt index 39dc6466..455a3924 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncManager.kt +++ b/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncManager.kt @@ -3,18 +3,27 @@ package ca.pkay.rcloneexplorer.workmanager import android.content.Context import android.util.Log import androidx.work.Data +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkRequest +import ca.pkay.rcloneexplorer.Activities.TriggerActivity.Companion.ID_ALL_TASKS +import ca.pkay.rcloneexplorer.Database.DatabaseHandler import ca.pkay.rcloneexplorer.Items.Task import ca.pkay.rcloneexplorer.Items.Trigger import java.util.Random class SyncManager(private var mContext: Context) { + private var mDatabase = DatabaseHandler(mContext) fun queue(trigger: Trigger) { - queue(trigger.triggerTarget) + if (trigger.triggerTarget == ID_ALL_TASKS) { + queueAllTasks() + } else { + queue(trigger.triggerTarget) + } } fun queue(task: Task) { @@ -22,6 +31,18 @@ class SyncManager(private var mContext: Context) { } fun queue(taskID: Long) { + work(getOneTimeWorkRequest(taskID)) + } + + private fun queueAllTasks() { + val mTaskList = mDatabase.allTasks + mTaskList.sortedBy { it.title } + for (i in mTaskList.indices) { + workOneByOne(getOneTimeWorkRequest(mTaskList[i].id)) + } + } + + private fun getOneTimeWorkRequest(taskID: Long): OneTimeWorkRequest { val uploadWorkRequest = OneTimeWorkRequestBuilder() val data = Data.Builder() @@ -29,7 +50,7 @@ class SyncManager(private var mContext: Context) { uploadWorkRequest.setInputData(data.build()) uploadWorkRequest.addTag(taskID.toString()) - work(uploadWorkRequest.build()) + return uploadWorkRequest.build() } fun queueEphemeral(task: Task) { @@ -50,6 +71,10 @@ class SyncManager(private var mContext: Context) { .enqueue(request) } + private fun workOneByOne(request: OneTimeWorkRequest) { + WorkManager.getInstance(mContext).enqueueUniqueWork("all_tasks_one_by_one", ExistingWorkPolicy.APPEND_OR_REPLACE, request) + } + fun cancel() { WorkManager.getInstance(mContext) .cancelAllWork() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5b8a020d..177fbf77 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -484,6 +484,8 @@ Copy new or modified files from local storage to remote storage. No files are deleted, existing files on the remote may be overwritten. Copy new or modified files from remote storage to local storage. No files are deleted, existing files on the local storage may be overwritten. Make sure local and remote storage have the same files by transferring, downloading, and deleting files as needed to maintain an identical folder structure. + All tasks (in alphabetical order) + All tasks Success! You are now ready to start! File access permission not granted From e0c252f1f5956361537d96350cbe754e6a904b2a Mon Sep 17 00:00:00 2001 From: Fabian No ID Date: Sat, 18 Nov 2023 12:01:27 +0100 Subject: [PATCH 2/2] Automatically group tasks by their titles' first character in Triggers' Task-dropdown to provide an option to run all Tasks starting with the specific character. (First character can be any unicode character.) This allows simply grouping Tasks in order to be executed with only one Trigger. --- .../Activities/TriggerActivity.kt | 23 ++++++++++++++++++- .../TriggerRecyclerViewAdapter.java | 4 ++++ .../rcloneexplorer/workmanager/SyncManager.kt | 17 ++++++++++---- app/src/main/res/values/strings.xml | 3 +++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TriggerActivity.kt b/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TriggerActivity.kt index 4d0442ca..acedf616 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TriggerActivity.kt +++ b/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TriggerActivity.kt @@ -30,11 +30,13 @@ class TriggerActivity : AppCompatActivity() { companion object { const val ID_EXTRA = "TRIGGER_EDIT_ID" const val ID_ALL_TASKS = -1000L + const val UNICODE_CHAR_RANGE = -10_000_000L } private lateinit var mTrigger: Trigger private lateinit var dbHandler: DatabaseHandler private var mTaskList: List = ArrayList() + private var firstCharOfTasksUnique: List = ArrayList() private lateinit var mCardInterval: CardView private lateinit var mCardWeekday: CardView @@ -88,6 +90,14 @@ class TriggerActivity : AppCompatActivity() { dbHandler = DatabaseHandler(this) mTaskList = dbHandler.allTasks + if (this.mTaskList.isNotEmpty()) { + val firstCharsOfTaskTitles : HashSet = HashSet() + for (i in this.mTaskList.indices) { + firstCharsOfTaskTitles.add(this.mTaskList[i].title.trim().uppercase().first()) + } + firstCharOfTasksUnique = firstCharsOfTaskTitles.toList().sortedBy { it.toString() } + } + val extras = intent.extras val triggerId: Long if (extras != null) { @@ -198,6 +208,9 @@ class TriggerActivity : AppCompatActivity() { override fun onItemSelected(parent: AdapterView<*>?, view: View, pos: Int, id: Long) { if (pos == (mTaskList.size)) { mTrigger.triggerTarget = ID_ALL_TASKS + } else if (pos > (mTaskList.size)) { + val charForGrouping = firstCharOfTasksUnique[pos - mTaskList.size - 1] + mTrigger.triggerTarget = UNICODE_CHAR_RANGE - charForGrouping.code.toLong() } else { mTrigger.triggerTarget = mTaskList[pos].id } @@ -218,11 +231,14 @@ class TriggerActivity : AppCompatActivity() { * Set up Task-Target Dropdown */ private fun setUpTargetsDropdown() { - val items = arrayOfNulls(if (this.mTaskList.size == 0) 0 else (this.mTaskList.size + 1)) + val items = arrayOfNulls(if (this.mTaskList.size == 0) 0 else (this.mTaskList.size + firstCharOfTasksUnique.size + 1)) for (i in this.mTaskList.indices) { items[i] = this.mTaskList[i].title } if (this.mTaskList.size > 0) items[this.mTaskList.size] = this.resources.getString(R.string.sync_option_all_tasks_asc) + for (i in firstCharOfTasksUnique.indices) { + items[i + this.mTaskList.size + 1] = this.resources.getString(R.string.sync_option_all) + " " + firstCharOfTasksUnique[i] + this.resources.getString(R.string.sync_option_all_tasks_prefixed_asc) + } val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, items) mTargetDropdown.adapter = adapter } @@ -268,6 +284,11 @@ class TriggerActivity : AppCompatActivity() { mTargetDropdown.setSelection(mTaskList.size) } else if (task.id == mTrigger.triggerTarget) { mTargetDropdown.setSelection(mTaskList.indexOf(task)) + } else if (UNICODE_CHAR_RANGE - mTrigger.triggerTarget >= 0) { + val firstChar = (UNICODE_CHAR_RANGE - mTrigger.triggerTarget).toInt().toChar() + if (firstCharOfTasksUnique.contains(firstChar)) { + mTargetDropdown.setSelection(mTaskList.size + firstCharOfTasksUnique.indexOf(firstChar) + 1) + } } } } diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/RecyclerViewAdapters/TriggerRecyclerViewAdapter.java b/app/src/main/java/ca/pkay/rcloneexplorer/RecyclerViewAdapters/TriggerRecyclerViewAdapter.java index be0579f2..acb07045 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/RecyclerViewAdapters/TriggerRecyclerViewAdapter.java +++ b/app/src/main/java/ca/pkay/rcloneexplorer/RecyclerViewAdapters/TriggerRecyclerViewAdapter.java @@ -2,6 +2,7 @@ import static ca.pkay.rcloneexplorer.Activities.TriggerActivity.ID_ALL_TASKS; +import static ca.pkay.rcloneexplorer.Activities.TriggerActivity.UNICODE_CHAR_RANGE; import static ca.pkay.rcloneexplorer.Items.Trigger.TRIGGER_DAY_FRI; import static ca.pkay.rcloneexplorer.Items.Trigger.TRIGGER_DAY_MON; import static ca.pkay.rcloneexplorer.Items.Trigger.TRIGGER_DAY_SAT; @@ -86,6 +87,9 @@ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, fina String targetTaskTitle = "ERR: NOTFOUND"; if (selectedTrigger.getTriggerTarget() == ID_ALL_TASKS) { targetTaskTitle = context.getResources().getString(R.string.sync_title_all_tasks_asc); + } else if (UNICODE_CHAR_RANGE - selectedTrigger.getTriggerTarget() >= 0) { + String firstCharOfTaskName = new String(Character.toChars((int) (UNICODE_CHAR_RANGE - selectedTrigger.getTriggerTarget()))); + targetTaskTitle = firstCharOfTaskName + context.getResources().getString(R.string.sync_title_all_tasks_prefixed_asc); } else { Task task = (new DatabaseHandler(context)).getTask(selectedTrigger.getTriggerTarget()); if (task != null) { targetTaskTitle = task.getTitle(); } diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncManager.kt b/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncManager.kt index 455a3924..eb86474f 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncManager.kt +++ b/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncManager.kt @@ -8,6 +8,7 @@ import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkRequest +import ca.pkay.rcloneexplorer.Activities.TriggerActivity import ca.pkay.rcloneexplorer.Activities.TriggerActivity.Companion.ID_ALL_TASKS import ca.pkay.rcloneexplorer.Database.DatabaseHandler import ca.pkay.rcloneexplorer.Items.Task @@ -20,8 +21,10 @@ class SyncManager(private var mContext: Context) { fun queue(trigger: Trigger) { if (trigger.triggerTarget == ID_ALL_TASKS) { - queueAllTasks() - } else { + queueAllTasks(null) + } else if (TriggerActivity.UNICODE_CHAR_RANGE - trigger.triggerTarget >= 0) { + queueAllTasks((TriggerActivity.UNICODE_CHAR_RANGE - trigger.triggerTarget).toInt().toChar().toString()) + } else{ queue(trigger.triggerTarget) } } @@ -34,11 +37,17 @@ class SyncManager(private var mContext: Context) { work(getOneTimeWorkRequest(taskID)) } - private fun queueAllTasks() { + private fun queueAllTasks(prefixFilter: String?) { val mTaskList = mDatabase.allTasks mTaskList.sortedBy { it.title } for (i in mTaskList.indices) { - workOneByOne(getOneTimeWorkRequest(mTaskList[i].id)) + if (prefixFilter == null) { + workOneByOne(getOneTimeWorkRequest(mTaskList[i].id)) + } else { + if (mTaskList[i].title.trim().uppercase().startsWith(prefixFilter)) { + workOneByOne(getOneTimeWorkRequest(mTaskList[i].id)) + } + } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 177fbf77..b7430f0b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -486,6 +486,9 @@ Make sure local and remote storage have the same files by transferring, downloading, and deleting files as needed to maintain an identical folder structure. All tasks (in alphabetical order) All tasks + All + … named tasks + … tasks Success! You are now ready to start! File access permission not granted