diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c975aafd..c1313196 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,7 @@ - + @@ -16,8 +18,10 @@ android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" android:usesCleartextTraffic="true" + android:enableOnBackInvokedCallback="true" android:networkSecurityConfig="@xml/network_security_config" - android:theme="@style/AppTheme.SplashScreen"> + android:theme="@style/AppTheme.SplashScreen" + tools:targetApi="tiramisu"> + try { + require(result.resultCode == RESULT_OK) { "result was ${result.resultCode}" } + requireNotNull(result.data) { "file path was null" } + + val uri = result.data!!.data ?: throw IllegalArgumentException("file path was null") + val fileStream = contentResolver.openInputStream(uri) + ?: throw IllegalArgumentException("file path was invalid") + + val content = Utils.readFileFromStream(fileStream) + val name = getNameOfCertContent(content) + + // temporarily set the contents (don't store to settings until they decide to login) + caCertContents = content + advancedDialog.showRemoveCACertificate(name) + } catch (e: Exception) { + Utils.showSnackBar(this, getString(R.string.select_ca_failed, e.message)) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) UncaughtExceptionHandler.registerCurrentThread() @@ -152,9 +169,8 @@ internal class LoginActivity : AppCompatActivity() { intent.addCategory(Intent.CATEGORY_OPENABLE) try { - startActivityForResult( - Intent.createChooser(intent, getString(R.string.select_ca_file)), - FILE_SELECT_CODE + certificateDialogResultLauncher.launch( + Intent.createChooser(intent, getString(R.string.select_ca_file)) ) } catch (e: ActivityNotFoundException) { // case for user not having a file browser installed @@ -162,30 +178,6 @@ internal class LoginActivity : AppCompatActivity() { } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - try { - if (requestCode == FILE_SELECT_CODE) { - require(resultCode == RESULT_OK) { "result was $resultCode" } - requireNotNull(data) { "file path was null" } - - val uri = data.data ?: throw IllegalArgumentException("file path was null") - - val fileStream = contentResolver.openInputStream(uri) - ?: throw IllegalArgumentException("file path was invalid") - - val content = Utils.readFileFromStream(fileStream) - val name = getNameOfCertContent(content) - - // temporarily set the contents (don't store to settings until they decide to login) - caCertContents = content - advancedDialog.showRemoveCACertificate(name) - } - } catch (e: Exception) { - Utils.showSnackBar(this, getString(R.string.select_ca_failed, e.message)) - } - } - private fun getNameOfCertContent(content: String): String { val ca = CertUtils.parseCertificate(content) return (ca as X509Certificate).subjectDN.name diff --git a/app/src/main/kotlin/com/github/gotify/messages/MessagesActivity.kt b/app/src/main/kotlin/com/github/gotify/messages/MessagesActivity.kt index 927bdca5..ac49dd66 100644 --- a/app/src/main/kotlin/com/github/gotify/messages/MessagesActivity.kt +++ b/app/src/main/kotlin/com/github/gotify/messages/MessagesActivity.kt @@ -15,6 +15,7 @@ import android.view.MenuItem import android.view.View import android.widget.ImageButton import android.widget.TextView +import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat @@ -68,6 +69,7 @@ internal class MessagesActivity : private var isLoadMore = false private var updateAppOnDrawerClose: Long? = null private lateinit var listMessageAdapter: ListMessageAdapter + private lateinit var onBackPressedCallback: OnBackPressedCallback private val receiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { @@ -108,6 +110,7 @@ internal class MessagesActivity : listAnimation ) } + addBackPressCallback() messagesView.addItemDecoration(dividerItemDecoration) messagesView.setHasFixedSize(true) @@ -126,6 +129,9 @@ internal class MessagesActivity : swipeRefreshLayout.setOnRefreshListener { onRefresh() } binding.drawerLayout.addDrawerListener( object : SimpleDrawerListener() { + override fun onDrawerOpened(drawerView: View) { + onBackPressedCallback.isEnabled = true + } override fun onDrawerClosed(drawerView: View) { updateAppOnDrawerClose?.let { selectApp -> updateAppOnDrawerClose = null @@ -135,6 +141,7 @@ internal class MessagesActivity : } invalidateOptionsMenu() } + onBackPressedCallback.isEnabled = false } } ) @@ -242,12 +249,15 @@ internal class MessagesActivity : refreshAll.setOnClickListener { refreshAll() } } - override fun onBackPressed() { - if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) { - binding.drawerLayout.closeDrawer(GravityCompat.START) - } else { - super.onBackPressed() + private fun addBackPressCallback() { + onBackPressedCallback = object : OnBackPressedCallback(false) { + override fun handleOnBackPressed() { + if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) { + binding.drawerLayout.closeDrawer(GravityCompat.START) + } + } } + onBackPressedDispatcher.addCallback(this, onBackPressedCallback) } override fun onNavigationItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/kotlin/com/github/gotify/messages/MessagesModelFactory.kt b/app/src/main/kotlin/com/github/gotify/messages/MessagesModelFactory.kt index 32d8defb..d22e8c4c 100644 --- a/app/src/main/kotlin/com/github/gotify/messages/MessagesModelFactory.kt +++ b/app/src/main/kotlin/com/github/gotify/messages/MessagesModelFactory.kt @@ -9,6 +9,7 @@ internal class MessagesModelFactory( ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { if (modelClass == MessagesModel::class.java) { + @Suppress("UNCHECKED_CAST") return modelClass.cast(MessagesModel(modelParameterActivity)) as T } throw IllegalArgumentException( diff --git a/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt b/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt index 9ff0e4e4..a92922bf 100644 --- a/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt +++ b/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt @@ -1,7 +1,6 @@ package com.github.gotify.service import android.app.AlarmManager -import android.net.ConnectivityManager import android.os.Build import android.os.Handler import android.os.Looper @@ -24,7 +23,6 @@ internal class WebSocketConnection( private val baseUrl: String, settings: SSLSettings, private val token: String?, - private val connectivityManager: ConnectivityManager, private val alarmManager: AlarmManager ) { companion object { @@ -200,12 +198,6 @@ internal class WebSocketConnection( } errorCount++ - - val network = connectivityManager.activeNetworkInfo - if (network == null || !network.isConnected) { - Log.i("WebSocket($id): Network not connected") - } - val minutes = (errorCount * 2 - 1).coerceAtMost(20) onNetworkFailure.execute(minutes) diff --git a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt index de63a91d..69401dfe 100644 --- a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt +++ b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt @@ -113,7 +113,6 @@ internal class WebSocketService : Service() { settings.url, settings.sslSettings(), settings.token, - cm, alarmManager ) .onOpen { onOpen() } diff --git a/app/src/main/kotlin/com/github/gotify/settings/SettingsActivity.kt b/app/src/main/kotlin/com/github/gotify/settings/SettingsActivity.kt index 9cc1cb24..9f4ef926 100644 --- a/app/src/main/kotlin/com/github/gotify/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/com/github/gotify/settings/SettingsActivity.kt @@ -110,6 +110,7 @@ internal class SettingsActivity : AppCompatActivity(), OnSharedPreferenceChangeL private fun showListPreferenceDialog(preference: ListPreference) { val dialogFragment = MaterialListPreference() dialogFragment.arguments = Bundle(1).apply { putString("key", preference.key) } + @Suppress("DEPRECATION") // https://issuetracker.google.com/issues/181793702#comment3 dialogFragment.setTargetFragment(this, 0) dialogFragment.show( parentFragmentManager, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 783a6475..d58f147c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,8 +3,8 @@ Close navigation drawer Navigation header Found Gotify v%s - Request to \'%s\' failed with status code %d - Request to \'%s\' failed. %s. + Request to \'%1$s\' failed with status code %2$d + Request to \'%1$s\' failed. %2$s. There is no user with this username and password Client Name Choose a name for your session @@ -18,7 +18,7 @@ Oops Cannot connect to %s Server returned 401 unauthorized while trying to get current user. This can be caused by deleting the client for this session. - Could not get current user. Server (%s) responded with code %d: body (first 200 chars): %s + Could not get current user. Server (%1$s) responded with code %2$d: body (first 200 chars): %3$s User action required Please login, the authentication token isn\'t valid anymore. Connection closed @@ -59,7 +59,7 @@ Connected Could not connect Initializing - gotify/android v%s; gotify/server v%s + gotify/android v%1$s; gotify/server v%2$s Advanced Settings Done No certificate selected