Merge pull request #12612 from nextcloud/feature/assistant
Assistant Feature
|
@ -2,9 +2,45 @@
|
|||
<profile version="1.0">
|
||||
<option name="myName" value="ktlint" />
|
||||
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.nio.channels.FileChannel,position" />
|
||||
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.nio.channels.FileChannel,position,okhttp3.Call,execute" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="KotlinUnusedImport" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="RedundantSemicolon" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
</component>
|
|
@ -19,7 +19,7 @@ buildscript {
|
|||
|
||||
plugins {
|
||||
id "com.diffplug.spotless" version "6.20.0"
|
||||
id 'com.google.devtools.ksp' version '1.9.23-1.0.19' apply false
|
||||
id 'com.google.devtools.ksp' version '1.9.22-1.0.17' apply false
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
@ -223,6 +223,7 @@ android {
|
|||
dataBinding true
|
||||
viewBinding true
|
||||
aidl true
|
||||
compose = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
@ -246,6 +247,10 @@ android {
|
|||
// Adds exported schema location as test app assets.
|
||||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.10"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -255,6 +260,14 @@ dependencies {
|
|||
exclude group: 'org.ogce', module: 'xpp3' // unused in Android and brings wrong Junit version
|
||||
}
|
||||
|
||||
// Jetpack Compose
|
||||
implementation(platform("androidx.compose:compose-bom:2024.02.01"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview:1.6.2")
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling:1.6.2'
|
||||
|
||||
compileOnly 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2'
|
||||
// remove after entire switch to lib v2
|
||||
implementation "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2
|
||||
|
|
|
@ -199,7 +199,7 @@ naming:
|
|||
minimumFunctionNameLength: 3
|
||||
FunctionNaming:
|
||||
active: true
|
||||
functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
|
||||
functionPattern: '^([a-z$A-Z][a-zA-Z$0-9]*)|(`.*`)$'
|
||||
excludeClassPattern: '$^'
|
||||
ignoreOverridden: true
|
||||
excludes:
|
||||
|
|
|
@ -1194,4 +1194,4 @@
|
|||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f26afed3b9b87a3acb578947a26223ac')"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1203
app/schemas/com.nextcloud.client.database.NextcloudDatabase/79.json
Normal file
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 2.4 KiB |
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
package com.nextcloud.client
|
||||
|
||||
import android.view.View
|
||||
import androidx.test.espresso.Espresso
|
||||
import androidx.test.espresso.contrib.DrawerActions
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
|
@ -60,15 +61,19 @@ class ActivitiesActivityIT : AbstractIT() {
|
|||
@Test
|
||||
@ScreenshotTest
|
||||
fun loading() {
|
||||
val sut: ActivitiesActivity = activityRule.launchActivity(null)
|
||||
sut.runOnUiThread {
|
||||
sut.dismissSnackbar()
|
||||
val sut: ActivitiesActivity = activityRule.launchActivity(null).apply {
|
||||
runOnUiThread {
|
||||
dismissSnackbar()
|
||||
binding.emptyList.root.visibility = View.GONE
|
||||
binding.swipeContainingList.visibility = View.GONE
|
||||
binding.loadingContent.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
Screenshot.snapActivity(sut).record()
|
||||
Screenshot.snap(sut.binding.loadingContent).record()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.content.Intent;
|
|||
|
||||
import com.nextcloud.client.preferences.SubFolderRule;
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.databinding.SyncedFoldersLayoutBinding;
|
||||
import com.owncloud.android.datamodel.MediaFolderType;
|
||||
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
|
||||
import com.owncloud.android.ui.activity.SyncedFoldersActivity;
|
||||
|
@ -51,9 +52,11 @@ public class SyncedFoldersActivityIT extends AbstractIT {
|
|||
@Test
|
||||
@ScreenshotTest
|
||||
public void open() {
|
||||
Activity sut = activityRule.launchActivity(null);
|
||||
|
||||
screenshot(sut);
|
||||
SyncedFoldersActivity activity = activityRule.launchActivity(null);
|
||||
activity.adapter.clear();
|
||||
SyncedFoldersLayoutBinding sut = activity.binding;
|
||||
shortSleep();
|
||||
screenshot(sut.emptyList.emptyListView);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant
|
||||
|
||||
import com.nextcloud.client.assistant.repository.AssistantRepository
|
||||
import com.owncloud.android.AbstractOnServerIT
|
||||
import com.owncloud.android.lib.resources.status.NextcloudVersion
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class AssistantRepositoryTests : AbstractOnServerIT() {
|
||||
|
||||
private var sut: AssistantRepository? = null
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
sut = AssistantRepository(nextcloudClient)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetTaskTypes() {
|
||||
testOnlyOnServer(NextcloudVersion.nextcloud_28)
|
||||
|
||||
if (capability.assistant.isFalse) {
|
||||
return
|
||||
}
|
||||
|
||||
val result = sut?.getTaskTypes()
|
||||
assertTrue(result?.isSuccess == true)
|
||||
|
||||
val taskTypes = result?.resultData?.types
|
||||
assertTrue(taskTypes?.isNotEmpty() == true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetTaskList() {
|
||||
testOnlyOnServer(NextcloudVersion.nextcloud_28)
|
||||
|
||||
if (capability.assistant.isFalse) {
|
||||
return
|
||||
}
|
||||
|
||||
val result = sut?.getTaskList("assistant")
|
||||
assertTrue(result?.isSuccess == true)
|
||||
|
||||
val taskList = result?.resultData?.tasks
|
||||
assertTrue(taskList?.isEmpty() == true || (taskList?.size ?: 0) > 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateTask() {
|
||||
testOnlyOnServer(NextcloudVersion.nextcloud_28)
|
||||
|
||||
if (capability.assistant.isFalse) {
|
||||
return
|
||||
}
|
||||
|
||||
val input = "Give me some random output for test purpose"
|
||||
val type = "OCP\\TextProcessing\\FreePromptTaskType"
|
||||
val result = sut?.createTask(input, type)
|
||||
assertTrue(result?.isSuccess == true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeleteTask() {
|
||||
testOnlyOnServer(NextcloudVersion.nextcloud_28)
|
||||
|
||||
if (capability.assistant.isFalse) {
|
||||
return
|
||||
}
|
||||
|
||||
testCreateTask()
|
||||
|
||||
sleep(120)
|
||||
|
||||
val resultOfTaskList = sut?.getTaskList("assistant")
|
||||
assertTrue(resultOfTaskList?.isSuccess == true)
|
||||
|
||||
sleep(120)
|
||||
|
||||
val taskList = resultOfTaskList?.resultData?.tasks
|
||||
|
||||
assert((taskList?.size ?: 0) > 0)
|
||||
|
||||
val result = sut?.deleteTask(taskList!!.first().id)
|
||||
assertTrue(result?.isSuccess == true)
|
||||
}
|
||||
}
|
|
@ -195,13 +195,18 @@ public abstract class AbstractIT {
|
|||
}
|
||||
|
||||
protected void testOnlyOnServer(OwnCloudVersion version) throws AccountUtils.AccountNotFoundException {
|
||||
OCCapability ocCapability = getCapability();
|
||||
assumeTrue(ocCapability.getVersion().isNewerOrEqual(version));
|
||||
}
|
||||
|
||||
protected OCCapability getCapability() throws AccountUtils.AccountNotFoundException {
|
||||
NextcloudClient client = OwnCloudClientFactory.createNextcloudClient(user, targetContext);
|
||||
|
||||
OCCapability ocCapability = (OCCapability) new GetCapabilitiesRemoteOperation()
|
||||
.execute(client)
|
||||
.getSingleData();
|
||||
|
||||
assumeTrue(ocCapability.getVersion().isNewerOrEqual(version));
|
||||
return ocCapability;
|
||||
}
|
||||
|
||||
@Before
|
||||
|
@ -334,6 +339,14 @@ public abstract class AbstractIT {
|
|||
}
|
||||
}
|
||||
|
||||
protected void sleep(int second) {
|
||||
try {
|
||||
Thread.sleep(1000L * second);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public OCFile createFolder(String remotePath) {
|
||||
RemoteOperationResult check = new ExistenceCheckRemoteOperation(remotePath, false).execute(client);
|
||||
|
||||
|
|
|
@ -53,8 +53,6 @@ class NotificationsActivityIT : AbstractIT() {
|
|||
@ScreenshotTest
|
||||
@SuppressWarnings("MagicNumber")
|
||||
fun showNotifications() {
|
||||
val sut: NotificationsActivity = activityRule.launchActivity(null)
|
||||
|
||||
val date = GregorianCalendar()
|
||||
date.set(2005, 4, 17, 10, 35, 30) // random date
|
||||
|
||||
|
@ -133,11 +131,13 @@ class NotificationsActivityIT : AbstractIT() {
|
|||
)
|
||||
)
|
||||
|
||||
sut.runOnUiThread { sut.populateList(notifications) }
|
||||
|
||||
shortSleep()
|
||||
|
||||
screenshot(sut)
|
||||
activityRule.launchActivity(null).apply {
|
||||
runOnUiThread {
|
||||
populateList(notifications)
|
||||
}
|
||||
shortSleep()
|
||||
screenshot(binding.list)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -72,7 +72,7 @@ class UploadFilesActivityIT : AbstractIT() {
|
|||
waitForIdleSync()
|
||||
shortSleep()
|
||||
|
||||
screenshot(sut)
|
||||
screenshot(sut.fileListFragment.binding.emptyList.emptyListView)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -94,12 +94,6 @@ class FileDetailFragmentStaticServerIT : AbstractIT() {
|
|||
@ScreenshotTest
|
||||
@Suppress("MagicNumber")
|
||||
fun showDetailsActivities() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
val sut = FileDetailFragment.newInstance(oCFile, user, 0)
|
||||
activity.addFragment(sut)
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
val date = GregorianCalendar()
|
||||
date.set(2005, 4, 17, 10, 35, 30) // random date
|
||||
|
||||
|
@ -152,13 +146,16 @@ class FileDetailFragmentStaticServerIT : AbstractIT() {
|
|||
)
|
||||
)
|
||||
|
||||
activity.runOnUiThread {
|
||||
sut.fileDetailActivitiesFragment.populateList(activities as List<Any>?, true)
|
||||
val sut = FileDetailFragment.newInstance(oCFile, user, 0)
|
||||
testActivityRule.launchActivity(null).apply {
|
||||
addFragment(sut)
|
||||
waitForIdleSync()
|
||||
runOnUiThread {
|
||||
sut.fileDetailActivitiesFragment.populateList(activities as List<Any>?, true)
|
||||
}
|
||||
longSleep()
|
||||
screenshot(sut.fileDetailActivitiesFragment.binding.swipeContainingList)
|
||||
}
|
||||
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
screenshot(activity)
|
||||
}
|
||||
|
||||
// @Test
|
||||
|
@ -176,7 +173,7 @@ class FileDetailFragmentStaticServerIT : AbstractIT() {
|
|||
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
screenshot(activity)
|
||||
screenshot(sut.fileDetailActivitiesFragment.binding.list)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -197,7 +194,7 @@ class FileDetailFragmentStaticServerIT : AbstractIT() {
|
|||
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
screenshot(activity)
|
||||
screenshot(sut.fileDetailActivitiesFragment.binding.emptyList.emptyListView)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -89,7 +89,7 @@ class TrashbinActivityIT : AbstractIT() {
|
|||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(sut)
|
||||
screenshot(sut.binding.emptyList.emptyListView)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -105,7 +105,7 @@ class TrashbinActivityIT : AbstractIT() {
|
|||
|
||||
shortSleep()
|
||||
|
||||
screenshot(sut)
|
||||
screenshot(sut.binding.listFragmentLayout)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -610,7 +610,7 @@ public class EncryptionTestIT extends AbstractIT {
|
|||
EncryptionUtils.encryptFileDropFiles(decryptedFolderMetadata1, encryptedFolderMetadata1, publicKey);
|
||||
|
||||
// serialize
|
||||
String encryptedJson = serializeJSON(encryptedFolderMetadata1);
|
||||
String encryptedJson = serializeJSON(encryptedFolderMetadata1, true);
|
||||
|
||||
// de-serialize
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
|
@ -626,8 +626,8 @@ public class EncryptionTestIT extends AbstractIT {
|
|||
folderID);
|
||||
|
||||
// compare
|
||||
assertFalse(compareJsonStrings(serializeJSON(decryptedFolderMetadata1),
|
||||
serializeJSON(decryptedFolderMetadata2)));
|
||||
assertFalse(compareJsonStrings(serializeJSON(decryptedFolderMetadata1, true),
|
||||
serializeJSON(decryptedFolderMetadata2, true)));
|
||||
|
||||
assertEquals(decryptedFolderMetadata1.getFiles().size() + decryptedFolderMetadata1.getFiledrop().size(),
|
||||
decryptedFolderMetadata2.getFiles().size());
|
||||
|
|
|
@ -831,7 +831,7 @@ class EncryptionUtilsV2IT : AbstractIT() {
|
|||
val signature = encryptionUtilsV2.getMessageSignature(enc1Cert, enc1PrivateKey, encryptedFolderMetadata1)
|
||||
|
||||
// serialize
|
||||
val encryptedJson = EncryptionUtils.serializeJSON(encryptedFolderMetadata1)
|
||||
val encryptedJson = EncryptionUtils.serializeJSON(encryptedFolderMetadata1, true)
|
||||
|
||||
// de-serialize
|
||||
val encryptedFolderMetadata2 = EncryptionUtils.deserializeJSON(
|
||||
|
|
|
@ -1,22 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2012 Bartek Przybylski
|
||||
Copyright (C) 2012-2016 ownCloud Inc.
|
||||
Copyright (C) 2016 Nextcloud
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
|
@ -24,16 +6,15 @@
|
|||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
|
||||
|
||||
<!-- Used for document scanning, but lib declares it as required, which it's not -->
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR" /> <!-- Used for document scanning, but lib declares it as required, which it's not -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera2"
|
||||
android:required="false"
|
||||
tools:node="replace" />
|
||||
|
||||
<!-- WRITE_EXTERNAL_STORAGE may be enabled or disabled by the user after installation in
|
||||
API >= 23; the app needs to handle this -->
|
||||
<!--
|
||||
WRITE_EXTERNAL_STORAGE may be enabled or disabled by the user after installation in
|
||||
API >= 23; the app needs to handle this
|
||||
-->
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"
|
||||
|
@ -45,9 +26,7 @@
|
|||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<!-- Next permissions are always approved in installation time, the apps needs to do nothing special in runtime -->
|
||||
<uses-permission android:name="android.permission.VIBRATE" /> <!-- Next permissions are always approved in installation time, the apps needs to do nothing special in runtime -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
|
@ -62,35 +41,72 @@
|
|||
<uses-permission
|
||||
android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
|
||||
android:maxSdkVersion="25" />
|
||||
|
||||
<!-- Apps that target Android 9 (API level 28) or higher and use foreground services
|
||||
must request the FOREGROUND_SERVICE permission -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<!-- Runtime permissions introduced in Android 13 (API level 33) -->
|
||||
<!--
|
||||
Apps that target Android 9 (API level 28) or higher and use foreground services
|
||||
must request the FOREGROUND_SERVICE permission
|
||||
-->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Runtime permissions introduced in Android 13 (API level 33) -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
|
||||
<!-- Needed for Android 14 (API level 34) -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- Needed for Android 14 (API level 34) -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
|
||||
<!-- Some Chromebooks don't support touch. Although not essential,
|
||||
it's a good idea to explicitly include this declaration. -->
|
||||
<!--
|
||||
Some Chromebooks don't support touch. Although not essential,
|
||||
it's a good idea to explicitly include this declaration.
|
||||
-->
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
|
||||
<queries>
|
||||
<package android:name="it.niedermann.nextcloud.deck" />
|
||||
<package android:name="it.niedermann.nextcloud.deck.play" />
|
||||
<package android:name="it.niedermann.nextcloud.deck.dev" />
|
||||
<package android:name="at.bitfire.davdroid" />
|
||||
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PICK" />
|
||||
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.media.action.IMAGE_CAPTURE" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.media.action.IMAGE_CAPTURE_SECURE" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.media.action.VIDEO_CAPTURE" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:name=".MainApp"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="@xml/backup_config"
|
||||
android:dataExtractionRules="@xml/backup_rules"
|
||||
android:fullBackupContent="@xml/backup_config"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:installLocation="internalOnly"
|
||||
android:label="@string/app_name"
|
||||
|
@ -101,8 +117,11 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.ownCloud.Toolbar"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:replace="android:allowBackup"
|
||||
tools:ignore="UnusedAttribute">
|
||||
tools:ignore="UnusedAttribute"
|
||||
tools:replace="android:allowBackup">
|
||||
<activity
|
||||
android:name="com.nextcloud.ui.composeActivity.ComposeActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<uses-library
|
||||
android:name="org.apache.http.legacy"
|
||||
|
@ -203,8 +222,8 @@
|
|||
<activity
|
||||
android:name=".ui.activity.SetupEncryptionActivity"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||
android:theme="@style/Theme.NoBackground"
|
||||
android:exported="false" />
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.NoBackground" />
|
||||
<activity
|
||||
android:name=".ui.activity.ContactsPreferenceActivity"
|
||||
android:exported="false"
|
||||
|
@ -217,12 +236,16 @@
|
|||
android:theme="@style/Theme.ownCloud.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
@ -236,9 +259,10 @@
|
|||
android:theme="@style/Theme.ownCloud.Overlay" />
|
||||
<activity
|
||||
android:name=".ui.preview.PreviewMediaActivity"
|
||||
android:exported="false"
|
||||
android:configChanges="orientation|screenLayout|screenSize|keyboardHidden"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.ownCloud.Media" />
|
||||
|
||||
<service
|
||||
android:name=".authentication.AccountAuthenticatorService"
|
||||
android:exported="false">
|
||||
|
@ -264,8 +288,8 @@
|
|||
</service>
|
||||
<service
|
||||
android:name="com.nextcloud.client.widget.DashboardWidgetService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS"
|
||||
android:exported="true" />
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
|
||||
<provider
|
||||
android:name=".providers.FileContentProvider"
|
||||
|
@ -303,14 +327,12 @@
|
|||
android:readPermission="false"
|
||||
android:writePermission="false" />
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name=".providers.UsersAndGroupsSearchProvider"
|
||||
android:authorities="@string/users_and_groups_search_authority"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:label="@string/share_search" />
|
||||
|
||||
<provider
|
||||
android:name=".providers.DocumentsStorageProvider"
|
||||
android:authorities="@string/document_provider_authority"
|
||||
|
@ -321,9 +343,7 @@
|
|||
<intent-filter>
|
||||
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
||||
</intent-filter>
|
||||
</provider>
|
||||
|
||||
<!-- new provider used to generate URIs without file:// scheme (forbidden from Android 7) -->
|
||||
</provider> <!-- new provider used to generate URIs without file:// scheme (forbidden from Android 7) -->
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="@string/file_provider_authority"
|
||||
|
@ -338,8 +358,7 @@
|
|||
android:authorities="@string/image_cache_provider_authority"
|
||||
android:exported="true"
|
||||
android:grantUriPermissions="true"
|
||||
android:permission="android.permission.MANAGE_DOCUMENTS" />
|
||||
<!-- Disable WorkManager initialization. Whoever designed this, should pay closer attention -->
|
||||
android:permission="android.permission.MANAGE_DOCUMENTS" /> <!-- Disable WorkManager initialization. Whoever designed this, should pay closer attention -->
|
||||
<!-- to "best before" dates in his fridge. -->
|
||||
<!-- disable default provider -->
|
||||
<provider
|
||||
|
@ -395,12 +414,12 @@
|
|||
android:exported="false" />
|
||||
<service
|
||||
android:name="com.nextcloud.client.jobs.transfer.FileTransferService"
|
||||
android:foregroundServiceType="dataSync"
|
||||
android:exported="false" />
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
<service
|
||||
android:name="com.nextcloud.client.media.PlayerService"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="false" />
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="mediaPlayback" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.PassCodeActivity"
|
||||
|
@ -478,6 +497,7 @@
|
|||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/users_and_groups_searchable" />
|
||||
|
@ -514,7 +534,6 @@
|
|||
android:name="com.nextcloud.client.editimage.EditImageActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.ownCloud.Toolbar.NullBackground" />
|
||||
|
||||
<activity
|
||||
android:name="com.nmc.android.ui.LauncherActivity"
|
||||
android:exported="true"
|
||||
|
@ -525,42 +544,6 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
<package android:name="it.niedermann.nextcloud.deck" />
|
||||
<package android:name="it.niedermann.nextcloud.deck.play" />
|
||||
<package android:name="it.niedermann.nextcloud.deck.dev" />
|
||||
<package android:name="at.bitfire.davdroid"/>
|
||||
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PICK" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.media.action.IMAGE_CAPTURE" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.media.action.IMAGE_CAPTURE_SECURE" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.media.action.VIDEO_CAPTURE" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
</manifest>
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.nextcloud.client.assistant.repository.AssistantRepositoryType
|
||||
import com.owncloud.android.MainApp
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.lib.resources.assistant.model.Task
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskType
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AssistantViewModel(private val repository: AssistantRepositoryType) : ViewModel() {
|
||||
|
||||
sealed class State {
|
||||
data object Idle : State()
|
||||
data object Loading : State()
|
||||
data class Error(val messageId: Int) : State()
|
||||
data class TaskCreated(val messageId: Int) : State()
|
||||
data class TaskDeleted(val messageId: Int) : State()
|
||||
}
|
||||
|
||||
private val _state = MutableStateFlow<State>(State.Loading)
|
||||
val state: StateFlow<State> = _state
|
||||
|
||||
private val _selectedTaskType = MutableStateFlow<TaskType?>(null)
|
||||
val selectedTaskType: StateFlow<TaskType?> = _selectedTaskType
|
||||
|
||||
private val _taskTypes = MutableStateFlow<List<TaskType>?>(null)
|
||||
val taskTypes: StateFlow<List<TaskType>?> = _taskTypes
|
||||
|
||||
private var _taskList: List<Task>? = null
|
||||
|
||||
private val _filteredTaskList = MutableStateFlow<List<Task>?>(null)
|
||||
val filteredTaskList: StateFlow<List<Task>?> = _filteredTaskList
|
||||
|
||||
init {
|
||||
getTaskTypes()
|
||||
getTaskList()
|
||||
}
|
||||
|
||||
fun createTask(
|
||||
input: String,
|
||||
type: String
|
||||
) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val result = repository.createTask(input, type)
|
||||
|
||||
val messageId = if (result.isSuccess) {
|
||||
R.string.assistant_screen_task_create_success_message
|
||||
} else {
|
||||
R.string.assistant_screen_task_create_fail_message
|
||||
}
|
||||
|
||||
_state.update {
|
||||
State.TaskCreated(messageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectTaskType(task: TaskType) {
|
||||
_selectedTaskType.update {
|
||||
filterTaskList(task.id)
|
||||
task
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTaskTypes() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val allTaskType = MainApp.getAppContext().getString(R.string.assistant_screen_all_task_type)
|
||||
val result = arrayListOf(TaskType(null, allTaskType, null))
|
||||
val taskTypesResult = repository.getTaskTypes()
|
||||
|
||||
if (taskTypesResult.isSuccess) {
|
||||
result.addAll(taskTypesResult.resultData.types)
|
||||
_taskTypes.update {
|
||||
result.toList()
|
||||
}
|
||||
|
||||
_selectedTaskType.update {
|
||||
result.first()
|
||||
}
|
||||
} else {
|
||||
_state.update {
|
||||
State.Error(R.string.assistant_screen_task_types_error_state_message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getTaskList(appId: String = "assistant", onCompleted: () -> Unit = {}) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val result = repository.getTaskList(appId)
|
||||
if (result.isSuccess) {
|
||||
_taskList = result.resultData.tasks
|
||||
|
||||
filterTaskList(_selectedTaskType.value?.id)
|
||||
|
||||
_state.update {
|
||||
State.Idle
|
||||
}
|
||||
|
||||
onCompleted()
|
||||
} else {
|
||||
_state.update {
|
||||
State.Error(R.string.assistant_screen_task_list_error_state_message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteTask(id: Long) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val result = repository.deleteTask(id)
|
||||
|
||||
val messageId = if (result.isSuccess) {
|
||||
R.string.assistant_screen_task_delete_success_message
|
||||
} else {
|
||||
R.string.assistant_screen_task_delete_fail_message
|
||||
}
|
||||
|
||||
_state.update {
|
||||
State.TaskDeleted(messageId)
|
||||
}
|
||||
|
||||
if (result.isSuccess) {
|
||||
removeTaskFromList(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resetState() {
|
||||
_state.update {
|
||||
State.Idle
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterTaskList(taskTypeId: String?) {
|
||||
if (taskTypeId == null) {
|
||||
_filteredTaskList.update {
|
||||
_taskList
|
||||
}
|
||||
} else {
|
||||
_filteredTaskList.update {
|
||||
_taskList?.filter { it.type == taskTypeId }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeTaskFromList(id: Long) {
|
||||
_filteredTaskList.update { currentList ->
|
||||
currentList?.filter { it.id != id }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
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.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nextcloud.client.assistant.component.AddTaskAlertDialog
|
||||
import com.nextcloud.client.assistant.component.CenterText
|
||||
import com.nextcloud.client.assistant.component.TaskTypesRow
|
||||
import com.nextcloud.client.assistant.component.TaskView
|
||||
import com.nextcloud.client.assistant.repository.AssistantMockRepository
|
||||
import com.nextcloud.ui.composeActivity.ComposeActivity
|
||||
import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.lib.resources.assistant.model.Task
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskType
|
||||
import com.owncloud.android.utils.DisplayUtils
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) {
|
||||
val state by viewModel.state.collectAsState()
|
||||
val selectedTaskType by viewModel.selectedTaskType.collectAsState()
|
||||
val filteredTaskList by viewModel.filteredTaskList.collectAsState()
|
||||
val taskTypes by viewModel.taskTypes.collectAsState()
|
||||
var showAddTaskAlertDialog by remember { mutableStateOf(false) }
|
||||
var showDeleteTaskAlertDialog by remember { mutableStateOf(false) }
|
||||
var taskIdToDeleted: Long? by remember {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
val pullRefreshState = rememberPullToRefreshState()
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
if (pullRefreshState.isRefreshing) {
|
||||
LaunchedEffect(true) {
|
||||
delay(1500)
|
||||
viewModel.getTaskList(onCompleted = {
|
||||
pullRefreshState.endRefresh()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Box(Modifier.nestedScroll(pullRefreshState.nestedScrollConnection)) {
|
||||
if (state == AssistantViewModel.State.Loading || pullRefreshState.isRefreshing) {
|
||||
CenterText(text = stringResource(id = R.string.assistant_screen_loading))
|
||||
} else {
|
||||
if (filteredTaskList.isNullOrEmpty()) {
|
||||
EmptyTaskList(selectedTaskType, taskTypes, viewModel)
|
||||
} else {
|
||||
AssistantContent(
|
||||
filteredTaskList!!,
|
||||
taskTypes,
|
||||
selectedTaskType,
|
||||
viewModel,
|
||||
showDeleteTaskAlertDialog = { taskId ->
|
||||
taskIdToDeleted = taskId
|
||||
showDeleteTaskAlertDialog = true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (pullRefreshState.isRefreshing) {
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||
} else {
|
||||
LinearProgressIndicator(progress = { pullRefreshState.progress }, modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
|
||||
if (selectedTaskType?.name != stringResource(id = R.string.assistant_screen_all_task_type)) {
|
||||
FloatingActionButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(16.dp),
|
||||
onClick = {
|
||||
showAddTaskAlertDialog = true
|
||||
}
|
||||
) {
|
||||
Icon(Icons.Filled.Add, "Add Task Icon")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScreenState(state, activity, viewModel)
|
||||
|
||||
if (showDeleteTaskAlertDialog) {
|
||||
taskIdToDeleted?.let { id ->
|
||||
SimpleAlertDialog(
|
||||
title = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_title),
|
||||
description = stringResource(id = R.string.assistant_screen_delete_task_alert_dialog_description),
|
||||
dismiss = { showDeleteTaskAlertDialog = false },
|
||||
onComplete = { viewModel.deleteTask(id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (showAddTaskAlertDialog) {
|
||||
selectedTaskType?.let { taskType ->
|
||||
AddTaskAlertDialog(
|
||||
title = taskType.name,
|
||||
description = taskType.description,
|
||||
addTask = { input ->
|
||||
taskType.id?.let {
|
||||
viewModel.createTask(input = input, type = it)
|
||||
}
|
||||
},
|
||||
dismiss = {
|
||||
showAddTaskAlertDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenState(
|
||||
state: AssistantViewModel.State,
|
||||
activity: Activity,
|
||||
viewModel: AssistantViewModel
|
||||
) {
|
||||
val messageId: Int? = when (state) {
|
||||
is AssistantViewModel.State.Error -> {
|
||||
state.messageId
|
||||
}
|
||||
|
||||
is AssistantViewModel.State.TaskCreated -> {
|
||||
state.messageId
|
||||
}
|
||||
|
||||
is AssistantViewModel.State.TaskDeleted -> {
|
||||
state.messageId
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
messageId?.let {
|
||||
DisplayUtils.showSnackMessage(
|
||||
activity,
|
||||
stringResource(id = messageId)
|
||||
)
|
||||
|
||||
viewModel.resetState()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun AssistantContent(
|
||||
taskList: List<Task>,
|
||||
taskTypes: List<TaskType>?,
|
||||
selectedTaskType: TaskType?,
|
||||
viewModel: AssistantViewModel,
|
||||
showDeleteTaskAlertDialog: (Long) -> Unit
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
stickyHeader {
|
||||
TaskTypesRow(selectedTaskType, data = taskTypes) { task ->
|
||||
viewModel.selectTaskType(task)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
items(taskList) { task ->
|
||||
TaskView(task, showDeleteTaskAlertDialog = { showDeleteTaskAlertDialog(task.id) })
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EmptyTaskList(selectedTaskType: TaskType?, taskTypes: List<TaskType>?, viewModel: AssistantViewModel) {
|
||||
val text = if (selectedTaskType?.name == stringResource(id = R.string.assistant_screen_all_task_type)) {
|
||||
stringResource(id = R.string.assistant_screen_no_task_available_for_all_task_filter_text)
|
||||
} else {
|
||||
stringResource(
|
||||
id = R.string.assistant_screen_no_task_available_text,
|
||||
selectedTaskType?.name ?: ""
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
TaskTypesRow(selectedTaskType, data = taskTypes) { task ->
|
||||
viewModel.selectTaskType(task)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
CenterText(text = text)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun AssistantScreenPreview() {
|
||||
val mockRepository = AssistantMockRepository()
|
||||
MaterialTheme(
|
||||
content = {
|
||||
AssistantScreen(
|
||||
viewModel = AssistantViewModel(repository = mockRepository),
|
||||
activity = ComposeActivity()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun AssistantEmptyScreenPreview() {
|
||||
val mockRepository = AssistantMockRepository(giveEmptyTasks = true)
|
||||
MaterialTheme(
|
||||
content = {
|
||||
AssistantScreen(
|
||||
viewModel = AssistantViewModel(repository = mockRepository),
|
||||
activity = ComposeActivity()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
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.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog
|
||||
import com.owncloud.android.R
|
||||
|
||||
@Composable
|
||||
fun AddTaskAlertDialog(title: String?, description: String?, addTask: (String) -> Unit, dismiss: () -> Unit) {
|
||||
var input by remember {
|
||||
mutableStateOf("")
|
||||
}
|
||||
|
||||
SimpleAlertDialog(
|
||||
title = title ?: "",
|
||||
description = description ?: "",
|
||||
dismiss = { dismiss() },
|
||||
onComplete = {
|
||||
addTask(input)
|
||||
},
|
||||
content = {
|
||||
TextField(
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.assistant_screen_create_task_alert_dialog_input_field_placeholder
|
||||
)
|
||||
)
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
|
||||
value = input,
|
||||
onValueChange = {
|
||||
input = it
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun AddTaskAlertDialogPreview() {
|
||||
AddTaskAlertDialog(title = "Title", description = "Description", addTask = { }, dismiss = {})
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant.component
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Composable
|
||||
fun CenterText(text: String) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = 18.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant.component
|
||||
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskType
|
||||
|
||||
@Composable
|
||||
fun TaskTypesRow(selectedTaskType: TaskType?, data: List<TaskType>?, selectTaskType: (TaskType) -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState())
|
||||
) {
|
||||
data?.forEach { taskType ->
|
||||
taskType.name?.let { taskTypeName ->
|
||||
FilledTonalButton(
|
||||
onClick = { selectTaskType(taskType) },
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = if (selectedTaskType?.id == taskType.id) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
Color.Gray
|
||||
}
|
||||
)
|
||||
) {
|
||||
Text(text = taskTypeName)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(end = 8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant.component
|
||||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
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.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.lib.resources.assistant.model.Task
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Suppress("LongMethod", "MagicNumber")
|
||||
@Composable
|
||||
fun TaskView(
|
||||
task: Task,
|
||||
showDeleteTaskAlertDialog: (Long) -> Unit
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
var showMoreActionsBottomSheet by remember { mutableStateOf(false) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(MaterialTheme.colorScheme.primary)
|
||||
.combinedClickable(onClick = {
|
||||
expanded = !expanded
|
||||
}, onLongClick = {
|
||||
showMoreActionsBottomSheet = true
|
||||
})
|
||||
.padding(start = 8.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
task.input?.let {
|
||||
Text(
|
||||
text = it,
|
||||
color = Color.White,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
task.output?.let {
|
||||
HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp))
|
||||
|
||||
Text(
|
||||
text = if (expanded) it else it.take(100) + "...",
|
||||
fontSize = 12.sp,
|
||||
color = Color.White,
|
||||
modifier = Modifier
|
||||
.animateContentSize(
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioLowBouncy,
|
||||
stiffness = Spring.StiffnessLow
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if ((task.output?.length ?: 0) >= 100) {
|
||||
Text(
|
||||
text = if (!expanded) {
|
||||
stringResource(id = R.string.assistant_screen_task_view_show_more)
|
||||
} else {
|
||||
stringResource(id = R.string.assistant_screen_task_view_show_less)
|
||||
},
|
||||
textAlign = TextAlign.End,
|
||||
color = Color.White,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
)
|
||||
}
|
||||
|
||||
if (showMoreActionsBottomSheet) {
|
||||
val bottomSheetAction = listOf(
|
||||
Triple(
|
||||
R.drawable.ic_delete,
|
||||
R.string.assistant_screen_task_more_actions_bottom_sheet_delete_action
|
||||
) {
|
||||
showDeleteTaskAlertDialog(task.id)
|
||||
}
|
||||
)
|
||||
|
||||
MoreActionsBottomSheet(
|
||||
title = task.input,
|
||||
actions = bottomSheetAction,
|
||||
dismiss = { showMoreActionsBottomSheet = false }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TaskViewPreview() {
|
||||
val output =
|
||||
"Lorem Ipsum is simply dummy text of the printing and " +
|
||||
"typesetting industry. Lorem Ipsum has been the " +
|
||||
"industry's standard dummy text ever since the 1500s, " +
|
||||
"when an unknown printer took a galley of type and " +
|
||||
"scrambled it to make a type specimen book. " +
|
||||
"It has survived not only five centuries, but also " +
|
||||
"the leap into electronic typesetting, remaining" +
|
||||
" essentially unchanged. It wLorem Ipsum is simply dummy" +
|
||||
" text of the printing and typesetting industry. " +
|
||||
"Lorem Ipsum has been the industry's standard dummy " +
|
||||
"text ever since the 1500s, when an unknown printer took a" +
|
||||
" galley of type and scrambled it to make a type specimen book. " +
|
||||
"It has survived not only five centuries, but also the leap " +
|
||||
"into electronic typesetting, remaining essentially unchanged."
|
||||
|
||||
TaskView(
|
||||
task = Task(
|
||||
1,
|
||||
"Free Prompt",
|
||||
0,
|
||||
"1",
|
||||
"1",
|
||||
"Give me text",
|
||||
output,
|
||||
"",
|
||||
""
|
||||
)
|
||||
) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant.repository
|
||||
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.resources.assistant.model.Task
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskList
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskType
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskTypes
|
||||
|
||||
class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : AssistantRepositoryType {
|
||||
override fun getTaskTypes(): RemoteOperationResult<TaskTypes> {
|
||||
return RemoteOperationResult<TaskTypes>(RemoteOperationResult.ResultCode.OK).apply {
|
||||
resultData = TaskTypes(
|
||||
listOf(
|
||||
TaskType("1", "FreePrompt", "You can create free prompt text"),
|
||||
TaskType("2", "Generate Headline", "You can create generate headline text")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createTask(input: String, type: String): RemoteOperationResult<Void> {
|
||||
return RemoteOperationResult<Void>(RemoteOperationResult.ResultCode.OK)
|
||||
}
|
||||
|
||||
override fun getTaskList(appId: String): RemoteOperationResult<TaskList> {
|
||||
val taskList = if (giveEmptyTasks) {
|
||||
TaskList(listOf())
|
||||
} else {
|
||||
TaskList(
|
||||
listOf(
|
||||
Task(
|
||||
1,
|
||||
"FreePrompt",
|
||||
null,
|
||||
"12",
|
||||
"",
|
||||
"Give me some text",
|
||||
"Lorem Ipsum is simply dummy text of the printing and typesetting industry. " +
|
||||
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s," +
|
||||
" when an unknown printer took a galley of type and scrambled it to make a type" +
|
||||
" specimen book. It has survived not only five centuries, " +
|
||||
"but also the leap into electronic typesetting, remaining essentially unchanged." +
|
||||
" It was popularised in the 1960s with the release of Letraset sheets containing " +
|
||||
"Lorem Ipsum passages, and more recently with desktop publishing software like Aldus" +
|
||||
" PageMaker including versions of Lorem Ipsum",
|
||||
"",
|
||||
""
|
||||
),
|
||||
Task(
|
||||
2,
|
||||
"GenerateHeadline",
|
||||
null,
|
||||
"12",
|
||||
"",
|
||||
"Give me some text 2",
|
||||
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
|
||||
"",
|
||||
""
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return RemoteOperationResult<TaskList>(RemoteOperationResult.ResultCode.OK).apply {
|
||||
resultData = taskList
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteTask(id: Long): RemoteOperationResult<Void> {
|
||||
return RemoteOperationResult<Void>(RemoteOperationResult.ResultCode.OK)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant.repository
|
||||
|
||||
import com.nextcloud.common.NextcloudClient
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.resources.assistant.CreateTaskRemoteOperation
|
||||
import com.owncloud.android.lib.resources.assistant.DeleteTaskRemoteOperation
|
||||
import com.owncloud.android.lib.resources.assistant.GetTaskListRemoteOperation
|
||||
import com.owncloud.android.lib.resources.assistant.GetTaskTypesRemoteOperation
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskList
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskTypes
|
||||
|
||||
class AssistantRepository(private val client: NextcloudClient) : AssistantRepositoryType {
|
||||
|
||||
override fun getTaskTypes(): RemoteOperationResult<TaskTypes> {
|
||||
return GetTaskTypesRemoteOperation().execute(client)
|
||||
}
|
||||
|
||||
override fun createTask(
|
||||
input: String,
|
||||
type: String
|
||||
): RemoteOperationResult<Void> {
|
||||
return CreateTaskRemoteOperation(input, type).execute(client)
|
||||
}
|
||||
|
||||
override fun getTaskList(appId: String): RemoteOperationResult<TaskList> {
|
||||
return GetTaskListRemoteOperation(appId).execute(client)
|
||||
}
|
||||
|
||||
override fun deleteTask(id: Long): RemoteOperationResult<Void> {
|
||||
return DeleteTaskRemoteOperation(id).execute(client)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.assistant.repository
|
||||
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskList
|
||||
import com.owncloud.android.lib.resources.assistant.model.TaskTypes
|
||||
|
||||
interface AssistantRepositoryType {
|
||||
fun getTaskTypes(): RemoteOperationResult<TaskTypes>
|
||||
|
||||
fun createTask(
|
||||
input: String,
|
||||
type: String
|
||||
): RemoteOperationResult<Void>
|
||||
|
||||
fun getTaskList(appId: String): RemoteOperationResult<TaskList>
|
||||
|
||||
fun deleteTask(id: Long): RemoteOperationResult<Void>
|
||||
}
|
|
@ -71,7 +71,8 @@ import com.owncloud.android.db.ProviderMeta
|
|||
AutoMigration(from = 74, to = 75),
|
||||
AutoMigration(from = 75, to = 76),
|
||||
AutoMigration(from = 76, to = 77),
|
||||
AutoMigration(from = 77, to = 78)
|
||||
AutoMigration(from = 77, to = 78),
|
||||
AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class)
|
||||
],
|
||||
exportSchema = true
|
||||
)
|
||||
|
|
|
@ -32,6 +32,8 @@ data class CapabilityEntity(
|
|||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = ProviderTableMeta._ID)
|
||||
val id: Int?,
|
||||
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_ASSISTANT)
|
||||
val assistant: Int?,
|
||||
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME)
|
||||
val accountName: String?,
|
||||
@ColumnInfo(name = ProviderTableMeta.CAPABILITIES_VERSION_MAYOR)
|
||||
|
|
|
@ -41,6 +41,7 @@ import com.nextcloud.client.widget.DashboardWidgetService;
|
|||
import com.nextcloud.ui.ChooseAccountDialogFragment;
|
||||
import com.nextcloud.ui.ImageDetailFragment;
|
||||
import com.nextcloud.ui.SetStatusDialogFragment;
|
||||
import com.nextcloud.ui.composeActivity.ComposeActivity;
|
||||
import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
|
||||
import com.nmc.android.ui.LauncherActivity;
|
||||
import com.owncloud.android.MainApp;
|
||||
|
@ -199,6 +200,9 @@ abstract class ComponentsModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract CommunityActivity participateActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ComposeActivity composeActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract PassCodeActivity passCodeActivity();
|
||||
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.ui.composeActivity
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
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 com.nextcloud.client.assistant.AssistantScreen
|
||||
import com.nextcloud.client.assistant.AssistantViewModel
|
||||
import com.nextcloud.client.assistant.repository.AssistantRepository
|
||||
import com.nextcloud.common.NextcloudClient
|
||||
import com.nextcloud.common.User
|
||||
import com.nextcloud.utils.extensions.getSerializableArgument
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.databinding.ActivityComposeBinding
|
||||
import com.owncloud.android.lib.common.OwnCloudClientFactory
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils
|
||||
import com.owncloud.android.lib.common.utils.Log_OC
|
||||
import com.owncloud.android.ui.activity.DrawerActivity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ComposeActivity : DrawerActivity() {
|
||||
|
||||
lateinit var binding: ActivityComposeBinding
|
||||
private var menuItemId: Int? = null
|
||||
|
||||
companion object {
|
||||
const val DESTINATION = "DESTINATION"
|
||||
const val TITLE = "TITLE"
|
||||
const val MENU_ITEM = "MENU_ITEM"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityComposeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
val destination = intent.getSerializableArgument(DESTINATION, ComposeDestination::class.java)
|
||||
val titleId = intent.getIntExtra(TITLE, R.string.empty)
|
||||
menuItemId = intent.getIntExtra(MENU_ITEM, -1)
|
||||
|
||||
setupToolbar()
|
||||
updateActionBarTitleAndHomeButtonByString(getString(titleId))
|
||||
|
||||
if (menuItemId != -1) {
|
||||
setupDrawer(menuItemId!!)
|
||||
}
|
||||
|
||||
binding.composeView.setContent {
|
||||
MaterialTheme(
|
||||
colorScheme = viewThemeUtils.getColorScheme(this),
|
||||
content = {
|
||||
Content(destination, storageManager.user, this)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (menuItemId != -1) {
|
||||
setDrawerMenuItemChecked(R.id.nav_assistant)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
if (isDrawerOpen) closeDrawer() else openDrawer()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(destination: ComposeDestination?, user: User, context: Context) {
|
||||
var nextcloudClient by remember { mutableStateOf<NextcloudClient?>(null) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
nextcloudClient = getNextcloudClient(user, context)
|
||||
}
|
||||
|
||||
if (destination == ComposeDestination.AssistantScreen) {
|
||||
nextcloudClient?.let { client ->
|
||||
AssistantScreen(
|
||||
viewModel = AssistantViewModel(
|
||||
repository = AssistantRepository(client)
|
||||
),
|
||||
activity = this
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getNextcloudClient(user: User, context: Context): NextcloudClient? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
OwnCloudClientFactory.createNextcloudClient(user, context)
|
||||
} catch (e: AccountUtils.AccountNotFoundException) {
|
||||
Log_OC.e(this, "Error caught at init of createNextcloudClient", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.ui.composeActivity
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
enum class ComposeDestination : Serializable {
|
||||
AssistantScreen
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Alper Ozturk
|
||||
* Copyright (C) 2024 Alper Ozturk
|
||||
* Copyright (C) 2024 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.ui.composeComponents.alertDialog
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.owncloud.android.R
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
fun SimpleAlertDialog(
|
||||
title: String,
|
||||
description: String?,
|
||||
heightFraction: Float? = null,
|
||||
content: @Composable (() -> Unit)? = null,
|
||||
onComplete: () -> Unit,
|
||||
dismiss: () -> Unit
|
||||
) {
|
||||
val modifier = if (heightFraction != null) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(heightFraction)
|
||||
} else {
|
||||
Modifier.fillMaxWidth()
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = { dismiss() },
|
||||
title = {
|
||||
Text(text = title)
|
||||
},
|
||||
text = {
|
||||
Column(modifier = modifier) {
|
||||
description?.let {
|
||||
Text(text = description)
|
||||
}
|
||||
|
||||
content?.let {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
content()
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
FilledTonalButton(onClick = {
|
||||
onComplete()
|
||||
dismiss()
|
||||
}) {
|
||||
Text(
|
||||
stringResource(id = R.string.common_ok)
|
||||
)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { dismiss() }) {
|
||||
Text(
|
||||
stringResource(id = R.string.common_cancel)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package com.nextcloud.ui.composeComponents.bottomSheet
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@SuppressLint("ResourceAsColor")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MoreActionsBottomSheet(
|
||||
title: String? = null,
|
||||
actions: List<Triple<Int, Int, () -> Unit>>,
|
||||
dismiss: () -> Unit
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
ModalBottomSheet(
|
||||
modifier = Modifier.padding(top = 32.dp),
|
||||
onDismissRequest = {
|
||||
dismiss()
|
||||
},
|
||||
sheetState = sheetState
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 8.dp)
|
||||
) {
|
||||
title?.let {
|
||||
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(text = title, fontSize = 18.sp)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
actions.forEach { action ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
scope
|
||||
.launch { sheetState.hide() }
|
||||
.invokeOnCompletion {
|
||||
if (!sheetState.isVisible) {
|
||||
action.third()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(all = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = action.first),
|
||||
contentDescription = "action icon",
|
||||
tint = colorScheme.primary,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(action.second),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2005,6 +2005,7 @@ public class FileDataStorageManager {
|
|||
capability.getUserStatusSupportsEmoji().getValue());
|
||||
contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION,
|
||||
capability.getFilesLockingVersion());
|
||||
contentValues.put(ProviderTableMeta.CAPABILITIES_ASSISTANT, capability.getAssistant().getValue());
|
||||
contentValues.put(ProviderTableMeta.CAPABILITIES_GROUPFOLDERS, capability.getGroupfolders().getValue());
|
||||
contentValues.put(ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT, capability.getDropAccount().getValue());
|
||||
contentValues.put(ProviderTableMeta.CAPABILITIES_SECURITY_GUARD, capability.getSecurityGuard().getValue());
|
||||
|
@ -2173,6 +2174,7 @@ public class FileDataStorageManager {
|
|||
getBoolean(cursor, ProviderTableMeta.CAPABILITIES_USER_STATUS_SUPPORTS_EMOJI));
|
||||
capability.setFilesLockingVersion(
|
||||
getString(cursor, ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION));
|
||||
capability.setAssistant(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_ASSISTANT));
|
||||
capability.setGroupfolders(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_GROUPFOLDERS));
|
||||
capability.setDropAccount(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT));
|
||||
capability.setSecurityGuard(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_SECURITY_GUARD));
|
||||
|
|
|
@ -35,7 +35,7 @@ import java.util.List;
|
|||
*/
|
||||
public class ProviderMeta {
|
||||
public static final String DB_NAME = "filelist";
|
||||
public static final int DB_VERSION = 78;
|
||||
public static final int DB_VERSION = 79;
|
||||
|
||||
private ProviderMeta() {
|
||||
// No instance
|
||||
|
@ -265,6 +265,7 @@ public class ProviderMeta {
|
|||
public static final String CAPABILITIES_ETAG = "etag";
|
||||
public static final String CAPABILITIES_USER_STATUS = "user_status";
|
||||
public static final String CAPABILITIES_USER_STATUS_SUPPORTS_EMOJI = "user_status_supports_emoji";
|
||||
public static final String CAPABILITIES_ASSISTANT = "assistant";
|
||||
public static final String CAPABILITIES_GROUPFOLDERS = "groupfolders";
|
||||
public static final String CAPABILITIES_DROP_ACCOUNT = "drop_account";
|
||||
public static final String CAPABILITIES_SECURITY_GUARD = "security_guard";
|
||||
|
|
|
@ -61,7 +61,7 @@ import static com.owncloud.android.ui.activity.FileActivity.EXTRA_USER;
|
|||
public class ActivitiesActivity extends DrawerActivity implements ActivityListInterface, ActivitiesContract.View {
|
||||
private static final String TAG = ActivitiesActivity.class.getSimpleName();
|
||||
|
||||
private ActivityListLayoutBinding binding;
|
||||
ActivityListLayoutBinding binding;
|
||||
private ActivityListAdapter adapter;
|
||||
private int lastGiven;
|
||||
private boolean isLoadingActivities;
|
||||
|
|
|
@ -42,7 +42,6 @@ open class CommunityActivity : DrawerActivity() {
|
|||
|
||||
setupToolbar()
|
||||
updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_community))
|
||||
|
||||
setupDrawer(R.id.nav_community)
|
||||
binding.communityReleaseCandidateText.movementMethod = LinkMovementMethod.getInstance()
|
||||
setupContributeForumView()
|
||||
|
|
|
@ -74,6 +74,8 @@ import com.nextcloud.client.preferences.AppPreferences;
|
|||
import com.nextcloud.common.NextcloudClient;
|
||||
import com.nextcloud.java.util.Optional;
|
||||
import com.nextcloud.ui.ChooseAccountDialogFragment;
|
||||
import com.nextcloud.ui.composeActivity.ComposeActivity;
|
||||
import com.nextcloud.ui.composeActivity.ComposeDestination;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.authentication.PassCodeManager;
|
||||
|
@ -123,6 +125,7 @@ import org.greenrobot.eventbus.ThreadMode;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
@ -357,15 +360,22 @@ public abstract class DrawerActivity extends ToolbarActivity
|
|||
if (getResources().getBoolean(R.bool.is_branded_client) || !preferences.isShowEcosystemApps()) {
|
||||
ecosystemApps.setVisibility(View.GONE);
|
||||
} else {
|
||||
LinearLayout[] views = {
|
||||
ecosystemApps.findViewById(R.id.drawer_ecosystem_notes),
|
||||
ecosystemApps.findViewById(R.id.drawer_ecosystem_talk),
|
||||
ecosystemApps.findViewById(R.id.drawer_ecosystem_more)
|
||||
};
|
||||
LinearLayout notesView = ecosystemApps.findViewById(R.id.drawer_ecosystem_notes);
|
||||
LinearLayout talkView = ecosystemApps.findViewById(R.id.drawer_ecosystem_talk);
|
||||
LinearLayout moreView = ecosystemApps.findViewById(R.id.drawer_ecosystem_more);
|
||||
LinearLayout assistantView = ecosystemApps.findViewById(R.id.drawer_ecosystem_assistant);
|
||||
|
||||
views[0].setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes"));
|
||||
views[1].setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2"));
|
||||
views[2].setOnClickListener(v -> openAppStore("Nextcloud", true));
|
||||
notesView.setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes"));
|
||||
talkView.setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2"));
|
||||
moreView.setOnClickListener(v -> openAppStore("Nextcloud", true));
|
||||
assistantView.setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, -1));
|
||||
if (getCapabilities() != null && getCapabilities().getAssistant().isTrue()) {
|
||||
assistantView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
assistantView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
List<LinearLayout> views = Arrays.asList(notesView, talkView, moreView, assistantView);
|
||||
|
||||
int iconColor;
|
||||
if (Hct.fromInt(primaryColor).getTone() < 80.0) {
|
||||
|
@ -373,6 +383,7 @@ public abstract class DrawerActivity extends ToolbarActivity
|
|||
} else {
|
||||
iconColor = getColor(R.color.grey_800_transparent);
|
||||
}
|
||||
|
||||
for (LinearLayout view : views) {
|
||||
ImageView imageView = (ImageView) view.getChildAt(0);
|
||||
imageView.setImageTintList(ColorStateList.valueOf(iconColor));
|
||||
|
@ -404,8 +415,8 @@ public abstract class DrawerActivity extends ToolbarActivity
|
|||
}
|
||||
|
||||
/**
|
||||
* Open app store page of specified app or search for specified string.
|
||||
* Will attempt to open browser when no app store is available.
|
||||
* Open app store page of specified app or search for specified string. Will attempt to open browser when no app
|
||||
* store is available.
|
||||
*
|
||||
* @param string packageName or url-encoded search string
|
||||
* @param search false -> show app corresponding to packageName; true -> open search for string
|
||||
|
@ -467,7 +478,7 @@ public abstract class DrawerActivity extends ToolbarActivity
|
|||
DrawerMenuUtil.filterTrashbinMenuItem(menu, capability);
|
||||
DrawerMenuUtil.filterActivityMenuItem(menu, capability);
|
||||
DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability);
|
||||
|
||||
DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources());
|
||||
DrawerMenuUtil.setupHomeMenuItem(menu, getResources());
|
||||
|
||||
DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community,
|
||||
|
@ -535,6 +546,8 @@ public abstract class DrawerActivity extends ToolbarActivity
|
|||
startSharedSearch(menuItem);
|
||||
} else if (itemId == R.id.nav_recently_modified) {
|
||||
startRecentlyModifiedSearch(menuItem);
|
||||
} else if (itemId == R.id.nav_assistant) {
|
||||
startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, itemId);
|
||||
} else if (itemId == R.id.nav_groupfolders) {
|
||||
MainApp.showOnlyFilesOnDevice(false);
|
||||
Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
|
||||
|
@ -553,6 +566,14 @@ public abstract class DrawerActivity extends ToolbarActivity
|
|||
}
|
||||
}
|
||||
|
||||
private void startComposeActivity(ComposeDestination destination, int titleId, int menuItemId) {
|
||||
Intent composeActivity = new Intent(getApplicationContext(), ComposeActivity.class);
|
||||
composeActivity.putExtra(ComposeActivity.DESTINATION, destination);
|
||||
composeActivity.putExtra(ComposeActivity.TITLE, titleId);
|
||||
composeActivity.putExtra(ComposeActivity.MENU_ITEM, menuItemId);
|
||||
startActivity(composeActivity);
|
||||
}
|
||||
|
||||
private void startActivity(Class<? extends Activity> activity) {
|
||||
startActivity(new Intent(getApplicationContext(), activity));
|
||||
}
|
||||
|
@ -692,8 +713,8 @@ public abstract class DrawerActivity extends ToolbarActivity
|
|||
/**
|
||||
* Enable or disable interaction with all drawers.
|
||||
*
|
||||
* @param lockMode The new lock mode for the given drawer. One of {@link DrawerLayout#LOCK_MODE_UNLOCKED}, {@link
|
||||
* DrawerLayout#LOCK_MODE_LOCKED_CLOSED} or {@link DrawerLayout#LOCK_MODE_LOCKED_OPEN}.
|
||||
* @param lockMode The new lock mode for the given drawer. One of {@link DrawerLayout#LOCK_MODE_UNLOCKED},
|
||||
* {@link DrawerLayout#LOCK_MODE_LOCKED_CLOSED} or {@link DrawerLayout#LOCK_MODE_LOCKED_OPEN}.
|
||||
*/
|
||||
public void setDrawerLockMode(int lockMode) {
|
||||
if (mDrawerLayout != null) {
|
||||
|
@ -1155,7 +1176,7 @@ public abstract class DrawerActivity extends ToolbarActivity
|
|||
return true;
|
||||
}
|
||||
|
||||
public AppPreferences getAppPreferences(){
|
||||
public AppPreferences getAppPreferences() {
|
||||
return preferences;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,6 @@ import java.io.File;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ import com.owncloud.android.utils.PushUtils
|
|||
*/
|
||||
class NotificationsActivity : DrawerActivity(), NotificationsContract.View {
|
||||
|
||||
private lateinit var binding: NotificationsLayoutBinding
|
||||
lateinit var binding: NotificationsLayoutBinding
|
||||
|
||||
private var adapter: NotificationListAdapter? = null
|
||||
private var snackbar: Snackbar? = null
|
||||
|
|
|
@ -80,7 +80,7 @@ import javax.inject.Inject
|
|||
/**
|
||||
* Activity displaying all auto-synced folders and/or instant upload media folders.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
class SyncedFoldersActivity :
|
||||
FileActivity(),
|
||||
SyncedFolderAdapter.ClickListener,
|
||||
|
@ -165,8 +165,8 @@ class SyncedFoldersActivity :
|
|||
@Inject
|
||||
lateinit var syncedFolderProvider: SyncedFolderProvider
|
||||
|
||||
private lateinit var binding: SyncedFoldersLayoutBinding
|
||||
private lateinit var adapter: SyncedFolderAdapter
|
||||
lateinit var binding: SyncedFoldersLayoutBinding
|
||||
lateinit var adapter: SyncedFolderAdapter
|
||||
|
||||
private var syncedFolderPreferencesDialogFragment: SyncedFolderPreferencesDialogFragment? = null
|
||||
private var path: String? = null
|
||||
|
|
|
@ -50,6 +50,7 @@ import java.util.concurrent.Executor;
|
|||
import java.util.concurrent.Executors;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* Adapter to display all auto-synced folders and/or instant upload media folders.
|
||||
|
@ -179,6 +180,12 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
|
|||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void clear() {
|
||||
filteredSyncFolderItems.clear();
|
||||
syncFolderItems.clear();
|
||||
}
|
||||
|
||||
public int getUnfilteredSectionCount() {
|
||||
if (syncFolderItems.size() > 0) {
|
||||
return syncFolderItems.size() + 1;
|
||||
|
|
|
@ -142,6 +142,10 @@ public class ExtendedListFragment extends Fragment implements
|
|||
|
||||
private ListFragmentBinding binding;
|
||||
|
||||
public ListFragmentBinding getBinding() {
|
||||
return binding;
|
||||
}
|
||||
|
||||
protected void setRecyclerViewAdapter(RecyclerView.Adapter recyclerViewAdapter) {
|
||||
mRecyclerView.setAdapter(recyclerViewAdapter);
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ public class FileDetailActivitiesFragment extends Fragment implements
|
|||
private FileOperationsHelper operationsHelper;
|
||||
private VersionListInterface.CommentCallback callback;
|
||||
|
||||
private FileDetailsActivitiesFragmentBinding binding;
|
||||
FileDetailsActivitiesFragmentBinding binding;
|
||||
|
||||
@Inject UserAccountManager accountManager;
|
||||
@Inject ClientFactory clientFactory;
|
||||
|
|
|
@ -84,7 +84,7 @@ class TrashbinActivity :
|
|||
var trashbinPresenter: TrashbinPresenter? = null
|
||||
|
||||
private var active = false
|
||||
private lateinit var binding: TrashbinActivityBinding
|
||||
lateinit var binding: TrashbinActivityBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
@ -64,6 +64,13 @@ public final class DrawerMenuUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability, Resources resources) {
|
||||
boolean showCondition = capability != null && capability.getAssistant().isTrue() && !resources.getBoolean(R.bool.is_branded_client);
|
||||
if (!showCondition) {
|
||||
filterMenuItems(menu, R.id.nav_assistant);
|
||||
}
|
||||
}
|
||||
|
||||
public static void filterGroupfoldersMenuItem(Menu menu, @Nullable OCCapability capability) {
|
||||
if (capability != null && !capability.getGroupfolders().isTrue()) {
|
||||
filterMenuItems(menu, R.id.nav_groupfolders);
|
||||
|
|
|
@ -123,7 +123,7 @@ class WebViewUtil(private val context: Context) {
|
|||
* @return
|
||||
*/
|
||||
@SuppressLint("PrivateApi", "DiscouragedPrivateApi")
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
@Suppress("TooGenericExceptionCaught", "NestedBlockDepth")
|
||||
fun setProxyKKPlus(webView: WebView) {
|
||||
val proxyHost = OwnCloudClientManagerFactory.getProxyHost()
|
||||
val proxyPort = OwnCloudClientManagerFactory.getProxyPort()
|
||||
|
|
32
app/src/main/res/drawable/ic_assistant.xml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!--
|
||||
~ Nextcloud Android client application
|
||||
~
|
||||
~ @author Alper Ozturk
|
||||
~ Copyright (C) 2024 Alper Ozturk
|
||||
~ Copyright (C) 2024 Nextcloud GmbH
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="1dp"
|
||||
android:height="1dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M9,4.5a0.75,0.75 0,0 1,0.721 0.544l0.813,2.846a3.75,3.75 0,0 0,2.576 2.576l2.846,0.813a0.75,0.75 0,0 1,0 1.442l-2.846,0.813a3.75,3.75 0,0 0,-2.576 2.576l-0.813,2.846a0.75,0.75 0,0 1,-1.442 0l-0.813,-2.846a3.75,3.75 0,0 0,-2.576 -2.576l-2.846,-0.813a0.75,0.75 0,0 1,0 -1.442l2.846,-0.813A3.75,3.75 0,0 0,7.466 7.89l0.813,-2.846A0.75,0.75 0,0 1,9 4.5ZM18,1.5a0.75,0.75 0,0 1,0.728 0.568l0.258,1.036c0.236,0.94 0.97,1.674 1.91,1.91l1.036,0.258a0.75,0.75 0,0 1,0 1.456l-1.036,0.258c-0.94,0.236 -1.674,0.97 -1.91,1.91l-0.258,1.036a0.75,0.75 0,0 1,-1.456 0l-0.258,-1.036a2.625,2.625 0,0 0,-1.91 -1.91l-1.036,-0.258a0.75,0.75 0,0 1,0 -1.456l1.036,-0.258a2.625,2.625 0,0 0,1.91 -1.91l0.258,-1.036A0.75,0.75 0,0 1,18 1.5ZM16.5,15a0.75,0.75 0,0 1,0.712 0.513l0.394,1.183c0.15,0.447 0.5,0.799 0.948,0.948l1.183,0.395a0.75,0.75 0,0 1,0 1.422l-1.183,0.395c-0.447,0.15 -0.799,0.5 -0.948,0.948l-0.395,1.183a0.75,0.75 0,0 1,-1.422 0l-0.395,-1.183a1.5,1.5 0,0 0,-0.948 -0.948l-1.183,-0.395a0.75,0.75 0,0 1,0 -1.422l1.183,-0.395c0.447,-0.15 0.799,-0.5 0.948,-0.948l0.395,-1.183A0.75,0.75 0,0 1,16.5 15Z"
|
||||
android:strokeWidth="0"
|
||||
android:fillColor="#FF000000"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
54
app/src/main/res/layout/activity_compose.xml
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Nextcloud Android client application
|
||||
~
|
||||
~ @author Alper Ozturk
|
||||
~ Copyright (C) 2024 Alper Ozturk
|
||||
~ Copyright (C) 2024 Nextcloud GmbH
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
android:id="@+id/drawer_layout"
|
||||
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:clickable="true"
|
||||
android:fitsSystemWindows="true"
|
||||
android:focusable="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/toolbar_standard" />
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/compose_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/drawer"
|
||||
android:layout_width="@dimen/drawer_width"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"/>
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
|
@ -71,6 +71,37 @@
|
|||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawer_ecosystem_assistant"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:background="?android:selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_margin="4dp"
|
||||
android:background="@drawable/white_outline"
|
||||
android:contentDescription="@string/ecosystem_apps_talk"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_assistant"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/ecosystem_apps_display_assistant"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawer_ecosystem_talk"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
31
app/src/main/res/layout/fragment_compose_view.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Android client application
|
||||
~
|
||||
~ @author Alper Ozturk
|
||||
~ Copyright (C) 2024 Alper Ozturk
|
||||
~ Copyright (C) 2024 Nextcloud GmbH
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU Affero General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/compose_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -25,11 +25,13 @@
|
|||
<group
|
||||
android:id="@+id/drawer_menu_standard"
|
||||
android:checkableBehavior="single">
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_all_files"
|
||||
android:icon="@drawable/all_files"
|
||||
android:orderInCategory="0"
|
||||
android:title="@string/drawer_item_all_files" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_personal_files"
|
||||
android:icon="@drawable/ic_user"
|
||||
|
@ -74,6 +76,13 @@
|
|||
android:id="@+id/nav_notifications"
|
||||
android:icon="@drawable/nav_notifications"
|
||||
android:title="@string/drawer_item_notifications"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_assistant"
|
||||
android:icon="@drawable/ic_assistant"
|
||||
android:orderInCategory="0"
|
||||
android:title="@string/drawer_item_assistant" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_uploads"
|
||||
android:icon="@drawable/uploads"
|
||||
|
|
|
@ -18,6 +18,33 @@
|
|||
<string name="menu_item_sort_by_size_biggest_first">Biggest first</string>
|
||||
<string name="menu_item_sort_by_size_smallest_first">Smallest first</string>
|
||||
|
||||
<string name="ecosystem_apps_display_assistant">Assistant</string>
|
||||
|
||||
<string name="assistant_screen_task_types_error_state_message">Cannot able to fetch task types, please check your internet connection.</string>
|
||||
<string name="assistant_screen_task_list_error_state_message">Cannot able to fetch task list, please check your internet connection.</string>
|
||||
|
||||
<string name="assistant_screen_top_bar_title">Assistant</string>
|
||||
<string name="assistant_screen_loading">Task List are loading, please wait</string>
|
||||
<string name="assistant_screen_no_task_available_for_all_task_filter_text">No task available. Select a task type to create a new task.</string>
|
||||
<string name="assistant_screen_no_task_available_text">No task available for %s task type, you can create a new task from bottom right.</string>
|
||||
<string name="assistant_screen_delete_task_alert_dialog_title">Delete Task</string>
|
||||
<string name="assistant_screen_delete_task_alert_dialog_description">Are you sure you want to delete this task?</string>
|
||||
|
||||
<string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Delete Task</string>
|
||||
|
||||
<string name="assistant_screen_task_create_success_message">Task successfully created</string>
|
||||
<string name="assistant_screen_task_create_fail_message">An error occurred while creating the task</string>
|
||||
|
||||
<string name="assistant_screen_task_delete_success_message">An error occurred while deleting the task</string>
|
||||
<string name="assistant_screen_task_delete_fail_message">Task successfully deleted</string>
|
||||
|
||||
<string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Type some text</string>
|
||||
|
||||
<string name="assistant_screen_all_task_type">All</string>
|
||||
<string name="assistant_screen_task_view_show_more">Show more</string>
|
||||
<string name="assistant_screen_task_view_show_less">Show less</string>
|
||||
|
||||
<string name="drawer_item_assistant">Assistant</string>
|
||||
<string name="drawer_item_all_files">All files</string>
|
||||
<string name="drawer_item_personal_files">Personal files</string>
|
||||
<string name="drawer_item_home">Home</string>
|
||||
|
@ -1135,7 +1162,7 @@
|
|||
<string name="image_preview_unit_megapixel">%s MP</string>
|
||||
<string name="image_preview_filedetails">File details</string>
|
||||
<string name="image_preview_image_taking_conditions">Image taking conditions</string>
|
||||
<string translatable="false" name="make_model">%1$s %2$s</string>
|
||||
<string name="make_model" translatable="false">%1$s %2$s</string>
|
||||
<string name="sub_folder_rule_year">Year</string>
|
||||
<string name="sub_folder_rule_month">Year/Month</string>
|
||||
<string name="sub_folder_rule_day">Year/Month/Day</string>
|
||||
|
|
|
@ -10,11 +10,11 @@ apply plugin: 'kotlin-android'
|
|||
|
||||
android {
|
||||
namespace 'com.nextcloud.appscan'
|
||||
compileSdk 33
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk 21
|
||||
targetSdk 33
|
||||
targetSdk 34
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
buildscript {
|
||||
ext {
|
||||
androidLibraryVersion ="5f92d92c490d2d9039bfd392cf16c61a37738646"
|
||||
androidLibraryVersion ="0c886d61f6"
|
||||
androidPluginVersion = '8.3.0'
|
||||
androidxMediaVersion = '1.3.0'
|
||||
androidxTestVersion = "1.5.0"
|
||||
|
@ -11,7 +11,7 @@ buildscript {
|
|||
espressoVersion = "3.5.1"
|
||||
fidoVersion = "4.1.0-patch2"
|
||||
jacoco_version = '0.8.11'
|
||||
kotlin_version = '1.9.23'
|
||||
kotlin_version = '1.9.22'
|
||||
markwonVersion = "4.6.2"
|
||||
mockitoVersion = "4.11.0"
|
||||
mockitoKotlinVersion = "4.1.0"
|
||||
|
|
|
@ -16,3 +16,6 @@ kotlin.daemon.jvmargs=-Xmx4096m
|
|||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.configureondemand=true
|
||||
|
||||
# Needed for local libs
|
||||
# org.gradle.dependency.verification=lenient
|
|
@ -187,6 +187,7 @@
|
|||
<trusting group="androidx.room"/>
|
||||
<trusting group="androidx.sqlite"/>
|
||||
<trusting group="androidx.webkit" name="webkit" version="1.10.0"/>
|
||||
<trusting group="androidx.webkit" name="webkit" version="1.9.0"/>
|
||||
<trusting group="androidx.work"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="84789D24DF77A32433CE1F079EB80E92EB2135B1">
|
||||
|
@ -439,6 +440,9 @@
|
|||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.annotation" name="annotation" version="1.0.0">
|
||||
<artifact name="annotation-1.0.0.jar">
|
||||
<sha256 value="0baae9755f7caf52aa80cd04324b91ba93af55d4d1d17dcc9a7b53d99ef7c016" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="annotation-1.0.0.pom">
|
||||
<sha256 value="a179c12db43d9c0300c9db63f4811db496504be5401b951d422b78490ad1e5b4" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
|
@ -534,6 +538,9 @@
|
|||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.annotation" name="annotation-jvm" version="1.6.0">
|
||||
<artifact name="annotation-jvm-1.6.0.jar">
|
||||
<sha256 value="60b10b5ef5769b79570172e015b8159405c92f034ba88b9391a977589c9deb4e" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="annotation-jvm-1.6.0.module">
|
||||
<sha256 value="3f5a8faa19de667e63dca9730ff8ef0e478e4bafb5feeb8258e5c086246dc90c" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
|
@ -1229,6 +1236,14 @@
|
|||
<sha256 value="64cad9fa6a92d5cbde2dfc0994619aec3704994923b8eb0aa8c0a0581bee18a8" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.databinding" name="viewbinding" version="8.2.0">
|
||||
<artifact name="viewbinding-8.2.0.aar">
|
||||
<sha256 value="7d20274fccaf968d1b9c7b30a61363e8f9bcab89d9a3c3a0472c670e334f3260" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="viewbinding-8.2.0.module">
|
||||
<sha256 value="45b4e919739627f4f98e3c693ea583125e6fdd39ac1913b4fc8a30111754e0eb" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.databinding" name="viewbinding" version="8.2.2">
|
||||
<artifact name="viewbinding-8.2.2.aar">
|
||||
<sha256 value="b808b84a1f998b6758540550e8e12bb163f293b13fa99c147a318b72e4cf67d0" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -1396,6 +1411,14 @@
|
|||
<sha256 value="b3955b619e8a16c38af39c19126867c72d1954db05551709e58c082b946078c4" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.fragment" name="fragment-ktx" version="1.3.6">
|
||||
<artifact name="fragment-ktx-1.3.6.aar">
|
||||
<sha256 value="3f84a013fdeb8bac92d4ab607aebf39a4ff945f4585a635960ed769cd0255df1" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="fragment-ktx-1.3.6.module">
|
||||
<sha256 value="a775bab4e5ef78605c2b0f8bc9dcd9e2c2dd6b5d5d9320012a69a5d01375059a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.fragment" name="fragment-ktx" version="1.6.0">
|
||||
<artifact name="fragment-ktx-1.6.0.aar">
|
||||
<sha256 value="48c57ecebc6c1b07baf7dd1b77095560bb1f158aec83ba155645b77a1ce53307" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -1513,6 +1536,11 @@
|
|||
<sha256 value="5fb7c8514d8c56cada5e29ef89dc0289e71942ab4cb0b2e6dca137b9dcb8fdd4" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.lifecycle" name="lifecycle-common" version="2.4.0">
|
||||
<artifact name="lifecycle-common-2.4.0.module">
|
||||
<sha256 value="5ad5eafc22e8b04e58fa81d18d2570562971977e18f009500b5bd449bc6337bc" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.lifecycle" name="lifecycle-common" version="2.5.1">
|
||||
<artifact name="lifecycle-common-2.5.1.jar">
|
||||
<sha256 value="20ad1520f625cf455e6afd7290988306d3a9886efa993e0860fbabf4bb3f7bda" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -1529,6 +1557,14 @@
|
|||
<sha256 value="93747a9145cb36bc71005f598ede32e2b1149ade5a16e62b0e4969345bc62d85" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.lifecycle" name="lifecycle-common-java8" version="2.6.1">
|
||||
<artifact name="lifecycle-common-java8-2.6.1.jar">
|
||||
<sha256 value="c6deada2fac53b8ea6523dbda77597b128006674616f140f04df23264c6d1aa3" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="lifecycle-common-java8-2.6.1.module">
|
||||
<sha256 value="1beb0b9fffb630a005deca1d3583d2acbec8685d6de809a3a6e0e433f418b6c3" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.lifecycle" name="lifecycle-livedata" version="2.0.0">
|
||||
<artifact name="lifecycle-livedata-2.0.0.aar">
|
||||
<sha256 value="c82609ced8c498f0a701a30fb6771bb7480860daee84d82e0a81ee86edf7ba39" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -2715,6 +2751,11 @@
|
|||
<sha256 value="f4063ab5094f35aa586ebfd26834f0f914fbcae33e7dea1b0238dab380e40089" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.android.application" name="com.android.application.gradle.plugin" version="8.3.0">
|
||||
<artifact name="com.android.application.gradle.plugin-8.3.0.pom">
|
||||
<sha256 value="f3d60bb5f262d10d4ed77fe987f69192a9595d28b991fd0c4a1d8524db65b3d5" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.android.databinding" name="baseLibrary" version="8.2.2">
|
||||
<artifact name="baseLibrary-8.2.2.jar">
|
||||
<sha256 value="794113709dab21b06c262b3795e73cb708fbacae61715f34361e1af6237a1870" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -2731,6 +2772,11 @@
|
|||
<sha256 value="15c10e317f27ab7e563c2ddd219b2cf5c9b95c85ec7e912d4610e8c1c30d998d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.android.library" name="com.android.library.gradle.plugin" version="8.3.0">
|
||||
<artifact name="com.android.library.gradle.plugin-8.3.0.pom">
|
||||
<sha256 value="b309d7723d3a21151538cc8843393da70d0dd1b4066a54f620f79464121aada4" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.android.tools" name="annotations" version="31.2.2">
|
||||
<artifact name="annotations-31.2.2.jar">
|
||||
<sha256 value="ee3bfd9cdb5012bdb61520f8654a785577e9bb337e5939c5c6149a446684ee16" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -3957,6 +4003,14 @@
|
|||
<sha256 value="6a91a2139a3cae8126c509cf65d136d49c35cb032b581ac1a56cb6a649cc0245" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.nextcloud" name="android-library" version="0c886d61f6">
|
||||
<artifact name="android-library-0c886d61f6.aar">
|
||||
<sha256 value="9c3a87487717acd878305a00f0d6f2337b9f5512d541f7ac46b9bdb5a53020b9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="android-library-0c886d61f6.module">
|
||||
<sha256 value="c29795ee883fc3364b2f16be5b9246b927271b961214f1a661b2caa2f42459a8" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.nextcloud" name="android-library" version="2b1da4cb14e2cd4b79e231b0be54e0bae699f143">
|
||||
<artifact name="android-library-2b1da4cb14e2cd4b79e231b0be54e0bae699f143.aar">
|
||||
<sha256 value="bdc44e874f1e14338213ae5723e71710940a31416ff1c52c9eb2f282e5d3f29a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -3973,6 +4027,14 @@
|
|||
<sha256 value="9f5dc2343cdf500b2cc17756418fce0506d13ef480ab8276cda415950e795991" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.nextcloud" name="android-library" version="6dcffdb0ba">
|
||||
<artifact name="android-library-6dcffdb0ba.aar">
|
||||
<sha256 value="00e121d803e9258b36cb2d6e20904552c9e88dbff6b193b3b9adc0fcd224959a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="android-library-6dcffdb0ba.module">
|
||||
<sha256 value="0e72841878595f83c6d8f93aa51f78a67e821f53446fcb7a5d94cfaad8ebbbac" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.nextcloud" name="android-library" version="acc7df66e4a43ed7f450136c13753f2743fb245e">
|
||||
<artifact name="android-library-acc7df66e4a43ed7f450136c13753f2743fb245e.aar">
|
||||
<sha256 value="f30c2d22c923c6ff8c605eb4cf713cfaf49eb967611f70dc3d139725b0891483" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -3989,6 +4051,14 @@
|
|||
<sha256 value="ddd0f25a7d363aba6de79e8160ab02be999706afa51d9f9e8a30e40399421697" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.nextcloud" name="android-library" version="fcb36db7ba">
|
||||
<artifact name="android-library-fcb36db7ba.aar">
|
||||
<sha256 value="2bae1a11ea687d92fcf6a76a52f4c20ee338e710d2281ce4cd8cae5d1e108151" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="android-library-fcb36db7ba.module">
|
||||
<sha256 value="9396eb66e3150b0d412d35e2dc9b29b078fbfc16e3c6e1c4f157318de797abfb" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.nextcloud-deps" name="qrcodescanner" version="0.1.2.4">
|
||||
<artifact name="qrcodescanner-0.1.2.4.aar">
|
||||
<sha256 value="b286128792cc04f59b0defa2c937c86d9e2fc824a8011b9af9eea7fd0ea84303" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -6050,6 +6120,11 @@
|
|||
<sha256 value="5be0fd6355d0e9539899cbb0ff733906de36a64897255b86fde6880e8ad1d871" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.gitlab.arturbosch.detekt" name="io.gitlab.arturbosch.detekt.gradle.plugin" version="1.23.5">
|
||||
<artifact name="io.gitlab.arturbosch.detekt.gradle.plugin-1.23.5.pom">
|
||||
<sha256 value="a326d380421a273859c741cb186a2ddba8cebf032ffa5feef94ff6d577e01185" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-api" version="1.45.1">
|
||||
<artifact name="grpc-api-1.45.1.jar">
|
||||
<sha256 value="dc381fe018fb10bba8cc66f98db1050a70cee49a8270017c22ec6f77b10f13e5" origin="Generated by Gradle"/>
|
||||
|
@ -7996,6 +8071,16 @@
|
|||
<sha256 value="5cb2601f0b639e17ed323637faaaf78585772a2383a43957c23655019a06fc00" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin.android" name="org.jetbrains.kotlin.android.gradle.plugin" version="1.9.22">
|
||||
<artifact name="org.jetbrains.kotlin.android.gradle.plugin-1.9.22.pom">
|
||||
<sha256 value="c1b02fe072557f4c45b68c9772dfcbadb27d87b80a39e1a2796408ebbe4d0211" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin.kapt" name="org.jetbrains.kotlin.kapt.gradle.plugin" version="1.9.22">
|
||||
<artifact name="org.jetbrains.kotlin.kapt.gradle.plugin-1.9.22.pom">
|
||||
<sha256 value="1daf64ddd8e90a6aa8a831f3e649b4b094e72fe91df0dfd91b5b1ba1dcd54d54" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="atomicfu" version="0.16.1">
|
||||
<artifact name="atomicfu-0.16.1.module">
|
||||
<sha256 value="fdcf04fc25f6a43f557f341ee0053caa25e759f591169c86566f1dad37fc77a6" origin="Generated by Gradle"/>
|
||||
|
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,7 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|