diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TaskActivity.kt b/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TaskActivity.kt index 99de31c3a..51127c18f 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TaskActivity.kt +++ b/app/src/main/java/ca/pkay/rcloneexplorer/Activities/TaskActivity.kt @@ -49,6 +49,7 @@ class TaskActivity : AppCompatActivity(), FolderSelectorCallback{ private lateinit var fab: FloatingActionButton private lateinit var switchWifi: Switch + private lateinit var switchCharge: Switch private lateinit var switchMD5sum: Switch @@ -133,6 +134,7 @@ class TaskActivity : AppCompatActivity(), FolderSelectorCallback{ onSuccessDropdown = findViewById(R.id.task_onSuccess_spinner) fab = findViewById(R.id.saveButton) switchWifi = findViewById(R.id.task_wifionly) + switchCharge = findViewById(R.id.task_chargeonly) switchMD5sum = findViewById(R.id.task_md5sum) rcloneInstance = Rclone(this) @@ -174,6 +176,7 @@ class TaskActivity : AppCompatActivity(), FolderSelectorCallback{ findViewById(R.id.task_title_textfield).text = existingTask?.title switchWifi.isChecked = existingTask?.wifionly ?: false + switchCharge.isChecked = existingTask?.chargeonly ?: false switchMD5sum.isChecked = existingTask?.md5sum ?: false switchDeleteExcluded.isChecked = existingTask?.deleteExcluded ?: false prepareSyncDirectionDropdown() @@ -249,6 +252,7 @@ class TaskActivity : AppCompatActivity(), FolderSelectorCallback{ taskToPopulate.direction = direction taskToPopulate.wifionly = switchWifi.isChecked + taskToPopulate.chargeonly = switchCharge.isChecked taskToPopulate.md5sum = switchMD5sum.isChecked taskToPopulate.deleteExcluded = switchDeleteExcluded.isChecked taskToPopulate.filterId = if(filterDropdown.selectedItemPosition == 0 || filterDropdown.selectedItemPosition == -1) null else filterItems[filterDropdown.selectedItemPosition - 1].id diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Database/DatabaseHandler.kt b/app/src/main/java/ca/pkay/rcloneexplorer/Database/DatabaseHandler.kt index 90b747264..5771450ae 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/Database/DatabaseHandler.kt +++ b/app/src/main/java/ca/pkay/rcloneexplorer/Database/DatabaseHandler.kt @@ -5,12 +5,12 @@ import android.content.Context import android.database.Cursor import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper -import android.util.Log import ca.pkay.rcloneexplorer.Database.DatabaseInfo.Companion.DATABASE_NAME import ca.pkay.rcloneexplorer.Database.DatabaseInfo.Companion.DATABASE_VERSION import ca.pkay.rcloneexplorer.Database.DatabaseInfo.Companion.SQL_CREATE_TABLES_TASKS import ca.pkay.rcloneexplorer.Database.DatabaseInfo.Companion.SQL_CREATE_TABLE_FILTERS import ca.pkay.rcloneexplorer.Database.DatabaseInfo.Companion.SQL_CREATE_TABLE_TRIGGER +import ca.pkay.rcloneexplorer.Database.DatabaseInfo.Companion.SQL_UPDATE_TASK_ADD_CHARGE import ca.pkay.rcloneexplorer.Database.DatabaseInfo.Companion.SQL_UPDATE_TASK_ADD_DELETE_EXCLUDED import ca.pkay.rcloneexplorer.Database.DatabaseInfo.Companion.SQL_UPDATE_TASK_ADD_MD5 import ca.pkay.rcloneexplorer.Database.DatabaseInfo.Companion.SQL_UPDATE_TASK_ADD_WIFI @@ -37,6 +37,7 @@ class DatabaseHandler(context: Context?) : sqLiteDatabase.execSQL(SQL_UPDATE_TASK_ADD_DELETE_EXCLUDED) sqLiteDatabase.execSQL(SQL_UPDATE_TASK_ADD_FOLLOWUPS_FAIL) sqLiteDatabase.execSQL(SQL_UPDATE_TASK_ADD_FOLLOWUPS_SUCCESS) + sqLiteDatabase.execSQL(SQL_UPDATE_TASK_ADD_CHARGE) } override fun onUpgrade(sqLiteDatabase: SQLiteDatabase, oldVersion: Int, newVersion: Int) { @@ -59,6 +60,9 @@ class DatabaseHandler(context: Context?) : sqLiteDatabase.execSQL(SQL_UPDATE_TASK_ADD_FOLLOWUPS_FAIL) sqLiteDatabase.execSQL(SQL_UPDATE_TASK_ADD_FOLLOWUPS_SUCCESS) } + if (oldVersion < 7) { + sqLiteDatabase.execSQL(SQL_UPDATE_TASK_ADD_CHARGE) + } } val allTasks: List @@ -143,7 +147,8 @@ class DatabaseHandler(context: Context?) : Task.COLUMN_NAME_FILTER_ID, Task.COLUMN_NAME_DELETE_EXCLUDED, Task.COLUMN_NAME_ONFAIL_FOLLOWUP, - Task.COLUMN_NAME_ONSUCCESS_FOLLOWUP + Task.COLUMN_NAME_ONSUCCESS_FOLLOWUP, + Task.COLUMN_NAME_CHARGE_ONLY ) private fun taskFromCursor(cursor: Cursor): Task { @@ -160,6 +165,7 @@ class DatabaseHandler(context: Context?) : task.deleteExcluded = getBoolean(cursor, 10) task.onFailFollowup = cursor.getLong(11) task.onSuccessFollowup = cursor.getLong(12) + task.chargeonly = getBoolean(cursor, 13) return task } @@ -192,6 +198,7 @@ class DatabaseHandler(context: Context?) : values.put(Task.COLUMN_NAME_DELETE_EXCLUDED, task.deleteExcluded) values.put(Task.COLUMN_NAME_ONFAIL_FOLLOWUP, task.onFailFollowup) values.put(Task.COLUMN_NAME_ONSUCCESS_FOLLOWUP, task.onSuccessFollowup) + values.put(Task.COLUMN_NAME_CHARGE_ONLY, task.chargeonly) return values } diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Database/DatabaseInfo.kt b/app/src/main/java/ca/pkay/rcloneexplorer/Database/DatabaseInfo.kt index 031da9458..7008c01c7 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/Database/DatabaseInfo.kt +++ b/app/src/main/java/ca/pkay/rcloneexplorer/Database/DatabaseInfo.kt @@ -9,7 +9,7 @@ class DatabaseInfo { companion object { // If you change the database schema, you must increment the database version. - const val DATABASE_VERSION = 6 + const val DATABASE_VERSION = 7 const val DATABASE_NAME = "rcloneExplorer.db" @@ -43,6 +43,7 @@ class DatabaseInfo { val SQL_UPDATE_TASK_ADD_DELETE_EXCLUDED = "ALTER TABLE ${Task.TABLE_NAME} ADD COLUMN ${Task.COLUMN_NAME_DELETE_EXCLUDED} INTEGER" val SQL_UPDATE_TASK_ADD_FOLLOWUPS_FAIL = "ALTER TABLE ${Task.TABLE_NAME} ADD COLUMN ${Task.COLUMN_NAME_ONFAIL_FOLLOWUP} INTEGER" val SQL_UPDATE_TASK_ADD_FOLLOWUPS_SUCCESS = "ALTER TABLE ${Task.TABLE_NAME} ADD COLUMN ${Task.COLUMN_NAME_ONSUCCESS_FOLLOWUP} INTEGER" + val SQL_UPDATE_TASK_ADD_CHARGE = "ALTER TABLE ${Task.TABLE_NAME} ADD COLUMN ${Task.COLUMN_NAME_CHARGE_ONLY} INTEGER" } } \ No newline at end of file diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/Items/Task.kt b/app/src/main/java/ca/pkay/rcloneexplorer/Items/Task.kt index 3e7ee57be..25f363410 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/Items/Task.kt +++ b/app/src/main/java/ca/pkay/rcloneexplorer/Items/Task.kt @@ -1,11 +1,15 @@ package ca.pkay.rcloneexplorer.Items +import android.annotation.SuppressLint +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonNames import org.json.JSONObject +@OptIn(ExperimentalSerializationApi::class) +@SuppressLint("UnsafeOptInUsageError") @Serializable data class Task(var id: Long) { // Alternatives are kept for backwards compatibility with old, manual parser @@ -17,6 +21,7 @@ data class Task(var id: Long) { @JsonNames("syncDirection") var direction = 0 var md5sum = TASK_MD5SUM_DEFAULT var wifionly = TASK_WIFIONLY_DEFAULT + var chargeonly = TASK_CHARGEONLY_DEFAULT var filterId: Long? = null var deleteExcluded = false var onFailFollowup: Long? = null @@ -37,6 +42,7 @@ data class Task(var id: Long) { var COLUMN_NAME_SYNC_DIRECTION = "task_direction" var COLUMN_NAME_MD5SUM = "task_use_md5sum" var COLUMN_NAME_WIFI_ONLY = "task_use_only_wifi" + var COLUMN_NAME_CHARGE_ONLY = "task_use_only_charge" var COLUMN_NAME_FILTER_ID = "task_filter_id" var COLUMN_NAME_DELETE_EXCLUDED = "task_delete_excluded" var COLUMN_NAME_ONFAIL_FOLLOWUP = "task_onFailFollowupTask" @@ -44,6 +50,7 @@ data class Task(var id: Long) { const val TASK_MD5SUM_DEFAULT = false const val TASK_WIFIONLY_DEFAULT = false + const val TASK_CHARGEONLY_DEFAULT = false fun fromString(json: String): Task { return Json.decodeFromString(json) diff --git a/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncWorker.kt b/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncWorker.kt index 7056e7bae..a7c478b22 100644 --- a/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncWorker.kt +++ b/app/src/main/java/ca/pkay/rcloneexplorer/workmanager/SyncWorker.kt @@ -24,6 +24,7 @@ import ca.pkay.rcloneexplorer.notifications.ReportNotifications import ca.pkay.rcloneexplorer.notifications.SyncServiceNotifications import ca.pkay.rcloneexplorer.notifications.SyncServiceNotifications.Companion.GROUP_ID import ca.pkay.rcloneexplorer.notifications.support.StatusObject +import ca.pkay.rcloneexplorer.util.ChargeStateUtil import ca.pkay.rcloneexplorer.util.FLog import ca.pkay.rcloneexplorer.util.SyncLog import ca.pkay.rcloneexplorer.util.WifiConnectivitiyUtil @@ -56,7 +57,7 @@ class SyncWorker (private var mContext: Context, workerParams: WorkerParameters) internal enum class FAILURE_REASON { - NO_FAILURE, NO_UNMETERED, NO_CONNECTION, RCLONE_ERROR, CONNECTIVITY_CHANGED, CANCELLED, NO_TASK + NO_FAILURE, NO_UNMETERED, NO_CONNECTION, RCLONE_ERROR, CONNECTIVITY_CHANGED, CANCELLED, NO_TASK, NO_CHARGE } // Objects @@ -258,6 +259,9 @@ class SyncWorker (private var mContext: Context, workerParams: WorkerParameters) FAILURE_REASON.RCLONE_ERROR -> { content = mContext.getString(R.string.operation_failed_unknown_rclone_error, mTitle) } + FAILURE_REASON.NO_CHARGE -> { + content = mContext.getString(R.string.operation_failed_no_charge, mTitle) + } } followupTask(mTask.onFailFollowup) showFailNotification(notificationId, content) @@ -348,12 +352,17 @@ class SyncWorker (private var mContext: Context, workerParams: WorkerParameters) private fun arePreconditionsMet(): Boolean { val connection = WifiConnectivitiyUtil.dataConnection(this.applicationContext) + val isCharging = ChargeStateUtil.checkIsCharging(this.applicationContext) + if (mTask.wifionly && connection === WifiConnectivitiyUtil.Connection.METERED) { failureReason = FAILURE_REASON.NO_UNMETERED return false } else if (connection === WifiConnectivitiyUtil.Connection.DISCONNECTED || connection === WifiConnectivitiyUtil.Connection.NOT_AVAILABLE) { failureReason = FAILURE_REASON.NO_CONNECTION return false + } else if (mTask.chargeonly && !isCharging) { + failureReason = FAILURE_REASON.NO_CHARGE + return false } return true diff --git a/app/src/main/res/layout/content_task.xml b/app/src/main/res/layout/content_task.xml index eb5de6841..b6cf58bbe 100644 --- a/app/src/main/res/layout/content_task.xml +++ b/app/src/main/res/layout/content_task.xml @@ -84,6 +84,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/task_hint_wifi_ignore_defaults" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 685fb6fe1..cf8cea5a0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -132,6 +132,7 @@ Dark theme Transfer files only over Wi-Fi Require Wi-Fi connection for file transfers + Transfer files only when charging Use Proxy Use proxy for all supported data transfers (HTTP, HTTPS) Proxy Protocol @@ -164,6 +165,7 @@ Sync failed %1$s could not be synchronized due to an unknown error Couldn\'t sync %1$s because of no Wi-Fi connection + Couldn\'t sync %1$s because device is not charging Couldn\'t sync %1$s because network was unavailable Couldn\'t sync %1$s due to mobile data sync being disabled on your device %1$s could not be synchronized due to an unknown rclone-error