diff --git a/README.md b/README.md index 3b95982..5e22b11 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ [![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) [![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) +[![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/pulse) [![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) -[![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) +[![Release](https://img.shields.io/github/actions/workflow/status/django-files/android-client/release.yaml?logo=github&logoColor=white&label=release)](https://github.com/django-files/android-client/actions/workflows/release.yaml) +[![AGP Version](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdjango-files%2Fandroid-client%2Frefs%2Fheads%2Fmaster%2Fgradle%2Flibs.versions.toml&query=%24.versions.agp&logo=gradle&label=AGP)](https://github.com/django-files/android-client/blob/master/gradle/libs.versions.toml#L2) [![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) [![GitHub Top Language](https://img.shields.io/github/languages/top/django-files/android-client?logo=htmx)](https://github.com/django-files/android-client) -[![GitHub Discussions](https://img.shields.io/github/discussions/django-files/android-client)](https://github.com/django-files/android-client/discussions) +[![GitHub Discussions](https://img.shields.io/github/discussions/django-files/android-client?logo=github)](https://github.com/django-files/android-client/discussions) [![GitHub Forks](https://img.shields.io/github/forks/django-files/android-client?style=flat&logo=github)](https://github.com/django-files/android-client/forks) [![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) [![GitHub Org Stars](https://img.shields.io/github/stars/django-files?style=flat&logo=github&label=org%20stars)](https://django-files.github.io/) @@ -12,7 +14,7 @@ # Django Files Android App -[![GitHub Release](https://img.shields.io/github/v/release/django-files/android-client?style=for-the-badge&logo=android&label=Download%20Android%20APK&color=A4C639)](https://github.com/django-files/android-client/releases/latest/download/app-release.apk) +[![GitHub Release](https://img.shields.io/github/v/release/django-files/android-client?style=for-the-badge&logo=android&label=Download%20Android%20APK&color=3ddc84)](https://github.com/django-files/android-client/releases/latest/download/app-release.apk) - [Install](#Install) - [Setup](#Setup) @@ -51,7 +53,7 @@ _If you are unsure how to install, [Obtainium](https://github.com/ImranR98/Obtai [![Get on GitHub](https://raw.githubusercontent.com/smashedr/repo-images/refs/heads/master/android/get80/github.png)](https://github.com/django-files/android-client/releases/latest/download/app-release.apk) [![Get on Obtainium](https://raw.githubusercontent.com/smashedr/repo-images/refs/heads/master/android/get80/obtainium.png)](https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://github.com/django-files/android-client) -
📲 Click to View QR Codes 📸 +
📲 Click to View QR Codes 📸 Supports Android 8 (API 26) 2017 + [![QR Code GitHub](https://raw.githubusercontent.com/smashedr/repo-images/refs/heads/master/django-files/android/qr-code-github.png)](https://github.com/django-files/android-client/releases/latest/download/app-release.apk) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d668305..bdb584f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -60,15 +60,15 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } //kotlinOptions { - // jvmTarget = "11" + // jvmTarget = "17" //} tasks.withType().configureEach { compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) + jvmTarget.set(JvmTarget.JVM_17) } } diff --git a/app/src/main/java/com/djangofiles/djangofiles/ui/login/AuthorizeFragment.kt b/app/src/main/java/com/djangofiles/djangofiles/ui/login/AuthorizeFragment.kt index 084ec8b..954eb8d 100644 --- a/app/src/main/java/com/djangofiles/djangofiles/ui/login/AuthorizeFragment.kt +++ b/app/src/main/java/com/djangofiles/djangofiles/ui/login/AuthorizeFragment.kt @@ -76,7 +76,7 @@ class AuthorizeFragment : Fragment() { super.onViewCreated(view, savedInstanceState) Log.d("Authorize[onViewCreated]", "savedInstanceState: ${savedInstanceState?.size()}") - ViewCompat.setOnApplyWindowInsetsListener(binding.scrollViewLayout) { v, insets -> + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets -> val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.updatePadding(top = bars.top, bottom = bars.bottom) insets @@ -107,6 +107,7 @@ class AuthorizeFragment : Fragment() { val api = ServerApi(ctx, authUrl) + // TODO: Cache methodsResponse in viewModel lifecycleScope.launch { val methodsResponse = api.methods() Log.d("Authorize[onViewCreated]", "methodsResponse: $methodsResponse") @@ -115,10 +116,10 @@ class AuthorizeFragment : Fragment() { Log.d("Authorize[onViewCreated]", "methodsData: $methodsData") if (methodsData != null) { Log.d("Authorize[onViewCreated]", "siteName: ${methodsData.siteName}") - binding.siteName.text = methodsData.siteName - binding.loadingLayout.visibility = View.GONE - binding.gotoLoginBtn.visibility = View.VISIBLE - binding.addServerBtn.visibility = View.VISIBLE + _binding?.siteName?.text = methodsData.siteName + _binding?.loadingLayout?.visibility = View.GONE + _binding?.gotoLoginBtn?.visibility = View.VISIBLE + _binding?.addServerBtn?.visibility = View.VISIBLE } } } 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 7044cf9..8d1f02c 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 @@ -65,16 +65,25 @@ class LoginFragment : Fragment() { super.onViewCreated(view, savedInstanceState) Log.d("Login[onViewCreated]", "savedInstanceState: ${savedInstanceState?.size()}") - ViewCompat.setOnApplyWindowInsetsListener(binding.scrollViewLayout) { v, insets -> + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets -> val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.updatePadding(top = bars.top, bottom = bars.bottom) insets } - val packageInfo = - requireContext().packageManager.getPackageInfo(requireContext().packageName, 0) - val versionName = packageInfo.versionName - Log.d("Login[onViewCreated]", "versionName: $versionName") + val ctx = requireContext() + + //val packageInfo = ctx.packageManager.getPackageInfo(ctx.packageName, 0) + //val formattedVersion = getString( + // R.string.version_code_string, + // packageInfo.versionName, + // packageInfo.versionCode.toString() + //) + //Log.d("showAppInfoDialog", "formattedVersion: $formattedVersion") + //binding.versionName.text = formattedVersion + + val packageInfo = ctx.packageManager.getPackageInfo(ctx.packageName, 0) + binding.versionName.text = packageInfo.versionName val authUrl = arguments?.getString("authUrl") Log.d("Login[onViewCreated]", "authUrl: $authUrl") @@ -107,7 +116,7 @@ class LoginFragment : Fragment() { //sharedPreferences?.edit { putString("saved_url", host) } Log.d("loginFunction", "Processing URL: $host") - val api = ServerApi(requireContext(), host) + val api = ServerApi(ctx, host) lifecycleScope.launch { try { // TODO: When a session expires the server will be a duplicate... @@ -160,7 +169,7 @@ class LoginFragment : Fragment() { e.printStackTrace() val msg = e.message ?: "Unknown Error Validating Server." Log.i("loginFunction", "msg: $msg") - binding.hostnameText.error = "Validation Error" + _binding?.hostnameText?.error = "Validation Error" withContext(Dispatchers.Main) { Toast.makeText(requireContext(), msg, Toast.LENGTH_LONG).show() } @@ -176,11 +185,10 @@ class LoginFragment : Fragment() { requireActivity().finishAffinity() } } - binding.websiteLink.setOnClickListener { - val url = binding.websiteLink.text.toString() - Log.d("websiteLink", "url: $url") - val intent = Intent(Intent.ACTION_VIEW, url.toUri()) - startActivity(intent) + + binding.websiteLink.paint?.isUnderlineText = true + binding.websiteLink.setOnClickListener { v -> + startActivity(Intent(Intent.ACTION_VIEW, v.tag.toString().toUri())) } if (authUrl != null) { diff --git a/app/src/main/java/com/djangofiles/djangofiles/ui/login/LoginTwoFragment.kt b/app/src/main/java/com/djangofiles/djangofiles/ui/login/LoginTwoFragment.kt index 8777e43..b024ca2 100644 --- a/app/src/main/java/com/djangofiles/djangofiles/ui/login/LoginTwoFragment.kt +++ b/app/src/main/java/com/djangofiles/djangofiles/ui/login/LoginTwoFragment.kt @@ -79,7 +79,7 @@ class LoginTwoFragment : Fragment() { super.onViewCreated(view, savedInstanceState) Log.d("Login[onViewCreated]", "savedInstanceState: ${savedInstanceState?.size()}") - ViewCompat.setOnApplyWindowInsetsListener(binding.scrollViewLayout) { v, insets -> + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets -> val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.updatePadding(top = bars.top, bottom = bars.bottom) insets 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 89cd466..65c151f 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 @@ -385,10 +385,22 @@ class SettingsFragment : PreferenceFragmentCompat() { .show() } - fun Context.showFeedbackDialog() { + private fun Context.showFeedbackDialog() { val inflater = LayoutInflater.from(this) val view = inflater.inflate(R.layout.dialog_feedback, null) + val input = view.findViewById(R.id.feedback_input) + val websiteLink = view.findViewById(R.id.website_link) + val githubLink = view.findViewById(R.id.github_link) + + websiteLink.paint?.isUnderlineText = true + websiteLink.setOnClickListener { + startActivity(Intent(Intent.ACTION_VIEW, websiteLink.tag.toString().toUri())) + } + githubLink.paint?.isUnderlineText = true + githubLink.setOnClickListener { + startActivity(Intent(Intent.ACTION_VIEW, githubLink.tag.toString().toUri())) + } val dialog = MaterialAlertDialogBuilder(this) .setView(view) @@ -417,7 +429,7 @@ class SettingsFragment : PreferenceFragmentCompat() { putString("message", response.message()) putString("code", response.code().toString()) } - Firebase.analytics.logEvent("send_feedback_failed", params) + Firebase.analytics.logEvent("feedback_failed", params) "Error: ${response.code()}" } Log.d("showFeedbackDialog", "msg: $msg") @@ -428,67 +440,45 @@ class SettingsFragment : PreferenceFragmentCompat() { input.error = "Feedback is Required" } } - input.requestFocus() - - val link = view.findViewById(R.id.github_link) - val linkText = getString(R.string.github_link, link.tag) - link.text = Html.fromHtml(linkText, Html.FROM_HTML_MODE_LEGACY) - link.movementMethod = LinkMovementMethod.getInstance() - - //val imm = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - //imm.showSoftInput(input, InputMethodManager.SHOW_IMPLICIT) } - - dialog.setButton(AlertDialog.BUTTON_POSITIVE, "Send") { _, _ -> } dialog.show() } - fun Context.showAppInfoDialog() { + private fun Context.showAppInfoDialog() { val inflater = LayoutInflater.from(this) val view = inflater.inflate(R.layout.dialog_app_info, null) + val appId = view.findViewById(R.id.app_identifier) - val appVersion = view.findViewById(R.id.app_version) - val sourceLink = view.findViewById(R.id.source_link) + val versionName = view.findViewById(R.id.version_name) + val githubLink = view.findViewById(R.id.github_link) val websiteLink = view.findViewById(R.id.website_link) - //val appInfo = view.findViewById(R.id.open_app_info) - - val sourceText = getString(R.string.github_link, sourceLink.tag) - Log.d("showAppInfoDialog", "sourceText: $sourceText") - val websiteText = getString(R.string.website_link, websiteLink.tag) - Log.d("showAppInfoDialog", "websiteText: $websiteText") + githubLink.paint?.isUnderlineText = true + githubLink.setOnClickListener { + startActivity(Intent(Intent.ACTION_VIEW, githubLink.tag.toString().toUri())) + } + websiteLink.paint?.isUnderlineText = true + websiteLink.setOnClickListener { + startActivity(Intent(Intent.ACTION_VIEW, websiteLink.tag.toString().toUri())) + } val packageInfo = this.packageManager.getPackageInfo(this.packageName, 0) - val versionName = packageInfo.versionName - Log.d("showAppInfoDialog", "versionName: $versionName") - - val formattedVersion = getString(R.string.version_string, versionName) + val formattedVersion = getString( + R.string.version_code_string, + packageInfo.versionName, + packageInfo.versionCode.toString() + ) Log.d("showAppInfoDialog", "formattedVersion: $formattedVersion") - val dialog = MaterialAlertDialogBuilder(this) + appId.text = this.packageName + versionName.text = formattedVersion + + MaterialAlertDialogBuilder(this) .setView(view) .setNegativeButton("Close", null) .create() - - dialog.setOnShowListener { - appId.text = this.packageName - appVersion.text = formattedVersion - - sourceLink.text = Html.fromHtml(sourceText, Html.FROM_HTML_MODE_LEGACY) - sourceLink.movementMethod = LinkMovementMethod.getInstance() - websiteLink.text = Html.fromHtml(websiteText, Html.FROM_HTML_MODE_LEGACY) - websiteLink.movementMethod = LinkMovementMethod.getInstance() - - //appInfo.setOnClickListener { - // Log.d("appInfo", "setOnClickListener") - // val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { - // data = Uri.fromParts("package", packageName, null) - // } - // startActivity(intent) - //} - } - dialog.show() + .show() } } diff --git a/app/src/main/java/com/djangofiles/djangofiles/widget/WidgetProvider.kt b/app/src/main/java/com/djangofiles/djangofiles/widget/WidgetProvider.kt index d07e49b..7242a15 100644 --- a/app/src/main/java/com/djangofiles/djangofiles/widget/WidgetProvider.kt +++ b/app/src/main/java/com/djangofiles/djangofiles/widget/WidgetProvider.kt @@ -17,15 +17,14 @@ import com.djangofiles.djangofiles.R import com.djangofiles.djangofiles.db.ServerDao import com.djangofiles.djangofiles.db.ServerDatabase import com.djangofiles.djangofiles.work.updateStats -import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import java.util.Date class WidgetProvider : AppWidgetProvider() { - @OptIn(DelicateCoroutinesApi::class) override fun onReceive(context: Context, intent: Intent) { super.onReceive(context, intent) Log.i("Widget[onReceive]", "intent: $intent") @@ -46,8 +45,8 @@ class WidgetProvider : AppWidgetProvider() { if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { return } - Log.d("Widget[onReceive]", "GlobalScope.launch: START") - GlobalScope.launch(Dispatchers.IO) { + Log.d("Widget[onReceive]", "CoroutineScope.launch: START") + CoroutineScope(SupervisorJob() + Dispatchers.IO).launch { context.updateStats() val appWidgetManager = AppWidgetManager.getInstance(context) onUpdate(context, appWidgetManager, intArrayOf(appWidgetId)) @@ -56,7 +55,6 @@ class WidgetProvider : AppWidgetProvider() { } } - @OptIn(DelicateCoroutinesApi::class) override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, @@ -158,7 +156,7 @@ class WidgetProvider : AppWidgetProvider() { //views.setOnClickPendingIntent(R.id.file_list_button, pendingIntent3) // Room Data - GlobalScope.launch(Dispatchers.IO) { + CoroutineScope(SupervisorJob() + Dispatchers.IO).launch { val dao: ServerDao = ServerDatabase.Companion.getInstance(context).serverDao() Log.d("Widget[onUpdate]", "dao: $dao") val server = dao.getByUrl(savedUrl) diff --git a/app/src/main/res/drawable/fa_github.xml b/app/src/main/res/drawable/fa_github.xml index 4dac7a2..59bb846 100644 --- a/app/src/main/res/drawable/fa_github.xml +++ b/app/src/main/res/drawable/fa_github.xml @@ -4,6 +4,6 @@ android:viewportWidth="496" android:viewportHeight="512"> diff --git a/app/src/main/res/layout/dialog_app_info.xml b/app/src/main/res/layout/dialog_app_info.xml index 846e265..a7e565e 100644 --- a/app/src/main/res/layout/dialog_app_info.xml +++ b/app/src/main/res/layout/dialog_app_info.xml @@ -23,17 +23,17 @@ android:gravity="center" tools:ignore="UseCompoundDrawables"> + android:src="@mipmap/ic_launcher_round" /> + android:labelFor="@+id/feedback_input" /> + tools:text="0.0.1 (1)" /> + android:ellipsize="end" + tools:text="org.cssnr.zipline" /> + android:textColor="?attr/colorPrimary" + android:text="django-files/android-client" + android:tag="@string/github_url" /> + android:textColor="?attr/colorPrimary" + android:text="django-files.github.io" + android:tag="@string/website_url" /> - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_feedback.xml b/app/src/main/res/layout/dialog_feedback.xml index 9025343..4fbe679 100644 --- a/app/src/main/res/layout/dialog_feedback.xml +++ b/app/src/main/res/layout/dialog_feedback.xml @@ -50,16 +50,28 @@ android:hint="Provide Feedback to Developers." /> + + app:drawableStartCompat="@drawable/fa_github" /> diff --git a/app/src/main/res/layout/fragment_authorize.xml b/app/src/main/res/layout/fragment_authorize.xml index 01c32ca..c5b3cfd 100644 --- a/app/src/main/res/layout/fragment_authorize.xml +++ b/app/src/main/res/layout/fragment_authorize.xml @@ -1,26 +1,28 @@ - + + android:padding="24dp" + android:gravity="center_vertical" + android:orientation="vertical"> - - + android:orientation="vertical"> + - - - - - + - - + + 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" /> + + + + + - - - + + diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml index 799ae49..24c07f6 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -1,51 +1,60 @@ - + + android:padding="24dp" + android:gravity="center_vertical" + android:orientation="vertical"> - - - - + android:orientation="vertical"> + + + + - + - + - - - + + diff --git a/app/src/main/res/layout/fragment_login_two.xml b/app/src/main/res/layout/fragment_login_two.xml index 69b931f..02bcd9e 100644 --- a/app/src/main/res/layout/fragment_login_two.xml +++ b/app/src/main/res/layout/fragment_login_two.xml @@ -1,31 +1,33 @@ - + + android:padding="24dp" + android:gravity="center_vertical" + android:orientation="vertical"> - - + android:orientation="vertical" + tools:ignore="UseCompoundDrawables"> @@ -33,24 +35,34 @@ android:id="@+id/site_name" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="16dp" android:ellipsize="end" android:maxLines="1" android:text="@string/app_name" android:textAppearance="@style/TextAppearance.AppCompat.Large" /> + + -