mirror of
https://github.com/nextcloud/android.git
synced 2024-12-22 00:34:31 +03:00
Merge remote-tracking branch 'origin/master' into dev
This commit is contained in:
commit
4ff8624534
92 changed files with 1261 additions and 1291 deletions
|
@ -42,7 +42,10 @@ trim_trailing_whitespace=false
|
||||||
indent_size=2
|
indent_size=2
|
||||||
|
|
||||||
[*.{kt,kts}]
|
[*.{kt,kts}]
|
||||||
|
ktlint_code_style = android_studio
|
||||||
# IDE does not follow this Ktlint rule strictly, but the default ordering is pretty good anyway, so let's ditch it
|
# IDE does not follow this Ktlint rule strictly, but the default ordering is pretty good anyway, so let's ditch it
|
||||||
ktlint_standard_import-ordering = disabled
|
ktlint_standard_import-ordering = disabled
|
||||||
|
ktlint_standard_no-consecutive-comments = disabled
|
||||||
|
ktlint_function_naming_ignore_when_annotated_with = Composable
|
||||||
ij_kotlin_allow_trailing_comma = false
|
ij_kotlin_allow_trailing_comma = false
|
||||||
ij_kotlin_allow_trailing_comma_on_call_site = false
|
ij_kotlin_allow_trailing_comma_on_call_site = false
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
## 3.29.1 (June 27, 2024)
|
||||||
|
|
||||||
|
- Bugfixes
|
||||||
|
|
||||||
|
Minimum: NC 16 Server, Android 7.0 Nougat
|
||||||
|
|
||||||
|
For a full list, please see https://github.com/nextcloud/android/milestone/93
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
~ SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
~ SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||||
~ SPDX-FileCopyrightText: 2016-2024 Tobias Kaminsky <tobias@kaminsky.me>
|
~ SPDX-FileCopyrightText: 2016-2024 Tobias Kaminsky <tobias@kaminsky.me>
|
||||||
|
|
30
Gemfile.lock
30
Gemfile.lock
|
@ -5,22 +5,22 @@ GEM
|
||||||
base64
|
base64
|
||||||
nkf
|
nkf
|
||||||
rexml
|
rexml
|
||||||
addressable (2.8.6)
|
addressable (2.8.7)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 7.0)
|
||||||
artifactory (3.0.17)
|
artifactory (3.0.17)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.3.0)
|
aws-eventstream (1.3.0)
|
||||||
aws-partitions (1.937.0)
|
aws-partitions (1.948.0)
|
||||||
aws-sdk-core (3.196.1)
|
aws-sdk-core (3.199.0)
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.651.0)
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
aws-sigv4 (~> 1.8)
|
aws-sigv4 (~> 1.8)
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sdk-kms (1.82.0)
|
aws-sdk-kms (1.87.0)
|
||||||
aws-sdk-core (~> 3, >= 3.193.0)
|
aws-sdk-core (~> 3, >= 3.199.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.151.0)
|
aws-sdk-s3 (1.154.0)
|
||||||
aws-sdk-core (~> 3, >= 3.194.0)
|
aws-sdk-core (~> 3, >= 3.199.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.8)
|
aws-sigv4 (~> 1.8)
|
||||||
aws-sigv4 (1.8.0)
|
aws-sigv4 (1.8.0)
|
||||||
|
@ -69,7 +69,7 @@ GEM
|
||||||
faraday_middleware (1.2.0)
|
faraday_middleware (1.2.0)
|
||||||
faraday (~> 1.0)
|
faraday (~> 1.0)
|
||||||
fastimage (2.3.1)
|
fastimage (2.3.1)
|
||||||
fastlane (2.220.0)
|
fastlane (2.221.1)
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
addressable (>= 2.8, < 3.0.0)
|
addressable (>= 2.8, < 3.0.0)
|
||||||
artifactory (~> 3.0)
|
artifactory (~> 3.0)
|
||||||
|
@ -150,14 +150,14 @@ GEM
|
||||||
os (>= 0.9, < 2.0)
|
os (>= 0.9, < 2.0)
|
||||||
signet (>= 0.16, < 2.a)
|
signet (>= 0.16, < 2.a)
|
||||||
highline (2.0.3)
|
highline (2.0.3)
|
||||||
http-cookie (1.0.5)
|
http-cookie (1.0.6)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.7.2)
|
json (2.7.2)
|
||||||
jwt (2.8.1)
|
jwt (2.8.2)
|
||||||
base64
|
base64
|
||||||
mini_magick (4.12.0)
|
mini_magick (4.13.1)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.4.1)
|
multipart-post (2.4.1)
|
||||||
|
@ -167,15 +167,15 @@ GEM
|
||||||
optparse (0.5.0)
|
optparse (0.5.0)
|
||||||
os (1.1.4)
|
os (1.1.4)
|
||||||
plist (3.7.1)
|
plist (3.7.1)
|
||||||
public_suffix (5.0.5)
|
public_suffix (5.1.1)
|
||||||
rake (13.2.1)
|
rake (13.2.1)
|
||||||
representable (3.2.0)
|
representable (3.2.0)
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||||
uber (< 0.2.0)
|
uber (< 0.2.0)
|
||||||
retriable (3.1.2)
|
retriable (3.1.2)
|
||||||
rexml (3.2.8)
|
rexml (3.2.9)
|
||||||
strscan (>= 3.0.9)
|
strscan
|
||||||
rouge (2.0.7)
|
rouge (2.0.7)
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
|
|
|
@ -29,7 +29,7 @@ buildscript {
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "org.jetbrains.kotlin.plugin.compose" version "2.0.0"
|
id "org.jetbrains.kotlin.plugin.compose" version "2.0.0"
|
||||||
id "com.diffplug.spotless" version "6.20.0"
|
id "com.diffplug.spotless" version "6.25.0"
|
||||||
id "org.jetbrains.kotlin.kapt" version "2.0.0"
|
id "org.jetbrains.kotlin.kapt" version "2.0.0"
|
||||||
id 'com.google.devtools.ksp' version '2.0.0-1.0.22' apply false
|
id 'com.google.devtools.ksp' version '2.0.0-1.0.22' apply false
|
||||||
}
|
}
|
||||||
|
@ -291,7 +291,7 @@ dependencies {
|
||||||
implementation "androidx.lifecycle:lifecycle-service:2.8.2"
|
implementation "androidx.lifecycle:lifecycle-service:2.8.2"
|
||||||
implementation "androidx.work:work-runtime:$workRuntime"
|
implementation "androidx.work:work-runtime:$workRuntime"
|
||||||
implementation "androidx.work:work-runtime-ktx:$workRuntime"
|
implementation "androidx.work:work-runtime-ktx:$workRuntime"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.8.0"
|
implementation "androidx.fragment:fragment-ktx:1.8.1"
|
||||||
implementation 'com.github.albfernandez:juniversalchardet:2.0.3' // need this version for Android <7
|
implementation 'com.github.albfernandez:juniversalchardet:2.0.3' // need this version for Android <7
|
||||||
compileOnly 'com.google.code.findbugs:annotations:3.0.1u2'
|
compileOnly 'com.google.code.findbugs:annotations:3.0.1u2'
|
||||||
implementation 'commons-io:commons-io:2.16.1'
|
implementation 'commons-io:commons-io:2.16.1'
|
||||||
|
@ -363,7 +363,7 @@ dependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (perfAnalysis) {
|
if (perfAnalysis) {
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.13'
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
|
||||||
}
|
}
|
||||||
|
|
||||||
// dependencies for local unit tests
|
// dependencies for local unit tests
|
||||||
|
@ -411,7 +411,8 @@ dependencies {
|
||||||
implementation "com.github.stateless4j:stateless4j:2.6.0"
|
implementation "com.github.stateless4j:stateless4j:2.6.0"
|
||||||
|
|
||||||
// upon each update first test: new registration, receive push
|
// upon each update first test: new registration, receive push
|
||||||
gplayImplementation "com.google.firebase:firebase-messaging:23.4.1"
|
gplayImplementation "com.google.firebase:firebase-messaging:24.0.0"
|
||||||
|
gplayImplementation 'com.google.android.gms:play-services-base:18.5.0'
|
||||||
gplayImplementation 'com.google.android.play:review-ktx:2.0.1'
|
gplayImplementation 'com.google.android.play:review-ktx:2.0.1'
|
||||||
|
|
||||||
implementation 'com.github.nextcloud.android-common:ui:0.17.0'
|
implementation 'com.github.nextcloud.android-common:ui:0.17.0'
|
||||||
|
|
|
@ -177,8 +177,7 @@ object DocumentsProviderUtils {
|
||||||
*/
|
*/
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal suspend fun getLoadedCursor(timeout: Long = 15_000, query: () -> Cursor?) =
|
internal suspend fun getLoadedCursor(timeout: Long = 15_000, query: () -> Cursor?) = withTimeout(timeout) {
|
||||||
withTimeout(timeout) {
|
|
||||||
suspendCancellableCoroutine<Cursor> { cont ->
|
suspendCancellableCoroutine<Cursor> { cont ->
|
||||||
val cursor = query() ?: throw IOException("Initial query returned no results")
|
val cursor = query() ?: throw IOException("Initial query returned no results")
|
||||||
cont.invokeOnCancellation { cursor.close() }
|
cont.invokeOnCancellation { cursor.close() }
|
||||||
|
|
|
@ -21,8 +21,8 @@ import com.owncloud.android.R
|
||||||
import com.owncloud.android.ui.TextDrawable
|
import com.owncloud.android.ui.TextDrawable
|
||||||
|
|
||||||
internal class AvatarTestFragment : Fragment() {
|
internal class AvatarTestFragment : Fragment() {
|
||||||
lateinit var list1: LinearLayout
|
private lateinit var list1: LinearLayout
|
||||||
lateinit var list2: LinearLayout
|
private lateinit var list2: LinearLayout
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
val view: View = inflater.inflate(R.layout.avatar_fragment, null)
|
val view: View = inflater.inflate(R.layout.avatar_fragment, null)
|
||||||
|
@ -34,7 +34,7 @@ internal class AvatarTestFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addAvatar(name: String, avatarRadius: Float, width: Int, targetContext: Context) {
|
fun addAvatar(name: String, avatarRadius: Float, width: Int, targetContext: Context) {
|
||||||
val margin = padding
|
val margin = PADDING
|
||||||
val imageView = ImageView(targetContext)
|
val imageView = ImageView(targetContext)
|
||||||
imageView.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadius))
|
imageView.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadius))
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ internal class AvatarTestFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addBitmap(bitmap: Bitmap, width: Int, list: Int, targetContext: Context) {
|
fun addBitmap(bitmap: Bitmap, width: Int, list: Int, targetContext: Context) {
|
||||||
val margin = padding
|
val margin = PADDING
|
||||||
val imageView = ImageView(targetContext)
|
val imageView = ImageView(targetContext)
|
||||||
imageView.setImageBitmap(bitmap)
|
imageView.setImageBitmap(bitmap)
|
||||||
|
|
||||||
|
@ -64,6 +64,6 @@ internal class AvatarTestFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val padding = 10
|
private const val PADDING = 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -672,10 +672,7 @@ class FileDetailSharingFragmentIT : AbstractIT() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// open bottom sheet with actions
|
// open bottom sheet with actions
|
||||||
private fun openAdvancedPermissions(
|
private fun openAdvancedPermissions(sut: FileDetailSharingFragment, userShare: OCShare) {
|
||||||
sut: FileDetailSharingFragment,
|
|
||||||
userShare: OCShare
|
|
||||||
) {
|
|
||||||
activity.handler.post {
|
activity.handler.post {
|
||||||
sut.showSharingMenuActionSheet(userShare)
|
sut.showSharingMenuActionSheet(userShare)
|
||||||
}
|
}
|
||||||
|
@ -723,10 +720,7 @@ class FileDetailSharingFragmentIT : AbstractIT() {
|
||||||
/**
|
/**
|
||||||
* verify send new email note text
|
* verify send new email note text
|
||||||
*/
|
*/
|
||||||
private fun verifySendNewEmail(
|
private fun verifySendNewEmail(sut: FileDetailSharingFragment, userShare: OCShare) {
|
||||||
sut: FileDetailSharingFragment,
|
|
||||||
userShare: OCShare
|
|
||||||
) {
|
|
||||||
activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) }
|
activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) }
|
||||||
|
|
||||||
waitForIdleSync()
|
waitForIdleSync()
|
||||||
|
|
|
@ -20,7 +20,9 @@ import org.junit.Test
|
||||||
|
|
||||||
class TrashbinActivityIT : AbstractIT() {
|
class TrashbinActivityIT : AbstractIT() {
|
||||||
enum class TestCase {
|
enum class TestCase {
|
||||||
ERROR, EMPTY, FILES
|
ERROR,
|
||||||
|
EMPTY,
|
||||||
|
FILES
|
||||||
}
|
}
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
|
|
|
@ -36,8 +36,10 @@ class TrashbinLocalRepository(private val testCase: TrashbinActivityIT.TestCase)
|
||||||
"image/png",
|
"image/png",
|
||||||
"/trashbin/test.png",
|
"/trashbin/test.png",
|
||||||
"subFolder/test.png",
|
"subFolder/test.png",
|
||||||
1395847838, // random date
|
// random date
|
||||||
1395847908 // random date
|
1395847838,
|
||||||
|
// random date
|
||||||
|
1395847908
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
files.add(
|
files.add(
|
||||||
|
@ -46,8 +48,10 @@ class TrashbinLocalRepository(private val testCase: TrashbinActivityIT.TestCase)
|
||||||
"image/jpeg",
|
"image/jpeg",
|
||||||
"/trashbin/image.jpg",
|
"/trashbin/image.jpg",
|
||||||
"image.jpg",
|
"image.jpg",
|
||||||
1395841858, // random date
|
// random date
|
||||||
1395837858 // random date
|
1395841858,
|
||||||
|
// random date
|
||||||
|
1395837858
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
files.add(
|
files.add(
|
||||||
|
@ -56,8 +60,10 @@ class TrashbinLocalRepository(private val testCase: TrashbinActivityIT.TestCase)
|
||||||
"DIR",
|
"DIR",
|
||||||
"/trashbin/folder/",
|
"/trashbin/folder/",
|
||||||
"folder",
|
"folder",
|
||||||
1395347858, // random date
|
// random date
|
||||||
1395849858 // random date
|
1395347858,
|
||||||
|
// random date
|
||||||
|
1395849858
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -290,7 +290,8 @@ class EncryptionUtilsV2IT : EncryptionIT() {
|
||||||
mimeType = MimeType.JPEG
|
mimeType = MimeType.JPEG
|
||||||
},
|
},
|
||||||
EncryptionUtils.generateIV(),
|
EncryptionUtils.generateIV(),
|
||||||
EncryptionUtils.generateUid(), // random string, not real tag
|
// random string, not real tag
|
||||||
|
EncryptionUtils.generateUid(),
|
||||||
EncryptionUtils.generateKey(),
|
EncryptionUtils.generateKey(),
|
||||||
metadataFile,
|
metadataFile,
|
||||||
storageManager
|
storageManager
|
||||||
|
|
|
@ -85,11 +85,7 @@ class InAppReviewHelperImpl(val appPreferences: AppPreferences) : InAppReviewHel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchAppReviewFlow(
|
private fun launchAppReviewFlow(manager: ReviewManager, activity: AppCompatActivity, reviewInfo: ReviewInfo) {
|
||||||
manager: ReviewManager,
|
|
||||||
activity: AppCompatActivity,
|
|
||||||
reviewInfo: ReviewInfo
|
|
||||||
) {
|
|
||||||
val flow = manager.launchReviewFlow(activity, reviewInfo)
|
val flow = manager.launchReviewFlow(activity, reviewInfo)
|
||||||
flow.addOnCompleteListener { _ ->
|
flow.addOnCompleteListener { _ ->
|
||||||
// The flow has finished. The API does not indicate whether the user
|
// The flow has finished. The API does not indicate whether the user
|
||||||
|
|
|
@ -44,7 +44,7 @@ class AssistantViewModel(
|
||||||
private val _taskTypes = MutableStateFlow<List<TaskType>?>(null)
|
private val _taskTypes = MutableStateFlow<List<TaskType>?>(null)
|
||||||
val taskTypes: StateFlow<List<TaskType>?> = _taskTypes
|
val taskTypes: StateFlow<List<TaskType>?> = _taskTypes
|
||||||
|
|
||||||
private var _taskList: List<Task>? = null
|
private var taskList: List<Task>? = null
|
||||||
|
|
||||||
private val _filteredTaskList = MutableStateFlow<List<Task>?>(null)
|
private val _filteredTaskList = MutableStateFlow<List<Task>?>(null)
|
||||||
val filteredTaskList: StateFlow<List<Task>?> = _filteredTaskList
|
val filteredTaskList: StateFlow<List<Task>?> = _filteredTaskList
|
||||||
|
@ -55,10 +55,7 @@ class AssistantViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
fun createTask(
|
fun createTask(input: String, type: String) {
|
||||||
input: String,
|
|
||||||
type: String
|
|
||||||
) {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val result = repository.createTask(input, type)
|
val result = repository.createTask(input, type)
|
||||||
|
|
||||||
|
@ -111,7 +108,7 @@ class AssistantViewModel(
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val result = repository.getTaskList(appId)
|
val result = repository.getTaskList(appId)
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
_taskList = result.resultData.tasks
|
taskList = result.resultData.tasks
|
||||||
|
|
||||||
filterTaskList(_selectedTaskType.value?.id)
|
filterTaskList(_selectedTaskType.value?.id)
|
||||||
|
|
||||||
|
@ -157,11 +154,11 @@ class AssistantViewModel(
|
||||||
private fun filterTaskList(taskTypeId: String?) {
|
private fun filterTaskList(taskTypeId: String?) {
|
||||||
if (taskTypeId == null) {
|
if (taskTypeId == null) {
|
||||||
_filteredTaskList.update {
|
_filteredTaskList.update {
|
||||||
_taskList
|
taskList
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_filteredTaskList.update {
|
_filteredTaskList.update {
|
||||||
_taskList?.filter { it.type == taskTypeId }
|
taskList?.filter { it.type == taskTypeId }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,11 +151,7 @@ fun AssistantScreen(viewModel: AssistantViewModel, activity: Activity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ScreenState(
|
private fun ScreenState(state: AssistantViewModel.State, activity: Activity, viewModel: AssistantViewModel) {
|
||||||
state: AssistantViewModel.State,
|
|
||||||
activity: Activity,
|
|
||||||
viewModel: AssistantViewModel
|
|
||||||
) {
|
|
||||||
val messageId: Int? = when (state) {
|
val messageId: Int? = when (state) {
|
||||||
is AssistantViewModel.State.Error -> {
|
is AssistantViewModel.State.Error -> {
|
||||||
state.messageId
|
state.messageId
|
||||||
|
|
|
@ -22,10 +22,7 @@ class AssistantRepository(private val client: NextcloudClient) : AssistantReposi
|
||||||
return GetTaskTypesRemoteOperation().execute(client)
|
return GetTaskTypesRemoteOperation().execute(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createTask(
|
override fun createTask(input: String, type: String): RemoteOperationResult<Void> {
|
||||||
input: String,
|
|
||||||
type: String
|
|
||||||
): RemoteOperationResult<Void> {
|
|
||||||
return CreateTaskRemoteOperation(input, type).execute(client)
|
return CreateTaskRemoteOperation(input, type).execute(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,7 @@ import com.owncloud.android.lib.resources.assistant.model.TaskTypes
|
||||||
interface AssistantRepositoryType {
|
interface AssistantRepositoryType {
|
||||||
fun getTaskTypes(): RemoteOperationResult<TaskTypes>
|
fun getTaskTypes(): RemoteOperationResult<TaskTypes>
|
||||||
|
|
||||||
fun createTask(
|
fun createTask(input: String, type: String): RemoteOperationResult<Void>
|
||||||
input: String,
|
|
||||||
type: String
|
|
||||||
): RemoteOperationResult<Void>
|
|
||||||
|
|
||||||
fun getTaskList(appId: String): RemoteOperationResult<TaskList>
|
fun getTaskList(appId: String): RemoteOperationResult<TaskList>
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,7 @@ import com.owncloud.android.lib.resources.assistant.model.Task
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Suppress("LongMethod", "MagicNumber")
|
@Suppress("LongMethod", "MagicNumber")
|
||||||
@Composable
|
@Composable
|
||||||
fun TaskView(
|
fun TaskView(task: Task, showDeleteTaskAlertDialog: (Long) -> Unit) {
|
||||||
task: Task,
|
|
||||||
showDeleteTaskAlertDialog: (Long) -> Unit
|
|
||||||
) {
|
|
||||||
var showTaskDetailBottomSheet by remember { mutableStateOf(false) }
|
var showTaskDetailBottomSheet by remember { mutableStateOf(false) }
|
||||||
var showMoreActionsBottomSheet by remember { mutableStateOf(false) }
|
var showMoreActionsBottomSheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
|
|
@ -55,21 +55,16 @@ fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) {
|
||||||
ModalBottomSheet(
|
ModalBottomSheet(
|
||||||
modifier = Modifier.padding(top = 32.dp),
|
modifier = Modifier.padding(top = 32.dp),
|
||||||
containerColor = Color.White,
|
containerColor = Color.White,
|
||||||
onDismissRequest = {
|
onDismissRequest = { dismiss() },
|
||||||
dismiss()
|
|
||||||
},
|
|
||||||
sheetState = sheetState
|
sheetState = sheetState
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(modifier = Modifier.fillMaxSize().padding(16.dp)) {
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(16.dp)
|
|
||||||
) {
|
|
||||||
stickyHeader {
|
stickyHeader {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth().background(
|
||||||
.fillMaxWidth()
|
color = colorResource(id = R.color.light_grey),
|
||||||
.background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp))
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
TextInputSelectButton(
|
TextInputSelectButton(
|
||||||
Modifier.weight(1f),
|
Modifier.weight(1f),
|
||||||
|
@ -95,10 +90,10 @@ fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) {
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize().background(
|
||||||
.fillMaxSize()
|
color = colorResource(id = R.color.light_grey),
|
||||||
.background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp))
|
shape = RoundedCornerShape(8.dp)
|
||||||
.padding(16.dp)
|
).padding(16.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = if (showInput) {
|
text = if (showInput) {
|
||||||
|
|
|
@ -71,7 +71,7 @@ abstract class NextcloudDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val FIRST_ROOM_DB_VERSION = 65
|
const val FIRST_ROOM_DB_VERSION = 65
|
||||||
private var INSTANCE: NextcloudDatabase? = null
|
private var instance: NextcloudDatabase? = null
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||||
|
@ -82,8 +82,8 @@ abstract class NextcloudDatabase : RoomDatabase() {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getInstance(context: Context, clock: Clock): NextcloudDatabase {
|
fun getInstance(context: Context, clock: Clock): NextcloudDatabase {
|
||||||
if (INSTANCE == null) {
|
if (instance == null) {
|
||||||
INSTANCE = Room
|
instance = Room
|
||||||
.databaseBuilder(context, NextcloudDatabase::class.java, ProviderMeta.DB_NAME)
|
.databaseBuilder(context, NextcloudDatabase::class.java, ProviderMeta.DB_NAME)
|
||||||
.allowMainThreadQueries()
|
.allowMainThreadQueries()
|
||||||
.addLegacyMigrations(clock, context)
|
.addLegacyMigrations(clock, context)
|
||||||
|
@ -92,7 +92,7 @@ abstract class NextcloudDatabase : RoomDatabase() {
|
||||||
.fallbackToDestructiveMigration()
|
.fallbackToDestructiveMigration()
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
return INSTANCE!!
|
return instance!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,11 +46,7 @@ object DatabaseMigrationUtil {
|
||||||
/**
|
/**
|
||||||
* Utility method to create a new table with the given columns
|
* Utility method to create a new table with the given columns
|
||||||
*/
|
*/
|
||||||
private fun createNewTable(
|
private fun createNewTable(database: SupportSQLiteDatabase, newTableName: String, columns: Map<String, String>) {
|
||||||
database: SupportSQLiteDatabase,
|
|
||||||
newTableName: String,
|
|
||||||
columns: Map<String, String>
|
|
||||||
) {
|
|
||||||
val columnsString = columns.entries.joinToString(",") { "${it.key} ${it.value}" }
|
val columnsString = columns.entries.joinToString(",") { "${it.key} ${it.value}" }
|
||||||
database.execSQL("CREATE TABLE $newTableName ($columnsString)")
|
database.execSQL("CREATE TABLE $newTableName ($columnsString)")
|
||||||
}
|
}
|
||||||
|
@ -80,11 +76,7 @@ object DatabaseMigrationUtil {
|
||||||
/**
|
/**
|
||||||
* Utility method to replace an old table with a new one, essentially deleting the old one and renaming the new one
|
* Utility method to replace an old table with a new one, essentially deleting the old one and renaming the new one
|
||||||
*/
|
*/
|
||||||
private fun replaceTable(
|
private fun replaceTable(database: SupportSQLiteDatabase, tableName: String, newTableTempName: String) {
|
||||||
database: SupportSQLiteDatabase,
|
|
||||||
tableName: String,
|
|
||||||
newTableTempName: String
|
|
||||||
) {
|
|
||||||
database.execSQL("DROP TABLE $tableName")
|
database.execSQL("DROP TABLE $tableName")
|
||||||
database.execSQL("ALTER TABLE $newTableTempName RENAME TO $tableName")
|
database.execSQL("ALTER TABLE $newTableTempName RENAME TO $tableName")
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,7 @@ import androidx.fragment.app.FragmentManager
|
||||||
import dagger.android.support.AndroidSupportInjection
|
import dagger.android.support.AndroidSupportInjection
|
||||||
|
|
||||||
internal class FragmentInjector : FragmentManager.FragmentLifecycleCallbacks() {
|
internal class FragmentInjector : FragmentManager.FragmentLifecycleCallbacks() {
|
||||||
override fun onFragmentPreAttached(
|
override fun onFragmentPreAttached(fragmentManager: FragmentManager, fragment: Fragment, context: Context) {
|
||||||
fragmentManager: FragmentManager,
|
|
||||||
fragment: Fragment,
|
|
||||||
context: Context
|
|
||||||
) {
|
|
||||||
super.onFragmentPreAttached(fragmentManager, fragment, context)
|
super.onFragmentPreAttached(fragmentManager, fragment, context)
|
||||||
if (fragment is Injectable) {
|
if (fragment is Injectable) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -39,10 +39,8 @@ class DocumentPageListAdapter :
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DiffItemCallback : DiffUtil.ItemCallback<String>() {
|
private class DiffItemCallback : DiffUtil.ItemCallback<String>() {
|
||||||
override fun areItemsTheSame(oldItem: String, newItem: String) =
|
override fun areItemsTheSame(oldItem: String, newItem: String) = oldItem == newItem
|
||||||
oldItem == newItem
|
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: String, newItem: String) =
|
override fun areContentsTheSame(oldItem: String, newItem: String) = oldItem == newItem
|
||||||
oldItem == newItem
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,7 @@ class GeneratePDFUseCase @Inject constructor(private val logger: Logger) {
|
||||||
/**
|
/**
|
||||||
* @return `true` if the PDF was generated successfully, `false` otherwise
|
* @return `true` if the PDF was generated successfully, `false` otherwise
|
||||||
*/
|
*/
|
||||||
private fun writePdfToFile(
|
private fun writePdfToFile(filePath: String, document: PdfDocument): Boolean {
|
||||||
filePath: String,
|
|
||||||
document: PdfDocument
|
|
||||||
): Boolean {
|
|
||||||
return try {
|
return try {
|
||||||
val fileOutputStream = FileOutputStream(filePath)
|
val fileOutputStream = FileOutputStream(filePath)
|
||||||
document.writeTo(fileOutputStream)
|
document.writeTo(fileOutputStream)
|
||||||
|
@ -52,10 +49,7 @@ class GeneratePDFUseCase @Inject constructor(private val logger: Logger) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillDocumentPages(
|
private fun fillDocumentPages(document: PdfDocument, imagePaths: List<String>) {
|
||||||
document: PdfDocument,
|
|
||||||
imagePaths: List<String>
|
|
||||||
) {
|
|
||||||
imagePaths.forEach { path ->
|
imagePaths.forEach { path ->
|
||||||
val bitmap = BitmapFactory.decodeFile(path)
|
val bitmap = BitmapFactory.decodeFile(path)
|
||||||
val pageInfo = PdfDocument.PageInfo.Builder(bitmap.width, bitmap.height, 1).create()
|
val pageInfo = PdfDocument.PageInfo.Builder(bitmap.width, bitmap.height, 1).create()
|
||||||
|
|
|
@ -112,7 +112,8 @@ class GeneratePdfFromImagesWork(
|
||||||
user,
|
user,
|
||||||
arrayOf(pdfPath),
|
arrayOf(pdfPath),
|
||||||
arrayOf(uploadPath),
|
arrayOf(uploadPath),
|
||||||
FileUploadWorker.LOCAL_BEHAVIOUR_DELETE, // MIME type will be detected from file name
|
// MIME type will be detected from file name
|
||||||
|
FileUploadWorker.LOCAL_BEHAVIOUR_DELETE,
|
||||||
true,
|
true,
|
||||||
UploadFileOperation.CREATED_BY_USER,
|
UploadFileOperation.CREATED_BY_USER,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -20,7 +20,7 @@ import com.owncloud.android.databinding.FragmentEtmAccountsBinding
|
||||||
|
|
||||||
class EtmAccountsFragment : EtmBaseFragment() {
|
class EtmAccountsFragment : EtmBaseFragment() {
|
||||||
private var _binding: FragmentEtmAccountsBinding? = null
|
private var _binding: FragmentEtmAccountsBinding? = null
|
||||||
private val binding get() = _binding!!
|
val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import java.util.Locale
|
||||||
|
|
||||||
class EtmMigrations : EtmBaseFragment() {
|
class EtmMigrations : EtmBaseFragment() {
|
||||||
private var _binding: FragmentEtmMigrationsBinding? = null
|
private var _binding: FragmentEtmMigrationsBinding? = null
|
||||||
private val binding get() = _binding!!
|
val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import com.owncloud.android.databinding.FragmentEtmPreferencesBinding
|
||||||
|
|
||||||
class EtmPreferencesFragment : EtmBaseFragment() {
|
class EtmPreferencesFragment : EtmBaseFragment() {
|
||||||
private var _binding: FragmentEtmPreferencesBinding? = null
|
private var _binding: FragmentEtmPreferencesBinding? = null
|
||||||
private val binding get() = _binding!!
|
val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -25,8 +25,5 @@ interface DeckApi {
|
||||||
* value otherwise
|
* value otherwise
|
||||||
* @see [Deck Server App](https://apps.nextcloud.com/apps/deck)
|
* @see [Deck Server App](https://apps.nextcloud.com/apps/deck)
|
||||||
*/
|
*/
|
||||||
fun createForwardToDeckActionIntent(
|
fun createForwardToDeckActionIntent(notification: Notification, user: User): Optional<PendingIntent>
|
||||||
notification: Notification,
|
|
||||||
user: User
|
|
||||||
): Optional<PendingIntent>
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,10 +100,7 @@ class BackgroundJobFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFilesExportWork(
|
private fun createFilesExportWork(context: Context, params: WorkerParameters): ListenableWorker {
|
||||||
context: Context,
|
|
||||||
params: WorkerParameters
|
|
||||||
): ListenableWorker {
|
|
||||||
return FilesExportWork(
|
return FilesExportWork(
|
||||||
context,
|
context,
|
||||||
accountManager.user,
|
accountManager.user,
|
||||||
|
@ -113,10 +110,7 @@ class BackgroundJobFactory @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createContentObserverJob(
|
private fun createContentObserverJob(context: Context, workerParameters: WorkerParameters): ListenableWorker {
|
||||||
context: Context,
|
|
||||||
workerParameters: WorkerParameters
|
|
||||||
): ListenableWorker {
|
|
||||||
return ContentObserverWork(
|
return ContentObserverWork(
|
||||||
context,
|
context,
|
||||||
workerParameters,
|
workerParameters,
|
||||||
|
|
|
@ -409,9 +409,7 @@ internal class BackgroundJobManagerImpl(
|
||||||
workManager.isWorkRunning(JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID)
|
workManager.isWorkRunning(JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun schedulePeriodicFilesSyncJob(
|
override fun schedulePeriodicFilesSyncJob(syncedFolderID: Long) {
|
||||||
syncedFolderID: Long
|
|
||||||
) {
|
|
||||||
val arguments = Data.Builder()
|
val arguments = Data.Builder()
|
||||||
.putLong(FilesSyncWork.SYNCED_FOLDER_ID, syncedFolderID)
|
.putLong(FilesSyncWork.SYNCED_FOLDER_ID, syncedFolderID)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -250,7 +250,8 @@ class FilesSyncWork(
|
||||||
localPaths,
|
localPaths,
|
||||||
remotePaths,
|
remotePaths,
|
||||||
uploadAction!!,
|
uploadAction!!,
|
||||||
true, // create parent folder if not existent
|
// create parent folder if not existent
|
||||||
|
true,
|
||||||
UploadFileOperation.CREATED_AS_INSTANT_PICTURE,
|
UploadFileOperation.CREATED_AS_INSTANT_PICTURE,
|
||||||
needsWifi,
|
needsWifi,
|
||||||
needsCharging,
|
needsCharging,
|
||||||
|
|
|
@ -8,5 +8,6 @@
|
||||||
package com.nextcloud.client.jobs.download
|
package com.nextcloud.client.jobs.download
|
||||||
|
|
||||||
enum class FileDownloadError {
|
enum class FileDownloadError {
|
||||||
Failed, Cancelled
|
Failed,
|
||||||
|
Cancelled
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,11 +81,7 @@ class FileDownloadHelper {
|
||||||
backgroundJobManager.cancelFilesDownloadJob(currentUser, currentFile.fileId)
|
backgroundJobManager.cancelFilesDownloadJob(currentUser, currentFile.fileId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveFile(
|
fun saveFile(file: OCFile, currentDownload: DownloadFileOperation?, storageManager: FileDataStorageManager?) {
|
||||||
file: OCFile,
|
|
||||||
currentDownload: DownloadFileOperation?,
|
|
||||||
storageManager: FileDataStorageManager?
|
|
||||||
) {
|
|
||||||
val syncDate = System.currentTimeMillis()
|
val syncDate = System.currentTimeMillis()
|
||||||
|
|
||||||
file.apply {
|
file.apply {
|
||||||
|
|
|
@ -22,10 +22,7 @@ import com.owncloud.android.ui.preview.PreviewImageFragment
|
||||||
|
|
||||||
class FileDownloadIntents(private val context: Context) {
|
class FileDownloadIntents(private val context: Context) {
|
||||||
|
|
||||||
fun newDownloadIntent(
|
fun newDownloadIntent(download: DownloadFileOperation, linkedToRemotePath: String): Intent {
|
||||||
download: DownloadFileOperation,
|
|
||||||
linkedToRemotePath: String
|
|
||||||
): Intent {
|
|
||||||
return Intent(FileDownloadWorker.getDownloadAddedMessage()).apply {
|
return Intent(FileDownloadWorker.getDownloadAddedMessage()).apply {
|
||||||
putExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME, download.user.accountName)
|
putExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME, download.user.accountName)
|
||||||
putExtra(FileDownloadWorker.EXTRA_REMOTE_PATH, download.remotePath)
|
putExtra(FileDownloadWorker.EXTRA_REMOTE_PATH, download.remotePath)
|
||||||
|
|
|
@ -369,10 +369,7 @@ class FileDownloadWorker(
|
||||||
notificationManager.showNewNotification(text)
|
notificationManager.showNewNotification(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun notifyDownloadResult(
|
private fun notifyDownloadResult(download: DownloadFileOperation, downloadResult: RemoteOperationResult<*>) {
|
||||||
download: DownloadFileOperation,
|
|
||||||
downloadResult: RemoteOperationResult<*>
|
|
||||||
) {
|
|
||||||
if (downloadResult.isCancelled) {
|
if (downloadResult.isCancelled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,17 +282,11 @@ class FileUploadHelper {
|
||||||
cancelAndRestartUploadJob(accountManager.getUser(accountName).get())
|
cancelAndRestartUploadJob(accountManager.getUser(accountName).get())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addUploadTransferProgressListener(
|
fun addUploadTransferProgressListener(listener: OnDatatransferProgressListener, targetKey: String) {
|
||||||
listener: OnDatatransferProgressListener,
|
|
||||||
targetKey: String
|
|
||||||
) {
|
|
||||||
mBoundListeners[targetKey] = listener
|
mBoundListeners[targetKey] = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeUploadTransferProgressListener(
|
fun removeUploadTransferProgressListener(listener: OnDatatransferProgressListener, targetKey: String) {
|
||||||
listener: OnDatatransferProgressListener,
|
|
||||||
targetKey: String
|
|
||||||
) {
|
|
||||||
if (mBoundListeners[targetKey] === listener) {
|
if (mBoundListeners[targetKey] === listener) {
|
||||||
mBoundListeners.remove(targetKey)
|
mBoundListeners.remove(targetKey)
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,7 +269,10 @@ class FileUploadWorker(
|
||||||
|
|
||||||
// Only notify if it is not same file on remote that causes conflict
|
// Only notify if it is not same file on remote that causes conflict
|
||||||
if (uploadResult.code == ResultCode.SYNC_CONFLICT && FileUploadHelper().isSameFileOnRemote(
|
if (uploadResult.code == ResultCode.SYNC_CONFLICT && FileUploadHelper().isSameFileOnRemote(
|
||||||
uploadFileOperation.user, File(uploadFileOperation.storagePath), uploadFileOperation.remotePath, context
|
uploadFileOperation.user,
|
||||||
|
File(uploadFileOperation.storagePath),
|
||||||
|
uploadFileOperation.remotePath,
|
||||||
|
context
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
context.showToast(R.string.file_upload_worker_same_file_already_exists)
|
context.showToast(R.string.file_upload_worker_same_file_already_exists)
|
||||||
|
|
|
@ -329,7 +329,7 @@ public final class AppPreferencesImpl implements AppPreferences {
|
||||||
userAccountManager.getUser(),
|
userAccountManager.getUser(),
|
||||||
PREF__FOLDER_SORT_ORDER,
|
PREF__FOLDER_SORT_ORDER,
|
||||||
folder,
|
folder,
|
||||||
FileSortOrder.sort_a_to_z.name));
|
FileSortOrder.SORT_A_TO_Z.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -343,7 +343,7 @@ public final class AppPreferencesImpl implements AppPreferences {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileSortOrder getSortOrderByType(FileSortOrder.Type type) {
|
public FileSortOrder getSortOrderByType(FileSortOrder.Type type) {
|
||||||
return getSortOrderByType(type, FileSortOrder.sort_a_to_z);
|
return getSortOrderByType(type, FileSortOrder.SORT_A_TO_Z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,5 +11,7 @@
|
||||||
package com.nextcloud.client.preferences
|
package com.nextcloud.client.preferences
|
||||||
|
|
||||||
enum class SubFolderRule {
|
enum class SubFolderRule {
|
||||||
YEAR_MONTH, YEAR, YEAR_MONTH_DAY
|
YEAR_MONTH,
|
||||||
|
YEAR,
|
||||||
|
YEAR_MONTH_DAY
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,23 +15,20 @@ import com.owncloud.android.datamodel.OCFile
|
||||||
object IntentUtil {
|
object IntentUtil {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
public fun createSendIntent(context: Context, file: OCFile): Intent =
|
public fun createSendIntent(context: Context, file: OCFile): Intent = createBaseSendFileIntent().apply {
|
||||||
createBaseSendFileIntent().apply {
|
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
type = file.mimeType
|
type = file.mimeType
|
||||||
putExtra(Intent.EXTRA_STREAM, file.getExposedFileUri(context))
|
putExtra(Intent.EXTRA_STREAM, file.getExposedFileUri(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
public fun createSendIntent(context: Context, files: Array<OCFile>): Intent =
|
public fun createSendIntent(context: Context, files: Array<OCFile>): Intent = createBaseSendFileIntent().apply {
|
||||||
createBaseSendFileIntent().apply {
|
|
||||||
action = Intent.ACTION_SEND_MULTIPLE
|
action = Intent.ACTION_SEND_MULTIPLE
|
||||||
type = getUniqueMimetype(files)
|
type = getUniqueMimetype(files)
|
||||||
putParcelableArrayListExtra(Intent.EXTRA_STREAM, getExposedFileUris(context, files))
|
putParcelableArrayListExtra(Intent.EXTRA_STREAM, getExposedFileUris(context, files))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createBaseSendFileIntent(): Intent =
|
private fun createBaseSendFileIntent(): Intent = Intent().apply {
|
||||||
Intent().apply {
|
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
|
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.nextcloud.ui
|
package com.nextcloud.ui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
@ -55,7 +54,7 @@ class ChooseAccountDialogFragment :
|
||||||
private var currentStatus: Status? = null
|
private var currentStatus: Status? = null
|
||||||
|
|
||||||
private var _binding: DialogChooseAccountBinding? = null
|
private var _binding: DialogChooseAccountBinding? = null
|
||||||
private val binding get() = _binding!!
|
val binding get() = _binding!!
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var clientFactory: ClientFactory
|
lateinit var clientFactory: ClientFactory
|
||||||
|
@ -187,8 +186,7 @@ class ChooseAccountDialogFragment :
|
||||||
*/
|
*/
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newInstance(user: User) =
|
fun newInstance(user: User) = ChooseAccountDialogFragment().apply {
|
||||||
ChooseAccountDialogFragment().apply {
|
|
||||||
arguments = Bundle().apply {
|
arguments = Bundle().apply {
|
||||||
putParcelable(ARG_CURRENT_USER_PARAM, user)
|
putParcelable(ARG_CURRENT_USER_PARAM, user)
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,8 @@ class ImageDetailFragment : Fragment(), Injectable {
|
||||||
}
|
}
|
||||||
|
|
||||||
nominatimClient = NominatimClient(
|
nominatimClient = NominatimClient(
|
||||||
getString(R.string.osm_geocoder_url), getString(R.string.osm_geocoder_contact)
|
getString(R.string.osm_geocoder_url),
|
||||||
|
getString(R.string.osm_geocoder_contact)
|
||||||
)
|
)
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -37,11 +37,7 @@ import kotlinx.coroutines.launch
|
||||||
@SuppressLint("ResourceAsColor")
|
@SuppressLint("ResourceAsColor")
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MoreActionsBottomSheet(
|
fun MoreActionsBottomSheet(title: String? = null, actions: List<Triple<Int, Int, () -> Unit>>, dismiss: () -> Unit) {
|
||||||
title: String? = null,
|
|
||||||
actions: List<Triple<Int, Int, () -> Unit>>,
|
|
||||||
dismiss: () -> Unit
|
|
||||||
) {
|
|
||||||
val sheetState = rememberModalBottomSheetState()
|
val sheetState = rememberModalBottomSheetState()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable {
|
||||||
private lateinit var viewModel: FileActionsViewModel
|
private lateinit var viewModel: FileActionsViewModel
|
||||||
|
|
||||||
private var _binding: FileActionsBottomSheetBinding? = null
|
private var _binding: FileActionsBottomSheetBinding? = null
|
||||||
private val binding
|
val binding
|
||||||
get() = _binding!!
|
get() = _binding!!
|
||||||
|
|
||||||
private lateinit var componentsGetter: ComponentsGetter
|
private lateinit var componentsGetter: ComponentsGetter
|
||||||
|
@ -99,9 +99,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable {
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleState(
|
private fun handleState(state: FileActionsViewModel.UiState) {
|
||||||
state: FileActionsViewModel.UiState
|
|
||||||
) {
|
|
||||||
toggleLoadingOrContent(state)
|
toggleLoadingOrContent(state)
|
||||||
when (state) {
|
when (state) {
|
||||||
is FileActionsViewModel.UiState.LoadedForSingleFile -> {
|
is FileActionsViewModel.UiState.LoadedForSingleFile -> {
|
||||||
|
@ -192,9 +190,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun displayActions(
|
private fun displayActions(actions: List<FileAction>) {
|
||||||
actions: List<FileAction>
|
|
||||||
) {
|
|
||||||
if (binding.fileActionsList.isEmpty()) {
|
if (binding.fileActionsList.isEmpty()) {
|
||||||
actions.forEach { action ->
|
actions.forEach { action ->
|
||||||
val view = inflateActionView(action)
|
val view = inflateActionView(action)
|
||||||
|
|
|
@ -54,10 +54,7 @@ class FileActionsViewModel @Inject constructor(
|
||||||
@IdRes
|
@IdRes
|
||||||
get() = _clickActionId
|
get() = _clickActionId
|
||||||
|
|
||||||
fun load(
|
fun load(arguments: Bundle, componentsGetter: ComponentsGetter) {
|
||||||
arguments: Bundle,
|
|
||||||
componentsGetter: ComponentsGetter
|
|
||||||
) {
|
|
||||||
val files: List<OCFile>? = arguments.getParcelableArrayList(ARG_FILES)
|
val files: List<OCFile>? = arguments.getParcelableArrayList(ARG_FILES)
|
||||||
val numberOfAllFiles: Int = arguments.getInt(ARG_ALL_FILES_COUNT, 1)
|
val numberOfAllFiles: Int = arguments.getInt(ARG_ALL_FILES_COUNT, 1)
|
||||||
val isOverflow = arguments.getBoolean(ARG_IS_OVERFLOW, false)
|
val isOverflow = arguments.getBoolean(ARG_IS_OVERFLOW, false)
|
||||||
|
@ -104,17 +101,11 @@ class FileActionsViewModel @Inject constructor(
|
||||||
.getToHide(inSingleFileFragment)
|
.getToHide(inSingleFileFragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getActionsToShow(
|
private fun getActionsToShow(additionalFilter: IntArray?, toHide: List<Int>) = FileAction.SORTED_VALUES
|
||||||
additionalFilter: IntArray?,
|
|
||||||
toHide: List<Int>
|
|
||||||
) = FileAction.SORTED_VALUES
|
|
||||||
.filter { additionalFilter == null || it.id !in additionalFilter }
|
.filter { additionalFilter == null || it.id !in additionalFilter }
|
||||||
.filter { it.id !in toHide }
|
.filter { it.id !in toHide }
|
||||||
|
|
||||||
private fun updateStateLoaded(
|
private fun updateStateLoaded(files: Collection<OCFile>, availableActions: List<FileAction>) {
|
||||||
files: Collection<OCFile>,
|
|
||||||
availableActions: List<FileAction>
|
|
||||||
) {
|
|
||||||
val state: UiState = when (files.size) {
|
val state: UiState = when (files.size) {
|
||||||
1 -> {
|
1 -> {
|
||||||
val file = files.first()
|
val file = files.first()
|
||||||
|
|
|
@ -14,7 +14,7 @@ import com.owncloud.android.lib.common.utils.Log_OC
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
@Suppress("TopLevelPropertyNaming")
|
@Suppress("TopLevelPropertyNaming")
|
||||||
private const val tag = "BundleExtension"
|
private const val TAG = "BundleExtension"
|
||||||
|
|
||||||
fun <T : Serializable?> Bundle?.getSerializableArgument(key: String, type: Class<T>): T? {
|
fun <T : Serializable?> Bundle?.getSerializableArgument(key: String, type: Class<T>): T? {
|
||||||
if (this == null) {
|
if (this == null) {
|
||||||
|
@ -33,7 +33,7 @@ fun <T : Serializable?> Bundle?.getSerializableArgument(key: String, type: Class
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
Log_OC.e(tag, e.localizedMessage)
|
Log_OC.e(TAG, e.localizedMessage)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ fun <T : Parcelable?> Bundle?.getParcelableArgument(key: String, type: Class<T>)
|
||||||
this.getParcelable(key)
|
this.getParcelable(key)
|
||||||
}
|
}
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
Log_OC.e(tag, e.localizedMessage)
|
Log_OC.e(TAG, e.localizedMessage)
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import com.owncloud.android.lib.common.utils.Log_OC
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
@Suppress("TopLevelPropertyNaming")
|
@Suppress("TopLevelPropertyNaming")
|
||||||
private const val tag = "IntentExtension"
|
private const val TAG = "IntentExtension"
|
||||||
|
|
||||||
fun <T : Serializable?> Intent?.getSerializableArgument(key: String, type: Class<T>): T? {
|
fun <T : Serializable?> Intent?.getSerializableArgument(key: String, type: Class<T>): T? {
|
||||||
if (this == null) {
|
if (this == null) {
|
||||||
|
@ -33,7 +33,7 @@ fun <T : Serializable?> Intent?.getSerializableArgument(key: String, type: Class
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
Log_OC.e(tag, e.localizedMessage)
|
Log_OC.e(TAG, e.localizedMessage)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ fun <T : Parcelable?> Intent?.getParcelableArgument(key: String, type: Class<T>)
|
||||||
this.getParcelableExtra(key)
|
this.getParcelableExtra(key)
|
||||||
}
|
}
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
Log_OC.e(tag, e.localizedMessage)
|
Log_OC.e(TAG, e.localizedMessage)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,5 @@ package com.nextcloud.utils.extensions
|
||||||
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
|
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
fun OnDatatransferProgressListener.getPercent(
|
fun OnDatatransferProgressListener.getPercent(totalTransferredSoFar: Long, totalToTransfer: Long): Int =
|
||||||
totalTransferredSoFar: Long,
|
((100.0 * totalTransferredSoFar.toDouble() / totalToTransfer.toDouble()).toInt()).coerceAtMost(100)
|
||||||
totalToTransfer: Long
|
|
||||||
): Int = ((100.0 * totalTransferredSoFar.toDouble() / totalToTransfer.toDouble()).toInt()).coerceAtMost(100)
|
|
||||||
|
|
|
@ -17,10 +17,7 @@ import javax.inject.Inject
|
||||||
|
|
||||||
class FastScrollUtils @Inject constructor(private val viewThemeUtils: ViewThemeUtils) {
|
class FastScrollUtils @Inject constructor(private val viewThemeUtils: ViewThemeUtils) {
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun applyFastScroll(
|
fun applyFastScroll(recyclerView: RecyclerView, viewHelper: FastScroller.ViewHelper? = null) {
|
||||||
recyclerView: RecyclerView,
|
|
||||||
viewHelper: FastScroller.ViewHelper? = null
|
|
||||||
) {
|
|
||||||
val builder =
|
val builder =
|
||||||
FastScrollerBuilder(recyclerView).let {
|
FastScrollerBuilder(recyclerView).let {
|
||||||
viewThemeUtils.files.themeFastScrollerBuilder(
|
viewThemeUtils.files.themeFastScrollerBuilder(
|
||||||
|
|
|
@ -33,7 +33,8 @@ class DeepLinkLoginActivity : AuthenticatorActivity(), Injectable {
|
||||||
val loginUrlInfo = parseLoginDataUrl(prefix, it.toString())
|
val loginUrlInfo = parseLoginDataUrl(prefix, it.toString())
|
||||||
val loginText = findViewById<TextView>(R.id.loginInfo)
|
val loginText = findViewById<TextView>(R.id.loginInfo)
|
||||||
loginText.text = String.format(
|
loginText.text = String.format(
|
||||||
getString(R.string.direct_login_text), loginUrlInfo.username,
|
getString(R.string.direct_login_text),
|
||||||
|
loginUrlInfo.username,
|
||||||
loginUrlInfo.serverAddress
|
loginUrlInfo.serverAddress
|
||||||
)
|
)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.owncloud.android.utils.SyncedFolderUtils;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -212,7 +213,8 @@ public class FilesystemDataProvider {
|
||||||
|
|
||||||
private long getFileChecksum(String filepath) {
|
private long getFileChecksum(String filepath) {
|
||||||
|
|
||||||
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(filepath))) {
|
try (FileInputStream fileInputStream = new FileInputStream(filepath);
|
||||||
|
InputStream inputStream = new BufferedInputStream(fileInputStream)) {
|
||||||
CRC32 crc = new CRC32();
|
CRC32 crc = new CRC32();
|
||||||
byte[] buf = new byte[1024 * 64];
|
byte[] buf = new byte[1024 * 64];
|
||||||
int size;
|
int size;
|
||||||
|
|
|
@ -18,7 +18,8 @@ import androidx.annotation.RequiresApi
|
||||||
* This wrapper is designed for compatibility on those versions.
|
* This wrapper is designed for compatibility on those versions.
|
||||||
*/
|
*/
|
||||||
enum class ForegroundServiceType {
|
enum class ForegroundServiceType {
|
||||||
DataSync, MediaPlayback;
|
DataSync,
|
||||||
|
MediaPlayback;
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.Q)
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
fun getId(): Int {
|
fun getId(): Int {
|
||||||
|
|
|
@ -12,7 +12,9 @@ import android.util.SparseArray
|
||||||
* Types of media folder.
|
* Types of media folder.
|
||||||
*/
|
*/
|
||||||
enum class MediaFolderType(@JvmField val id: Int) {
|
enum class MediaFolderType(@JvmField val id: Int) {
|
||||||
CUSTOM(0), IMAGE(1), VIDEO(2);
|
CUSTOM(0),
|
||||||
|
IMAGE(1),
|
||||||
|
VIDEO(2);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val reverseMap = SparseArray<MediaFolderType>(3)
|
private val reverseMap = SparseArray<MediaFolderType>(3)
|
||||||
|
|
|
@ -22,7 +22,10 @@ data class Template(
|
||||||
val extension: String
|
val extension: String
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
DOCUMENT, SPREADSHEET, PRESENTATION, UNKNOWN;
|
DOCUMENT,
|
||||||
|
SPREADSHEET,
|
||||||
|
PRESENTATION,
|
||||||
|
UNKNOWN;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|
|
@ -286,33 +286,30 @@ public final class ThumbnailsCacheManager {
|
||||||
protected Bitmap doInBackground(Object... params) {
|
protected Bitmap doInBackground(Object... params) {
|
||||||
Bitmap thumbnail;
|
Bitmap thumbnail;
|
||||||
|
|
||||||
|
if (params == null || params.length == 0 || !(params[0] instanceof OCFile)) {
|
||||||
|
Log_OC.d(TAG, "Downloaded file is null or is not an instance of OCFile");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
file = (OCFile) params[0];
|
file = (OCFile) params[0];
|
||||||
|
|
||||||
|
if (file.getRemoteId() != null || file.isPreviewAvailable()) {
|
||||||
if (file.getRemoteId() != null && file.isPreviewAvailable()) {
|
|
||||||
// Thumbnail in cache?
|
// Thumbnail in cache?
|
||||||
thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
|
thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
|
||||||
ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + file.getRemoteId()
|
ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + file.getRemoteId());
|
||||||
);
|
|
||||||
|
|
||||||
if (thumbnail != null && !file.isUpdateThumbnailNeeded()) {
|
if (thumbnail != null && !file.isUpdateThumbnailNeeded())
|
||||||
Float size = (float) ThumbnailsCacheManager.getThumbnailDimension();
|
return getThumbnailFromCache(thumbnail);
|
||||||
|
|
||||||
// resized dimensions
|
return getThumbnailFromServerAndAddToCache(thumbnail);
|
||||||
ImageDimension imageDimension = file.getImageDimension();
|
|
||||||
if (imageDimension == null ||
|
|
||||||
imageDimension.getWidth() != size ||
|
|
||||||
imageDimension.getHeight() != size) {
|
|
||||||
file.setImageDimension(new ImageDimension(thumbnail.getWidth(), thumbnail.getHeight()));
|
|
||||||
storageManager.saveFile(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MimeTypeUtil.isVideo(file)) {
|
Log_OC.d(TAG, "File cannot be previewed");
|
||||||
return ThumbnailsCacheManager.addVideoOverlay(thumbnail, MainApp.getAppContext());
|
return null;
|
||||||
} else {
|
|
||||||
return thumbnail;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
@Nullable
|
||||||
|
private Bitmap getThumbnailFromServerAndAddToCache(Bitmap thumbnail) {
|
||||||
try {
|
try {
|
||||||
mClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(user.toOwnCloudAccount(),
|
mClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(user.toOwnCloudAccount(),
|
||||||
MainApp.getAppContext());
|
MainApp.getAppContext());
|
||||||
|
@ -333,9 +330,24 @@ public final class ThumbnailsCacheManager {
|
||||||
|
|
||||||
return thumbnail;
|
return thumbnail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Bitmap getThumbnailFromCache(Bitmap thumbnail) {
|
||||||
|
float size = (float) ThumbnailsCacheManager.getThumbnailDimension();
|
||||||
|
|
||||||
|
// resized dimensions
|
||||||
|
ImageDimension imageDimension = file.getImageDimension();
|
||||||
|
if (imageDimension == null ||
|
||||||
|
imageDimension.getWidth() != size ||
|
||||||
|
imageDimension.getHeight() != size) {
|
||||||
|
file.setImageDimension(new ImageDimension(thumbnail.getWidth(), thumbnail.getHeight()));
|
||||||
|
storageManager.saveFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
if (MimeTypeUtil.isVideo(file)) {
|
||||||
|
return ThumbnailsCacheManager.addVideoOverlay(thumbnail, MainApp.getAppContext());
|
||||||
|
} else {
|
||||||
|
return thumbnail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onPostExecute(Bitmap bitmap) {
|
protected void onPostExecute(Bitmap bitmap) {
|
||||||
|
@ -1423,9 +1435,10 @@ public final class ThumbnailsCacheManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resized dimensions
|
// resized dimensions and set update thumbnail needed to false to prevent rendering loop
|
||||||
if (thumbnail != null) {
|
if (thumbnail != null) {
|
||||||
file.setImageDimension(new ImageDimension(thumbnail.getWidth(), thumbnail.getHeight()));
|
file.setImageDimension(new ImageDimension(thumbnail.getWidth(), thumbnail.getHeight()));
|
||||||
|
file.setUpdateThumbnailNeeded(false);
|
||||||
storageManager.saveFile(file);
|
storageManager.saveFile(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,10 +227,20 @@ class MediaControlView(context: Context, attrs: AttributeSet?) :
|
||||||
context,
|
context,
|
||||||
if (playerControl?.isPlaying == true) {
|
if (playerControl?.isPlaying == true) {
|
||||||
R.drawable.ic_pause
|
R.drawable.ic_pause
|
||||||
} else { R.drawable.ic_play }
|
} else {
|
||||||
|
R.drawable.ic_play
|
||||||
|
}
|
||||||
)
|
)
|
||||||
binding.forwardBtn.visibility = if (playerControl?.canSeekForward() == true) { VISIBLE } else { INVISIBLE }
|
binding.forwardBtn.visibility = if (playerControl?.canSeekForward() == true) {
|
||||||
binding.rewindBtn.visibility = if (playerControl?.canSeekBackward() == true) { VISIBLE } else { INVISIBLE }
|
VISIBLE
|
||||||
|
} else {
|
||||||
|
INVISIBLE
|
||||||
|
}
|
||||||
|
binding.rewindBtn.visibility = if (playerControl?.canSeekBackward() == true) {
|
||||||
|
VISIBLE
|
||||||
|
} else {
|
||||||
|
INVISIBLE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doPauseResume() {
|
private fun doPauseResume() {
|
||||||
|
|
|
@ -254,13 +254,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
|
||||||
private val TAG = ConflictsResolveActivity::class.java.simpleName
|
private val TAG = ConflictsResolveActivity::class.java.simpleName
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createIntent(
|
fun createIntent(file: OCFile?, user: User?, conflictUploadId: Long, flag: Int?, context: Context?): Intent {
|
||||||
file: OCFile?,
|
|
||||||
user: User?,
|
|
||||||
conflictUploadId: Long,
|
|
||||||
flag: Int?,
|
|
||||||
context: Context?
|
|
||||||
): Intent {
|
|
||||||
val intent = Intent(context, ConflictsResolveActivity::class.java)
|
val intent = Intent(context, ConflictsResolveActivity::class.java)
|
||||||
if (flag != null) {
|
if (flag != null) {
|
||||||
intent.flags = intent.flags or flag
|
intent.flags = intent.flags or flag
|
||||||
|
|
|
@ -475,10 +475,7 @@ open class FolderPickerActivity :
|
||||||
* @param operation Creation operation performed.
|
* @param operation Creation operation performed.
|
||||||
* @param result Result of the creation.
|
* @param result Result of the creation.
|
||||||
*/
|
*/
|
||||||
private fun onCreateFolderOperationFinish(
|
private fun onCreateFolderOperationFinish(operation: CreateFolderOperation, result: RemoteOperationResult<*>) {
|
||||||
operation: CreateFolderOperation,
|
|
||||||
result: RemoteOperationResult<*>
|
|
||||||
) {
|
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
val fileListFragment = listOfFilesFragment
|
val fileListFragment = listOfFilesFragment
|
||||||
fileListFragment?.onItemClicked(storageManager.getFileByPath(operation.remotePath))
|
fileListFragment?.onItemClicked(storageManager.getFileByPath(operation.remotePath))
|
||||||
|
|
|
@ -343,11 +343,7 @@ class NotificationsActivity : DrawerActivity(), NotificationsContract.View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActionCallback(
|
override fun onActionCallback(isSuccess: Boolean, notification: Notification, holder: NotificationViewHolder) {
|
||||||
isSuccess: Boolean,
|
|
||||||
notification: Notification,
|
|
||||||
holder: NotificationViewHolder
|
|
||||||
) {
|
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
adapter?.removeNotification(holder)
|
adapter?.removeNotification(holder)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -222,11 +222,12 @@ class SyncedFoldersActivity :
|
||||||
|
|
||||||
private fun showPowerCheckDialog() {
|
private fun showPowerCheckDialog() {
|
||||||
val alertDialog = AlertDialog.Builder(this)
|
val alertDialog = AlertDialog.Builder(this)
|
||||||
.setView(findViewById(R.id.root_layout))
|
.setView(R.id.root_layout)
|
||||||
.setPositiveButton(R.string.common_ok) { dialog, _ -> dialog.dismiss() }
|
.setPositiveButton(R.string.common_ok) { dialog, _ -> dialog.dismiss() }
|
||||||
.setTitle(R.string.autoupload_disable_power_save_check)
|
.setTitle(R.string.autoupload_disable_power_save_check)
|
||||||
.setMessage(getString(R.string.power_save_check_dialog_message))
|
.setMessage(getString(R.string.power_save_check_dialog_message))
|
||||||
.show()
|
.show()
|
||||||
|
|
||||||
viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE))
|
viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +308,8 @@ class SyncedFoldersActivity :
|
||||||
}
|
}
|
||||||
val syncFolderItems = sortSyncedFolderItems(
|
val syncFolderItems = sortSyncedFolderItems(
|
||||||
mergeFolderData(currentAccountSyncedFoldersList, mediaFolders)
|
mergeFolderData(currentAccountSyncedFoldersList, mediaFolders)
|
||||||
)
|
).filterNotNull()
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
adapter.setSyncFolderItems(syncFolderItems)
|
adapter.setSyncFolderItems(syncFolderItems)
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
|
@ -315,7 +317,9 @@ class SyncedFoldersActivity :
|
||||||
if (!TextUtils.isEmpty(path)) {
|
if (!TextUtils.isEmpty(path)) {
|
||||||
val section = adapter.getSectionByLocalPathAndType(path, type)
|
val section = adapter.getSectionByLocalPathAndType(path, type)
|
||||||
if (section >= 0) {
|
if (section >= 0) {
|
||||||
onSyncFolderSettingsClick(section, adapter[section])
|
adapter.get(section)?.let {
|
||||||
|
onSyncFolderSettingsClick(section, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadJob = null
|
loadJob = null
|
||||||
|
@ -559,7 +563,9 @@ class SyncedFoldersActivity :
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSyncStatusToggleClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem) {
|
override fun onSyncStatusToggleClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem?) {
|
||||||
|
if (syncedFolderDisplayItem == null) return
|
||||||
|
|
||||||
if (syncedFolderDisplayItem.id > SyncedFolder.UNPERSISTED_ID) {
|
if (syncedFolderDisplayItem.id > SyncedFolder.UNPERSISTED_ID) {
|
||||||
syncedFolderProvider.updateSyncedFolderEnabled(
|
syncedFolderProvider.updateSyncedFolderEnabled(
|
||||||
syncedFolderDisplayItem.id,
|
syncedFolderDisplayItem.id,
|
||||||
|
@ -577,7 +583,7 @@ class SyncedFoldersActivity :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSyncFolderSettingsClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem) {
|
override fun onSyncFolderSettingsClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem?) {
|
||||||
val fragmentTransaction = supportFragmentManager.beginTransaction().apply {
|
val fragmentTransaction = supportFragmentManager.beginTransaction().apply {
|
||||||
addToBackStack(null)
|
addToBackStack(null)
|
||||||
}
|
}
|
||||||
|
@ -596,7 +602,9 @@ class SyncedFoldersActivity :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVisibilityToggleClick(section: Int, syncedFolder: SyncedFolderDisplayItem) {
|
override fun onVisibilityToggleClick(section: Int, syncedFolder: SyncedFolderDisplayItem?) {
|
||||||
|
if (syncedFolder == null) return
|
||||||
|
|
||||||
syncedFolder.isHidden = !syncedFolder.isHidden
|
syncedFolder.isHidden = !syncedFolder.isHidden
|
||||||
saveOrUpdateSyncedFolder(syncedFolder)
|
saveOrUpdateSyncedFolder(syncedFolder)
|
||||||
adapter.setSyncFolderItem(section, syncedFolder)
|
adapter.setSyncFolderItem(section, syncedFolder)
|
||||||
|
@ -676,7 +684,7 @@ class SyncedFoldersActivity :
|
||||||
saveOrUpdateSyncedFolder(newCustomFolder)
|
saveOrUpdateSyncedFolder(newCustomFolder)
|
||||||
adapter.addSyncFolderItem(newCustomFolder)
|
adapter.addSyncFolderItem(newCustomFolder)
|
||||||
} else {
|
} else {
|
||||||
val item = adapter[syncedFolder.section]
|
val item = adapter.get(syncedFolder.section) ?: return
|
||||||
updateSyncedFolderItem(
|
updateSyncedFolderItem(
|
||||||
item,
|
item,
|
||||||
syncedFolder.id,
|
syncedFolder.id,
|
||||||
|
@ -793,11 +801,7 @@ class SyncedFoldersActivity :
|
||||||
item.setExcludeHidden(excludeHidden)
|
item.setExcludeHidden(excludeHidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
requestCode: Int,
|
|
||||||
permissions: Array<String>,
|
|
||||||
grantResults: IntArray
|
|
||||||
) {
|
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
PermissionUtil.PERMISSIONS_EXTERNAL_STORAGE -> {
|
PermissionUtil.PERMISSIONS_EXTERNAL_STORAGE -> {
|
||||||
// If request is cancelled, result arrays are empty.
|
// If request is cancelled, result arrays are empty.
|
||||||
|
|
|
@ -17,19 +17,17 @@ import android.print.PrintDocumentInfo;
|
||||||
|
|
||||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class PrintAdapter extends PrintDocumentAdapter {
|
public class PrintAdapter extends PrintDocumentAdapter {
|
||||||
private static final String TAG = PrintAdapter.class.getSimpleName();
|
private static final String TAG = PrintAdapter.class.getSimpleName();
|
||||||
private static final String PDF_NAME = "finalPrint.pdf";
|
private static final String PDF_NAME = "finalPrint.pdf";
|
||||||
|
|
||||||
private String filePath;
|
private final String filePath;
|
||||||
|
|
||||||
public PrintAdapter(String filePath) {
|
public PrintAdapter(String filePath) {
|
||||||
this.filePath = filePath;
|
this.filePath = filePath;
|
||||||
|
@ -58,11 +56,9 @@ public class PrintAdapter extends PrintDocumentAdapter {
|
||||||
ParcelFileDescriptor destination,
|
ParcelFileDescriptor destination,
|
||||||
CancellationSignal cancellationSignal,
|
CancellationSignal cancellationSignal,
|
||||||
WriteResultCallback callback) {
|
WriteResultCallback callback) {
|
||||||
InputStream in = null;
|
|
||||||
OutputStream out = null;
|
try (InputStream in = new FileInputStream(filePath);
|
||||||
try {
|
OutputStream out = new FileOutputStream(destination.getFileDescriptor())) {
|
||||||
in = new FileInputStream(new File(filePath));
|
|
||||||
out = new FileOutputStream(destination.getFileDescriptor());
|
|
||||||
|
|
||||||
byte[] buf = new byte[16384];
|
byte[] buf = new byte[16384];
|
||||||
int size;
|
int size;
|
||||||
|
@ -79,14 +75,6 @@ public class PrintAdapter extends PrintDocumentAdapter {
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log_OC.e(TAG, "Error using temp file", e);
|
Log_OC.e(TAG, "Error using temp file", e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
Objects.requireNonNull(in).close();
|
|
||||||
Objects.requireNonNull(out).close();
|
|
||||||
|
|
||||||
} catch (IOException | NullPointerException e) {
|
|
||||||
Log_OC.e(TAG, "Error closing streams", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,454 +0,0 @@
|
||||||
/*
|
|
||||||
* Nextcloud - Android Client
|
|
||||||
*
|
|
||||||
* SPDX-FileCopyrightText: 2016 Andy Scherzinger
|
|
||||||
* SPDX-FileCopyrightText: 2016 Nextcloud
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
|
|
||||||
*/
|
|
||||||
package com.owncloud.android.ui.adapter;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.PopupMenu;
|
|
||||||
|
|
||||||
import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter;
|
|
||||||
import com.afollestad.sectionedrecyclerview.SectionedViewHolder;
|
|
||||||
import com.nextcloud.client.core.Clock;
|
|
||||||
import com.owncloud.android.R;
|
|
||||||
import com.owncloud.android.databinding.GridSyncItemBinding;
|
|
||||||
import com.owncloud.android.databinding.SyncedFoldersEmptyBinding;
|
|
||||||
import com.owncloud.android.databinding.SyncedFoldersFooterBinding;
|
|
||||||
import com.owncloud.android.databinding.SyncedFoldersItemHeaderBinding;
|
|
||||||
import com.owncloud.android.datamodel.MediaFolderType;
|
|
||||||
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
|
|
||||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
|
||||||
import com.owncloud.android.utils.theme.ViewThemeUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedViewHolder> {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final Clock clock;
|
|
||||||
private final int gridWidth;
|
|
||||||
private final int gridTotal;
|
|
||||||
private final ClickListener clickListener;
|
|
||||||
private final List<SyncedFolderDisplayItem> syncFolderItems;
|
|
||||||
private final List<SyncedFolderDisplayItem> filteredSyncFolderItems;
|
|
||||||
private final boolean light;
|
|
||||||
private static final int VIEW_TYPE_EMPTY = Integer.MAX_VALUE;
|
|
||||||
private static final int VIEW_TYPE_ITEM = 1;
|
|
||||||
private static final int VIEW_TYPE_HEADER = 2;
|
|
||||||
private static final int VIEW_TYPE_FOOTER = 3;
|
|
||||||
private boolean hideItems;
|
|
||||||
private final ViewThemeUtils viewThemeUtils;
|
|
||||||
private final Executor thumbnailThreadPool;
|
|
||||||
|
|
||||||
public SyncedFolderAdapter(Context context,
|
|
||||||
Clock clock,
|
|
||||||
int gridWidth,
|
|
||||||
ClickListener listener,
|
|
||||||
boolean light,
|
|
||||||
ViewThemeUtils viewThemeUtils) {
|
|
||||||
this.context = context;
|
|
||||||
this.clock = clock;
|
|
||||||
this.gridWidth = gridWidth;
|
|
||||||
gridTotal = gridWidth * 2;
|
|
||||||
clickListener = listener;
|
|
||||||
syncFolderItems = new ArrayList<>();
|
|
||||||
filteredSyncFolderItems = new ArrayList<>();
|
|
||||||
this.light = light;
|
|
||||||
this.hideItems = true;
|
|
||||||
this.viewThemeUtils = viewThemeUtils;
|
|
||||||
this.thumbnailThreadPool = Executors.newCachedThreadPool();
|
|
||||||
|
|
||||||
shouldShowHeadersForEmptySections(true);
|
|
||||||
shouldShowFooters(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toggleHiddenItemsVisibility() {
|
|
||||||
hideItems = !hideItems;
|
|
||||||
filteredSyncFolderItems.clear();
|
|
||||||
filteredSyncFolderItems.addAll(filterHiddenItems(syncFolderItems, hideItems));
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSyncFolderItems(List<SyncedFolderDisplayItem> syncFolderItems) {
|
|
||||||
this.syncFolderItems.clear();
|
|
||||||
this.syncFolderItems.addAll(syncFolderItems);
|
|
||||||
|
|
||||||
this.filteredSyncFolderItems.clear();
|
|
||||||
this.filteredSyncFolderItems.addAll(filterHiddenItems(this.syncFolderItems, hideItems));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSyncFolderItem(int location, SyncedFolderDisplayItem syncFolderItem) {
|
|
||||||
if (hideItems && syncFolderItem.isHidden() && filteredSyncFolderItems.contains(syncFolderItem)) {
|
|
||||||
filteredSyncFolderItems.remove(location);
|
|
||||||
} else {
|
|
||||||
if (filteredSyncFolderItems.contains(syncFolderItem)) {
|
|
||||||
filteredSyncFolderItems.set(filteredSyncFolderItems.indexOf(syncFolderItem), syncFolderItem);
|
|
||||||
} else {
|
|
||||||
filteredSyncFolderItems.add(syncFolderItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syncFolderItems.contains(syncFolderItem)) {
|
|
||||||
syncFolderItems.set(syncFolderItems.indexOf(syncFolderItem), syncFolderItem);
|
|
||||||
} else {
|
|
||||||
syncFolderItems.add(syncFolderItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSyncFolderItem(SyncedFolderDisplayItem syncFolderItem) {
|
|
||||||
syncFolderItems.add(syncFolderItem);
|
|
||||||
|
|
||||||
// add item for display when either all items should be shown (!hideItems)
|
|
||||||
// or if item should be shown (!.isHidden())
|
|
||||||
if (!hideItems || !syncFolderItem.isHidden()) {
|
|
||||||
filteredSyncFolderItems.add(syncFolderItem);
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeItem(int section) {
|
|
||||||
if (filteredSyncFolderItems.contains(syncFolderItems.get(section))) {
|
|
||||||
filteredSyncFolderItems.remove(syncFolderItems.get(section));
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
syncFolderItems.remove(section);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter for hidden items
|
|
||||||
*
|
|
||||||
* @param items Collection of items to filter
|
|
||||||
* @return Non-hidden items
|
|
||||||
*/
|
|
||||||
private List<SyncedFolderDisplayItem> filterHiddenItems(List<SyncedFolderDisplayItem> items, boolean hide) {
|
|
||||||
if (!hide) {
|
|
||||||
return items;
|
|
||||||
} else {
|
|
||||||
List<SyncedFolderDisplayItem> result = new ArrayList<>();
|
|
||||||
|
|
||||||
for (SyncedFolderDisplayItem item : items) {
|
|
||||||
if (!item.isHidden() && !result.contains(item)) {
|
|
||||||
result.add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSectionCount() {
|
|
||||||
if (filteredSyncFolderItems.size() > 0) {
|
|
||||||
return filteredSyncFolderItems.size() + 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void clear() {
|
|
||||||
filteredSyncFolderItems.clear();
|
|
||||||
syncFolderItems.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getUnfilteredSectionCount() {
|
|
||||||
if (syncFolderItems.size() > 0) {
|
|
||||||
return syncFolderItems.size() + 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount(int section) {
|
|
||||||
if (section < filteredSyncFolderItems.size()) {
|
|
||||||
List<String> filePaths = filteredSyncFolderItems.get(section).getFilePaths();
|
|
||||||
|
|
||||||
if (filePaths != null) {
|
|
||||||
return filteredSyncFolderItems.get(section).getFilePaths().size();
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SyncedFolderDisplayItem get(int section) {
|
|
||||||
return filteredSyncFolderItems.get(section);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int section, int relativePosition, int absolutePosition) {
|
|
||||||
if (isLastSection(section)) {
|
|
||||||
return VIEW_TYPE_EMPTY;
|
|
||||||
} else {
|
|
||||||
return VIEW_TYPE_ITEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHeaderViewType(int section) {
|
|
||||||
if (isLastSection(section)) {
|
|
||||||
return VIEW_TYPE_EMPTY;
|
|
||||||
} else {
|
|
||||||
return VIEW_TYPE_HEADER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getFooterViewType(int section) {
|
|
||||||
if (isLastSection(section) && showFooter()) {
|
|
||||||
return VIEW_TYPE_FOOTER;
|
|
||||||
} else {
|
|
||||||
// only show footer after last item and only if folders have been hidden
|
|
||||||
return VIEW_TYPE_EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean showFooter() {
|
|
||||||
return syncFolderItems.size() > filteredSyncFolderItems.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the section of a synced folder for the given local path and type.
|
|
||||||
*
|
|
||||||
* @param localPath the local path of the synced folder
|
|
||||||
* @param type the of the synced folder
|
|
||||||
* @return the section index of the looked up synced folder, <code>-1</code> if not present
|
|
||||||
*/
|
|
||||||
public int getSectionByLocalPathAndType(String localPath, int type) {
|
|
||||||
for (int i = 0; i < filteredSyncFolderItems.size(); i++) {
|
|
||||||
if (filteredSyncFolderItems.get(i).getLocalPath().equalsIgnoreCase(localPath) &&
|
|
||||||
filteredSyncFolderItems.get(i).getType().id == type) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindHeaderViewHolder(SectionedViewHolder commonHolder, final int section, boolean expanded) {
|
|
||||||
if (section < filteredSyncFolderItems.size()) {
|
|
||||||
HeaderViewHolder holder = (HeaderViewHolder) commonHolder;
|
|
||||||
holder.binding.headerContainer.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
holder.binding.title.setText(filteredSyncFolderItems.get(section).getFolderName());
|
|
||||||
|
|
||||||
if (MediaFolderType.VIDEO == filteredSyncFolderItems.get(section).getType()) {
|
|
||||||
holder.binding.type.setImageResource(R.drawable.video_32dp);
|
|
||||||
} else if (MediaFolderType.IMAGE == filteredSyncFolderItems.get(section).getType()) {
|
|
||||||
holder.binding.type.setImageResource(R.drawable.image_32dp);
|
|
||||||
} else {
|
|
||||||
holder.binding.type.setImageResource(R.drawable.folder_star_32dp);
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.binding.syncStatusButton.setVisibility(View.VISIBLE);
|
|
||||||
holder.binding.syncStatusButton.setTag(section);
|
|
||||||
holder.binding.syncStatusButton.setOnClickListener(v -> {
|
|
||||||
filteredSyncFolderItems.get(section).setEnabled(
|
|
||||||
!filteredSyncFolderItems.get(section).isEnabled(),
|
|
||||||
clock.getCurrentTime()
|
|
||||||
);
|
|
||||||
setSyncButtonActiveIcon(
|
|
||||||
holder.binding.syncStatusButton,
|
|
||||||
filteredSyncFolderItems.get(section).isEnabled());
|
|
||||||
clickListener.onSyncStatusToggleClick(section, filteredSyncFolderItems.get(section));
|
|
||||||
});
|
|
||||||
setSyncButtonActiveIcon(holder.binding.syncStatusButton, filteredSyncFolderItems.get(section).isEnabled());
|
|
||||||
|
|
||||||
if (light) {
|
|
||||||
holder.binding.settingsButton.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
holder.binding.settingsButton.setVisibility(View.VISIBLE);
|
|
||||||
holder.binding.settingsButton.setTag(section);
|
|
||||||
holder.binding.settingsButton.setOnClickListener(
|
|
||||||
v -> onOverflowIconClicked(section, filteredSyncFolderItems.get(section), v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onOverflowIconClicked(int section, SyncedFolderDisplayItem item, View view) {
|
|
||||||
PopupMenu popup = new PopupMenu(context, view);
|
|
||||||
popup.inflate(R.menu.synced_folders_adapter);
|
|
||||||
popup.setOnMenuItemClickListener(i -> optionsItemSelected(i, section, item));
|
|
||||||
popup.getMenu()
|
|
||||||
.findItem(R.id.action_auto_upload_folder_toggle_visibility)
|
|
||||||
.setChecked(item.isHidden());
|
|
||||||
|
|
||||||
popup.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean optionsItemSelected(MenuItem menuItem, int section, SyncedFolderDisplayItem item) {
|
|
||||||
if (menuItem.getItemId() == R.id.action_auto_upload_folder_toggle_visibility) {
|
|
||||||
clickListener.onVisibilityToggleClick(section, item);
|
|
||||||
} else {
|
|
||||||
// default: R.id.action_create_custom_folder
|
|
||||||
clickListener.onSyncFolderSettingsClick(section, item);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindFooterViewHolder(SectionedViewHolder holder, int section) {
|
|
||||||
if (isLastSection(section) && showFooter()) {
|
|
||||||
FooterViewHolder footerHolder = (FooterViewHolder) holder;
|
|
||||||
footerHolder.binding.footerText.setOnClickListener(v -> toggleHiddenItemsVisibility());
|
|
||||||
footerHolder.binding.footerText.setText(
|
|
||||||
context.getResources().getQuantityString(
|
|
||||||
R.plurals.synced_folders_show_hidden_folders,
|
|
||||||
getHiddenFolderCount(),
|
|
||||||
getHiddenFolderCount()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(SectionedViewHolder commonHolder, int section, int relativePosition,
|
|
||||||
int absolutePosition) {
|
|
||||||
if (section < filteredSyncFolderItems.size() && filteredSyncFolderItems.get(section).getFilePaths() != null) {
|
|
||||||
MainViewHolder holder = (MainViewHolder) commonHolder;
|
|
||||||
|
|
||||||
File file = new File(filteredSyncFolderItems.get(section).getFilePaths().get(relativePosition));
|
|
||||||
|
|
||||||
ThumbnailsCacheManager.MediaThumbnailGenerationTask task =
|
|
||||||
new ThumbnailsCacheManager.MediaThumbnailGenerationTask(holder.binding.thumbnail,
|
|
||||||
context,
|
|
||||||
viewThemeUtils);
|
|
||||||
|
|
||||||
ThumbnailsCacheManager.AsyncMediaThumbnailDrawable asyncDrawable =
|
|
||||||
new ThumbnailsCacheManager.AsyncMediaThumbnailDrawable(
|
|
||||||
context.getResources(),
|
|
||||||
ThumbnailsCacheManager.mDefaultImg
|
|
||||||
);
|
|
||||||
holder.binding.thumbnail.setImageDrawable(asyncDrawable);
|
|
||||||
|
|
||||||
task.executeOnExecutor(thumbnailThreadPool, file);
|
|
||||||
|
|
||||||
// set proper tag
|
|
||||||
holder.binding.thumbnail.setTag(file.hashCode());
|
|
||||||
|
|
||||||
holder.itemView.setTag(relativePosition % gridWidth);
|
|
||||||
|
|
||||||
if (filteredSyncFolderItems.get(section).getNumberOfFiles() > gridTotal &&
|
|
||||||
relativePosition >= gridTotal - 1) {
|
|
||||||
holder.binding.counter.setText(
|
|
||||||
String.format(
|
|
||||||
Locale.US,
|
|
||||||
"%d",
|
|
||||||
filteredSyncFolderItems.get(section).getNumberOfFiles() - gridTotal));
|
|
||||||
holder.binding.counterLayout.setVisibility(View.VISIBLE);
|
|
||||||
holder.binding.thumbnailDarkener.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
holder.binding.counterLayout.setVisibility(View.GONE);
|
|
||||||
holder.binding.thumbnailDarkener.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public SectionedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
|
||||||
if (viewType == VIEW_TYPE_HEADER) {
|
|
||||||
return new HeaderViewHolder(
|
|
||||||
SyncedFoldersItemHeaderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
|
|
||||||
);
|
|
||||||
} else if (viewType == VIEW_TYPE_FOOTER) {
|
|
||||||
return new FooterViewHolder(
|
|
||||||
SyncedFoldersFooterBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
|
|
||||||
);
|
|
||||||
} else if (viewType == VIEW_TYPE_EMPTY) {
|
|
||||||
return new EmptyViewHolder(
|
|
||||||
SyncedFoldersEmptyBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return new MainViewHolder(
|
|
||||||
GridSyncItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isLastSection(int section) {
|
|
||||||
return section >= getSectionCount() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getHiddenFolderCount() {
|
|
||||||
if (syncFolderItems != null && filteredSyncFolderItems != null) {
|
|
||||||
return syncFolderItems.size() - filteredSyncFolderItems.size();
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ClickListener {
|
|
||||||
void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem);
|
|
||||||
void onSyncFolderSettingsClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem);
|
|
||||||
void onVisibilityToggleClick(int section, SyncedFolderDisplayItem item);
|
|
||||||
}
|
|
||||||
|
|
||||||
static class HeaderViewHolder extends SectionedViewHolder {
|
|
||||||
protected SyncedFoldersItemHeaderBinding binding;
|
|
||||||
|
|
||||||
private HeaderViewHolder(SyncedFoldersItemHeaderBinding binding) {
|
|
||||||
super(binding.getRoot());
|
|
||||||
this.binding = binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class FooterViewHolder extends SectionedViewHolder {
|
|
||||||
protected SyncedFoldersFooterBinding binding;
|
|
||||||
|
|
||||||
private FooterViewHolder(SyncedFoldersFooterBinding binding) {
|
|
||||||
super(binding.getRoot());
|
|
||||||
this.binding = binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class EmptyViewHolder extends SectionedViewHolder {
|
|
||||||
private EmptyViewHolder(SyncedFoldersEmptyBinding binding) {
|
|
||||||
super(binding.getRoot());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MainViewHolder extends SectionedViewHolder {
|
|
||||||
protected GridSyncItemBinding binding;
|
|
||||||
|
|
||||||
private MainViewHolder(GridSyncItemBinding binding) {
|
|
||||||
super(binding.getRoot());
|
|
||||||
this.binding = binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSyncButtonActiveIcon(ImageButton syncStatusButton, boolean enabled) {
|
|
||||||
if (enabled) {
|
|
||||||
syncStatusButton.setImageDrawable(
|
|
||||||
viewThemeUtils.platform.tintPrimaryDrawable(context, R.drawable.ic_cloud_sync_on)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
syncStatusButton.setImageResource(R.drawable.ic_cloud_sync_off);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,456 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2016 Andy Scherzinger
|
||||||
|
* SPDX-FileCopyrightText: 2016 Nextcloud
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import android.widget.PopupMenu
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter
|
||||||
|
import com.afollestad.sectionedrecyclerview.SectionedViewHolder
|
||||||
|
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||||
|
import com.nextcloud.client.core.Clock
|
||||||
|
import com.owncloud.android.R
|
||||||
|
import com.owncloud.android.databinding.GridSyncItemBinding
|
||||||
|
import com.owncloud.android.databinding.SyncedFoldersEmptyBinding
|
||||||
|
import com.owncloud.android.databinding.SyncedFoldersFooterBinding
|
||||||
|
import com.owncloud.android.databinding.SyncedFoldersItemHeaderBinding
|
||||||
|
import com.owncloud.android.datamodel.MediaFolderType
|
||||||
|
import com.owncloud.android.datamodel.SyncedFolderDisplayItem
|
||||||
|
import com.owncloud.android.datamodel.ThumbnailsCacheManager
|
||||||
|
import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncMediaThumbnailDrawable
|
||||||
|
import com.owncloud.android.datamodel.ThumbnailsCacheManager.MediaThumbnailGenerationTask
|
||||||
|
import com.owncloud.android.utils.theme.ViewThemeUtils
|
||||||
|
import java.io.File
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter to display all auto-synced folders and/or instant upload media folders.
|
||||||
|
*/
|
||||||
|
@Suppress("LongParameterList")
|
||||||
|
class SyncedFolderAdapter(
|
||||||
|
private val context: Context,
|
||||||
|
private val clock: Clock,
|
||||||
|
private val gridWidth: Int,
|
||||||
|
private val clickListener: ClickListener,
|
||||||
|
private val light: Boolean,
|
||||||
|
private val viewThemeUtils: ViewThemeUtils
|
||||||
|
) : SectionedRecyclerViewAdapter<SectionedViewHolder>() {
|
||||||
|
|
||||||
|
private val gridTotal = gridWidth * 2
|
||||||
|
private val syncFolderItems: MutableList<SyncedFolderDisplayItem> = ArrayList()
|
||||||
|
private val filteredSyncFolderItems: MutableList<SyncedFolderDisplayItem> = ArrayList()
|
||||||
|
private var hideItems = true
|
||||||
|
private val thumbnailThreadPool: Executor = Executors.newCachedThreadPool()
|
||||||
|
|
||||||
|
init {
|
||||||
|
shouldShowHeadersForEmptySections(true)
|
||||||
|
shouldShowFooters(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun toggleHiddenItemsVisibility() {
|
||||||
|
hideItems = !hideItems
|
||||||
|
|
||||||
|
filterHiddenItems(syncFolderItems, hideItems)?.let {
|
||||||
|
filteredSyncFolderItems.clear()
|
||||||
|
filteredSyncFolderItems.addAll(it)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSyncFolderItems(syncFolderItems: List<SyncedFolderDisplayItem>) {
|
||||||
|
this.syncFolderItems.clear()
|
||||||
|
this.syncFolderItems.addAll(syncFolderItems)
|
||||||
|
|
||||||
|
filterHiddenItems(this.syncFolderItems, hideItems)?.let {
|
||||||
|
filteredSyncFolderItems.clear()
|
||||||
|
filteredSyncFolderItems.addAll(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun setSyncFolderItem(location: Int, syncFolderItem: SyncedFolderDisplayItem) {
|
||||||
|
if (hideItems && syncFolderItem.isHidden && filteredSyncFolderItems.contains(syncFolderItem)) {
|
||||||
|
filteredSyncFolderItems.removeAt(location)
|
||||||
|
} else {
|
||||||
|
if (filteredSyncFolderItems.contains(syncFolderItem)) {
|
||||||
|
filteredSyncFolderItems[filteredSyncFolderItems.indexOf(syncFolderItem)] = syncFolderItem
|
||||||
|
} else {
|
||||||
|
filteredSyncFolderItems.add(syncFolderItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncFolderItems.contains(syncFolderItem)) {
|
||||||
|
syncFolderItems[syncFolderItems.indexOf(syncFolderItem)] = syncFolderItem
|
||||||
|
} else {
|
||||||
|
syncFolderItems.add(syncFolderItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun addSyncFolderItem(syncFolderItem: SyncedFolderDisplayItem) {
|
||||||
|
syncFolderItems.add(syncFolderItem)
|
||||||
|
|
||||||
|
// add item for display when either all items should be shown (!hideItems)
|
||||||
|
// or if item should be shown (!.isHidden())
|
||||||
|
if (!hideItems || !syncFolderItem.isHidden) {
|
||||||
|
filteredSyncFolderItems.add(syncFolderItem)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun removeItem(section: Int) {
|
||||||
|
if (filteredSyncFolderItems.contains(syncFolderItems[section])) {
|
||||||
|
filteredSyncFolderItems.remove(syncFolderItems[section])
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
syncFolderItems.removeAt(section)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter for hidden items
|
||||||
|
*
|
||||||
|
* @param items Collection of items to filter
|
||||||
|
* @return Non-hidden items
|
||||||
|
*/
|
||||||
|
private fun filterHiddenItems(
|
||||||
|
items: List<SyncedFolderDisplayItem>?,
|
||||||
|
hide: Boolean
|
||||||
|
): List<SyncedFolderDisplayItem>? {
|
||||||
|
if (!hide) {
|
||||||
|
return items
|
||||||
|
} else {
|
||||||
|
val result: MutableList<SyncedFolderDisplayItem> = ArrayList()
|
||||||
|
|
||||||
|
for (item in items!!) {
|
||||||
|
if (!item.isHidden && !result.contains(item)) {
|
||||||
|
result.add(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSectionCount(): Int {
|
||||||
|
return if (filteredSyncFolderItems.size > 0) {
|
||||||
|
filteredSyncFolderItems.size + 1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun clear() {
|
||||||
|
filteredSyncFolderItems.clear()
|
||||||
|
syncFolderItems.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
val unfilteredSectionCount: Int
|
||||||
|
get() = if (syncFolderItems.size > 0) {
|
||||||
|
syncFolderItems.size + 1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(section: Int): Int {
|
||||||
|
if (section < filteredSyncFolderItems.size) {
|
||||||
|
val filePaths = filteredSyncFolderItems[section].filePaths
|
||||||
|
|
||||||
|
return if (filePaths != null) {
|
||||||
|
filteredSyncFolderItems[section].filePaths.size
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(section: Int): SyncedFolderDisplayItem? {
|
||||||
|
return if (section in filteredSyncFolderItems.indices) {
|
||||||
|
filteredSyncFolderItems[section]
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(section: Int, relativePosition: Int, absolutePosition: Int): Int {
|
||||||
|
return if (isLastSection(section)) {
|
||||||
|
VIEW_TYPE_EMPTY
|
||||||
|
} else {
|
||||||
|
VIEW_TYPE_ITEM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getHeaderViewType(section: Int): Int {
|
||||||
|
return if (isLastSection(section)) {
|
||||||
|
VIEW_TYPE_EMPTY
|
||||||
|
} else {
|
||||||
|
VIEW_TYPE_HEADER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFooterViewType(section: Int): Int {
|
||||||
|
return if (isLastSection(section) && showFooter()) {
|
||||||
|
VIEW_TYPE_FOOTER
|
||||||
|
} else {
|
||||||
|
// only show footer after last item and only if folders have been hidden
|
||||||
|
VIEW_TYPE_EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showFooter(): Boolean {
|
||||||
|
return syncFolderItems.size > filteredSyncFolderItems.size
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the section of a synced folder for the given local path and type.
|
||||||
|
*
|
||||||
|
* @param localPath the local path of the synced folder
|
||||||
|
* @param type the of the synced folder
|
||||||
|
* @return the section index of the looked up synced folder, `-1` if not present
|
||||||
|
*/
|
||||||
|
fun getSectionByLocalPathAndType(localPath: String?, type: Int): Int {
|
||||||
|
for (i in filteredSyncFolderItems.indices) {
|
||||||
|
if (filteredSyncFolderItems[i].localPath.equals(localPath, ignoreCase = true) &&
|
||||||
|
filteredSyncFolderItems[i].type.id == type
|
||||||
|
) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindHeaderViewHolder(commonHolder: SectionedViewHolder, section: Int, expanded: Boolean) {
|
||||||
|
if (section < filteredSyncFolderItems.size) {
|
||||||
|
val holder = commonHolder as HeaderViewHolder
|
||||||
|
holder.binding.headerContainer.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
holder.binding.title.text = filteredSyncFolderItems[section].folderName
|
||||||
|
|
||||||
|
if (MediaFolderType.VIDEO == filteredSyncFolderItems[section].type) {
|
||||||
|
holder.binding.type.setImageResource(R.drawable.video_32dp)
|
||||||
|
} else if (MediaFolderType.IMAGE == filteredSyncFolderItems[section].type) {
|
||||||
|
holder.binding.type.setImageResource(R.drawable.image_32dp)
|
||||||
|
} else {
|
||||||
|
holder.binding.type.setImageResource(R.drawable.folder_star_32dp)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.binding.syncStatusButton.visibility = View.VISIBLE
|
||||||
|
holder.binding.syncStatusButton.tag = section
|
||||||
|
holder.binding.syncStatusButton.setOnClickListener {
|
||||||
|
filteredSyncFolderItems[section].setEnabled(
|
||||||
|
!filteredSyncFolderItems[section].isEnabled,
|
||||||
|
clock.currentTime
|
||||||
|
)
|
||||||
|
setSyncButtonActiveIcon(
|
||||||
|
holder.binding.syncStatusButton,
|
||||||
|
filteredSyncFolderItems[section].isEnabled
|
||||||
|
)
|
||||||
|
clickListener.onSyncStatusToggleClick(section, filteredSyncFolderItems[section])
|
||||||
|
}
|
||||||
|
setSyncButtonActiveIcon(holder.binding.syncStatusButton, filteredSyncFolderItems[section].isEnabled)
|
||||||
|
|
||||||
|
if (light) {
|
||||||
|
holder.binding.settingsButton.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
holder.binding.settingsButton.visibility = View.VISIBLE
|
||||||
|
holder.binding.settingsButton.tag = section
|
||||||
|
holder.binding.settingsButton.setOnClickListener { v: View ->
|
||||||
|
onOverflowIconClicked(
|
||||||
|
section,
|
||||||
|
filteredSyncFolderItems[section],
|
||||||
|
v
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onOverflowIconClicked(section: Int, item: SyncedFolderDisplayItem, view: View) {
|
||||||
|
val popup = PopupMenu(context, view).apply {
|
||||||
|
inflate(R.menu.synced_folders_adapter)
|
||||||
|
setOnMenuItemClickListener { i: MenuItem -> optionsItemSelected(i, section, item) }
|
||||||
|
menu
|
||||||
|
.findItem(R.id.action_auto_upload_folder_toggle_visibility)
|
||||||
|
.setChecked(item.isHidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
popup.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optionsItemSelected(menuItem: MenuItem, section: Int, item: SyncedFolderDisplayItem): Boolean {
|
||||||
|
if (menuItem.itemId == R.id.action_auto_upload_folder_toggle_visibility) {
|
||||||
|
clickListener.onVisibilityToggleClick(section, item)
|
||||||
|
} else {
|
||||||
|
// default: R.id.action_create_custom_folder
|
||||||
|
clickListener.onSyncFolderSettingsClick(section, item)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindFooterViewHolder(holder: SectionedViewHolder, section: Int) {
|
||||||
|
if (isLastSection(section) && showFooter()) {
|
||||||
|
val footerHolder = holder as FooterViewHolder
|
||||||
|
footerHolder.binding.footerText.setOnClickListener { toggleHiddenItemsVisibility() }
|
||||||
|
footerHolder.binding.footerText.text = context.resources.getQuantityString(
|
||||||
|
R.plurals.synced_folders_show_hidden_folders,
|
||||||
|
hiddenFolderCount,
|
||||||
|
hiddenFolderCount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
commonHolder: SectionedViewHolder,
|
||||||
|
section: Int,
|
||||||
|
relativePosition: Int,
|
||||||
|
absolutePosition: Int
|
||||||
|
) {
|
||||||
|
if (section < filteredSyncFolderItems.size && filteredSyncFolderItems[section].filePaths != null) {
|
||||||
|
val holder = commonHolder as MainViewHolder
|
||||||
|
|
||||||
|
val file = File(filteredSyncFolderItems[section].filePaths[relativePosition])
|
||||||
|
|
||||||
|
val task =
|
||||||
|
MediaThumbnailGenerationTask(
|
||||||
|
holder.binding.thumbnail,
|
||||||
|
context,
|
||||||
|
viewThemeUtils
|
||||||
|
)
|
||||||
|
|
||||||
|
val asyncDrawable =
|
||||||
|
AsyncMediaThumbnailDrawable(
|
||||||
|
context.resources,
|
||||||
|
ThumbnailsCacheManager.mDefaultImg
|
||||||
|
)
|
||||||
|
holder.binding.thumbnail.setImageDrawable(asyncDrawable)
|
||||||
|
|
||||||
|
task.executeOnExecutor(thumbnailThreadPool, file)
|
||||||
|
|
||||||
|
// set proper tag
|
||||||
|
holder.binding.thumbnail.tag = file.hashCode()
|
||||||
|
|
||||||
|
holder.itemView.tag = relativePosition % gridWidth
|
||||||
|
|
||||||
|
if (filteredSyncFolderItems[section].numberOfFiles > gridTotal &&
|
||||||
|
relativePosition >= gridTotal - 1
|
||||||
|
) {
|
||||||
|
holder.binding.counter.text = String.format(
|
||||||
|
Locale.US,
|
||||||
|
"%d",
|
||||||
|
filteredSyncFolderItems[section].numberOfFiles - gridTotal
|
||||||
|
)
|
||||||
|
holder.binding.counterLayout.visibility = View.VISIBLE
|
||||||
|
holder.binding.thumbnailDarkener.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
holder.binding.counterLayout.visibility = View.GONE
|
||||||
|
holder.binding.thumbnailDarkener.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionedViewHolder {
|
||||||
|
return when (viewType) {
|
||||||
|
VIEW_TYPE_HEADER -> {
|
||||||
|
HeaderViewHolder(
|
||||||
|
SyncedFoldersItemHeaderBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
VIEW_TYPE_FOOTER -> {
|
||||||
|
FooterViewHolder(
|
||||||
|
SyncedFoldersFooterBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
VIEW_TYPE_EMPTY -> {
|
||||||
|
EmptyViewHolder(
|
||||||
|
SyncedFoldersEmptyBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
MainViewHolder(
|
||||||
|
GridSyncItemBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isLastSection(section: Int): Boolean {
|
||||||
|
return section >= sectionCount - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
val hiddenFolderCount: Int
|
||||||
|
get() = syncFolderItems.size - filteredSyncFolderItems.size
|
||||||
|
|
||||||
|
interface ClickListener {
|
||||||
|
fun onSyncStatusToggleClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem?)
|
||||||
|
fun onSyncFolderSettingsClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem?)
|
||||||
|
fun onVisibilityToggleClick(section: Int, item: SyncedFolderDisplayItem?)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class HeaderViewHolder(var binding: SyncedFoldersItemHeaderBinding) : SectionedViewHolder(
|
||||||
|
binding.root
|
||||||
|
)
|
||||||
|
|
||||||
|
internal class FooterViewHolder(var binding: SyncedFoldersFooterBinding) : SectionedViewHolder(
|
||||||
|
binding.root
|
||||||
|
)
|
||||||
|
|
||||||
|
internal class EmptyViewHolder(binding: SyncedFoldersEmptyBinding) : SectionedViewHolder(binding.root)
|
||||||
|
|
||||||
|
internal class MainViewHolder(var binding: GridSyncItemBinding) : SectionedViewHolder(
|
||||||
|
binding.root
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun setSyncButtonActiveIcon(syncStatusButton: ImageButton, enabled: Boolean) {
|
||||||
|
if (enabled) {
|
||||||
|
syncStatusButton.setImageDrawable(
|
||||||
|
viewThemeUtils.platform.tintDrawable(context, R.drawable.ic_cloud_sync_on, ColorRole.PRIMARY)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
syncStatusButton.setImageResource(R.drawable.ic_cloud_sync_off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val VIEW_TYPE_EMPTY = Int.MAX_VALUE
|
||||||
|
private const val VIEW_TYPE_ITEM = 1
|
||||||
|
private const val VIEW_TYPE_HEADER = 2
|
||||||
|
private const val VIEW_TYPE_FOOTER = 3
|
||||||
|
}
|
||||||
|
}
|
|
@ -83,7 +83,7 @@ public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||||
files.addAll(trashbinFiles);
|
files.addAll(trashbinFiles);
|
||||||
|
|
||||||
files = preferences.getSortOrderByType(FileSortOrder.Type.trashBinView,
|
files = preferences.getSortOrderByType(FileSortOrder.Type.trashBinView,
|
||||||
FileSortOrder.sort_new_to_old).sortTrashbinFiles(files);
|
FileSortOrder.SORT_NEW_TO_OLD).sortTrashbinFiles(files);
|
||||||
|
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,10 +78,7 @@ class UnifiedSearchItemViewHolder(
|
||||||
binding.unifiedSearchItemLayout.setOnClickListener { listInterface.onSearchResultClicked(entry) }
|
binding.unifiedSearchItemLayout.setOnClickListener { listInterface.onSearchResultClicked(entry) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPlaceholder(
|
private fun getPlaceholder(entry: SearchResultEntry, mimetype: String?): Drawable {
|
||||||
entry: SearchResultEntry,
|
|
||||||
mimetype: String?
|
|
||||||
): Drawable {
|
|
||||||
val drawable = with(entry.icon) {
|
val drawable = with(entry.icon) {
|
||||||
when {
|
when {
|
||||||
equals("icon-folder") ->
|
equals("icon-folder") ->
|
||||||
|
|
|
@ -103,14 +103,16 @@ class UnifiedSearchListAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindHeaderViewHolder(holder: SectionedViewHolder, section: Int, expanded: Boolean) {
|
override fun onBindHeaderViewHolder(holder: SectionedViewHolder, section: Int, expanded: Boolean) {
|
||||||
val headerViewHolder = holder as UnifiedSearchHeaderViewHolder
|
(holder as UnifiedSearchHeaderViewHolder).run {
|
||||||
headerViewHolder.bind(sections[section])
|
bind(sections[section])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindFooterViewHolder(holder: SectionedViewHolder, section: Int) {
|
override fun onBindFooterViewHolder(holder: SectionedViewHolder, section: Int) {
|
||||||
if (sections[section].hasMoreResults) {
|
if (sections[section].hasMoreResults) {
|
||||||
val footerViewHolder = holder as UnifiedSearchFooterViewHolder
|
(holder as UnifiedSearchFooterViewHolder).run {
|
||||||
footerViewHolder.bind(sections[section])
|
bind(sections[section])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,17 +128,19 @@ class UnifiedSearchListAdapter(
|
||||||
absolutePosition: Int
|
absolutePosition: Int
|
||||||
) {
|
) {
|
||||||
// TODO different binding (and also maybe diff UI) for non-file results
|
// TODO different binding (and also maybe diff UI) for non-file results
|
||||||
val itemViewHolder = holder as UnifiedSearchItemViewHolder
|
(holder as UnifiedSearchItemViewHolder).run {
|
||||||
val entry = sections[section].entries[relativePosition]
|
val entry = sections[section].entries[relativePosition]
|
||||||
itemViewHolder.bind(entry)
|
bind(entry)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewAttachedToWindow(holder: SectionedViewHolder) {
|
override fun onViewAttachedToWindow(holder: SectionedViewHolder) {
|
||||||
if (holder is UnifiedSearchItemViewHolder) {
|
if (holder is UnifiedSearchItemViewHolder) {
|
||||||
val thumbnailShimmer = holder.binding.thumbnailShimmer
|
holder.binding.thumbnailShimmer.run {
|
||||||
if (thumbnailShimmer.visibility == View.VISIBLE) {
|
if (visibility == View.VISIBLE) {
|
||||||
thumbnailShimmer.setImageResource(R.drawable.background)
|
setImageResource(R.drawable.background)
|
||||||
thumbnailShimmer.resetLoader()
|
resetLoader()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ class AccountRemovalDialog : DialogFragment(), AvatarGenerationListener, Injecta
|
||||||
private var user: User? = null
|
private var user: User? = null
|
||||||
private lateinit var alertDialog: AlertDialog
|
private lateinit var alertDialog: AlertDialog
|
||||||
private var _binding: AccountRemovalDialogBinding? = null
|
private var _binding: AccountRemovalDialogBinding? = null
|
||||||
private val binding get() = _binding!!
|
val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -80,11 +80,13 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem
|
||||||
private var creator: Creator? = null
|
private var creator: Creator? = null
|
||||||
|
|
||||||
enum class Type {
|
enum class Type {
|
||||||
DOCUMENT, SPREADSHEET, PRESENTATION
|
DOCUMENT,
|
||||||
|
SPREADSHEET,
|
||||||
|
PRESENTATION
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _binding: ChooseTemplateBinding? = null
|
private var _binding: ChooseTemplateBinding? = null
|
||||||
private val binding get() = _binding!!
|
val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
|
|
@ -52,11 +52,7 @@ class SendFilesDialog : BottomSheetDialogFragment(R.layout.send_files_fragment),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
binding = SendFilesFragmentBinding.inflate(inflater, container, false)
|
binding = SendFilesFragmentBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
setupSendButtonRecyclerView()
|
setupSendButtonRecyclerView()
|
||||||
|
@ -102,7 +98,8 @@ class SendFilesDialog : BottomSheetDialogFragment(R.layout.send_files_fragment),
|
||||||
icon = match.loadIcon(requireActivity().packageManager)
|
icon = match.loadIcon(requireActivity().packageManager)
|
||||||
label = match.loadLabel(requireActivity().packageManager)
|
label = match.loadLabel(requireActivity().packageManager)
|
||||||
sendButtonData = SendButtonData(
|
sendButtonData = SendButtonData(
|
||||||
icon, label,
|
icon,
|
||||||
|
label,
|
||||||
match.activityInfo.packageName,
|
match.activityInfo.packageName,
|
||||||
match.activityInfo.name
|
match.activityInfo.name
|
||||||
)
|
)
|
||||||
|
|
|
@ -200,7 +200,8 @@ class SendShareDialog : BottomSheetDialogFragment(R.layout.send_share_fragment),
|
||||||
icon = match.loadIcon(requireActivity().packageManager)
|
icon = match.loadIcon(requireActivity().packageManager)
|
||||||
label = match.loadLabel(requireActivity().packageManager)
|
label = match.loadLabel(requireActivity().packageManager)
|
||||||
sendButtonData = SendButtonData(
|
sendButtonData = SendButtonData(
|
||||||
icon, label,
|
icon,
|
||||||
|
label,
|
||||||
match.activityInfo.packageName,
|
match.activityInfo.packageName,
|
||||||
match.activityInfo.name
|
match.activityInfo.name
|
||||||
)
|
)
|
||||||
|
|
|
@ -41,7 +41,7 @@ class SortingOrderDialogFragment : DialogFragment(), Injectable {
|
||||||
retainInstance = true
|
retainInstance = true
|
||||||
|
|
||||||
binding = null
|
binding = null
|
||||||
currentSortOrderName = requireArguments().getString(KEY_SORT_ORDER, FileSortOrder.sort_a_to_z.name)
|
currentSortOrderName = requireArguments().getString(KEY_SORT_ORDER, FileSortOrder.SORT_A_TO_Z.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,12 +51,12 @@ class SortingOrderDialogFragment : DialogFragment(), Injectable {
|
||||||
*/
|
*/
|
||||||
private fun setupDialogElements(binding: SortingOrderFragmentBinding) {
|
private fun setupDialogElements(binding: SortingOrderFragmentBinding) {
|
||||||
val bindings = listOf(
|
val bindings = listOf(
|
||||||
binding.sortByNameAscending to FileSortOrder.sort_a_to_z,
|
binding.sortByNameAscending to FileSortOrder.SORT_A_TO_Z,
|
||||||
binding.sortByNameDescending to FileSortOrder.sort_z_to_a,
|
binding.sortByNameDescending to FileSortOrder.SORT_Z_TO_A,
|
||||||
binding.sortByModificationDateAscending to FileSortOrder.sort_old_to_new,
|
binding.sortByModificationDateAscending to FileSortOrder.SORT_OLD_TO_NEW,
|
||||||
binding.sortByModificationDateDescending to FileSortOrder.sort_new_to_old,
|
binding.sortByModificationDateDescending to FileSortOrder.SORT_NEW_TO_OLD,
|
||||||
binding.sortBySizeAscending to FileSortOrder.sort_small_to_big,
|
binding.sortBySizeAscending to FileSortOrder.SORT_SMALL_TO_BIG,
|
||||||
binding.sortBySizeDescending to FileSortOrder.sort_big_to_small
|
binding.sortBySizeDescending to FileSortOrder.SORT_BIG_TO_SMALL
|
||||||
)
|
)
|
||||||
|
|
||||||
bindings.forEach { (view, sortOrder) ->
|
bindings.forEach { (view, sortOrder) ->
|
||||||
|
|
|
@ -97,7 +97,9 @@ class StoragePermissionDialogFragment : DialogFragment(), Injectable {
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
enum class Result : Parcelable {
|
enum class Result : Parcelable {
|
||||||
CANCEL, FULL_ACCESS, MEDIA_READ_ONLY
|
CANCEL,
|
||||||
|
FULL_ACCESS,
|
||||||
|
MEDIA_READ_ONLY
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -274,17 +274,17 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
|
||||||
private fun checkWritableFolder() {
|
private fun checkWritableFolder() {
|
||||||
if (!syncedFolder!!.isEnabled) {
|
if (!syncedFolder!!.isEnabled) {
|
||||||
binding?.settingInstantBehaviourContainer?.isEnabled = false
|
binding?.settingInstantBehaviourContainer?.isEnabled = false
|
||||||
binding?.settingInstantBehaviourContainer?.alpha = alphaDisabled
|
binding?.settingInstantBehaviourContainer?.alpha = ALPHA_DISABLED
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (syncedFolder!!.localPath != null && File(syncedFolder!!.localPath).canWrite()) {
|
if (syncedFolder!!.localPath != null && File(syncedFolder!!.localPath).canWrite()) {
|
||||||
binding?.settingInstantBehaviourContainer?.isEnabled = true
|
binding?.settingInstantBehaviourContainer?.isEnabled = true
|
||||||
binding?.settingInstantBehaviourContainer?.alpha = alphaEnabled
|
binding?.settingInstantBehaviourContainer?.alpha = ALPHA_ENABLED
|
||||||
binding?.settingInstantBehaviourSummary?.text =
|
binding?.settingInstantBehaviourSummary?.text =
|
||||||
uploadBehaviorItemStrings[syncedFolder!!.uploadActionInteger]
|
uploadBehaviorItemStrings[syncedFolder!!.uploadActionInteger]
|
||||||
} else {
|
} else {
|
||||||
binding?.settingInstantBehaviourContainer?.isEnabled = false
|
binding?.settingInstantBehaviourContainer?.isEnabled = false
|
||||||
binding?.settingInstantBehaviourContainer?.alpha = alphaDisabled
|
binding?.settingInstantBehaviourContainer?.alpha = ALPHA_DISABLED
|
||||||
syncedFolder?.setUploadAction(
|
syncedFolder?.setUploadAction(
|
||||||
resources.getTextArray(R.array.pref_behaviour_entryValues)[0].toString()
|
resources.getTextArray(R.array.pref_behaviour_entryValues)[0].toString()
|
||||||
)
|
)
|
||||||
|
@ -294,9 +294,9 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
|
||||||
|
|
||||||
private fun setupViews(optionalBinding: SyncedFoldersSettingsLayoutBinding?, enable: Boolean) {
|
private fun setupViews(optionalBinding: SyncedFoldersSettingsLayoutBinding?, enable: Boolean) {
|
||||||
val alpha: Float = if (enable) {
|
val alpha: Float = if (enable) {
|
||||||
alphaEnabled
|
ALPHA_ENABLED
|
||||||
} else {
|
} else {
|
||||||
alphaDisabled
|
ALPHA_DISABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
optionalBinding?.let { binding ->
|
optionalBinding?.let { binding ->
|
||||||
|
@ -518,8 +518,8 @@ class SyncedFolderPreferencesDialogFragment : DialogFragment(), Injectable {
|
||||||
private val TAG = SyncedFolderPreferencesDialogFragment::class.java.simpleName
|
private val TAG = SyncedFolderPreferencesDialogFragment::class.java.simpleName
|
||||||
private const val BEHAVIOUR_DIALOG_STATE = "BEHAVIOUR_DIALOG_STATE"
|
private const val BEHAVIOUR_DIALOG_STATE = "BEHAVIOUR_DIALOG_STATE"
|
||||||
private const val NAME_COLLISION_POLICY_DIALOG_STATE = "NAME_COLLISION_POLICY_DIALOG_STATE"
|
private const val NAME_COLLISION_POLICY_DIALOG_STATE = "NAME_COLLISION_POLICY_DIALOG_STATE"
|
||||||
private const val alphaEnabled = 1.0f
|
private const val ALPHA_ENABLED = 1.0f
|
||||||
private const val alphaDisabled = 0.7f
|
private const val ALPHA_DISABLED = 0.7f
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newInstance(syncedFolder: SyncedFolderDisplayItem?, section: Int): SyncedFolderPreferencesDialogFragment? {
|
fun newInstance(syncedFolder: SyncedFolderDisplayItem?, section: Int): SyncedFolderPreferencesDialogFragment? {
|
||||||
|
|
|
@ -91,8 +91,12 @@ class FileDetailsSharingProcessFragment :
|
||||||
* fragment instance to be called while modifying existing share information
|
* fragment instance to be called while modifying existing share information
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newInstance(share: OCShare, screenType: Int, isReshareShown: Boolean, isExpirationDateShown: Boolean):
|
fun newInstance(
|
||||||
FileDetailsSharingProcessFragment {
|
share: OCShare,
|
||||||
|
screenType: Int,
|
||||||
|
isReshareShown: Boolean,
|
||||||
|
isExpirationDateShown: Boolean
|
||||||
|
): FileDetailsSharingProcessFragment {
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putParcelable(ARG_OCSHARE, share)
|
args.putParcelable(ARG_OCSHARE, share)
|
||||||
args.putInt(ARG_SCREEN_TYPE, screenType)
|
args.putInt(ARG_SCREEN_TYPE, screenType)
|
||||||
|
|
|
@ -40,7 +40,7 @@ class ProfileBottomSheetDialog(
|
||||||
private var _binding: ProfileBottomSheetFragmentBinding? = null
|
private var _binding: ProfileBottomSheetFragmentBinding? = null
|
||||||
|
|
||||||
// This property is only valid between onCreateView and onDestroyView.
|
// This property is only valid between onCreateView and onDestroyView.
|
||||||
private val binding get() = _binding!!
|
val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -59,7 +59,7 @@ class UnifiedSearchFragment :
|
||||||
UnifiedSearchItemViewHolder.FilesAction {
|
UnifiedSearchItemViewHolder.FilesAction {
|
||||||
private lateinit var adapter: UnifiedSearchListAdapter
|
private lateinit var adapter: UnifiedSearchListAdapter
|
||||||
private var _binding: ListFragmentBinding? = null
|
private var _binding: ListFragmentBinding? = null
|
||||||
private val binding get() = _binding!!
|
val binding get() = _binding!!
|
||||||
private var searchView: SearchView? = null
|
private var searchView: SearchView? = null
|
||||||
lateinit var vm: IUnifiedSearchViewModel
|
lateinit var vm: IUnifiedSearchViewModel
|
||||||
|
|
||||||
|
|
|
@ -132,12 +132,9 @@ public class FileOperationsHelper {
|
||||||
private String getUrlFromFile(String storagePath, Pattern pattern) {
|
private String getUrlFromFile(String storagePath, Pattern pattern) {
|
||||||
String url = null;
|
String url = null;
|
||||||
|
|
||||||
InputStreamReader fr = null;
|
try (FileInputStream inputStream = new FileInputStream(storagePath);
|
||||||
BufferedReader br = null;
|
InputStreamReader fr = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||||
try {
|
BufferedReader br = new BufferedReader(fr)) {
|
||||||
fr = new InputStreamReader(new FileInputStream(storagePath), StandardCharsets.UTF_8);
|
|
||||||
br = new BufferedReader(fr);
|
|
||||||
|
|
||||||
String line;
|
String line;
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
Matcher m = pattern.matcher(line);
|
Matcher m = pattern.matcher(line);
|
||||||
|
@ -148,23 +145,8 @@ public class FileOperationsHelper {
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log_OC.d(TAG, e.getMessage());
|
Log_OC.d(TAG, e.getMessage());
|
||||||
} finally {
|
|
||||||
if (br != null) {
|
|
||||||
try {
|
|
||||||
br.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log_OC.d(TAG, "Error closing buffered reader for URL file", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fr != null) {
|
|
||||||
try {
|
|
||||||
fr.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log_OC.d(TAG, "Error closing file reader for URL file", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,11 @@ class UriUploader(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
enum class UriUploaderResultCode {
|
enum class UriUploaderResultCode {
|
||||||
OK, ERROR_UNKNOWN, ERROR_NO_FILE_TO_UPLOAD, ERROR_READ_PERMISSION_NOT_GRANTED, ERROR_SENSITIVE_PATH
|
OK,
|
||||||
|
ERROR_UNKNOWN,
|
||||||
|
ERROR_NO_FILE_TO_UPLOAD,
|
||||||
|
ERROR_READ_PERMISSION_NOT_GRANTED,
|
||||||
|
ERROR_SENSITIVE_PATH
|
||||||
}
|
}
|
||||||
|
|
||||||
fun uploadUris(): UriUploaderResultCode {
|
fun uploadUris(): UriUploaderResultCode {
|
||||||
|
@ -121,11 +125,12 @@ class UriUploader(
|
||||||
arrayOf(localPath ?: ""),
|
arrayOf(localPath ?: ""),
|
||||||
arrayOf(remotePath),
|
arrayOf(remotePath),
|
||||||
mBehaviour,
|
mBehaviour,
|
||||||
false, // do not create parent folder if not existent
|
// do not create parent folder if not existent
|
||||||
|
false,
|
||||||
UploadFileOperation.CREATED_BY_USER,
|
UploadFileOperation.CREATED_BY_USER,
|
||||||
false,
|
requiresWifi = false,
|
||||||
false,
|
requiresCharging = false,
|
||||||
NameCollisionPolicy.ASK_USER
|
nameCollisionPolicy = NameCollisionPolicy.ASK_USER
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ class PreviewPdfFragment : Fragment(), Injectable {
|
||||||
requireContext().startActivity(intent)
|
requireContext().startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModel.shouldShowZoomTip.observe(viewLifecycleOwner) { shouldShow ->
|
viewModel.showZoomTip.observe(viewLifecycleOwner) { shouldShow ->
|
||||||
if (shouldShow) {
|
if (shouldShow) {
|
||||||
snack = DisplayUtils.showSnackMessage(binding.root, R.string.pdf_zoom_tip)
|
snack = DisplayUtils.showSnackMessage(binding.root, R.string.pdf_zoom_tip)
|
||||||
viewModel.onZoomTipShown()
|
viewModel.onZoomTipShown()
|
||||||
|
@ -100,8 +100,7 @@ class PreviewPdfFragment : Fragment(), Injectable {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getScreenWidth(): Int =
|
private fun getScreenWidth(): Int = requireContext().resources.displayMetrics.widthPixels
|
||||||
requireContext().resources.displayMetrics.widthPixels
|
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||||
super.onPrepareOptionsMenu(menu)
|
super.onPrepareOptionsMenu(menu)
|
||||||
|
|
|
@ -36,7 +36,7 @@ class PreviewPdfViewModel @Inject constructor(val appPreferences: AppPreferences
|
||||||
get() = _previewImagePath
|
get() = _previewImagePath
|
||||||
|
|
||||||
private var _showZoomTip = MutableLiveData<Boolean>()
|
private var _showZoomTip = MutableLiveData<Boolean>()
|
||||||
val shouldShowZoomTip: LiveData<Boolean>
|
val showZoomTip: LiveData<Boolean>
|
||||||
get() = _showZoomTip
|
get() = _showZoomTip
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
|
|
|
@ -159,7 +159,7 @@ class TrashbinActivity :
|
||||||
supportFragmentManager,
|
supportFragmentManager,
|
||||||
preferences?.getSortOrderByType(
|
preferences?.getSortOrderByType(
|
||||||
FileSortOrder.Type.trashBinView,
|
FileSortOrder.Type.trashBinView,
|
||||||
FileSortOrder.sort_new_to_old
|
FileSortOrder.SORT_NEW_TO_OLD
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -247,10 +247,10 @@ class TrashbinActivity :
|
||||||
onBackPressedCallback.isEnabled = !isRoot
|
onBackPressedCallback.isEnabled = !isRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSortingOrderChosen(sortOrder: FileSortOrder?) {
|
override fun onSortingOrderChosen(selection: FileSortOrder?) {
|
||||||
val sortButton = findViewById<TextView>(R.id.sort_button)
|
val sortButton = findViewById<TextView>(R.id.sort_button)
|
||||||
sortButton.setText(DisplayUtils.getSortOrderStringId(sortOrder))
|
sortButton.setText(DisplayUtils.getSortOrderStringId(selection))
|
||||||
trashbinListAdapter?.setSortOrder(sortOrder)
|
trashbinListAdapter?.setSortOrder(selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showTrashbinFolder(trashbinFiles: List<TrashbinFile?>?) {
|
override fun showTrashbinFolder(trashbinFiles: List<TrashbinFile?>?) {
|
||||||
|
|
|
@ -30,26 +30,20 @@ import javax.inject.Inject
|
||||||
class UnifiedSearchViewModel(application: Application) : AndroidViewModel(application), IUnifiedSearchViewModel {
|
class UnifiedSearchViewModel(application: Application) : AndroidViewModel(application), IUnifiedSearchViewModel {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UnifiedSearchViewModel"
|
private const val TAG = "UnifiedSearchViewModel"
|
||||||
private const val DEFAULT_LIMIT = 5
|
|
||||||
private const val FILES_PROVIDER_ID = "files"
|
private const val FILES_PROVIDER_ID = "files"
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class UnifiedSearchMetadata(
|
private data class UnifiedSearchMetadata(
|
||||||
var results: MutableList<SearchResult> = mutableListOf()
|
var results: MutableList<SearchResult> = mutableListOf()
|
||||||
) {
|
) {
|
||||||
fun nextCursor(): Int? = results.lastOrNull()?.cursor?.toInt()
|
fun nextCursor(): Int? {
|
||||||
|
return try {
|
||||||
|
results.lastOrNull()?.cursor?.toInt()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
fun name(): String? = results.lastOrNull()?.name
|
fun name(): String? = results.lastOrNull()?.name
|
||||||
fun isFinished(): Boolean {
|
|
||||||
if (results.isEmpty()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val lastResult = results.last()
|
|
||||||
return when {
|
|
||||||
!lastResult.isPaginated -> true
|
|
||||||
lastResult.entries.size < DEFAULT_LIMIT -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var currentAccountProvider: CurrentAccountProvider
|
private lateinit var currentAccountProvider: CurrentAccountProvider
|
||||||
|
@ -62,7 +56,6 @@ class UnifiedSearchViewModel(application: Application) : AndroidViewModel(applic
|
||||||
get() = getApplication<Application>().applicationContext
|
get() = getApplication<Application>().applicationContext
|
||||||
|
|
||||||
private lateinit var repository: IUnifiedSearchRepository
|
private lateinit var repository: IUnifiedSearchRepository
|
||||||
private var loadingStarted: Boolean = false
|
|
||||||
private var results: MutableMap<ProviderID, UnifiedSearchMetadata> = mutableMapOf()
|
private var results: MutableMap<ProviderID, UnifiedSearchMetadata> = mutableMapOf()
|
||||||
|
|
||||||
override val isLoading = MutableLiveData(false)
|
override val isLoading = MutableLiveData(false)
|
||||||
|
@ -94,14 +87,6 @@ class UnifiedSearchViewModel(application: Application) : AndroidViewModel(applic
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun startLoading(query: String) {
|
|
||||||
if (!loadingStarted) {
|
|
||||||
loadingStarted = true
|
|
||||||
this.query.value = query
|
|
||||||
initialQuery()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears data and queries all available providers
|
* Clears data and queries all available providers
|
||||||
*/
|
*/
|
||||||
|
@ -146,6 +131,7 @@ class UnifiedSearchViewModel(application: Application) : AndroidViewModel(applic
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> block()
|
else -> block()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,6 +152,7 @@ class UnifiedSearchViewModel(application: Application) : AndroidViewModel(applic
|
||||||
val fullUrl = serverUrl + result.resourceUrl
|
val fullUrl = serverUrl + result.resourceUrl
|
||||||
Uri.parse(fullUrl)
|
Uri.parse(fullUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> uri
|
else -> uri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,10 +172,6 @@ class UnifiedSearchViewModel(application: Application) : AndroidViewModel(applic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun clearError() {
|
|
||||||
error.value = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onError(error: Throwable) {
|
fun onError(error: Throwable) {
|
||||||
Log_OC.e(TAG, "Error: " + error.stackTrace)
|
Log_OC.e(TAG, "Error: " + error.stackTrace)
|
||||||
}
|
}
|
||||||
|
@ -213,11 +196,13 @@ class UnifiedSearchViewModel(application: Application) : AndroidViewModel(applic
|
||||||
searchResults.value = results
|
searchResults.value = results
|
||||||
.filter { it.value.results.isNotEmpty() }
|
.filter { it.value.results.isNotEmpty() }
|
||||||
.map { (key, value) ->
|
.map { (key, value) ->
|
||||||
|
val isLastEntryHaveValue = results[key]?.results?.last()?.entries?.isEmpty() != true
|
||||||
|
|
||||||
UnifiedSearchSection(
|
UnifiedSearchSection(
|
||||||
providerID = key,
|
providerID = key,
|
||||||
name = value.name()!!,
|
name = value.name()!!,
|
||||||
entries = value.results.flatMap { it.entries },
|
entries = value.results.flatMap { it.entries },
|
||||||
hasMoreResults = !value.isFinished()
|
hasMoreResults = isLastEntryHaveValue && results[key]?.nextCursor() != null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.sortedWith { o1, o2 ->
|
.sortedWith { o1, o2 ->
|
||||||
|
|
|
@ -110,12 +110,12 @@ import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
|
||||||
import static com.owncloud.android.ui.dialog.SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT;
|
import static com.owncloud.android.ui.dialog.SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT;
|
||||||
import static com.owncloud.android.utils.FileSortOrder.sort_a_to_z_id;
|
import static com.owncloud.android.utils.FileSortOrder.SORT_A_TO_Z_ID;
|
||||||
import static com.owncloud.android.utils.FileSortOrder.sort_big_to_small_id;
|
import static com.owncloud.android.utils.FileSortOrder.SORT_BIG_TO_SMALL_ID;
|
||||||
import static com.owncloud.android.utils.FileSortOrder.sort_new_to_old_id;
|
import static com.owncloud.android.utils.FileSortOrder.SORT_NEW_TO_OLD_ID;
|
||||||
import static com.owncloud.android.utils.FileSortOrder.sort_old_to_new_id;
|
import static com.owncloud.android.utils.FileSortOrder.SORT_OLD_TO_NEW_ID;
|
||||||
import static com.owncloud.android.utils.FileSortOrder.sort_small_to_big_id;
|
import static com.owncloud.android.utils.FileSortOrder.SORT_SMALL_TO_BIG_ID;
|
||||||
import static com.owncloud.android.utils.FileSortOrder.sort_z_to_a_id;
|
import static com.owncloud.android.utils.FileSortOrder.SORT_Z_TO_A_ID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper class for UI/display related operations.
|
* A helper class for UI/display related operations.
|
||||||
|
@ -819,17 +819,17 @@ public final class DisplayUtils {
|
||||||
|
|
||||||
public static @StringRes int getSortOrderStringId(FileSortOrder sortOrder) {
|
public static @StringRes int getSortOrderStringId(FileSortOrder sortOrder) {
|
||||||
switch (sortOrder.name) {
|
switch (sortOrder.name) {
|
||||||
case sort_z_to_a_id:
|
case SORT_Z_TO_A_ID:
|
||||||
return R.string.menu_item_sort_by_name_z_a;
|
return R.string.menu_item_sort_by_name_z_a;
|
||||||
case sort_new_to_old_id:
|
case SORT_NEW_TO_OLD_ID:
|
||||||
return R.string.menu_item_sort_by_date_newest_first;
|
return R.string.menu_item_sort_by_date_newest_first;
|
||||||
case sort_old_to_new_id:
|
case SORT_OLD_TO_NEW_ID:
|
||||||
return R.string.menu_item_sort_by_date_oldest_first;
|
return R.string.menu_item_sort_by_date_oldest_first;
|
||||||
case sort_big_to_small_id:
|
case SORT_BIG_TO_SMALL_ID:
|
||||||
return R.string.menu_item_sort_by_size_biggest_first;
|
return R.string.menu_item_sort_by_size_biggest_first;
|
||||||
case sort_small_to_big_id:
|
case SORT_SMALL_TO_BIG_ID:
|
||||||
return R.string.menu_item_sort_by_size_smallest_first;
|
return R.string.menu_item_sort_by_size_smallest_first;
|
||||||
case sort_a_to_z_id:
|
case SORT_A_TO_Z_ID:
|
||||||
default:
|
default:
|
||||||
return R.string.menu_item_sort_by_name_a_z;
|
return R.string.menu_item_sort_by_name_a_z;
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,7 +196,8 @@ class EncryptionUtilsV2 {
|
||||||
|
|
||||||
DecryptedFolderMetadataFile(
|
DecryptedFolderMetadataFile(
|
||||||
decryptedMetadata,
|
decryptedMetadata,
|
||||||
mutableListOf(), // subfolder do not store user array
|
// subfolder do not store user array
|
||||||
|
mutableListOf(),
|
||||||
mutableMapOf()
|
mutableMapOf()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -530,20 +531,13 @@ class EncryptionUtilsV2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalStateException::class)
|
@Throws(IllegalStateException::class)
|
||||||
fun removeFileFromMetadata(
|
fun removeFileFromMetadata(fileName: String, metadata: DecryptedFolderMetadataFile) {
|
||||||
fileName: String,
|
|
||||||
metadata: DecryptedFolderMetadataFile
|
|
||||||
) {
|
|
||||||
metadata.metadata.files.remove(fileName)
|
metadata.metadata.files.remove(fileName)
|
||||||
?: throw IllegalStateException("File $fileName not found in metadata!")
|
?: throw IllegalStateException("File $fileName not found in metadata!")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalStateException::class)
|
@Throws(IllegalStateException::class)
|
||||||
fun renameFile(
|
fun renameFile(key: String, newName: String, metadataFile: DecryptedFolderMetadataFile) {
|
||||||
key: String,
|
|
||||||
newName: String,
|
|
||||||
metadataFile: DecryptedFolderMetadataFile
|
|
||||||
) {
|
|
||||||
if (!metadataFile.metadata.files.containsKey(key)) {
|
if (!metadataFile.metadata.files.containsKey(key)) {
|
||||||
throw IllegalStateException("File with key $key not found in metadata!")
|
throw IllegalStateException("File with key $key not found in metadata!")
|
||||||
}
|
}
|
||||||
|
@ -956,7 +950,8 @@ class EncryptionUtilsV2 {
|
||||||
encryptedFolderMetadataFile: EncryptedFolderMetadataFile,
|
encryptedFolderMetadataFile: EncryptedFolderMetadataFile,
|
||||||
decryptedFolderMetadataFile: DecryptedFolderMetadataFile,
|
decryptedFolderMetadataFile: DecryptedFolderMetadataFile,
|
||||||
oldCounter: Long,
|
oldCounter: Long,
|
||||||
ans: String // base 64 encoded BER
|
// base 64 encoded BER
|
||||||
|
ans: String
|
||||||
) {
|
) {
|
||||||
// check counter
|
// check counter
|
||||||
if (decryptedFolderMetadataFile.metadata.counter < oldCounter) {
|
if (decryptedFolderMetadataFile.metadata.counter < oldCounter) {
|
||||||
|
|
|
@ -25,13 +25,7 @@ import java.io.InputStream
|
||||||
|
|
||||||
class FileExportUtils {
|
class FileExportUtils {
|
||||||
@Throws(IllegalStateException::class)
|
@Throws(IllegalStateException::class)
|
||||||
fun exportFile(
|
fun exportFile(fileName: String, mimeType: String, contentResolver: ContentResolver, ocFile: OCFile?, file: File?) {
|
||||||
fileName: String,
|
|
||||||
mimeType: String,
|
|
||||||
contentResolver: ContentResolver,
|
|
||||||
ocFile: OCFile?,
|
|
||||||
file: File?
|
|
||||||
) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
exportFileAndroid10AndAbove(
|
exportFileAndroid10AndAbove(
|
||||||
fileName,
|
fileName,
|
||||||
|
|
|
@ -29,44 +29,46 @@ open class FileSortOrder(@JvmField var name: String, var isAscending: Boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class SortType {
|
enum class SortType {
|
||||||
SIZE, ALPHABET, DATE
|
SIZE,
|
||||||
|
ALPHABET,
|
||||||
|
DATE
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val sort_a_to_z_id = "sort_a_to_z"
|
const val SORT_A_TO_Z_ID = "sort_a_to_z"
|
||||||
const val sort_z_to_a_id = "sort_z_to_a"
|
const val SORT_Z_TO_A_ID = "sort_z_to_a"
|
||||||
const val sort_old_to_new_id = "sort_old_to_new"
|
const val SORT_OLD_TO_NEW_ID = "sort_old_to_new"
|
||||||
const val sort_new_to_old_id = "sort_new_to_old"
|
const val SORT_NEW_TO_OLD_ID = "sort_new_to_old"
|
||||||
const val sort_small_to_big_id = "sort_small_to_big"
|
const val SORT_SMALL_TO_BIG_ID = "sort_small_to_big"
|
||||||
const val sort_big_to_small_id = "sort_big_to_small"
|
const val SORT_BIG_TO_SMALL_ID = "sort_big_to_small"
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val sort_a_to_z: FileSortOrder = FileSortOrderByName(sort_a_to_z_id, true)
|
val SORT_A_TO_Z: FileSortOrder = FileSortOrderByName(SORT_A_TO_Z_ID, true)
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val sort_z_to_a: FileSortOrder = FileSortOrderByName(sort_z_to_a_id, false)
|
val SORT_Z_TO_A: FileSortOrder = FileSortOrderByName(SORT_Z_TO_A_ID, false)
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val sort_old_to_new: FileSortOrder = FileSortOrderByDate(sort_old_to_new_id, true)
|
val SORT_OLD_TO_NEW: FileSortOrder = FileSortOrderByDate(SORT_OLD_TO_NEW_ID, true)
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val sort_new_to_old: FileSortOrder = FileSortOrderByDate(sort_new_to_old_id, false)
|
val SORT_NEW_TO_OLD: FileSortOrder = FileSortOrderByDate(SORT_NEW_TO_OLD_ID, false)
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val sort_small_to_big: FileSortOrder = FileSortOrderBySize(sort_small_to_big_id, true)
|
val SORT_SMALL_TO_BIG: FileSortOrder = FileSortOrderBySize(SORT_SMALL_TO_BIG_ID, true)
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val sort_big_to_small: FileSortOrder = FileSortOrderBySize(sort_big_to_small_id, false)
|
val SORT_BIG_TO_SMALL: FileSortOrder = FileSortOrderBySize(SORT_BIG_TO_SMALL_ID, false)
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val sortOrders: Map<String, FileSortOrder> = Collections.unmodifiableMap(
|
val sortOrders: Map<String, FileSortOrder> = Collections.unmodifiableMap(
|
||||||
mapOf(
|
mapOf(
|
||||||
sort_a_to_z.name to sort_a_to_z,
|
SORT_A_TO_Z.name to SORT_A_TO_Z,
|
||||||
sort_z_to_a.name to sort_z_to_a,
|
SORT_Z_TO_A.name to SORT_Z_TO_A,
|
||||||
sort_old_to_new.name to sort_old_to_new,
|
SORT_OLD_TO_NEW.name to SORT_OLD_TO_NEW,
|
||||||
sort_new_to_old.name to sort_new_to_old,
|
SORT_NEW_TO_OLD.name to SORT_NEW_TO_OLD,
|
||||||
sort_small_to_big.name to sort_small_to_big,
|
SORT_SMALL_TO_BIG.name to SORT_SMALL_TO_BIG,
|
||||||
sort_big_to_small.name to sort_big_to_small
|
SORT_BIG_TO_SMALL.name to SORT_BIG_TO_SMALL
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -103,14 +105,14 @@ open class FileSortOrder(@JvmField var name: String, var isAscending: Boolean) {
|
||||||
|
|
||||||
open fun getType(): SortType {
|
open fun getType(): SortType {
|
||||||
return when (name) {
|
return when (name) {
|
||||||
sort_z_to_a_id,
|
SORT_Z_TO_A_ID,
|
||||||
sort_a_to_z_id -> SortType.ALPHABET
|
SORT_A_TO_Z_ID -> SortType.ALPHABET
|
||||||
|
|
||||||
sort_small_to_big_id,
|
SORT_SMALL_TO_BIG_ID,
|
||||||
sort_big_to_small_id -> SortType.SIZE
|
SORT_BIG_TO_SMALL_ID -> SortType.SIZE
|
||||||
|
|
||||||
sort_new_to_old_id,
|
SORT_NEW_TO_OLD_ID,
|
||||||
sort_old_to_new_id -> SortType.DATE
|
SORT_OLD_TO_NEW_ID -> SortType.DATE
|
||||||
|
|
||||||
else -> SortType.ALPHABET
|
else -> SortType.ALPHABET
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,12 +376,8 @@ public final class FileStorageUtils {
|
||||||
public static boolean copyFile(File src, File target) {
|
public static boolean copyFile(File src, File target) {
|
||||||
boolean ret = true;
|
boolean ret = true;
|
||||||
|
|
||||||
InputStream in = null;
|
try (InputStream in = new FileInputStream(src);
|
||||||
OutputStream out = null;
|
OutputStream out = new FileOutputStream(target)) {
|
||||||
|
|
||||||
try {
|
|
||||||
in = new FileInputStream(src);
|
|
||||||
out = new FileOutputStream(target);
|
|
||||||
byte[] buf = new byte[1024];
|
byte[] buf = new byte[1024];
|
||||||
int len;
|
int len;
|
||||||
while ((len = in.read(buf)) > 0) {
|
while ((len = in.read(buf)) > 0) {
|
||||||
|
@ -389,21 +385,6 @@ public final class FileStorageUtils {
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
ret = false;
|
ret = false;
|
||||||
} finally {
|
|
||||||
if (in != null) {
|
|
||||||
try {
|
|
||||||
in.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log_OC.e(TAG, "Error closing input stream during copy", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (out != null) {
|
|
||||||
try {
|
|
||||||
out.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log_OC.e(TAG, "Error closing output stream during copy", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -108,9 +108,7 @@ class FilesSpecificViewThemeUtils @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getThumbDrawable(
|
private fun getThumbDrawable(context: Context): Drawable {
|
||||||
context: Context
|
|
||||||
): Drawable {
|
|
||||||
val thumbDrawable =
|
val thumbDrawable =
|
||||||
ResourcesCompat.getDrawable(
|
ResourcesCompat.getDrawable(
|
||||||
context.resources,
|
context.resources,
|
||||||
|
|
|
@ -574,6 +574,7 @@
|
||||||
<string name="upload_action_global_upload_pause">Posar toles descargues</string>
|
<string name="upload_action_global_upload_pause">Posar toles descargues</string>
|
||||||
<string name="upload_action_global_upload_resume">Siguir con toles descargues</string>
|
<string name="upload_action_global_upload_resume">Siguir con toles descargues</string>
|
||||||
<string name="upload_cannot_create_file">Nun se pue crear el ficheru llocal</string>
|
<string name="upload_cannot_create_file">Nun se pue crear el ficheru llocal</string>
|
||||||
|
<string name="upload_direct_camera_video">Videu</string>
|
||||||
<string name="upload_file_dialog_filename">Nome de ficheru</string>
|
<string name="upload_file_dialog_filename">Nome de ficheru</string>
|
||||||
<string name="upload_file_dialog_filetype">Triba de ficheru</string>
|
<string name="upload_file_dialog_filetype">Triba de ficheru</string>
|
||||||
<string name="upload_global_pause_title">Toles descargues tán posaes</string>
|
<string name="upload_global_pause_title">Toles descargues tán posaes</string>
|
||||||
|
|
|
@ -33,7 +33,7 @@ class OCFileSortTest {
|
||||||
fun testFileSortOrder() {
|
fun testFileSortOrder() {
|
||||||
val toSort = getShuffledList()
|
val toSort = getShuffledList()
|
||||||
|
|
||||||
FileSortOrder.sort_a_to_z.sortCloudFiles(toSort)
|
FileSortOrder.SORT_A_TO_Z.sortCloudFiles(toSort)
|
||||||
|
|
||||||
verifySort(toSort)
|
verifySort(toSort)
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,6 +25,7 @@
|
||||||
</trusted-key>
|
</trusted-key>
|
||||||
<trusted-key id="08EF3EC268A80497ED2030812838A2C567F74226" group="org.scala-lang.modules" name="scala-parallel-collections_2.13" version="1.0.4"/>
|
<trusted-key id="08EF3EC268A80497ED2030812838A2C567F74226" group="org.scala-lang.modules" name="scala-parallel-collections_2.13" version="1.0.4"/>
|
||||||
<trusted-key id="08F0AAB4D0C1A4BDDE340765B341DDB020FCB6AB" group="org.bouncycastle"/>
|
<trusted-key id="08F0AAB4D0C1A4BDDE340765B341DDB020FCB6AB" group="org.bouncycastle"/>
|
||||||
|
<trusted-key id="09939C73246B4BA7444CAA453D002DBC5EA9615F" group="dev.drewhamilton.poko"/>
|
||||||
<trusted-key id="0A784736254716AC0EBEF6E2365D826E84B11B58" group="com.karumi"/>
|
<trusted-key id="0A784736254716AC0EBEF6E2365D826E84B11B58" group="com.karumi"/>
|
||||||
<trusted-key id="0C6914BDDF32BD0AF3AAEB8A2148325ADD28A1AD" group="^com[.]atlassian($|([.].*))" regex="true"/>
|
<trusted-key id="0C6914BDDF32BD0AF3AAEB8A2148325ADD28A1AD" group="^com[.]atlassian($|([.].*))" regex="true"/>
|
||||||
<trusted-key id="0CC641C3A62453AB390066C4A41F13C999945293">
|
<trusted-key id="0CC641C3A62453AB390066C4A41F13C999945293">
|
||||||
|
@ -1652,6 +1653,11 @@
|
||||||
<sha256 value="7ff14d11a3167a854ac12e62aa6b5a68d230821664199244d1563cf30b5c6fd0" origin="Generated by Gradle"/>
|
<sha256 value="7ff14d11a3167a854ac12e62aa6b5a68d230821664199244d1563cf30b5c6fd0" origin="Generated by Gradle"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="androidx.fragment" name="fragment" version="1.8.1">
|
||||||
|
<artifact name="fragment-1.8.1.aar">
|
||||||
|
<sha256 value="81526b28c3e4b513760174ee26142c1372c9e13d1c8bf3a220830307aca1971f" origin="Generated by Gradle"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="androidx.fragment" name="fragment-ktx" version="1.3.2">
|
<component group="androidx.fragment" name="fragment-ktx" version="1.3.2">
|
||||||
<artifact name="fragment-ktx-1.3.2.aar">
|
<artifact name="fragment-ktx-1.3.2.aar">
|
||||||
<sha256 value="29af1e9ee0e93b5fc638600c230705584aecc49205c363f0923ba1e5be675533" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="29af1e9ee0e93b5fc638600c230705584aecc49205c363f0923ba1e5be675533" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -3050,6 +3056,11 @@
|
||||||
<sha256 value="80717d62484256843ba1221b6f2ef867acd1c515a08311dad487b7272685bb45" origin="Generated by Gradle"/>
|
<sha256 value="80717d62484256843ba1221b6f2ef867acd1c515a08311dad487b7272685bb45" origin="Generated by Gradle"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="ch.qos.logback" name="logback-parent" version="1.3.14">
|
||||||
|
<artifact name="logback-parent-1.3.14.pom">
|
||||||
|
<sha256 value="796dcc4cbfd42aeb7f72432445f5364619678928242b9f3e4749d4c4e0dd9dc4" origin="Generated by Gradle"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.adobe.xmp" name="xmpcore" version="5.1.2">
|
<component group="com.adobe.xmp" name="xmpcore" version="5.1.2">
|
||||||
<artifact name="xmpcore-5.1.2.jar">
|
<artifact name="xmpcore-5.1.2.jar">
|
||||||
<sha256 value="0adcd63003aaff0a87b938f6accc2d890a2169c751a9b36881237f8546287090" origin="Generated by Gradle"/>
|
<sha256 value="0adcd63003aaff0a87b938f6accc2d890a2169c751a9b36881237f8546287090" origin="Generated by Gradle"/>
|
||||||
|
@ -6276,6 +6287,14 @@
|
||||||
<sha256 value="42698696ebd5af1eb33f917e582baa416e289a21cd83ee329c255852216b8a78" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="42698696ebd5af1eb33f917e582baa416e289a21cd83ee329c255852216b8a78" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.android.datatransport" name="transport-backend-cct" version="3.1.9">
|
||||||
|
<artifact name="transport-backend-cct-3.1.9.aar">
|
||||||
|
<sha256 value="07a32025a65b08ee7e11d14dc539a758b66713f0c1d13900a81599ceadbaf44d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="transport-backend-cct-3.1.9.pom">
|
||||||
|
<sha256 value="3ce9eb56f52c15e96683030fffc8db553bc41d1e999e207cb904cbec45d4fd49" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.android.datatransport" name="transport-runtime" version="3.1.8">
|
<component group="com.google.android.datatransport" name="transport-runtime" version="3.1.8">
|
||||||
<artifact name="transport-runtime-3.1.8.aar">
|
<artifact name="transport-runtime-3.1.8.aar">
|
||||||
<sha256 value="cb9353ef1791ae17097d878ca711e25a9c32cec9042adc49b00cadfee1a7290b" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="cb9353ef1791ae17097d878ca711e25a9c32cec9042adc49b00cadfee1a7290b" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -6284,6 +6303,14 @@
|
||||||
<sha256 value="d6ff762251fb35594afe5efe860b5871064e18c0bd1bdb77084e3573f90e4e8f" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="d6ff762251fb35594afe5efe860b5871064e18c0bd1bdb77084e3573f90e4e8f" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.android.datatransport" name="transport-runtime" version="3.1.9">
|
||||||
|
<artifact name="transport-runtime-3.1.9.aar">
|
||||||
|
<sha256 value="41745c5b8f427d24439015b84f651ad420718991867d2afc262c264009b802c1" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="transport-runtime-3.1.9.pom">
|
||||||
|
<sha256 value="86f452b74551062623fad0b0bb6bc4dc2a6ba9f44a99834d0e50fb047051357b" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.android.flexbox" name="flexbox" version="3.0.0">
|
<component group="com.google.android.flexbox" name="flexbox" version="3.0.0">
|
||||||
<artifact name="flexbox-3.0.0.aar">
|
<artifact name="flexbox-3.0.0.aar">
|
||||||
<sha256 value="5e19500486fd7c8e2e8c7aad6bbba9c8d0ada7057c6b32b9b5c61439814e7574" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="5e19500486fd7c8e2e8c7aad6bbba9c8d0ada7057c6b32b9b5c61439814e7574" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -6300,6 +6327,14 @@
|
||||||
<sha256 value="ec5c1be82d425fcaa6da54c6202bc25bc63fd61519d36c3ee813ffae7d203f25" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="ec5c1be82d425fcaa6da54c6202bc25bc63fd61519d36c3ee813ffae7d203f25" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.android.gms" name="play-services-base" version="18.5.0">
|
||||||
|
<artifact name="play-services-base-18.5.0.aar">
|
||||||
|
<sha256 value="59a5c0c2da12311d75d965ce1f419498536b1a167fb28ff7dfc2dfd9cefa4157" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="play-services-base-18.5.0.pom">
|
||||||
|
<sha256 value="2570b515c2717af386c890e97f644782e3f86da7b677a4ff1036147e7ea622a4" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.android.gms" name="play-services-basement" version="18.1.0">
|
<component group="com.google.android.gms" name="play-services-basement" version="18.1.0">
|
||||||
<artifact name="play-services-basement-18.1.0.aar">
|
<artifact name="play-services-basement-18.1.0.aar">
|
||||||
<sha256 value="03ba15f386cf260bde72fc4010fb89a820c47d51ce63bc09297eabe6cce09bdd" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="03ba15f386cf260bde72fc4010fb89a820c47d51ce63bc09297eabe6cce09bdd" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -6308,6 +6343,22 @@
|
||||||
<sha256 value="c633e42daf94df10b7e1d75cd6efb8247c924b8a35c385f5cd1f3cbaeee1bf7d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="c633e42daf94df10b7e1d75cd6efb8247c924b8a35c385f5cd1f3cbaeee1bf7d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.android.gms" name="play-services-basement" version="18.3.0">
|
||||||
|
<artifact name="play-services-basement-18.3.0.aar">
|
||||||
|
<sha256 value="6c11ae3eb2dd7f17373f919c4c557a70e4cf891bc0c9b66926a0a6445d654352" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="play-services-basement-18.3.0.pom">
|
||||||
|
<sha256 value="9cef5dc9a6950ff09a85ff522b476f855eb7ef2373aa4c17339cb114ac5397e2" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
|
<component group="com.google.android.gms" name="play-services-basement" version="18.4.0">
|
||||||
|
<artifact name="play-services-basement-18.4.0.aar">
|
||||||
|
<sha256 value="ce5c936fd66814b3602f5c6a5e929911ff973d4b05f6de9996da596bef97cad2" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="play-services-basement-18.4.0.pom">
|
||||||
|
<sha256 value="05ca7c0ace0d6260931f97ed32c60ce598071ff560d3fa44f49e6f0695d2b687" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.android.gms" name="play-services-cloud-messaging" version="17.0.1">
|
<component group="com.google.android.gms" name="play-services-cloud-messaging" version="17.0.1">
|
||||||
<artifact name="play-services-cloud-messaging-17.0.1.aar">
|
<artifact name="play-services-cloud-messaging-17.0.1.aar">
|
||||||
<sha256 value="1e759adcf0350731ce4dc73b035705e4c7d08bdf7db069cc0468eca3e7bb9dc2" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="1e759adcf0350731ce4dc73b035705e4c7d08bdf7db069cc0468eca3e7bb9dc2" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -6324,6 +6375,14 @@
|
||||||
<sha256 value="adec3fb60acc63f7a95927a69961936772f5ec2cceb0f8c235767d2d8940b3e4" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="adec3fb60acc63f7a95927a69961936772f5ec2cceb0f8c235767d2d8940b3e4" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.android.gms" name="play-services-cloud-messaging" version="17.2.0">
|
||||||
|
<artifact name="play-services-cloud-messaging-17.2.0.aar">
|
||||||
|
<sha256 value="27255e7fe9706483816b158db25cf319f6a26a0566feff41597ce8807a350e37" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="play-services-cloud-messaging-17.2.0.pom">
|
||||||
|
<sha256 value="65f5833e621368dfb0eb203dcafcf070c5cece5a60ca18842ce7081d440e0419" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.android.gms" name="play-services-stats" version="17.0.2">
|
<component group="com.google.android.gms" name="play-services-stats" version="17.0.2">
|
||||||
<artifact name="play-services-stats-17.0.2.aar">
|
<artifact name="play-services-stats-17.0.2.aar">
|
||||||
<sha256 value="dd4314a53f49a378ec146103d36232b96c75454d29526336ccbdf132941764d3" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="dd4314a53f49a378ec146103d36232b96c75454d29526336ccbdf132941764d3" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -6345,6 +6404,22 @@
|
||||||
<sha256 value="e07cdb8e9e63f3e94596bcd8984c7379c47d9f289ecb9a5e35fcb9f2f573ba9f" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="e07cdb8e9e63f3e94596bcd8984c7379c47d9f289ecb9a5e35fcb9f2f573ba9f" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.android.gms" name="play-services-tasks" version="18.1.0">
|
||||||
|
<artifact name="play-services-tasks-18.1.0.aar">
|
||||||
|
<sha256 value="d60575eae39350e6234858bc9d7d775375707ae82a684e6caf7f3e41a12e25a2" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="play-services-tasks-18.1.0.pom">
|
||||||
|
<sha256 value="cf29ed846108d7a8f2c17d8ef5b63399735757c5a09bb663d7e784e02ad84bcd" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
|
<component group="com.google.android.gms" name="play-services-tasks" version="18.2.0">
|
||||||
|
<artifact name="play-services-tasks-18.2.0.aar">
|
||||||
|
<sha256 value="7f2aaa8f502068eaf54356ca92aec04271d6e7c416c52c45c0d23440fcbd1654" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="play-services-tasks-18.2.0.pom">
|
||||||
|
<sha256 value="6b99c48a895d155e58abcee66cc22146db83abd5d84caf6c8f7a2aea9b1bcd21" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.android.material" name="material" version="1.10.0">
|
<component group="com.google.android.material" name="material" version="1.10.0">
|
||||||
<artifact name="material-1.10.0.aar">
|
<artifact name="material-1.10.0.aar">
|
||||||
<sha256 value="2d204ee4f116b988a6a6b5db8cf43de3f8a74d0ae1ba17586f90aae38e3e1a2d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="2d204ee4f116b988a6a6b5db8cf43de3f8a74d0ae1ba17586f90aae38e3e1a2d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -6887,6 +6962,14 @@
|
||||||
<sha256 value="d8e2223227f267d95f5580f746ca009342e466938964c515b8d66f5da0e493b6" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="d8e2223227f267d95f5580f746ca009342e466938964c515b8d66f5da0e493b6" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.firebase" name="firebase-common" version="21.0.0">
|
||||||
|
<artifact name="firebase-common-21.0.0.aar">
|
||||||
|
<sha256 value="379287d7171371512493681b398e78c341b333cf974ee81a18b1d56e7c2e385d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="firebase-common-21.0.0.pom">
|
||||||
|
<sha256 value="11c26461b6bff2549a7554fdaaa58c070550abf47255e9bda32dd238324cc73f" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.firebase" name="firebase-common-ktx" version="20.4.2">
|
<component group="com.google.firebase" name="firebase-common-ktx" version="20.4.2">
|
||||||
<artifact name="firebase-common-ktx-20.4.2.aar">
|
<artifact name="firebase-common-ktx-20.4.2.aar">
|
||||||
<sha256 value="170b44690e480d1dde79a8d163586a1025ab5542ee0aac3874b76e7d1c27718f" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="170b44690e480d1dde79a8d163586a1025ab5542ee0aac3874b76e7d1c27718f" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -6895,6 +6978,14 @@
|
||||||
<sha256 value="749101fe70375aefd4d87ea01255dc990d6477745ba752f18d31b960a2a3074b" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="749101fe70375aefd4d87ea01255dc990d6477745ba752f18d31b960a2a3074b" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.firebase" name="firebase-common-ktx" version="21.0.0">
|
||||||
|
<artifact name="firebase-common-ktx-21.0.0.aar">
|
||||||
|
<sha256 value="25fc80c9bb9ecb1672908718c294afcc4ac1e47473677be060c795e04f120066" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="firebase-common-ktx-21.0.0.pom">
|
||||||
|
<sha256 value="c27eccb48b9588116d6fd32f4657bc59df855002432293558dbb97e9878adeb8" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.firebase" name="firebase-components" version="17.1.0">
|
<component group="com.google.firebase" name="firebase-components" version="17.1.0">
|
||||||
<artifact name="firebase-components-17.1.0.aar">
|
<artifact name="firebase-components-17.1.0.aar">
|
||||||
<sha256 value="352c71f89994cb4481daeb45ed3630bbf1789bdf1be4563988e2c18618af4f60" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="352c71f89994cb4481daeb45ed3630bbf1789bdf1be4563988e2c18618af4f60" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -6911,6 +7002,14 @@
|
||||||
<sha256 value="e781684bd1d2132ab32ba29c419ba2174ff8983edeb4d93782178e8b6dc6f804" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="e781684bd1d2132ab32ba29c419ba2174ff8983edeb4d93782178e8b6dc6f804" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.firebase" name="firebase-components" version="18.0.0">
|
||||||
|
<artifact name="firebase-components-18.0.0.aar">
|
||||||
|
<sha256 value="c7c48a3a80f44a499ebd6473bcfc7dd5a45ef523fabe2414a62515ff3c640972" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="firebase-components-18.0.0.pom">
|
||||||
|
<sha256 value="6e6dd34d4512c7393e89ecdd8b829ebcdd31e6be2c931e7d1100a87d56880049" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.firebase" name="firebase-datatransport" version="18.1.7">
|
<component group="com.google.firebase" name="firebase-datatransport" version="18.1.7">
|
||||||
<artifact name="firebase-datatransport-18.1.7.aar">
|
<artifact name="firebase-datatransport-18.1.7.aar">
|
||||||
<sha256 value="5f23d5750ae248dca2312c5963aa89f851be36132fb89f15114c3585e605ff08" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="5f23d5750ae248dca2312c5963aa89f851be36132fb89f15114c3585e605ff08" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -6919,6 +7018,14 @@
|
||||||
<sha256 value="c4a7ad00ca868d373e15c5eb7c88db618fec495241c69f80feb3447d2f56e50c" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="c4a7ad00ca868d373e15c5eb7c88db618fec495241c69f80feb3447d2f56e50c" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.firebase" name="firebase-datatransport" version="18.2.0">
|
||||||
|
<artifact name="firebase-datatransport-18.2.0.aar">
|
||||||
|
<sha256 value="b2b28f6ba173f5e0c4fe3d36a2d7ee5239c7acbf4143b549ecf031a3485b35bb" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="firebase-datatransport-18.2.0.pom">
|
||||||
|
<sha256 value="b8be11825ba56c04de8374094840b56383df24634931802668d70268d0c7bbe8" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.firebase" name="firebase-encoders" version="17.0.0">
|
<component group="com.google.firebase" name="firebase-encoders" version="17.0.0">
|
||||||
<artifact name="firebase-encoders-17.0.0.jar">
|
<artifact name="firebase-encoders-17.0.0.jar">
|
||||||
<sha256 value="282a5a703f9b7eb56508dde97ea918e95d73318b157050f457f7a86dca750150" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="282a5a703f9b7eb56508dde97ea918e95d73318b157050f457f7a86dca750150" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
@ -7007,6 +7114,14 @@
|
||||||
<sha256 value="188ee169e2d73488e9b14c7610bf3fd9a834658c0759af51b16b0b255865f7c6" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="188ee169e2d73488e9b14c7610bf3fd9a834658c0759af51b16b0b255865f7c6" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
|
<component group="com.google.firebase" name="firebase-messaging" version="24.0.0">
|
||||||
|
<artifact name="firebase-messaging-24.0.0.aar">
|
||||||
|
<sha256 value="6f22a7905a2d304d28724b0bacaf6717813693ffb7fcdb9cbfd9417881539c35" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
<artifact name="firebase-messaging-24.0.0.pom">
|
||||||
|
<sha256 value="ae232cb0e20e90ecf2153f3bd213dd9cec915e7e2a721d6e51b032b1b9a27ca8" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
<component group="com.google.flatbuffers" name="flatbuffers-java" version="1.12.0">
|
<component group="com.google.flatbuffers" name="flatbuffers-java" version="1.12.0">
|
||||||
<artifact name="flatbuffers-java-1.12.0.jar">
|
<artifact name="flatbuffers-java-1.12.0.jar">
|
||||||
<sha256 value="3f8c088b4dd04a9858721f2e162508c94db0dd86f961e306ee63ef2eda871bf7" origin="Generated by Gradle"/>
|
<sha256 value="3f8c088b4dd04a9858721f2e162508c94db0dd86f961e306ee63ef2eda871bf7" origin="Generated by Gradle"/>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
DO NOT TOUCH; GENERATED BY DRONE
|
DO NOT TOUCH; GENERATED BY DRONE
|
||||||
<span class="mdl-layout-title">Lint Report: 3 errors and 68 warnings</span>
|
<span class="mdl-layout-title">Lint Report: 3 errors and 64 warnings</span>
|
||||||
|
|
Loading…
Reference in a new issue