From 447a05ee58d0ce9668b3a327e2d040bc2a61bc49 Mon Sep 17 00:00:00 2001
From: Shane <6071159+smashedr@users.noreply.github.com>
Date: Fri, 15 Aug 2025 23:55:50 -0700
Subject: [PATCH 1/5] Update Layouts and URL Parsing
---
.../djangofiles/djangofiles/MainActivity.kt | 38 +--
.../djangofiles/ui/login/LoginFragment.kt | 72 ++++-
.../main/res/layout/fragment_authorize.xml | 235 ++++++++--------
app/src/main/res/layout/fragment_login.xml | 109 +++++---
.../main/res/layout/fragment_login_two.xml | 254 +++++++++---------
app/src/main/res/layout/nav_header_main.xml | 3 +-
6 files changed, 388 insertions(+), 323 deletions(-)
diff --git a/app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt b/app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt
index aa0d30f..bc61edf 100644
--- a/app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt
+++ b/app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt
@@ -58,8 +58,8 @@ import com.djangofiles.djangofiles.work.DailyWorker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import java.io.File
-import java.net.URL
import java.util.UUID
import java.util.concurrent.TimeUnit
@@ -202,12 +202,14 @@ class MainActivity : AppCompatActivity() {
window.setNavigationBarContrastEnforced(false)
}
- // Set Nav Header Top Padding
+ // Update Header Padding
val headerView = binding.navView.getHeaderView(0)
- ViewCompat.setOnApplyWindowInsetsListener(headerView) { v, insets ->
- val bars = insets.getInsets(WindowInsetsCompat.Type.statusBars())
- Log.d("ViewCompat", "top: ${bars.top}")
- v.updatePadding(top = bars.top)
+ ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
+ val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ Log.d("ViewCompat", "binding.root: top: ${bars.top}")
+ if (bars.top > 0) {
+ headerView.updatePadding(top = bars.top)
+ }
insets
}
@@ -418,8 +420,8 @@ class MainActivity : AppCompatActivity() {
Log.d("onNewIntent", "SEND TEXT DETECTED")
//if (extraText.lowercase().startsWith("http")) {
//if (Patterns.WEB_URL.matcher(extraText).matches()) {
- if (isURL(extraText)) {
- Log.d("onNewIntent", "URL DETECTED: $extraText")
+ if (isTextUrl(extraText)) {
+ Log.i("onNewIntent", "URL DETECTED: $extraText")
val bundle = Bundle().apply { putString("url", extraText) }
navController.navigate(
R.id.nav_item_short, bundle, NavOptions.Builder()
@@ -662,6 +664,15 @@ class MainActivity : AppCompatActivity() {
}
}
+ private fun isTextUrl(input: String): Boolean {
+ val url = input.toHttpUrlOrNull() ?: return false
+ if (input != url.toString()) return false
+ if (url.scheme !in listOf("http", "https")) return false
+ if (url.host.isBlank()) return false
+ if (url.toString().length > 2048) return false
+ return true
+ }
+
fun setDrawerLockMode(enabled: Boolean) {
Log.d("setDrawerLockMode", "enabled: $enabled")
val lockMode =
@@ -709,14 +720,3 @@ fun copyToClipboard(context: Context, text: String, msg: String? = null) {
clipboard.setPrimaryClip(clip)
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
-
-fun isURL(url: String): Boolean {
- return try {
- URL(url)
- Log.d("isURL", "TRUE")
- true
- } catch (_: Exception) {
- Log.d("isURL", "FALSE")
- false
- }
-}
diff --git a/app/src/main/java/com/djangofiles/djangofiles/ui/login/LoginFragment.kt b/app/src/main/java/com/djangofiles/djangofiles/ui/login/LoginFragment.kt
index 5d8b475..7044cf9 100644
--- a/app/src/main/java/com/djangofiles/djangofiles/ui/login/LoginFragment.kt
+++ b/app/src/main/java/com/djangofiles/djangofiles/ui/login/LoginFragment.kt
@@ -18,11 +18,11 @@ import androidx.navigation.fragment.findNavController
import com.djangofiles.djangofiles.R
import com.djangofiles.djangofiles.ServerApi
import com.djangofiles.djangofiles.databinding.FragmentLoginBinding
-import com.djangofiles.djangofiles.isURL
import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
class LoginFragment : Fragment() {
@@ -92,7 +92,10 @@ class LoginFragment : Fragment() {
binding.hostnameText.setSelection(binding.hostnameText.text.length)
}
Log.d("loginFunction", "host: $host")
- if (!isURL(host)) {
+ // TODO: Look into the usage of (host.toHttpUrlOrNull() == null) here.
+ // NOTE: This seems to be less restrictive so should work and can be improved...
+ //if (!isURL(host)) {
+ if (host.toHttpUrlOrNull() == null) {
binding.hostnameText.error = "Invalid Hostname"
return@OnClickListener
}
@@ -190,16 +193,63 @@ class LoginFragment : Fragment() {
}
private fun parseHost(urlString: String): String {
- var url = urlString.trim()
- if (url.isEmpty()) {
+ try {
+ var url = urlString.trim()
+ if (url.isEmpty()) {
+ return ""
+ }
+ if (!url.lowercase().startsWith("http")) {
+ url = "https://$url"
+ }
+ if (url.toHttpUrlOrNull() == null) {
+ return url
+ }
+ val uri = url.toUri()
+ Log.d("parseHost", "uri: $uri")
+ Log.d("parseHost", "uri.scheme: ${uri.scheme}")
+ if (uri.scheme.isNullOrEmpty()) {
+ return "https://"
+ }
+ Log.d("parseHost", "uri.host: ${uri.host}")
+ if (uri.host.isNullOrEmpty()) {
+ return "${uri.scheme}://"
+ }
+ Log.d("parseHost", "uri.path: ${uri.path}")
+ val result = "${uri.scheme}://${uri.host}${uri.path}"
+ Log.i("parseHost", "result: $result")
+ return if (result.endsWith("/")) {
+ result.dropLast(1)
+ } else {
+ result
+ }
+ } catch (e: Throwable) {
+ Log.d("parseHost", "Exception: $e")
return ""
}
- if (!url.lowercase().startsWith("http")) {
- url = "https://$url"
- }
- if (url.endsWith("/")) {
- url = url.substring(0, url.length - 1)
- }
- return url
}
+
+ //private fun parseHost(urlString: String): String {
+ // var url = urlString.trim()
+ // if (url.isEmpty()) {
+ // return ""
+ // }
+ // if (!url.lowercase().startsWith("http")) {
+ // url = "https://$url"
+ // }
+ // if (url.endsWith("/")) {
+ // url = url.substring(0, url.length - 1)
+ // }
+ // return url
+ //}
+
+ //private fun isURL(url: String): Boolean {
+ // return try {
+ // URL(url)
+ // Log.d("isURL", "TRUE")
+ // true
+ // } catch (_: Exception) {
+ // Log.d("isURL", "FALSE")
+ // false
+ // }
+ //}
}
diff --git a/app/src/main/res/layout/fragment_authorize.xml b/app/src/main/res/layout/fragment_authorize.xml
index ae1b64d..01c32ca 100644
--- a/app/src/main/res/layout/fragment_authorize.xml
+++ b/app/src/main/res/layout/fragment_authorize.xml
@@ -4,143 +4,138 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fillViewport="true">
+ android:fillViewport="true"
+ tools:ignore="HardcodedText">
+
+
-
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:padding="24dp"
+ tools:ignore="UselessParent">
+
+
+
+
+
+
-
-
-
+ android:visibility="visible"
+ tools:visibility="visible">
-
-
+ android:indeterminate="true"
+ app:layout_constraintTop_toTopOf="@+id/refresh_layout"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
-
+
-
+
+
-
+ android:layout_height="48dp"
+ android:paddingStart="48dp"
+ android:textAppearance="@style/TextAppearance.AppCompat.Medium"
+ android:text="Add Server"
+ android:drawableEnd="@drawable/md_login_24px"
+ android:visibility="gone"
+ tools:visibility="visible"
+ tools:ignore="RtlSymmetry" />
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml
index e4f1a28..799ae49 100644
--- a/app/src/main/res/layout/fragment_login.xml
+++ b/app/src/main/res/layout/fragment_login.xml
@@ -4,41 +4,54 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fillViewport="true">
+ android:fillViewport="true"
+ tools:ignore="HardcodedText">
+
+
-
-
-
-
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:padding="24dp"
+ tools:ignore="UselessParent">
-
+
+
+
+
-
+
+
-
+
+
-
+
-
+
+
diff --git a/app/src/main/res/layout/fragment_login_two.xml b/app/src/main/res/layout/fragment_login_two.xml
index 644b676..69b931f 100644
--- a/app/src/main/res/layout/fragment_login_two.xml
+++ b/app/src/main/res/layout/fragment_login_two.xml
@@ -4,154 +4,148 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fillViewport="true">
+ android:fillViewport="true"
+ tools:ignore="HardcodedText">
+
+
-
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:padding="24dp"
+ tools:ignore="UselessParent">
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
-
+ android:padding="10dp"
+ android:autofillHints="password"
+ android:hint="Password"
+ android:inputType="textPassword"
+ android:drawableEnd="@drawable/md_key_24"
+ android:drawableTint="?android:attr/textColorTertiary" />
+ android:text="Local Login"
+ android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
+
-
+
+
+
+
-
+
+
diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml
index 5cd14f3..67f7b39 100644
--- a/app/src/main/res/layout/nav_header_main.xml
+++ b/app/src/main/res/layout/nav_header_main.xml
@@ -6,7 +6,8 @@
android:layout_height="wrap_content"
android:background="@drawable/nav_header_bg"
android:orientation="horizontal"
- tools:ignore="RtlSymmetry">
+ tools:ignore="RtlSymmetry"
+ tools:paddingTop="32dp">
Date: Sat, 16 Aug 2025 18:01:30 -0700
Subject: [PATCH 2/5] Update Dialog Layouts
---
app/src/main/res/layout/dialog_app_info.xml | 37 ++++++++----
app/src/main/res/layout/dialog_feedback.xml | 63 ++++++++++++++-------
2 files changed, 66 insertions(+), 34 deletions(-)
diff --git a/app/src/main/res/layout/dialog_app_info.xml b/app/src/main/res/layout/dialog_app_info.xml
index e1036dd..846e265 100644
--- a/app/src/main/res/layout/dialog_app_info.xml
+++ b/app/src/main/res/layout/dialog_app_info.xml
@@ -1,15 +1,25 @@
-
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_feedback.xml b/app/src/main/res/layout/dialog_feedback.xml
index 52f1c72..9025343 100644
--- a/app/src/main/res/layout/dialog_feedback.xml
+++ b/app/src/main/res/layout/dialog_feedback.xml
@@ -1,37 +1,54 @@
-
+
+
+ android:layout_marginBottom="12dp"
+ android:textAppearance="?attr/textAppearanceHeadline5"
+ android:text="Send Feedback" />
-
+ android:layout_marginBottom="12dp"
+ android:clipChildren="false">
+
+
+
+
From 85caa8266c59d5b7168fa9cbb62fd1b05abd734e Mon Sep 17 00:00:00 2001
From: Shane <6071159+smashedr@users.noreply.github.com>
Date: Sat, 16 Aug 2025 18:38:03 -0700
Subject: [PATCH 3/5] Update Work Manager
---
.../djangofiles/djangofiles/MainActivity.kt | 25 ++---
.../ui/settings/SettingsFragment.kt | 101 ++++++++++++------
.../djangofiles/work/DailyWorkManager.kt | 56 ++++++++++
.../djangofiles/work/WorkConstants.kt | 11 --
app/src/main/res/values/strings.xml | 30 ++++--
app/src/main/res/xml/preferences.xml | 13 ++-
6 files changed, 162 insertions(+), 74 deletions(-)
create mode 100644 app/src/main/java/com/djangofiles/djangofiles/work/DailyWorkManager.kt
delete mode 100644 app/src/main/java/com/djangofiles/djangofiles/work/WorkConstants.kt
diff --git a/app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt b/app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt
index bc61edf..d5ffc18 100644
--- a/app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt
+++ b/app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt
@@ -45,7 +45,6 @@ import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager
import androidx.work.ExistingPeriodicWorkPolicy
-import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import com.djangofiles.djangofiles.databinding.ActivityMainBinding
import com.djangofiles.djangofiles.db.Server
@@ -53,15 +52,13 @@ import com.djangofiles.djangofiles.db.ServerDao
import com.djangofiles.djangofiles.db.ServerDatabase
import com.djangofiles.djangofiles.ui.home.HomeViewModel
import com.djangofiles.djangofiles.widget.WidgetProvider
-import com.djangofiles.djangofiles.work.DAILY_WORKER_CONSTRAINTS
-import com.djangofiles.djangofiles.work.DailyWorker
+import com.djangofiles.djangofiles.work.enqueueWorkRequest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import java.io.File
import java.util.UUID
-import java.util.concurrent.TimeUnit
class MainActivity : AppCompatActivity() {
@@ -214,30 +211,22 @@ class MainActivity : AppCompatActivity() {
}
// Update Header Text
- val packageInfo = packageManager.getPackageInfo(this.packageName, 0)
+ val packageInfo = packageManager.getPackageInfo(packageName, 0)
val versionName = packageInfo.versionName
Log.d("Main[onCreate]", "versionName: $versionName")
val versionTextView = headerView.findViewById(R.id.header_version)
versionTextView.text = "v${versionName}"
- // TODO: Improve initialization of the WorkRequest
+ // Work Manager
val workInterval = preferences.getString("work_interval", null) ?: "0"
- Log.i("Main[onCreate]", "workInterval: $workInterval")
+ Log.d("Main[onCreate]", "workInterval: $workInterval")
+ // NOTE: This just ensures work manager is enabled or disabled based on preference
if (workInterval != "0") {
- val workRequest =
- PeriodicWorkRequestBuilder(workInterval.toLong(), TimeUnit.MINUTES)
- .setConstraints(DAILY_WORKER_CONSTRAINTS)
- .build()
- Log.i("Main[onCreate]", "workRequest: $workRequest")
- WorkManager.getInstance(this).enqueueUniquePeriodicWork(
- "daily_worker",
- ExistingPeriodicWorkPolicy.KEEP,
- workRequest
- )
+ enqueueWorkRequest(workInterval, ExistingPeriodicWorkPolicy.KEEP)
} else {
// TODO: Confirm this is necessary...
Log.i("Main[onCreate]", "Ensuring Work is Disabled")
- WorkManager.getInstance(this).cancelUniqueWork("app_worker")
+ WorkManager.getInstance(this).cancelUniqueWork("daily_worker")
}
// Handle Custom Navigation Items
diff --git a/app/src/main/java/com/djangofiles/djangofiles/ui/settings/SettingsFragment.kt b/app/src/main/java/com/djangofiles/djangofiles/ui/settings/SettingsFragment.kt
index e453511..89cd466 100644
--- a/app/src/main/java/com/djangofiles/djangofiles/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/djangofiles/djangofiles/ui/settings/SettingsFragment.kt
@@ -29,16 +29,13 @@ import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SeekBarPreference
import androidx.preference.SwitchPreferenceCompat
-import androidx.work.ExistingPeriodicWorkPolicy
-import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import com.djangofiles.djangofiles.R
import com.djangofiles.djangofiles.api.FeedbackApi
import com.djangofiles.djangofiles.db.Server
import com.djangofiles.djangofiles.db.ServerDao
import com.djangofiles.djangofiles.db.ServerDatabase
-import com.djangofiles.djangofiles.work.DAILY_WORKER_CONSTRAINTS
-import com.djangofiles.djangofiles.work.DailyWorker
+import com.djangofiles.djangofiles.work.enqueueWorkRequest
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.firebase.Firebase
@@ -47,7 +44,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import java.util.concurrent.TimeUnit
class SettingsFragment : PreferenceFragmentCompat() {
@@ -115,12 +111,25 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
- // Background Update Interval
+ // Update Interval
val workInterval = findPreference("work_interval")
+ updateWorkIntervalSettings(workInterval?.value)
workInterval?.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance()
workInterval?.setOnPreferenceChangeListener { _, newValue ->
- Log.d("work_interval", "newValue: $newValue")
- ctx.updateWorkManager(workInterval, newValue)
+ Log.d("work_interval", "newValue: ${newValue as String}")
+ updateWorkIntervalSettings(newValue)
+ ctx.updateWorkManager(newValue, workInterval.value)
+ }
+
+ // Updates on Metered Connection
+ val workMeteredPref = findPreference("work_metered")
+ workMeteredPref?.setOnPreferenceChangeListener { _, newValue ->
+ Log.d("work_metered", "newValue: $newValue")
+ val result = newValue as Boolean
+ Log.d("work_metered", "result: $result")
+ workMeteredPref.isChecked = result
+ ctx.enqueueWorkRequest()
+ false
}
// Background Restriction
@@ -248,40 +257,70 @@ class SettingsFragment : PreferenceFragmentCompat() {
buildServerList()
}
- fun Context.updateWorkManager(listPref: ListPreference, newValue: Any): Boolean {
- Log.d("updateWorkManager", "listPref: ${listPref.value} - newValue: $newValue")
- val value = newValue as? String
- Log.d("updateWorkManager", "String value: $value")
- if (value.isNullOrEmpty()) {
- Log.w("updateWorkManager", "NULL OR EMPTY - false")
+ private fun updateWorkIntervalSettings(selectedValue: String?) {
+ Log.d("updateWorkIntervalSettings", "selectedValue: $selectedValue")
+ if (selectedValue != null) {
+ val enabled = selectedValue != "0"
+ Log.d("updateWorkIntervalSettings", "enabled: $enabled")
+ findPreference("work_metered")?.isEnabled = enabled
+ }
+ }
+
+ private fun Context.updateWorkManager(newValue: String?, curValue: String? = null): Boolean {
+ Log.i("updateWorkManager", "newValue: $newValue - curValue: $curValue")
+ if (newValue.isNullOrEmpty()) {
+ Log.w("updateWorkManager", "newValue.isNullOrEmpty() - false")
return false
- } else if (listPref.value == value) {
- Log.i("updateWorkManager", "NO CHANGE - false")
+ } else if (curValue == newValue) {
+ Log.i("updateWorkManager", "curValue == newValue - false")
return false
} else {
- Log.i("updateWorkManager", "RESCHEDULING WORK - true")
- val interval = value.toLongOrNull()
- Log.i("updateWorkManager", "interval: $interval")
- if (interval == null || interval == 0L) {
- Log.i("updateWorkManager", "DISABLING WORK")
+ Log.d("updateWorkManager", "ELSE - RESCHEDULING WORK - true")
+ if (newValue == "0" || newValue.toLongOrNull() == null) {
+ Log.i("updateWorkManager", "DISABLING WORK - newValue is 0 or null")
WorkManager.getInstance(this).cancelUniqueWork("daily_worker")
return true
} else {
- val newRequest =
- PeriodicWorkRequestBuilder(interval, TimeUnit.MINUTES)
- .setInitialDelay(1, TimeUnit.MINUTES)
- .setConstraints(DAILY_WORKER_CONSTRAINTS)
- .build()
- WorkManager.getInstance(this).enqueueUniquePeriodicWork(
- "daily_worker",
- ExistingPeriodicWorkPolicy.REPLACE,
- newRequest
- )
+ enqueueWorkRequest(newValue)
return true
}
}
}
+// fun Context.updateWorkManager(listPref: ListPreference, newValue: Any): Boolean {
+// Log.d("updateWorkManager", "listPref: ${listPref.value} - newValue: $newValue")
+// val value = newValue as? String
+// Log.d("updateWorkManager", "String value: $value")
+// if (value.isNullOrEmpty()) {
+// Log.w("updateWorkManager", "NULL OR EMPTY - false")
+// return false
+// } else if (listPref.value == value) {
+// Log.i("updateWorkManager", "NO CHANGE - false")
+// return false
+// } else {
+// Log.i("updateWorkManager", "RESCHEDULING WORK - true")
+// val interval = value.toLongOrNull()
+// Log.i("updateWorkManager", "interval: $interval")
+// if (interval == null || interval == 0L) {
+// Log.i("updateWorkManager", "DISABLING WORK")
+// WorkManager.getInstance(this).cancelUniqueWork("daily_worker")
+// return true
+// } else {
+// val newRequest =
+// PeriodicWorkRequestBuilder(interval, TimeUnit.MINUTES)
+// .setInitialDelay(1, TimeUnit.MINUTES)
+// .setConstraints(DAILY_WORKER_CONSTRAINTS)
+// .build()
+// WorkManager.getInstance(this).enqueueUniquePeriodicWork(
+// "daily_worker",
+// ExistingPeriodicWorkPolicy.REPLACE,
+// newRequest
+// )
+// return true
+// }
+// }
+// }
+
fun Context.toggleAnalytics(switchPreference: SwitchPreferenceCompat, newValue: Any) {
Log.d("toggleAnalytics", "newValue: $newValue")
if (newValue as Boolean) {
diff --git a/app/src/main/java/com/djangofiles/djangofiles/work/DailyWorkManager.kt b/app/src/main/java/com/djangofiles/djangofiles/work/DailyWorkManager.kt
new file mode 100644
index 0000000..af4487d
--- /dev/null
+++ b/app/src/main/java/com/djangofiles/djangofiles/work/DailyWorkManager.kt
@@ -0,0 +1,56 @@
+package com.djangofiles.djangofiles.work
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.util.Log
+import androidx.preference.PreferenceManager
+import androidx.work.Constraints
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.NetworkType
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import java.util.concurrent.TimeUnit
+
+fun Context.enqueueWorkRequest(
+ workInterval: String? = null,
+ existingPeriodicWorkPolicy: ExistingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.UPDATE,
+) {
+ Log.i("AppWorkManager", "enqueueWorkRequest: $existingPeriodicWorkPolicy")
+ val preferences = PreferenceManager.getDefaultSharedPreferences(this)
+ //val interval = (preferences.getString("work_interval", null) ?: "0").toLong()
+ val interval = workInterval?.toLongOrNull()
+ ?: preferences.getString("work_interval", null)?.toLongOrNull() ?: 0
+ Log.i("AppWorkManager", "interval: $interval")
+ if (interval < 15) {
+ Log.i("AppWorkManager", "RETURN on interval < 15")
+ return
+ }
+ val workRequestBuilder =
+ PeriodicWorkRequestBuilder(interval, TimeUnit.MINUTES)
+ .setConstraints(getWorkerConstraints(preferences))
+ if (existingPeriodicWorkPolicy == ExistingPeriodicWorkPolicy.UPDATE) {
+ Log.i("AppWorkManager", "workRequestBuilder.setInitialDelay: $interval")
+ workRequestBuilder.setInitialDelay(interval, TimeUnit.MINUTES)
+ }
+ //val workRequest = workRequestBuilder.build()
+ //Log.i("AppWorkManager", "workRequest: $workRequest")
+ WorkManager.getInstance(this).enqueueUniquePeriodicWork(
+ "daily_worker",
+ existingPeriodicWorkPolicy,
+ workRequestBuilder.build(),
+ )
+}
+
+fun getWorkerConstraints(preferences: SharedPreferences): Constraints {
+ val workMetered = preferences.getBoolean("work_metered", false)
+ Log.d("AppWorkManager", "getWorkerConstraints: workMetered: $workMetered")
+ val networkType = if (workMetered) NetworkType.CONNECTED else NetworkType.UNMETERED
+ Log.d("AppWorkManager", "getWorkerConstraints: networkType: $networkType")
+ return Constraints.Builder()
+ .setRequiresBatteryNotLow(true)
+ .setRequiresCharging(false)
+ .setRequiresDeviceIdle(false)
+ .setRequiredNetworkType(networkType)
+ .build()
+}
+
diff --git a/app/src/main/java/com/djangofiles/djangofiles/work/WorkConstants.kt b/app/src/main/java/com/djangofiles/djangofiles/work/WorkConstants.kt
deleted file mode 100644
index 02e01e7..0000000
--- a/app/src/main/java/com/djangofiles/djangofiles/work/WorkConstants.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.djangofiles.djangofiles.work
-
-import androidx.work.Constraints
-import androidx.work.NetworkType
-
-val DAILY_WORKER_CONSTRAINTS: Constraints = Constraints.Builder()
- .setRequiresBatteryNotLow(true)
- .setRequiresCharging(false)
- .setRequiresDeviceIdle(false)
- .setRequiredNetworkType(NetworkType.CONNECTED)
- .build()
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c0477f6..2150e3c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -42,25 +42,33 @@
- files
-
- - Every 15 Minutes
- - Every 30 Minutes
- - Hourly
- - Every 2 Hours
- - Every 4 Hours
- - Twice a Day
- - Once Daily
- - Never
+
+ - Disabled
+ - 15 Minutes
+ - 30 Minutes
+ - 1 Hour
+ - 2 Hours
+ - 4 Hours
+ - 6 Hours
+ - 12 Hours
+ - 1 Day
+ - 2 Days
+ - 3 Days
+ - 1 Week
-
+
+ - 0
- 15
- 30
- 60
- 120
- 240
+ - 360
- 720
- 1440
- - 0
+ - 2880
+ - 4320
+ - 10080
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e3b7a66..45de860 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -74,9 +74,16 @@
android:key="work_interval"
android:title="Background Update Interval"
android:defaultValue="1440"
- android:entries="@array/dropdown_entries"
- android:entryValues="@array/dropdown_values"
- android:icon="@drawable/md_timer_24" />
+ android:entries="@array/work_interval_entries"
+ android:entryValues="@array/work_interval_values"
+ android:icon="@drawable/md_timer_24"
+ tools:summary="1 Hour" />
+
+
Date: Sat, 16 Aug 2025 18:57:08 -0700
Subject: [PATCH 4/5] Update FeedbackApi.kt for Deprecation
---
.../java/com/djangofiles/djangofiles/api/FeedbackApi.kt | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/com/djangofiles/djangofiles/api/FeedbackApi.kt b/app/src/main/java/com/djangofiles/djangofiles/api/FeedbackApi.kt
index a93ec6c..b8d9c0a 100644
--- a/app/src/main/java/com/djangofiles/djangofiles/api/FeedbackApi.kt
+++ b/app/src/main/java/com/djangofiles/djangofiles/api/FeedbackApi.kt
@@ -29,12 +29,12 @@ class FeedbackApi(val context: Context) {
}
suspend fun sendFeedback(messageText: String): Response {
- val versionName = context.packageManager.getPackageInfo(context.packageName, 0).versionName
Log.d("sendFeedback", "messageText: $messageText")
+ val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val feedbackText = context.getString(
R.string.feedback_message,
context.getString(R.string.app_name),
- versionName,
+ packageInfo.versionName,
Build.VERSION.SDK_INT.toString(),
messageText
)
@@ -51,8 +51,7 @@ class FeedbackApi(val context: Context) {
@JsonClass(generateAdapter = true)
data class Message(
- @Json(name = "content")
- val content: String
+ @param:Json(name = "content") val content: String
)
interface ApiService {
From ed88769f1df43a9da8fd2ec6237a21aa2912ee0b Mon Sep 17 00:00:00 2001
From: Shane <6071159+smashedr@users.noreply.github.com>
Date: Sat, 16 Aug 2025 19:08:21 -0700
Subject: [PATCH 5/5] Update README.md
---
README.md | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index d76f12d..3b95982 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
-[](https://github.com/django-files/android-client/releases/latest/download/app-release.apk)
+[](https://github.com/django-files/android-client/releases/latest/download/app-release.apk)
[](https://github.com/django-files/android-client/releases/latest)
[](https://github.com/django-files/android-client/actions/workflows/lint.yaml)
-[](https://github.com/django-files/android-client)
[](https://github.com/django-files/android-client/graphs/commit-activity)
[](https://github.com/django-files/android-client)
+[](https://github.com/django-files/android-client)
[](https://github.com/django-files/android-client/discussions)
[](https://github.com/django-files/android-client/forks)
[](https://github.com/django-files/android-client/stargazers)
@@ -59,16 +59,14 @@ _If you are unsure how to install, [Obtainium](https://github.com/ImranR98/Obtai
-_Note: Until published on the play store, you may need to allow installation of apps from unknown sources._
-
-- Supports Android 8 (API 26) 2017 +
-
-Downloading and Installing the [apk](https://github.com/django-files/android-client/releases/latest/download/app-release.apk)
-should take you to the settings area to allow installation if not already enabled.
-For more information, see [Release through a website](https://developer.android.com/studio/publish#publishing-website).
+_Note: If installing directly, you may need to allow installation of apps from unknown sources.
+For more information, see [Release through a website](https://developer.android.com/studio/publish#publishing-website)._
View Manual Steps to Install from Unknown Sources
+Note: Downloading and Installing the [apk](https://github.com/django-files/android-client/releases/latest/download/app-release.apk)
+should take you to the settings area to allow installation if not already enabled. Otherwise:
+
1. Go to your device settings.
2. Search for "Install unknown apps" or similar.
3. Choose the app you will install the apk file from.