Migrate start activity to jetpack

This commit is contained in:
sim 2024-11-18 09:33:48 +00:00
parent 9ac56626cb
commit c53f304c03
15 changed files with 356 additions and 250 deletions

View File

@ -64,6 +64,7 @@ if (project.hasProperty("sign")) {
}
dependencies {
implementation(libs.accompanist.permissions)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.coordinatorlayout)

View File

@ -28,15 +28,15 @@ object AccountFactory {
account = null
}
fun Context.setTypeSSO() {
fun setTypeSSO(context: Context) {
account = null
AccountStore(this).accountType = AccountType.SSO
DirectAccountStore(this).wipe() // Just in case
AccountStore(context).accountType = AccountType.SSO
DirectAccountStore(context).wipe() // Just in case
}
fun Context.setTypeDirect(url: String, username: String, password: String) {
fun setTypeDirect(context: Context, url: String, username: String, password: String) {
account = null
AccountStore(this).accountType = AccountType.Direct
DirectAccount.setCredentials(this, url, username, password)
AccountStore(context).accountType = AccountType.Direct
DirectAccount.setCredentials(context, url, username, password)
}
}

View File

@ -32,10 +32,14 @@ class AppAction(private val type: Type, private val argv: Map<String, Any>? = nu
DisableBatteryOptimisation,
CopyEndpoint,
DeleteRegistration,
LoginSSO,
LoginDirect
}
fun handle(context: Context) {
when (type) {
Type.LoginSSO -> loginSSO(context)
Type.LoginDirect -> loginDirect(context, argv)
Type.RestartService -> restartService(context)
Type.Logout -> logout(context)
Type.AddChannel -> addChannel(context, argv)
@ -45,6 +49,19 @@ class AppAction(private val type: Type, private val argv: Map<String, Any>? = nu
}
}
private fun loginSSO(context: Context) {
AccountFactory.setTypeSSO(context)
UiAction.publish(UiAction.Type.Login)
}
private fun loginDirect(context: Context, argv: Map<String, Any>?) {
val username = argv?.get(ARG_USERNAME) as String? ?: return
val password = argv?.get(ARG_PASSWORD) as String? ?: return
val url = argv?.get(ARG_URL) as String? ?: return
AccountFactory.setTypeDirect(context, url, username, password)
UiAction.publish(UiAction.Type.Login)
}
private fun restartService(context: Context) {
Log.d(TAG, "Restarting the Listener")
FailureHandler.clearFails()
@ -112,6 +129,9 @@ class AppAction(private val type: Type, private val argv: Map<String, Any>? = nu
}
companion object {
const val ARG_USERNAME = "username"
const val ARG_PASSWORD = "password"
const val ARG_URL = "url"
const val ARG_NEW_CHANNEL_TITLE = "title"
const val ARG_TOKEN = "token"
const val ARG_REGISTRATIONS = "registrations"

View File

@ -55,6 +55,7 @@ class MainActivity : ComponentActivity() {
StartActivity.goToStartActivity(this@MainActivity)
finish()
}
else -> {}
}
}
}

View File

@ -1,68 +0,0 @@
package org.unifiedpush.distributor.nextpush.activities
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import org.unifiedpush.distributor.nextpush.R
import org.unifiedpush.distributor.nextpush.activities.PermissionsRequest.requestAppPermissions
import org.unifiedpush.distributor.nextpush.utils.TAG
object PermissionsRequest {
fun AppCompatActivity.requestAppPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED
) {
Log.d(TAG, "Requesting POST_NOTIFICATIONS permission")
this.registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
Log.d(TAG, "POST_NOTIFICATIONS permission granted: $granted")
if (!granted) {
if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
Log.d(TAG, "Show POST_NOTIFICATIONS permission rationale")
AlertDialog.Builder(this)
.setTitle(getString(R.string.no_notification_dialog_title))
.setMessage(R.string.no_notification_dialog_message)
.show()
}
}
}.launch(
Manifest.permission.POST_NOTIFICATIONS
)
}
} else if (Build.VERSION.SDK_INT < 26) {
this.requestAccountPermission()
}
}
@Deprecated("For SDK<26 only")
fun AppCompatActivity.requestAccountPermission() {
if (checkSelfPermission(Manifest.permission.GET_ACCOUNTS)
!= PackageManager.PERMISSION_GRANTED
) {
Log.d(TAG, "Requesting GET_ACCOUNT permission")
this.registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
Log.d(TAG, "GET_ACCOUNT permission granted: $granted")
if (!granted) {
if (shouldShowRequestPermissionRationale(Manifest.permission.GET_ACCOUNTS)) {
Log.d(TAG, "Show GET_ACCOUNTS permission rationale")
AlertDialog.Builder(this)
.setTitle(getString(R.string.no_notification_dialog_title))
.setMessage(R.string.no_notification_dialog_message)
.show()
}
}
}.launch(
Manifest.permission.GET_ACCOUNTS
)
}
}
}

View File

@ -1,61 +1,82 @@
package org.unifiedpush.distributor.nextpush.activities
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone
import org.unifiedpush.distributor.nextpush.R
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.unifiedpush.distributor.nextpush.EventBus
import org.unifiedpush.distributor.nextpush.account.AccountFactory
import org.unifiedpush.distributor.nextpush.account.AccountFactory.setTypeDirect
import org.unifiedpush.distributor.nextpush.account.AccountFactory.setTypeSSO
import org.unifiedpush.distributor.nextpush.activities.MainActivity.Companion.goToMainActivity
import org.unifiedpush.distributor.nextpush.activities.PermissionsRequest.requestAppPermissions
import org.unifiedpush.distributor.nextpush.activities.ui.StartUi
import org.unifiedpush.distributor.nextpush.activities.ui.theme.AppTheme
import org.unifiedpush.distributor.nextpush.utils.TAG
class StartActivity : AppCompatActivity() {
private var onResult: ((activity: Activity, requestCode: Int, resultCode: Int, data: Intent?, block: (success: Boolean) -> Unit) -> Unit)? = null
class StartActivity : ComponentActivity() {
private var jobs: MutableList<Job> = emptyList<Job>().toMutableList()
private var onResult: (
(
activity: Activity,
requestCode: Int,
resultCode: Int,
data: Intent?,
block: (success: Boolean) -> Unit
) -> Unit
)? = null
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_start)
this.requestAppPermissions()
if (AccountFactory.getAccount(this)?.connected == true) {
goToMainActivity(this)
finish()
}
findViewById<Button>(R.id.btn_sso_login).setOnClickListener {
setTypeSSO()
login()
setContent {
val viewModel =
viewModel {
StartViewModel()
}
AppTheme {
StartUi(viewModel)
}
subscribeActions()
}
findViewById<EditText>(R.id.edt_url).setText("https://")
findViewById<Button>(R.id.btn_manual_login).setOnClickListener {
val url = findViewById<EditText>(R.id.edt_url).text.toString().let {
if (it.last() != '/') {
"$it/"
} else {
it
}
private fun subscribeActions() {
Log.d(TAG, "Subscribing to actions")
jobs += CoroutineScope(Dispatchers.IO).launch {
EventBus.subscribe<AppAction> { it.handle(this@StartActivity) }
}
jobs += CoroutineScope(Dispatchers.IO).launch {
EventBus.subscribe<UiAction> {
it.handle { type ->
when (type) {
UiAction.Type.Login -> login()
else -> {}
}
}
}
val username = findViewById<EditText>(R.id.edt_username).text.toString()
val password = findViewById<EditText>(R.id.edt_password).text.toString()
setTypeDirect(url, username, password)
login()
}
findViewById<TextView>(R.id.manual_login).setOnClickListener {
findViewById<LinearLayout>(R.id.manual_login_wrapper).apply {
isGone = !isGone
}
}
override fun onDestroy() {
Log.d(TAG, "Destroy")
jobs.removeAll {
it.cancel()
true
}
super.onDestroy()
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@ -67,7 +88,11 @@ class StartActivity : AppCompatActivity() {
goToMainActivity(this)
finish()
} else {
Toast.makeText(applicationContext, "Could not connect to UnifiedPush provider", Toast.LENGTH_SHORT).show()
Toast.makeText(
applicationContext,
"Could not connect to UnifiedPush provider",
Toast.LENGTH_SHORT
).show()
}
}
}
@ -75,7 +100,10 @@ class StartActivity : AppCompatActivity() {
private fun login() {
AccountFactory.getAccount(this, connected = false)?.let {
onResult = { activity: Activity, i: Int, i1: Int, intent: Intent?, block: (success: Boolean) -> Unit ->
onResult = { activity: Activity, i: Int, i1: Int, intent: Intent?,
block: (
success: Boolean
) -> Unit ->
it.onActivityResult(activity, i, i1, intent, block)
}
it.connect(this)
@ -84,10 +112,11 @@ class StartActivity : AppCompatActivity() {
companion object {
fun goToStartActivity(context: Context) {
val intent = Intent(
context,
StartActivity::class.java
)
val intent =
Intent(
context,
StartActivity::class.java
)
context.startActivity(intent)
}
}

View File

@ -0,0 +1,5 @@
package org.unifiedpush.distributor.nextpush.activities
import androidx.lifecycle.ViewModel
class StartViewModel : ViewModel()

View File

@ -9,6 +9,7 @@ class UiAction(val type: Type) {
enum class Type {
UpdateRegistrations,
Logout,
Login
}
fun handle(action: (Type) -> Unit) {

View File

@ -0,0 +1,46 @@
package org.unifiedpush.distributor.nextpush.activities.ui
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import org.unifiedpush.distributor.nextpush.R
@OptIn(ExperimentalPermissionsApi::class)
@Preview
@Composable
fun PermissionsUi(onDone: () -> Unit = {}) {
val notificationsPermissionState =
rememberPermissionState(
android.Manifest.permission.POST_NOTIFICATIONS
)
if (!notificationsPermissionState.status.isGranted) {
AlertDialog(
title = {
Text(stringResource(R.string.dialog_permissions_title))
},
text = {
Text(stringResource(R.string.dialog_permissions_content))
},
onDismissRequest = {
onDone()
},
confirmButton = {
TextButton(
onClick = {
notificationsPermissionState.launchPermissionRequest()
onDone
}
) {
Text(stringResource(android.R.string.ok))
}
},
dismissButton = {}
)
}
}

View File

@ -0,0 +1,191 @@
package org.unifiedpush.distributor.nextpush.activities.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.unifiedpush.distributor.nextpush.R
import org.unifiedpush.distributor.nextpush.activities.AppAction
import org.unifiedpush.distributor.nextpush.activities.StartViewModel
import org.unifiedpush.distributor.nextpush.activities.publishAction
import org.unifiedpush.distributor.nextpush.activities.ui.theme.nextcloud
@Composable
fun StartUi(viewModel: StartViewModel, showManualLogin: Boolean = false) {
var showManualLogin by remember { mutableStateOf(showManualLogin) }
var showPassword by remember { mutableStateOf(false) }
var usernameValue by remember { mutableStateOf("") }
var passwordValue by remember { mutableStateOf("www") }
var urlValue by remember { mutableStateOf("https://") }
var showPermissionDialog by remember { mutableStateOf(true) }
if (showPermissionDialog) {
PermissionsUi {
showPermissionDialog = false
}
}
Column(
modifier = Modifier
.background(nextcloud)
.fillMaxSize()
.imePadding(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(R.drawable.ic_logo),
contentDescription = "NextPush Logo"
)
Text(
text = stringResource(R.string.app_name),
style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Bold),
color = Color.White
)
Spacer(Modifier.height(16.dp))
Button(
colors = ButtonDefaults.buttonColors(containerColor = Color.White),
onClick = {
viewModel.publishAction(AppAction(AppAction.Type.LoginSSO))
}
) {
Text(
color = nextcloud,
fontWeight = FontWeight.Bold,
text = stringResource(R.string.button_start_sso_connection)
)
}
Spacer(Modifier.height(16.dp))
Text(
color = Color.White,
fontWeight = FontWeight.Bold,
text = stringResource(R.string.button_start_manual_login),
modifier = Modifier.clickable {
showManualLogin = !showManualLogin
}
)
if (showManualLogin) {
Spacer(Modifier.height(16.dp))
TextField(
colors = textFieldColor(),
value = usernameValue,
onValueChange = { usernameValue = it },
label = { Text(stringResource(R.string.login_hint_username)) },
maxLines = 1
)
Spacer(Modifier.height(16.dp))
TextField(
colors = textFieldColor(),
value = passwordValue,
onValueChange = { passwordValue = it },
label = { Text(stringResource(R.string.login_hint_password)) },
maxLines = 1,
visualTransformation = if (showPassword) {
VisualTransformation.None
} else {
PasswordVisualTransformation()
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
trailingIcon = {
IconButton(
onClick = { showPassword = !showPassword }
) {
Icon(
painter = painterResource(
if (showPassword) {
R.drawable.ic_visibility_24
} else {
R.drawable.ic_visibility_off_24
}
),
tint = Color.White,
contentDescription = if (showPassword) {
"Hide password"
} else {
"Show password"
}
)
}
}
)
Spacer(Modifier.height(16.dp))
TextField(
colors = textFieldColor(),
value = urlValue,
onValueChange = { urlValue = it },
label = { Text(stringResource(R.string.login_hint_nextcloud_root_url)) },
maxLines = 1
)
Spacer(Modifier.height(16.dp))
Button(
colors = ButtonDefaults.buttonColors(containerColor = Color.White),
onClick = {
viewModel.publishAction(
AppAction(
AppAction.Type.LoginDirect,
mapOf(
AppAction.ARG_USERNAME to usernameValue,
AppAction.ARG_PASSWORD to passwordValue,
AppAction.ARG_URL to urlValue
)
)
)
}
) {
Text(
color = nextcloud,
fontWeight = FontWeight.Bold,
text = stringResource(R.string.login_button_sign_in)
)
}
}
}
}
@Composable
private fun textFieldColor(): TextFieldColors {
return TextFieldDefaults.colors(
focusedContainerColor = nextcloud,
unfocusedContainerColor = nextcloud,
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
focusedLabelColor = Color.White,
unfocusedLabelColor = Color.White
)
}
@Preview
@Composable
fun PreviewStartUi() {
StartUi(StartViewModel(), true)
}

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
</vector>

View File

@ -1,134 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/nextcloud">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:animateLayoutChanges="true"
android:orientation="vertical"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:gravity="center"
android:text="@string/app_name"
android:textColor="@color/material_grey_100"
android:textSize="20sp"
android:textStyle="bold"
app:drawableTopCompat="@drawable/ic_logo" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_sso_login"
style="@style/NextcloudButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/sso_connection_button"
app:cornerRadius="24dp" />
<TextView
android:id="@+id/manual_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:gravity="center"
android:text="@string/manual_login"
android:textColor="@color/material_grey_100" />
<LinearLayout
android:id="@+id/manual_login_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/username_container"
style="@style/TextInputLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/TextInputLayoutAppearance">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edt_username"
style="@style/TextInputEditTextLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_hint_username"
android:inputType="textEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/password_container"
style="@style/TextInputLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/TextInputLayoutAppearance"
app:endIconMode="password_toggle"
app:endIconTint="@color/white">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edt_password"
style="@style/TextInputEditTextLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_hint_password"
android:inputType="textPassword"
android:maxLines="1"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/url_container"
style="@style/TextInputLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/TextInputLayoutAppearance">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edt_url"
style="@style/TextInputEditTextLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_hint_nextcloud_root_url"
android:inputType="textUri"
android:maxLines="1"
android:selectAllOnFocus="false"
android:singleLine="true"
android:theme="@style/TextInputEditTextLogin" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_manual_login"
style="@style/NextcloudButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="32dp"
android:text="@string/login_button_sign_in"
app:cornerRadius="24dp" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -3,7 +3,7 @@
<string name="foreground_notif_description">Notification to run in the foreground</string>
<string name="foreground_notif_content_no_reg">Waiting for registration to connect</string>
<string name="foreground_notif_content_with_reg">Connected for %s registration(s)</string>
<string name="sso_connection_button">Login with the Nextcloud File application</string>
<string name="button_start_sso_connection">Login with the Nextcloud File application</string>
<string name="logout_alert_title">Logout</string>
<string name="logout_alert_content">Confirm to logout</string>
<string name="main_account_desc">You are connected as %s</string>
@ -27,7 +27,7 @@
<string name="nextcloud_files_not_found_title">Nextcloud Files not found</string>
<string name="no_notification_dialog_title">Notifications refused</string>
<string name="no_notification_dialog_message">You won\'t be notified when the application is disconnected.\nYou can enable notifications later in the settings.</string>
<string name="manual_login">Manual login</string>
<string name="button_start_manual_login">Manual login</string>
<string name="login_hint_username">Username</string>
<string name="login_hint_password">Application password</string>
<string name="login_hint_nextcloud_root_url">Nextcloud root url</string>
@ -49,4 +49,6 @@
<string name="bar_unregister_title">%d selected</string>
<string name="bar_unregister_back_description">Unselect all</string>
<string name="bar_unregister_delete_description">Unregister selection</string>
<string name="dialog_permissions_title">Permissions</string>
<string name="dialog_permissions_content">This application requires notifications permission to work.</string>
</resources>

View File

@ -1,4 +1,5 @@
[versions]
accompanistPermissions = "0.36.0"
android-gradle-plugin = "8.7.2"
androidx-activityCompose = "1.9.3"
androidx-constraintlayout = "2.2.0"
@ -20,6 +21,7 @@ material3Android = "1.3.1"
uiTooling = "1.7.5"
[libraries]
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" }
androidx-coordinatorlayout = { module = "androidx.coordinatorlayout:coordinatorlayout", version.ref = "androidx-coordinatorlayout" }