Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
19c9da8
feat: unified share
alperozturk96 Apr 20, 2026
569b392
add api models
alperozturk96 Apr 21, 2026
362f4c3
adopt mock models to api models
alperozturk96 Apr 21, 2026
38c34f0
add translations, bind ui actions
alperozturk96 Apr 21, 2026
73869d4
add translations, bind ui actions
alperozturk96 Apr 21, 2026
de4598e
wip
alperozturk96 Apr 21, 2026
3f72cec
wip
alperozturk96 May 5, 2026
3cc1841
add test ocs call
alperozturk96 Apr 24, 2026
4e242b8
fix test call
alperozturk96 May 5, 2026
5b73728
use client
alperozturk96 May 5, 2026
1bb4574
wip
alperozturk96 May 5, 2026
6a76774
wip
alperozturk96 May 5, 2026
0433bf0
wip
alperozturk96 May 5, 2026
cecb3e9
wip
alperozturk96 May 6, 2026
f8e9e89
wip
alperozturk96 May 6, 2026
66f177c
wip
alperozturk96 May 27, 2026
ce736aa
wip
alperozturk96 May 27, 2026
6adec5b
wip
alperozturk96 May 27, 2026
c1aa2ad
add empty view state
alperozturk96 May 27, 2026
3f9087a
wip
alperozturk96 May 27, 2026
6392ed0
wip
alperozturk96 May 28, 2026
22f7e36
wip
alperozturk96 May 28, 2026
1352224
wip
alperozturk96 May 28, 2026
ee8ee78
wip
alperozturk96 May 29, 2026
ab810bc
wip
alperozturk96 May 29, 2026
0050987
wip
alperozturk96 May 29, 2026
76e624d
wip
alperozturk96 May 29, 2026
408f0e8
wip
alperozturk96 May 29, 2026
3d60ff3
wip
alperozturk96 May 29, 2026
46c04be
wip
alperozturk96 May 29, 2026
a7fbfab
wip
alperozturk96 Jun 1, 2026
ff1afc4
wip
alperozturk96 Jun 1, 2026
4cdfa97
wip
alperozturk96 Jun 1, 2026
872e439
wip
alperozturk96 Jun 1, 2026
1f6dce7
wip
alperozturk96 Jun 1, 2026
6d5f217
wip
alperozturk96 Jun 1, 2026
4f6e597
wip
alperozturk96 Jun 1, 2026
e8b64c4
wip
alperozturk96 Jun 1, 2026
d1ef5d0
wip
alperozturk96 Jun 1, 2026
720ad20
wip
alperozturk96 Jun 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
<trusted-key id="19BEAB2D799C020F17C69126B16698A4ADF4D638" group="org.checkerframework"/>
<trusted-key id="1A55F091AD28C07F831FA44D7905DE25C78AD456" group="com.google.protobuf"/>
<trusted-key id="1D0A8B5E77C678A7C724445ABF984B4145EA13F7" group="com.squareup" name="javapoet" version="1.13.0"/>
<trusted-key id="1D217F8475EEE9F19AB8DD6B793FD5751A0F0780" group="^com[.]squareup($|([.].*))" regex="true"/>
<trusted-key id="1D2C7EF8ADA0F794B58C7C63436902AF59EDF60E">
<trusting group="dev.equo.ide" name="solstice" version="1.7.5"/>
<trusting group="dev.equo.ide" name="solstice" version="1.8.0"/>
Expand Down Expand Up @@ -240,6 +241,7 @@
<trusting group="androidx.graphics" name="graphics-path" version="1.0.1"/>
<trusting group="androidx.lifecycle"/>
<trusting group="androidx.profileinstaller"/>
<trusting group="androidx.startup" name="startup-runtime" version="1.2.0"/>
<trusting group="androidx.transition" name="transition" version="1.5.0"/>
<trusting group="androidx.webkit"/>
<trusting group="^androidx[.]compose($|([.].*))" regex="true"/>
Expand Down Expand Up @@ -329,6 +331,11 @@
<sha256 value="c8923871e556cd5467addabac6773e778f3a4d3da19bfc8153bbaee0d145298f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.activity" name="activity-compose" version="1.7.0">
<artifact name="activity-compose-1.7.0.module">
<sha256 value="f7a29bcba338575dcf89a553cff9cfad3f140340eaf2b56fd0193244da602c0a" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="androidx.activity" name="activity-compose" version="1.8.2">
<artifact name="activity-compose-1.8.2.aar">
<sha256 value="5a67e984f14ed2afc585aa3a23edff1c1791c80caa2bf68a0f799c1b11a39038" origin="Generated by Gradle"/>
Expand Down Expand Up @@ -20771,6 +20778,11 @@
<sha256 value="35bbd365a61afa59ad8c116528bced5bd628d3351704173abfc8b75fec04ffad" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin.plugin.serialization" name="org.jetbrains.kotlin.plugin.serialization.gradle.plugin" version="2.3.21">
<artifact name="org.jetbrains.kotlin.plugin.serialization.gradle.plugin-2.3.21.pom">
<sha256 value="d1e12aeedc9fab44409b6c08b2a45384a4bb851fe4e90391efade14d347c48d7" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu" version="0.17.3">
<artifact name="atomicfu-0.17.3.module">
<sha256 value="854a75a9ebf30cb588e8ceda7da1b7089d4272a12324d3cffcaf5b62902738bd" origin="Generated by Gradle"/>
Expand Down
1 change: 1 addition & 0 deletions material-color-utilities/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

/*
* Nextcloud Android Common Library
*
Expand Down
1 change: 1 addition & 0 deletions sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.10.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.10.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0'
implementation project(path: ':ui')
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
Expand Down
1 change: 1 addition & 0 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.Androidcommon">
<activity
android:name=".MainActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ class MainActivity : AppCompatActivity() {
material.colorMaterialButtonPrimaryBorderless(negativeButton)
}

binding.testApiBtn.setOnClickListener {
val baseUrl = binding.baseUrl.text?.toString().orEmpty()
val username = binding.username.text?.toString().orEmpty()
val token = binding.token.text?.toString().orEmpty()
mainViewModel.testPredefinedStatuses(baseUrl, username, token)
}

mainViewModel.apiTestResult.observe(this) { result ->
Toast.makeText(this, result, Toast.LENGTH_LONG).show()
}

setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
mainViewModel.color.observe(this) { applyTheme(it) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,41 @@ package com.nextcloud.android.common.sample

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.nextcloud.android.common.ui.network.auth.ServerCredentials
import com.nextcloud.android.common.ui.network.model.NetworkResult
import com.nextcloud.android.common.ui.network.http.NextcloudHttpClient
import com.nextcloud.android.common.ui.network.UserStatusRepository
import kotlinx.coroutines.launch

class MainViewModel : ViewModel() {
val color = MutableLiveData<Int>()
val apiTestResult = MutableLiveData<String>()

fun testPredefinedStatuses(
baseUrl: String,
username: String,
token: String
) {
viewModelScope.launch {
val credentials = ServerCredentials(baseUrl, username, token)
val client = NextcloudHttpClient.create(credentials, enableLogging = true)
val service = UserStatusRepository(client)

when (val result = service.fetchPredefinedStatuses()) {
is NetworkResult.Success ->
apiTestResult.value =
"✅ Success (${result.data.size} statuses):\n" +
result.data.joinToString("\n") { "${it.icon} ${it.message}" }

is NetworkResult.ServerError ->
apiTestResult.value =
"❌ Error ${result.response.ocs.meta.statusCode}: ${result.response.ocs.meta.message}"

is NetworkResult.NetworkException ->
apiTestResult.value =
"❌ Exception: ${result.throwable.message}"
}
}
}
}
61 changes: 61 additions & 0 deletions sample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,67 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/circular_progress_bar" />

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/baseUrlTil"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/hint_base_url"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/dialogBtn">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/baseUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri"
android:text="https://cloud.example.com" />

</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/usernameTil"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="@string/hint_username"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/baseUrlTil">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />

</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tokenTil"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="@string/hint_token"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/usernameTil">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/token"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />

</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.button.MaterialButton
android:id="@+id/testApiBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/test_user_status_api"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tokenTil" />

</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

Expand Down
4 changes: 4 additions & 0 deletions sample/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<string name="suggestion_chip">Suggestion Chip</string>
<string name="filter_chip">Filter Chip</string>
<string name="hint_color">Color</string>
<string name="hint_base_url">Base URL</string>
<string name="hint_username">Username</string>
<string name="hint_token">App token</string>
<string name="test_user_status_api">Test User Status API</string>
<string name="headline_theming">Theming</string>
<string name="headline_ui_module">UI Module</string>
</resources>
16 changes: 15 additions & 1 deletion ui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

plugins {
id 'org.jetbrains.kotlin.plugin.compose'
id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlinVersion"
id 'com.android.library'
id 'com.android.built-in-kotlin'
id 'com.android.legacy-kapt'
Expand All @@ -15,7 +16,7 @@ plugins {

android {
defaultConfig {
minSdk = 21
minSdk = 26
compileSdk = 36

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -45,12 +46,19 @@ android {
}

dependencies {
implementation 'androidx.compose.ui:ui-tooling-preview:1.11.0'
debugImplementation 'androidx.compose.ui:ui-tooling:1.11.0'
kapt "org.jetbrains.kotlin:kotlin-metadata-jvm:${kotlinVersion}"

implementation(platform("androidx.compose:compose-bom:2026.05.01"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material:material-icons-core")

implementation("io.coil-kt.coil3:coil-compose:3.4.0")
implementation("io.coil-kt.coil3:coil-network-okhttp:3.4.0")
implementation("io.coil-kt.coil3:coil-svg:3.4.0")

implementation("com.vanniktech:ui:0.10.0")

Expand All @@ -60,6 +68,12 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'

implementation(platform("com.squareup.okhttp3:okhttp-bom:5.3.2"))
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")

implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0")

implementation project(':core')
api project(':material-color-utilities')

Expand Down
2 changes: 1 addition & 1 deletion ui/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
~ SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
~ SPDX-License-Identifier: MIT
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Nextcloud Android Common Library
*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: MIT
*/

package com.nextcloud.android.common.ui.component

import androidx.compose.foundation.Image
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp

@Composable
fun ContentUnavailableView(title: String, description: String? = null, iconId: Int? = null) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
iconId?.let {
Image(
painter = painterResource(iconId),
modifier = Modifier.size(48.dp),
contentDescription = "",
)
}

Spacer(modifier = Modifier.height(16.dp))

Text(
text = title,
style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center
)

description?.let {
Spacer(modifier = Modifier.height(8.dp))

Text(
text = description,
style = MaterialTheme.typography.headlineSmall,
textAlign = TextAlign.Center
)
}

Spacer(modifier = Modifier.height(32.dp))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Nextcloud Android Common Library
*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: MIT
*/

package com.nextcloud.android.common.ui.network

import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonPrimitive

object ClearAtTimeSerializer : KSerializer<String> {
override val descriptor = PrimitiveSerialDescriptor("ClearAtTime", PrimitiveKind.STRING)

override fun deserialize(decoder: Decoder): String {
val jsonDecoder = decoder as? JsonDecoder ?: return decoder.decodeString()
return (jsonDecoder.decodeJsonElement() as? JsonPrimitive)?.content ?: ""
}

override fun serialize(encoder: Encoder, value: String) = encoder.encodeString(value)
}

@Serializable
data class ClearAt(
val type: String,
@Serializable(with = ClearAtTimeSerializer::class)
val time: String
)

@Serializable
data class PredefinedStatus(
val id: String,
val icon: String,
val message: String,
val clearAt: ClearAt? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Nextcloud Android Common Library
*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: MIT
*/

package com.nextcloud.android.common.ui.network

import com.nextcloud.android.common.ui.network.http.HttpMethod
import com.nextcloud.android.common.ui.network.http.NextcloudHttpClient
import com.nextcloud.android.common.ui.network.model.NetworkResult
import com.nextcloud.android.common.ui.network.model.OcsResponse
import com.nextcloud.android.common.ui.network.serialization.OCSSerializer

class UserStatusRepository(private val client: NextcloudHttpClient) {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For test activity


private companion object {
private const val PREDEFINED_STATUSES_ENDPOINT =
"/ocs/v2.php/apps/user_status/api/v1/predefined_statuses"
}

suspend fun fetchPredefinedStatuses(): NetworkResult<List<PredefinedStatus>> =
client.executeRequest(
endpoint = PREDEFINED_STATUSES_ENDPOINT,
method = HttpMethod.GET
) { body ->
OCSSerializer.json.decodeFromString<OcsResponse<List<PredefinedStatus>>>(body).ocs.data
}
}
Loading
Loading