Port Nextcloud login UI to compose
This commit is contained in:
parent
9822777acb
commit
4485b59ade
@ -54,7 +54,6 @@
|
||||
android:label="@string/settings"
|
||||
android:launchMode="singleTop"
|
||||
android:parentActivityName=".launcher.LauncherActivity"
|
||||
android:taskAffinity="de.mm20.launcher2.settings"
|
||||
android:theme="@style/SettingsTheme.NoActionBar"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
>
|
||||
|
||||
@ -10,7 +10,7 @@ pluginSdk = "2.2.0-SNAPSHOT"
|
||||
|
||||
gradle = "8.1.2"
|
||||
android-gradle-plugin = "8.6.1"
|
||||
ksp-gradle-plugin = "2.1.10-1.0.29"
|
||||
ksp-gradle-plugin = "2.1.20-2.0.0"
|
||||
|
||||
kotlin = "2.1.20"
|
||||
kotlinx-coroutines = "1.9.0"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.plugin.compose)
|
||||
}
|
||||
|
||||
android {
|
||||
@ -28,7 +29,7 @@ android {
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
compose = true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
@ -39,11 +40,11 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation(libs.bundles.kotlin)
|
||||
implementation(libs.androidx.activitycompose)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.core)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.materialcomponents.core)
|
||||
implementation(libs.androidx.browser)
|
||||
implementation(libs.androidx.constraintlayout.views)
|
||||
implementation(libs.androidx.securitycrypto)
|
||||
|
||||
implementation(libs.bundles.androidx.lifecycle)
|
||||
|
||||
@ -2,10 +2,11 @@
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
|
||||
android:name="de.mm20.launcher2.nextcloud.LoginActivity"
|
||||
android:label="@string/preference_nextcloud"
|
||||
android:taskAffinity="de.mm20.launcher2.nextcloud"
|
||||
android:theme="@style/NextcloudLoginTheme" />
|
||||
android:launchMode="singleTop"
|
||||
android:enableOnBackInvokedCallback="true"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -2,41 +2,135 @@ package de.mm20.launcher2.nextcloud
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import de.mm20.launcher2.nextcloud.databinding.ActivityNextcloudLoginBinding
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class LoginActivity : AppCompatActivity() {
|
||||
|
||||
private val nextcloudClient = NextcloudApiHelper(this)
|
||||
|
||||
private lateinit var binding: ActivityNextcloudLoginBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityNextcloudLoginBinding.inflate(LayoutInflater.from(this))
|
||||
setContentView(binding.root)
|
||||
binding.nextButton.setOnClickListener {
|
||||
binding.serverUrlInputLayout.error = null
|
||||
lifecycleScope.launch {
|
||||
var url = binding.serverUrlInput.text.toString()
|
||||
if (!(url.startsWith("http://") || url.startsWith("https://"))) {
|
||||
url = "https://$url"
|
||||
|
||||
setContent {
|
||||
MaterialTheme(
|
||||
colorScheme = if (isSystemInDarkTheme()) nextcloudDark else nextcloudLight
|
||||
) {
|
||||
var nextcloudUrl by remember { mutableStateOf("") }
|
||||
var error by remember { mutableStateOf<String?>(null) }
|
||||
var loading by remember { mutableStateOf(false) }
|
||||
|
||||
val dark = isSystemInDarkTheme()
|
||||
|
||||
LaunchedEffect(dark) {
|
||||
enableEdgeToEdge(
|
||||
statusBarStyle = if (dark) SystemBarStyle.dark(0) else SystemBarStyle.light(
|
||||
0,
|
||||
0x33000000.toInt()
|
||||
),
|
||||
navigationBarStyle = if (dark) SystemBarStyle.dark(0) else SystemBarStyle.light(
|
||||
0,
|
||||
0x33000000.toInt()
|
||||
),
|
||||
)
|
||||
}
|
||||
if (url.isBlank()) {
|
||||
binding.serverUrlInputLayout.error = getString(R.string.nextcloud_server_url_empty)
|
||||
return@launch
|
||||
}
|
||||
if (nextcloudClient.checkNextcloudInstallation(url)) {
|
||||
openLoginPage(url)
|
||||
} else {
|
||||
binding.serverUrlInputLayout.error = getString(R.string.nextcloud_server_invalid_url)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.padding(32.dp)
|
||||
.systemBarsPadding()
|
||||
.imePadding(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(
|
||||
painterResource(R.drawable.ic_nextcloud_logo),
|
||||
contentDescription = "Nextcloud Logo",
|
||||
colorFilter = ColorFilter.tint(
|
||||
MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
)
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 32.dp),
|
||||
label = {
|
||||
Text(stringResource(R.string.nextcloud_server_url))
|
||||
},
|
||||
value = nextcloudUrl,
|
||||
onValueChange = { nextcloudUrl = it },
|
||||
enabled = !loading,
|
||||
isError = error != null,
|
||||
supportingText = error?.let { { Text(it) } },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Uri,
|
||||
)
|
||||
)
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = nextcloudUrl.isNotBlank() && !loading,
|
||||
onClick = {
|
||||
lifecycleScope.launch {
|
||||
loading = true
|
||||
error = null
|
||||
var url = nextcloudUrl
|
||||
if (!(url.startsWith("http://") || url.startsWith("https://"))) {
|
||||
url = "https://$url"
|
||||
}
|
||||
if (nextcloudClient.checkNextcloudInstallation(url)) {
|
||||
openLoginPage(url)
|
||||
} else {
|
||||
error = getString(R.string.nextcloud_server_invalid_url)
|
||||
}
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.login_flow_continue))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,7 +139,10 @@ class LoginActivity : AppCompatActivity() {
|
||||
val webView = WebView(this)
|
||||
webView.settings.userAgentString = getString(R.string.app_name)
|
||||
webView.webViewClient = object : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
): Boolean {
|
||||
if (request?.url?.scheme == "nc") {
|
||||
val path = request.url?.path?.trim('/') ?: run {
|
||||
setResult(0)
|
||||
@ -82,4 +179,76 @@ class LoginActivity : AppCompatActivity() {
|
||||
webView.loadUrl("$url/index.php/login/flow", headers)
|
||||
|
||||
}
|
||||
|
||||
private val nextcloudLight: ColorScheme
|
||||
get() = lightColorScheme(
|
||||
primary = Color(0xFF00639B),
|
||||
onPrimary = Color(0xFFFFFFFF),
|
||||
primaryContainer = Color(0xFFCEE5FF),
|
||||
onPrimaryContainer = Color(0xFF001D33),
|
||||
inversePrimary = Color(0xFF96CBFF),
|
||||
secondary = Color(0xFF51606F),
|
||||
onSecondary = Color(0xFFFFFFFF),
|
||||
secondaryContainer = Color(0xFFD4E4F6),
|
||||
onSecondaryContainer = Color(0xFF0D1D2A),
|
||||
tertiary = Color(0xFF68587A),
|
||||
onTertiary = Color(0xFFFFFFFF),
|
||||
tertiaryContainer = Color(0xFFEEDBFF),
|
||||
onTertiaryContainer = Color(0xFF231533),
|
||||
surface = Color(0xFFF9F9FC),
|
||||
surfaceBright = Color(0xFFF9F9FC),
|
||||
surfaceDim = Color(0xFFD9DADD),
|
||||
surfaceContainer = Color(0xFFEDEEF1),
|
||||
surfaceContainerHighest = Color(0xFFE2E2E5),
|
||||
surfaceContainerHigh = Color(0xFFE8E8EB),
|
||||
surfaceContainerLow = Color(0xFFF3F3F6),
|
||||
surfaceContainerLowest = Color(0xFFFFFFFF),
|
||||
onSurface = Color(0xFF1A1C1E),
|
||||
onSurfaceVariant = Color(0xFF42474E),
|
||||
inverseSurface = Color(0xFF2F3133),
|
||||
inverseOnSurface = Color(0xFFF0F0F3),
|
||||
error = Color(0xFFBA1A1A),
|
||||
onError = Color(0xFFFFFFFF),
|
||||
errorContainer = Color(0xFFFFDAD5),
|
||||
onErrorContainer = Color(0xFF410002),
|
||||
outline = Color(0xFF72787F),
|
||||
outlineVariant = Color(0xFFC2C7CF),
|
||||
scrim = Color(0xFF000000),
|
||||
)
|
||||
|
||||
private val nextcloudDark: ColorScheme
|
||||
get() = darkColorScheme(
|
||||
primary = Color(0xFF96CBFF),
|
||||
onPrimary = Color(0xFF003353),
|
||||
primaryContainer = Color(0xFF004A76),
|
||||
onPrimaryContainer = Color(0xFFCEE5FF),
|
||||
inversePrimary = Color(0xFF00639B),
|
||||
secondary = Color(0xFFB9C8DA),
|
||||
onSecondary = Color(0xFF23323F),
|
||||
secondaryContainer = Color(0xFF394857),
|
||||
onSecondaryContainer = Color(0xFFD4E4F6),
|
||||
tertiary = Color(0xFFD3BFE6),
|
||||
onTertiary = Color(0xFF382A49),
|
||||
tertiaryContainer = Color(0xFF4F4061),
|
||||
onTertiaryContainer = Color(0xFFEEDBFF),
|
||||
surface = Color(0xFF1A1C1E),
|
||||
surfaceBright = Color(0xFF37393C),
|
||||
surfaceDim = Color(0xFF111416),
|
||||
surfaceContainer = Color(0xFF1E2022),
|
||||
surfaceContainerHighest = Color(0xFF333537),
|
||||
surfaceContainerHigh = Color(0xFF282A2D),
|
||||
surfaceContainerLow = Color(0xFF1A1C1E),
|
||||
surfaceContainerLowest = Color(0xFF0C0E11),
|
||||
onSurface = Color(0xFFE2E2E5),
|
||||
onSurfaceVariant = Color(0xFFC2C7CF),
|
||||
inverseSurface = Color(0xFFE2E2E5),
|
||||
inverseOnSurface = Color(0xFF2F3133),
|
||||
error = Color(0xFFFFB4AB),
|
||||
onError = Color(0xFF690004),
|
||||
errorContainer = Color(0xFF930009),
|
||||
onErrorContainer = Color(0xFFFFB4AB),
|
||||
outline = Color(0xFF8C9198),
|
||||
outlineVariant = Color(0xFF42474E),
|
||||
scrim = Color(0xFF000000),
|
||||
)
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout 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">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="32dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_nextcloud_logo" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/serverUrlInputLayout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:hint="@string/nextcloud_server_url"
|
||||
app:helperTextEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/serverUrlInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textUri" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/nextButton"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login_flow_continue" />
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="NextcloudLoginTheme" parent="Theme.MaterialComponents.NoActionBar">
|
||||
<item name="colorAccent">#0082c9</item>
|
||||
<item name="colorPrimary">#0082c9</item>
|
||||
<item name="colorPrimaryDark">@color/settings_color_primary_dark</item>
|
||||
</style>
|
||||
</resources>
|
||||
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="NextcloudLoginTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
<item name="android:windowLightNavigationBar">true</item>
|
||||
<item name="colorAccent">#0082c9</item>
|
||||
<item name="colorPrimary">#0082c9</item>
|
||||
<item name="colorPrimaryDark">@color/settings_color_primary_dark</item>
|
||||
<item name="android:navigationBarColor">@color/settings_color_primary_dark</item>
|
||||
</style>
|
||||
</resources>
|
||||
Loading…
x
Reference in New Issue
Block a user