Skip to content

Commit 829812e

Browse files
authored
Update Layouts and URL Parsing (#69)
- Add Work Manager Metered Option - Update Work Manager Initialization - Update Login Layout Constraints - Update Material Dialog Layouts - Update URL Parsing on Short and Login
1 parent ab53de9 commit 829812e

File tree

15 files changed

+626
-444
lines changed

15 files changed

+626
-444
lines changed

README.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
[![GitHub Downloads](https://img.shields.io/github/downloads/django-files/android-client/total?logo=github)](https://github.com/django-files/android-client/releases/latest/download/app-release.apk)
1+
[![GitHub Downloads](https://img.shields.io/github/downloads/django-files/android-client/total?logo=android)](https://github.com/django-files/android-client/releases/latest/download/app-release.apk)
22
[![GitHub Release Version](https://img.shields.io/github/v/release/django-files/android-client?logo=github)](https://github.com/django-files/android-client/releases/latest)
33
[![Lint](https://img.shields.io/github/actions/workflow/status/django-files/android-client/lint.yaml?logo=github&logoColor=white&label=lint)](https://github.com/django-files/android-client/actions/workflows/lint.yaml)
4-
[![GitHub Top Language](https://img.shields.io/github/languages/top/django-files/android-client?logo=htmx)](https://github.com/django-files/android-client)
54
[![GitHub Last Commit](https://img.shields.io/github/last-commit/django-files/android-client?logo=github&label=updated)](https://github.com/django-files/android-client/graphs/commit-activity)
65
[![GitHub Repo Size](https://img.shields.io/github/repo-size/django-files/android-client?logo=bookstack&logoColor=white&label=repo%20size)](https://github.com/django-files/android-client)
6+
[![GitHub Top Language](https://img.shields.io/github/languages/top/django-files/android-client?logo=htmx)](https://github.com/django-files/android-client)
77
[![GitHub Discussions](https://img.shields.io/github/discussions/django-files/android-client)](https://github.com/django-files/android-client/discussions)
88
[![GitHub Forks](https://img.shields.io/github/forks/django-files/android-client?style=flat&logo=github)](https://github.com/django-files/android-client/forks)
99
[![GitHub Repo Stars](https://img.shields.io/github/stars/django-files/android-client?style=flat&logo=github)](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
5959

6060
</details>
6161

62-
_Note: Until published on the play store, you may need to allow installation of apps from unknown sources._
63-
64-
- Supports Android 8 (API 26) 2017 +
65-
66-
Downloading and Installing the [apk](https://github.com/django-files/android-client/releases/latest/download/app-release.apk)
67-
should take you to the settings area to allow installation if not already enabled.
68-
For more information, see [Release through a website](https://developer.android.com/studio/publish#publishing-website).
62+
_Note: If installing directly, you may need to allow installation of apps from unknown sources.
63+
For more information, see [Release through a website](https://developer.android.com/studio/publish#publishing-website)._
6964

7065
<details><summary>View Manual Steps to Install from Unknown Sources</summary>
7166

67+
Note: Downloading and Installing the [apk](https://github.com/django-files/android-client/releases/latest/download/app-release.apk)
68+
should take you to the settings area to allow installation if not already enabled. Otherwise:
69+
7270
1. Go to your device settings.
7371
2. Search for "Install unknown apps" or similar.
7472
3. Choose the app you will install the apk file from.

app/src/main/java/com/djangofiles/djangofiles/MainActivity.kt

Lines changed: 26 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,20 @@ import androidx.navigation.ui.NavigationUI
4545
import androidx.navigation.ui.setupWithNavController
4646
import androidx.preference.PreferenceManager
4747
import androidx.work.ExistingPeriodicWorkPolicy
48-
import androidx.work.PeriodicWorkRequestBuilder
4948
import androidx.work.WorkManager
5049
import com.djangofiles.djangofiles.databinding.ActivityMainBinding
5150
import com.djangofiles.djangofiles.db.Server
5251
import com.djangofiles.djangofiles.db.ServerDao
5352
import com.djangofiles.djangofiles.db.ServerDatabase
5453
import com.djangofiles.djangofiles.ui.home.HomeViewModel
5554
import com.djangofiles.djangofiles.widget.WidgetProvider
56-
import com.djangofiles.djangofiles.work.DAILY_WORKER_CONSTRAINTS
57-
import com.djangofiles.djangofiles.work.DailyWorker
55+
import com.djangofiles.djangofiles.work.enqueueWorkRequest
5856
import kotlinx.coroutines.Dispatchers
5957
import kotlinx.coroutines.launch
6058
import kotlinx.coroutines.withContext
59+
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
6160
import java.io.File
62-
import java.net.URL
6361
import java.util.UUID
64-
import java.util.concurrent.TimeUnit
6562

6663
class MainActivity : AppCompatActivity() {
6764

@@ -202,40 +199,34 @@ class MainActivity : AppCompatActivity() {
202199
window.setNavigationBarContrastEnforced(false)
203200
}
204201

205-
// Set Nav Header Top Padding
202+
// Update Header Padding
206203
val headerView = binding.navView.getHeaderView(0)
207-
ViewCompat.setOnApplyWindowInsetsListener(headerView) { v, insets ->
208-
val bars = insets.getInsets(WindowInsetsCompat.Type.statusBars())
209-
Log.d("ViewCompat", "top: ${bars.top}")
210-
v.updatePadding(top = bars.top)
204+
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
205+
val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
206+
Log.d("ViewCompat", "binding.root: top: ${bars.top}")
207+
if (bars.top > 0) {
208+
headerView.updatePadding(top = bars.top)
209+
}
211210
insets
212211
}
213212

214213
// Update Header Text
215-
val packageInfo = packageManager.getPackageInfo(this.packageName, 0)
214+
val packageInfo = packageManager.getPackageInfo(packageName, 0)
216215
val versionName = packageInfo.versionName
217216
Log.d("Main[onCreate]", "versionName: $versionName")
218217
val versionTextView = headerView.findViewById<TextView>(R.id.header_version)
219218
versionTextView.text = "v${versionName}"
220219

221-
// TODO: Improve initialization of the WorkRequest
220+
// Work Manager
222221
val workInterval = preferences.getString("work_interval", null) ?: "0"
223-
Log.i("Main[onCreate]", "workInterval: $workInterval")
222+
Log.d("Main[onCreate]", "workInterval: $workInterval")
223+
// NOTE: This just ensures work manager is enabled or disabled based on preference
224224
if (workInterval != "0") {
225-
val workRequest =
226-
PeriodicWorkRequestBuilder<DailyWorker>(workInterval.toLong(), TimeUnit.MINUTES)
227-
.setConstraints(DAILY_WORKER_CONSTRAINTS)
228-
.build()
229-
Log.i("Main[onCreate]", "workRequest: $workRequest")
230-
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
231-
"daily_worker",
232-
ExistingPeriodicWorkPolicy.KEEP,
233-
workRequest
234-
)
225+
enqueueWorkRequest(workInterval, ExistingPeriodicWorkPolicy.KEEP)
235226
} else {
236227
// TODO: Confirm this is necessary...
237228
Log.i("Main[onCreate]", "Ensuring Work is Disabled")
238-
WorkManager.getInstance(this).cancelUniqueWork("app_worker")
229+
WorkManager.getInstance(this).cancelUniqueWork("daily_worker")
239230
}
240231

241232
// Handle Custom Navigation Items
@@ -418,8 +409,8 @@ class MainActivity : AppCompatActivity() {
418409
Log.d("onNewIntent", "SEND TEXT DETECTED")
419410
//if (extraText.lowercase().startsWith("http")) {
420411
//if (Patterns.WEB_URL.matcher(extraText).matches()) {
421-
if (isURL(extraText)) {
422-
Log.d("onNewIntent", "URL DETECTED: $extraText")
412+
if (isTextUrl(extraText)) {
413+
Log.i("onNewIntent", "URL DETECTED: $extraText")
423414
val bundle = Bundle().apply { putString("url", extraText) }
424415
navController.navigate(
425416
R.id.nav_item_short, bundle, NavOptions.Builder()
@@ -662,6 +653,15 @@ class MainActivity : AppCompatActivity() {
662653
}
663654
}
664655

656+
private fun isTextUrl(input: String): Boolean {
657+
val url = input.toHttpUrlOrNull() ?: return false
658+
if (input != url.toString()) return false
659+
if (url.scheme !in listOf("http", "https")) return false
660+
if (url.host.isBlank()) return false
661+
if (url.toString().length > 2048) return false
662+
return true
663+
}
664+
665665
fun setDrawerLockMode(enabled: Boolean) {
666666
Log.d("setDrawerLockMode", "enabled: $enabled")
667667
val lockMode =
@@ -709,14 +709,3 @@ fun copyToClipboard(context: Context, text: String, msg: String? = null) {
709709
clipboard.setPrimaryClip(clip)
710710
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
711711
}
712-
713-
fun isURL(url: String): Boolean {
714-
return try {
715-
URL(url)
716-
Log.d("isURL", "TRUE")
717-
true
718-
} catch (_: Exception) {
719-
Log.d("isURL", "FALSE")
720-
false
721-
}
722-
}

app/src/main/java/com/djangofiles/djangofiles/api/FeedbackApi.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ class FeedbackApi(val context: Context) {
2929
}
3030

3131
suspend fun sendFeedback(messageText: String): Response<Unit> {
32-
val versionName = context.packageManager.getPackageInfo(context.packageName, 0).versionName
3332
Log.d("sendFeedback", "messageText: $messageText")
33+
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
3434
val feedbackText = context.getString(
3535
R.string.feedback_message,
3636
context.getString(R.string.app_name),
37-
versionName,
37+
packageInfo.versionName,
3838
Build.VERSION.SDK_INT.toString(),
3939
messageText
4040
)
@@ -51,8 +51,7 @@ class FeedbackApi(val context: Context) {
5151

5252
@JsonClass(generateAdapter = true)
5353
data class Message(
54-
@Json(name = "content")
55-
val content: String
54+
@param:Json(name = "content") val content: String
5655
)
5756

5857
interface ApiService {

app/src/main/java/com/djangofiles/djangofiles/ui/login/LoginFragment.kt

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ import androidx.navigation.fragment.findNavController
1818
import com.djangofiles.djangofiles.R
1919
import com.djangofiles.djangofiles.ServerApi
2020
import com.djangofiles.djangofiles.databinding.FragmentLoginBinding
21-
import com.djangofiles.djangofiles.isURL
2221
import com.google.android.material.bottomnavigation.BottomNavigationView
2322
import kotlinx.coroutines.Dispatchers
2423
import kotlinx.coroutines.launch
2524
import kotlinx.coroutines.withContext
25+
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
2626

2727
class LoginFragment : Fragment() {
2828

@@ -92,7 +92,10 @@ class LoginFragment : Fragment() {
9292
binding.hostnameText.setSelection(binding.hostnameText.text.length)
9393
}
9494
Log.d("loginFunction", "host: $host")
95-
if (!isURL(host)) {
95+
// TODO: Look into the usage of (host.toHttpUrlOrNull() == null) here.
96+
// NOTE: This seems to be less restrictive so should work and can be improved...
97+
//if (!isURL(host)) {
98+
if (host.toHttpUrlOrNull() == null) {
9699
binding.hostnameText.error = "Invalid Hostname"
97100
return@OnClickListener
98101
}
@@ -190,16 +193,63 @@ class LoginFragment : Fragment() {
190193
}
191194

192195
private fun parseHost(urlString: String): String {
193-
var url = urlString.trim()
194-
if (url.isEmpty()) {
196+
try {
197+
var url = urlString.trim()
198+
if (url.isEmpty()) {
199+
return ""
200+
}
201+
if (!url.lowercase().startsWith("http")) {
202+
url = "https://$url"
203+
}
204+
if (url.toHttpUrlOrNull() == null) {
205+
return url
206+
}
207+
val uri = url.toUri()
208+
Log.d("parseHost", "uri: $uri")
209+
Log.d("parseHost", "uri.scheme: ${uri.scheme}")
210+
if (uri.scheme.isNullOrEmpty()) {
211+
return "https://"
212+
}
213+
Log.d("parseHost", "uri.host: ${uri.host}")
214+
if (uri.host.isNullOrEmpty()) {
215+
return "${uri.scheme}://"
216+
}
217+
Log.d("parseHost", "uri.path: ${uri.path}")
218+
val result = "${uri.scheme}://${uri.host}${uri.path}"
219+
Log.i("parseHost", "result: $result")
220+
return if (result.endsWith("/")) {
221+
result.dropLast(1)
222+
} else {
223+
result
224+
}
225+
} catch (e: Throwable) {
226+
Log.d("parseHost", "Exception: $e")
195227
return ""
196228
}
197-
if (!url.lowercase().startsWith("http")) {
198-
url = "https://$url"
199-
}
200-
if (url.endsWith("/")) {
201-
url = url.substring(0, url.length - 1)
202-
}
203-
return url
204229
}
230+
231+
//private fun parseHost(urlString: String): String {
232+
// var url = urlString.trim()
233+
// if (url.isEmpty()) {
234+
// return ""
235+
// }
236+
// if (!url.lowercase().startsWith("http")) {
237+
// url = "https://$url"
238+
// }
239+
// if (url.endsWith("/")) {
240+
// url = url.substring(0, url.length - 1)
241+
// }
242+
// return url
243+
//}
244+
245+
//private fun isURL(url: String): Boolean {
246+
// return try {
247+
// URL(url)
248+
// Log.d("isURL", "TRUE")
249+
// true
250+
// } catch (_: Exception) {
251+
// Log.d("isURL", "FALSE")
252+
// false
253+
// }
254+
//}
205255
}

0 commit comments

Comments
 (0)