mirror of
https://github.com/nextcloud/android.git
synced 2024-12-23 01:00:26 +03:00
Merge remote-tracking branch 'origin/master' into dev
This commit is contained in:
commit
9a3062d7ff
21 changed files with 509 additions and 63 deletions
|
@ -159,7 +159,7 @@ class FileDownloadWorker(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setIdleWorkerState() {
|
private fun setIdleWorkerState() {
|
||||||
WorkerStateLiveData.instance().setWorkState(WorkerState.Idle)
|
WorkerStateLiveData.instance().setWorkState(WorkerState.Idle(getCurrentFile()))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removePendingDownload(accountName: String?) {
|
private fun removePendingDownload(accountName: String?) {
|
||||||
|
|
|
@ -64,6 +64,9 @@ class FileUploadHelper {
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = FileUploadWorker::class.java.simpleName
|
private val TAG = FileUploadWorker::class.java.simpleName
|
||||||
|
|
||||||
|
@Suppress("MagicNumber")
|
||||||
|
const val MAX_FILE_COUNT = 500
|
||||||
|
|
||||||
val mBoundListeners = HashMap<String, OnDatatransferProgressListener>()
|
val mBoundListeners = HashMap<String, OnDatatransferProgressListener>()
|
||||||
|
|
||||||
private var instance: FileUploadHelper? = null
|
private var instance: FileUploadHelper? = null
|
||||||
|
|
|
@ -124,7 +124,7 @@ class FileUploadWorker(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setIdleWorkerState() {
|
private fun setIdleWorkerState() {
|
||||||
WorkerStateLiveData.instance().setWorkState(WorkerState.Idle)
|
WorkerStateLiveData.instance().setWorkState(WorkerState.Idle(currentUploadFileOperation?.file))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ReturnCount")
|
@Suppress("ReturnCount")
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.model
|
||||||
|
|
||||||
|
import com.owncloud.android.R
|
||||||
|
|
||||||
|
enum class SearchResultEntryType {
|
||||||
|
CalendarEvent,
|
||||||
|
Folder,
|
||||||
|
Note,
|
||||||
|
Contact,
|
||||||
|
Deck,
|
||||||
|
Unknown;
|
||||||
|
|
||||||
|
fun iconId(): Int {
|
||||||
|
return when (this) {
|
||||||
|
Folder -> R.drawable.folder
|
||||||
|
Note -> R.drawable.ic_edit
|
||||||
|
Contact -> R.drawable.file_vcard
|
||||||
|
CalendarEvent -> R.drawable.file_calendar
|
||||||
|
Deck -> R.drawable.ic_deck
|
||||||
|
else -> R.drawable.ic_find_in_page
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,11 +8,12 @@
|
||||||
package com.nextcloud.model
|
package com.nextcloud.model
|
||||||
|
|
||||||
import com.nextcloud.client.account.User
|
import com.nextcloud.client.account.User
|
||||||
|
import com.owncloud.android.datamodel.OCFile
|
||||||
import com.owncloud.android.db.OCUpload
|
import com.owncloud.android.db.OCUpload
|
||||||
import com.owncloud.android.operations.DownloadFileOperation
|
import com.owncloud.android.operations.DownloadFileOperation
|
||||||
|
|
||||||
sealed class WorkerState {
|
sealed class WorkerState {
|
||||||
object Idle : WorkerState()
|
data class Idle(var currentFile: OCFile?) : WorkerState()
|
||||||
class Download(var user: User?, var currentDownload: DownloadFileOperation?) : WorkerState()
|
data class Download(var user: User?, var currentDownload: DownloadFileOperation?) : WorkerState()
|
||||||
class Upload(var user: User?, var uploads: List<OCUpload>) : WorkerState()
|
data class Upload(var user: User?, var uploads: List<OCUpload>) : WorkerState()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.utils
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.ContentUris
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.CalendarContract
|
||||||
|
import com.nextcloud.utils.extensions.showToast
|
||||||
|
import com.owncloud.android.R
|
||||||
|
import com.owncloud.android.lib.common.SearchResultEntry
|
||||||
|
import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface
|
||||||
|
import com.owncloud.android.utils.PermissionUtil.checkSelfPermission
|
||||||
|
|
||||||
|
class CalendarEventManager(private val context: Context) {
|
||||||
|
|
||||||
|
fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) {
|
||||||
|
val havePermission = checkSelfPermission(context, Manifest.permission.READ_CALENDAR)
|
||||||
|
val createdAt = searchResult.createdAt()
|
||||||
|
val eventId: Long? = if (havePermission && createdAt != null) {
|
||||||
|
getCalendarEventId(searchResult.title, createdAt)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventId == null) {
|
||||||
|
val messageId = if (havePermission) {
|
||||||
|
R.string.unified_search_fragment_calendar_event_not_found
|
||||||
|
} else {
|
||||||
|
R.string.unified_search_fragment_permission_needed
|
||||||
|
}
|
||||||
|
context.showToast(messageId)
|
||||||
|
listInterface.onSearchResultClicked(searchResult)
|
||||||
|
} else {
|
||||||
|
val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventId)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCalendarEventId(eventTitle: String, eventStartDate: Long): Long? {
|
||||||
|
val projection = arrayOf(
|
||||||
|
CalendarContract.Events._ID,
|
||||||
|
CalendarContract.Events.TITLE,
|
||||||
|
CalendarContract.Events.DTSTART
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = "${CalendarContract.Events.TITLE} = ? AND ${CalendarContract.Events.DTSTART} = ?"
|
||||||
|
val selectionArgs = arrayOf(eventTitle, eventStartDate.toString())
|
||||||
|
|
||||||
|
val cursor = context.contentResolver.query(
|
||||||
|
CalendarContract.Events.CONTENT_URI,
|
||||||
|
projection,
|
||||||
|
selection,
|
||||||
|
selectionArgs,
|
||||||
|
"${CalendarContract.Events.DTSTART} ASC"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor?.use {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID)
|
||||||
|
return cursor.getLong(idIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MagicNumber")
|
||||||
|
private fun SearchResultEntry.createdAt(): Long? = attributes["createdAt"]?.toLongOrNull()?.times(1000L)
|
144
app/src/main/java/com/nextcloud/utils/ContactManager.kt
Normal file
144
app/src/main/java/com/nextcloud/utils/ContactManager.kt
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.utils
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.ContactsContract
|
||||||
|
import com.nextcloud.utils.extensions.showToast
|
||||||
|
import com.owncloud.android.R
|
||||||
|
import com.owncloud.android.lib.common.SearchResultEntry
|
||||||
|
import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface
|
||||||
|
import com.owncloud.android.utils.PermissionUtil.checkSelfPermission
|
||||||
|
|
||||||
|
class ContactManager(private val context: Context) {
|
||||||
|
|
||||||
|
fun openContact(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) {
|
||||||
|
val havePermission = checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
|
||||||
|
val displayName = searchResult.displayName()
|
||||||
|
val contactId: Long? = if (havePermission && displayName != null) {
|
||||||
|
getContactIds(displayName).let { contactIds ->
|
||||||
|
if (contactIds.size > 1) getContactId(searchResult, contactIds) else contactIds.firstOrNull()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contactId == null) {
|
||||||
|
val messageId = if (havePermission) {
|
||||||
|
R.string.unified_search_fragment_contact_not_found
|
||||||
|
} else {
|
||||||
|
R.string.unified_search_fragment_permission_needed
|
||||||
|
}
|
||||||
|
context.showToast(messageId)
|
||||||
|
listInterface.onSearchResultClicked(searchResult)
|
||||||
|
} else {
|
||||||
|
val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactId.toString())
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
setData(uri)
|
||||||
|
}
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContactId(searchResult: SearchResultEntry, contactIds: List<Long>): Long? {
|
||||||
|
val email = searchResult.email()
|
||||||
|
val phoneNumber = searchResult.phoneNumber()
|
||||||
|
|
||||||
|
contactIds.forEach {
|
||||||
|
val targetEmail = getEmailById(it) ?: ""
|
||||||
|
val targetPhoneNumber = getPhoneNumberById(it) ?: ""
|
||||||
|
if (targetEmail == email && targetPhoneNumber == phoneNumber) {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getEmailById(contactId: Long): String? {
|
||||||
|
var result: String? = null
|
||||||
|
val projection = arrayOf(ContactsContract.CommonDataKinds.Email.ADDRESS)
|
||||||
|
val selection = "${ContactsContract.CommonDataKinds.Email.CONTACT_ID} = ?"
|
||||||
|
val selectionArgs = arrayOf(contactId.toString())
|
||||||
|
|
||||||
|
val cursor = context.contentResolver.query(
|
||||||
|
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
|
||||||
|
projection,
|
||||||
|
selection,
|
||||||
|
selectionArgs,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor?.use {
|
||||||
|
val emailIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS)
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
result = cursor.getString(emailIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPhoneNumberById(contactId: Long): String? {
|
||||||
|
var result: String? = null
|
||||||
|
val projection = arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER)
|
||||||
|
val selection = "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?"
|
||||||
|
val selectionArgs = arrayOf(contactId.toString())
|
||||||
|
|
||||||
|
val cursor = context.contentResolver.query(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
|
||||||
|
projection,
|
||||||
|
selection,
|
||||||
|
selectionArgs,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor?.use {
|
||||||
|
val phoneIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
result = cursor.getString(phoneIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContactIds(displayName: String): List<Long> {
|
||||||
|
val result = arrayListOf<Long>()
|
||||||
|
val projection = arrayOf(ContactsContract.Contacts._ID)
|
||||||
|
val selection = "${ContactsContract.Contacts.DISPLAY_NAME} = ?"
|
||||||
|
val selectionArgs = arrayOf(displayName)
|
||||||
|
|
||||||
|
val cursor = context.contentResolver.query(
|
||||||
|
ContactsContract.Contacts.CONTENT_URI,
|
||||||
|
projection,
|
||||||
|
selection,
|
||||||
|
selectionArgs,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor?.use {
|
||||||
|
val idIndex = cursor.getColumnIndex(ContactsContract.Contacts._ID)
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
val id = cursor.getLong(idIndex)
|
||||||
|
result.add(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SearchResultEntry.displayName(): String? = attributes["displayName"]
|
||||||
|
|
||||||
|
private fun SearchResultEntry.email(): String? = attributes["email"]
|
||||||
|
|
||||||
|
private fun SearchResultEntry.phoneNumber(): String? = attributes["phoneNumber"]
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.utils.extensions
|
||||||
|
|
||||||
|
import com.nextcloud.model.SearchResultEntryType
|
||||||
|
import com.owncloud.android.lib.common.SearchResultEntry
|
||||||
|
|
||||||
|
fun SearchResultEntry.getType(): SearchResultEntryType {
|
||||||
|
return if (icon == "icon-folder") {
|
||||||
|
SearchResultEntryType.Folder
|
||||||
|
} else if (icon.startsWith("icon-note")) {
|
||||||
|
SearchResultEntryType.Note
|
||||||
|
} else if (icon.startsWith("icon-contacts")) {
|
||||||
|
SearchResultEntryType.Contact
|
||||||
|
} else if (icon.startsWith("icon-calendar")) {
|
||||||
|
SearchResultEntryType.CalendarEvent
|
||||||
|
} else if (icon.startsWith("icon-deck")) {
|
||||||
|
SearchResultEntryType.Deck
|
||||||
|
} else {
|
||||||
|
SearchResultEntryType.Unknown
|
||||||
|
}
|
||||||
|
}
|
|
@ -880,7 +880,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean somethingToUpload() {
|
private boolean somethingToUpload() {
|
||||||
return (mStreamsToUpload != null && mStreamsToUpload.size() > 0 && mStreamsToUpload.get(0) != null ||
|
return (mStreamsToUpload != null && !mStreamsToUpload.isEmpty() && mStreamsToUpload.get(0) != null ||
|
||||||
mUploadFromTmpFile);
|
mUploadFromTmpFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -904,6 +904,11 @@ public class ReceiveExternalFilesActivity extends FileActivity
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mStreamsToUpload.size() > FileUploadHelper.MAX_FILE_COUNT) {
|
||||||
|
DisplayUtils.showSnackMessage(this, R.string.max_file_count_warning_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
UriUploader uploader = new UriUploader(
|
UriUploader uploader = new UriUploader(
|
||||||
this,
|
this,
|
||||||
mStreamsToUpload,
|
mStreamsToUpload,
|
||||||
|
|
|
@ -30,6 +30,7 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import com.nextcloud.client.account.User;
|
import com.nextcloud.client.account.User;
|
||||||
import com.nextcloud.client.di.Injectable;
|
import com.nextcloud.client.di.Injectable;
|
||||||
|
import com.nextcloud.client.jobs.upload.FileUploadHelper;
|
||||||
import com.nextcloud.client.jobs.upload.FileUploadWorker;
|
import com.nextcloud.client.jobs.upload.FileUploadWorker;
|
||||||
import com.nextcloud.client.preferences.AppPreferences;
|
import com.nextcloud.client.preferences.AppPreferences;
|
||||||
import com.nextcloud.utils.extensions.ActivityExtensionsKt;
|
import com.nextcloud.utils.extensions.ActivityExtensionsKt;
|
||||||
|
@ -653,6 +654,11 @@ public class UploadFilesActivity extends DrawerActivity implements LocalFileList
|
||||||
@Override
|
@Override
|
||||||
public void onConfirmation(String callerTag) {
|
public void onConfirmation(String callerTag) {
|
||||||
Log_OC.d(TAG, "Positive button in dialog was clicked; dialog tag is " + callerTag);
|
Log_OC.d(TAG, "Positive button in dialog was clicked; dialog tag is " + callerTag);
|
||||||
|
if (mFileListFragment.getCheckedFilePaths().length > FileUploadHelper.MAX_FILE_COUNT) {
|
||||||
|
DisplayUtils.showSnackMessage(this, R.string.max_file_count_warning_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (QUERY_TO_MOVE_DIALOG_TAG.equals(callerTag)) {
|
if (QUERY_TO_MOVE_DIALOG_TAG.equals(callerTag)) {
|
||||||
// return the list of selected files to the caller activity (success),
|
// return the list of selected files to the caller activity (success),
|
||||||
// signaling that they should be moved to the ownCloud folder, instead of copied
|
// signaling that they should be moved to the ownCloud folder, instead of copied
|
||||||
|
|
|
@ -16,9 +16,13 @@ import com.afollestad.sectionedrecyclerview.SectionedViewHolder
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestListener
|
import com.bumptech.glide.request.RequestListener
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||||
import com.nextcloud.client.account.User
|
import com.nextcloud.client.account.User
|
||||||
import com.nextcloud.client.network.ClientFactory
|
import com.nextcloud.client.network.ClientFactory
|
||||||
import com.owncloud.android.R
|
import com.nextcloud.model.SearchResultEntryType
|
||||||
|
import com.nextcloud.utils.CalendarEventManager
|
||||||
|
import com.nextcloud.utils.ContactManager
|
||||||
|
import com.nextcloud.utils.extensions.getType
|
||||||
import com.owncloud.android.databinding.UnifiedSearchItemBinding
|
import com.owncloud.android.databinding.UnifiedSearchItemBinding
|
||||||
import com.owncloud.android.datamodel.FileDataStorageManager
|
import com.owncloud.android.datamodel.FileDataStorageManager
|
||||||
import com.owncloud.android.lib.common.SearchResultEntry
|
import com.owncloud.android.lib.common.SearchResultEntry
|
||||||
|
@ -30,6 +34,7 @@ import com.owncloud.android.utils.theme.ViewThemeUtils
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
class UnifiedSearchItemViewHolder(
|
class UnifiedSearchItemViewHolder(
|
||||||
|
private val supportsOpeningCalendarContactsLocally: Boolean,
|
||||||
val binding: UnifiedSearchItemBinding,
|
val binding: UnifiedSearchItemBinding,
|
||||||
val user: User,
|
val user: User,
|
||||||
val clientFactory: ClientFactory,
|
val clientFactory: ClientFactory,
|
||||||
|
@ -38,13 +43,15 @@ class UnifiedSearchItemViewHolder(
|
||||||
private val filesAction: FilesAction,
|
private val filesAction: FilesAction,
|
||||||
val context: Context,
|
val context: Context,
|
||||||
private val viewThemeUtils: ViewThemeUtils
|
private val viewThemeUtils: ViewThemeUtils
|
||||||
) :
|
) : SectionedViewHolder(binding.root) {
|
||||||
SectionedViewHolder(binding.root) {
|
|
||||||
|
|
||||||
interface FilesAction {
|
interface FilesAction {
|
||||||
fun showFilesAction(searchResultEntry: SearchResultEntry)
|
fun showFilesAction(searchResultEntry: SearchResultEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val contactManager = ContactManager(context)
|
||||||
|
private val calendarEventManager = CalendarEventManager(context)
|
||||||
|
|
||||||
fun bind(entry: SearchResultEntry) {
|
fun bind(entry: SearchResultEntry) {
|
||||||
binding.title.text = entry.title
|
binding.title.text = entry.title
|
||||||
binding.subline.text = entry.subline
|
binding.subline.text = entry.subline
|
||||||
|
@ -57,7 +64,8 @@ class UnifiedSearchItemViewHolder(
|
||||||
|
|
||||||
val mimetype = MimeTypeUtil.getBestMimeTypeByFilename(entry.title)
|
val mimetype = MimeTypeUtil.getBestMimeTypeByFilename(entry.title)
|
||||||
|
|
||||||
val placeholder = getPlaceholder(entry, mimetype)
|
val entryType = entry.getType()
|
||||||
|
val placeholder = getPlaceholder(entry, entryType, mimetype)
|
||||||
|
|
||||||
Glide.with(context).using(CustomGlideStreamLoader(user, clientFactory))
|
Glide.with(context).using(CustomGlideStreamLoader(user, clientFactory))
|
||||||
.load(entry.thumbnailUrl)
|
.load(entry.thumbnailUrl)
|
||||||
|
@ -70,32 +78,50 @@ class UnifiedSearchItemViewHolder(
|
||||||
|
|
||||||
if (entry.isFile) {
|
if (entry.isFile) {
|
||||||
binding.more.visibility = View.VISIBLE
|
binding.more.visibility = View.VISIBLE
|
||||||
binding.more.setOnClickListener { filesAction.showFilesAction(entry) }
|
binding.more.setOnClickListener {
|
||||||
|
filesAction.showFilesAction(entry)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
binding.more.visibility = View.GONE
|
binding.more.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.unifiedSearchItemLayout.setOnClickListener { listInterface.onSearchResultClicked(entry) }
|
binding.unifiedSearchItemLayout.setOnClickListener {
|
||||||
|
searchEntryOnClick(entry, entryType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPlaceholder(entry: SearchResultEntry, mimetype: String?): Drawable {
|
private fun searchEntryOnClick(entry: SearchResultEntry, entryType: SearchResultEntryType) {
|
||||||
val drawable = with(entry.icon) {
|
if (supportsOpeningCalendarContactsLocally) {
|
||||||
when {
|
when (entryType) {
|
||||||
equals("icon-folder") ->
|
SearchResultEntryType.Contact -> {
|
||||||
ResourcesCompat.getDrawable(context.resources, R.drawable.folder, null)
|
contactManager.openContact(entry, listInterface)
|
||||||
startsWith("icon-note") ->
|
}
|
||||||
ResourcesCompat.getDrawable(context.resources, R.drawable.ic_edit, null)
|
|
||||||
startsWith("icon-contacts") ->
|
SearchResultEntryType.CalendarEvent -> {
|
||||||
ResourcesCompat.getDrawable(context.resources, R.drawable.file_vcard, null)
|
calendarEventManager.openCalendarEvent(entry, listInterface)
|
||||||
startsWith("icon-calendar") ->
|
}
|
||||||
ResourcesCompat.getDrawable(context.resources, R.drawable.file_calendar, null)
|
|
||||||
startsWith("icon-deck") ->
|
else -> {
|
||||||
ResourcesCompat.getDrawable(context.resources, R.drawable.ic_deck, null)
|
listInterface.onSearchResultClicked(entry)
|
||||||
else ->
|
|
||||||
MimeTypeUtil.getFileTypeIcon(mimetype, entry.title, context, viewThemeUtils)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return viewThemeUtils.platform.tintPrimaryDrawable(context, drawable)!!
|
} else {
|
||||||
|
listInterface.onSearchResultClicked(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPlaceholder(
|
||||||
|
entry: SearchResultEntry,
|
||||||
|
entryType: SearchResultEntryType,
|
||||||
|
mimetype: String?
|
||||||
|
): Drawable {
|
||||||
|
val iconId = entryType.run {
|
||||||
|
iconId()
|
||||||
|
}
|
||||||
|
|
||||||
|
val defaultDrawable = MimeTypeUtil.getFileTypeIcon(mimetype, entry.title, context, viewThemeUtils)
|
||||||
|
val drawable: Drawable = ResourcesCompat.getDrawable(context.resources, iconId, null) ?: defaultDrawable
|
||||||
|
return viewThemeUtils.platform.tintDrawable(context, drawable, ColorRole.PRIMARY)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class RoundIfNeededListener(private val entry: SearchResultEntry) :
|
private inner class RoundIfNeededListener(private val entry: SearchResultEntry) :
|
||||||
|
|
|
@ -33,6 +33,7 @@ import com.owncloud.android.utils.theme.ViewThemeUtils
|
||||||
*/
|
*/
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
class UnifiedSearchListAdapter(
|
class UnifiedSearchListAdapter(
|
||||||
|
private val supportsOpeningCalendarContactsLocally: Boolean,
|
||||||
private val storageManager: FileDataStorageManager,
|
private val storageManager: FileDataStorageManager,
|
||||||
private val listInterface: UnifiedSearchListInterface,
|
private val listInterface: UnifiedSearchListInterface,
|
||||||
private val filesAction: UnifiedSearchItemViewHolder.FilesAction,
|
private val filesAction: UnifiedSearchItemViewHolder.FilesAction,
|
||||||
|
@ -73,6 +74,7 @@ class UnifiedSearchListAdapter(
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
UnifiedSearchItemViewHolder(
|
UnifiedSearchItemViewHolder(
|
||||||
|
supportsOpeningCalendarContactsLocally,
|
||||||
binding,
|
binding,
|
||||||
user,
|
user,
|
||||||
clientFactory,
|
clientFactory,
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
package com.owncloud.android.ui.fragment
|
package com.owncloud.android.ui.fragment
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -16,6 +17,7 @@ import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
|
@ -23,6 +25,7 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.nextcloud.client.account.CurrentAccountProvider
|
import com.nextcloud.client.account.CurrentAccountProvider
|
||||||
|
import com.nextcloud.client.account.UserAccountManager
|
||||||
import com.nextcloud.client.core.AsyncRunner
|
import com.nextcloud.client.core.AsyncRunner
|
||||||
import com.nextcloud.client.di.Injectable
|
import com.nextcloud.client.di.Injectable
|
||||||
import com.nextcloud.client.di.ViewModelFactory
|
import com.nextcloud.client.di.ViewModelFactory
|
||||||
|
@ -33,6 +36,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager
|
||||||
import com.owncloud.android.datamodel.OCFile
|
import com.owncloud.android.datamodel.OCFile
|
||||||
import com.owncloud.android.lib.common.SearchResultEntry
|
import com.owncloud.android.lib.common.SearchResultEntry
|
||||||
import com.owncloud.android.lib.common.utils.Log_OC
|
import com.owncloud.android.lib.common.utils.Log_OC
|
||||||
|
import com.owncloud.android.lib.resources.status.NextcloudVersion
|
||||||
import com.owncloud.android.ui.activity.FileDisplayActivity
|
import com.owncloud.android.ui.activity.FileDisplayActivity
|
||||||
import com.owncloud.android.ui.adapter.UnifiedSearchItemViewHolder
|
import com.owncloud.android.ui.adapter.UnifiedSearchItemViewHolder
|
||||||
import com.owncloud.android.ui.adapter.UnifiedSearchListAdapter
|
import com.owncloud.android.ui.adapter.UnifiedSearchListAdapter
|
||||||
|
@ -44,6 +48,7 @@ import com.owncloud.android.ui.unifiedsearch.UnifiedSearchSection
|
||||||
import com.owncloud.android.ui.unifiedsearch.UnifiedSearchViewModel
|
import com.owncloud.android.ui.unifiedsearch.UnifiedSearchViewModel
|
||||||
import com.owncloud.android.ui.unifiedsearch.filterOutHiddenFiles
|
import com.owncloud.android.ui.unifiedsearch.filterOutHiddenFiles
|
||||||
import com.owncloud.android.utils.DisplayUtils
|
import com.owncloud.android.utils.DisplayUtils
|
||||||
|
import com.owncloud.android.utils.PermissionUtil
|
||||||
import com.owncloud.android.utils.theme.ViewThemeUtils
|
import com.owncloud.android.utils.theme.ViewThemeUtils
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -97,8 +102,10 @@ class UnifiedSearchFragment :
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var viewThemeUtils: ViewThemeUtils
|
lateinit var viewThemeUtils: ViewThemeUtils
|
||||||
|
|
||||||
private var listOfHiddenFiles = ArrayList<String>()
|
@Inject
|
||||||
|
lateinit var accountManager: UserAccountManager
|
||||||
|
|
||||||
|
private var listOfHiddenFiles = ArrayList<String>()
|
||||||
private var showMoreActions = false
|
private var showMoreActions = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -132,7 +139,15 @@ class UnifiedSearchFragment :
|
||||||
|
|
||||||
setupFileDisplayActivity()
|
setupFileDisplayActivity()
|
||||||
setupAdapter()
|
setupAdapter()
|
||||||
|
if (supportsOpeningCalendarContactsLocally()) {
|
||||||
|
checkPermissions()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun supportsOpeningCalendarContactsLocally(): Boolean = storageManager
|
||||||
|
.getCapability(accountManager.user)
|
||||||
|
.version
|
||||||
|
.isNewerOrEqual(NextcloudVersion.nextcloud_30)
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
@Deprecated("Deprecated in Java")
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -140,6 +155,21 @@ class UnifiedSearchFragment :
|
||||||
setupSearchView(item)
|
setupSearchView(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkPermissions() {
|
||||||
|
val permissions = arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.READ_CALENDAR)
|
||||||
|
if (!PermissionUtil.checkPermissions(requireContext(), permissions)) {
|
||||||
|
permissionLauncher.launch(permissions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val permissionLauncher =
|
||||||
|
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
|
||||||
|
val granted = permissions.entries.all { it.value }
|
||||||
|
if (!granted) {
|
||||||
|
DisplayUtils.showSnackMessage(binding.root, R.string.unified_search_fragment_permission_needed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupSearchView(item: MenuItem) {
|
private fun setupSearchView(item: MenuItem) {
|
||||||
(item.actionView as? SearchView?)?.run {
|
(item.actionView as? SearchView?)?.run {
|
||||||
// Required to align with TextView width.
|
// Required to align with TextView width.
|
||||||
|
@ -230,6 +260,7 @@ class UnifiedSearchFragment :
|
||||||
private fun setupAdapter() {
|
private fun setupAdapter() {
|
||||||
val gridLayoutManager = GridLayoutManager(requireContext(), 1)
|
val gridLayoutManager = GridLayoutManager(requireContext(), 1)
|
||||||
adapter = UnifiedSearchListAdapter(
|
adapter = UnifiedSearchListAdapter(
|
||||||
|
supportsOpeningCalendarContactsLocally(),
|
||||||
storageManager,
|
storageManager,
|
||||||
this,
|
this,
|
||||||
this,
|
this,
|
||||||
|
|
|
@ -11,7 +11,6 @@ import com.owncloud.android.lib.common.SearchResultEntry
|
||||||
import com.owncloud.android.ui.unifiedsearch.ProviderID
|
import com.owncloud.android.ui.unifiedsearch.ProviderID
|
||||||
|
|
||||||
interface UnifiedSearchListInterface {
|
interface UnifiedSearchListInterface {
|
||||||
|
|
||||||
fun onSearchResultClicked(searchResultEntry: SearchResultEntry)
|
fun onSearchResultClicked(searchResultEntry: SearchResultEntry)
|
||||||
fun onLoadMoreClicked(providerID: ProviderID)
|
fun onLoadMoreClicked(providerID: ProviderID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Nextcloud - Android Client
|
* Nextcloud - Android Client
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2020-2024 Andy Scherzinger <info@andy-scherzinger.de>
|
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
|
||||||
* SPDX-FileCopyrightText: 2023 Alper Ozturk <alper.ozturk@nextcloud.com>
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
|
|
||||||
* SPDX-FileCopyrightText: 2019 Tobias Kaminsky <tobias@kaminsky.me>
|
|
||||||
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
|
|
||||||
* SPDX-FileCopyrightText: 2016 ownCloud Inc.
|
|
||||||
* SPDX-FileCopyrightText: 2015 María Asensio Valverde <masensio@solidgear.es>
|
|
||||||
* SPDX-FileCopyrightText: 2013 David A. Velasco <dvelasco@solidgear.es>
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only)
|
|
||||||
*/
|
*/
|
||||||
package com.owncloud.android.ui.preview
|
package com.owncloud.android.ui.preview
|
||||||
|
|
||||||
|
@ -56,6 +49,8 @@ import com.owncloud.android.ui.activity.FileDisplayActivity
|
||||||
import com.owncloud.android.ui.fragment.FileFragment
|
import com.owncloud.android.ui.fragment.FileFragment
|
||||||
import com.owncloud.android.ui.fragment.GalleryFragment
|
import com.owncloud.android.ui.fragment.GalleryFragment
|
||||||
import com.owncloud.android.ui.fragment.OCFileListFragment
|
import com.owncloud.android.ui.fragment.OCFileListFragment
|
||||||
|
import com.owncloud.android.ui.preview.model.PreviewImageActivityState
|
||||||
|
import com.owncloud.android.utils.DisplayUtils
|
||||||
import com.owncloud.android.utils.MimeTypeUtil
|
import com.owncloud.android.utils.MimeTypeUtil
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
@ -65,16 +60,18 @@ import kotlin.math.max
|
||||||
/**
|
/**
|
||||||
* Holds a swiping gallery where image files contained in an Nextcloud directory are shown.
|
* Holds a swiping gallery where image files contained in an Nextcloud directory are shown.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("TooManyFunctions")
|
||||||
class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnRemoteOperationListener, Injectable {
|
class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnRemoteOperationListener, Injectable {
|
||||||
private var livePhotoFile: OCFile? = null
|
private var livePhotoFile: OCFile? = null
|
||||||
private var viewPager: ViewPager2? = null
|
private var viewPager: ViewPager2? = null
|
||||||
private var previewImagePagerAdapter: PreviewImagePagerAdapter? = null
|
private var previewImagePagerAdapter: PreviewImagePagerAdapter? = null
|
||||||
private var savedPosition = 0
|
private var savedPosition = 0
|
||||||
private var hasSavedPosition = false
|
private var hasSavedPosition = false
|
||||||
private var requestWaitingForBinder = false
|
|
||||||
private var downloadFinishReceiver: DownloadFinishReceiver? = null
|
private var downloadFinishReceiver: DownloadFinishReceiver? = null
|
||||||
private var fullScreenAnchorView: View? = null
|
private var fullScreenAnchorView: View? = null
|
||||||
|
|
||||||
private var isDownloadWorkStarted = false
|
private var isDownloadWorkStarted = false
|
||||||
|
private var screenState = PreviewImageActivityState.Idle
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var preferences: AppPreferences
|
lateinit var preferences: AppPreferences
|
||||||
|
@ -115,7 +112,10 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
// to keep our UI controls visibility in line with system bars visibility
|
// to keep our UI controls visibility in line with system bars visibility
|
||||||
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||||
|
|
||||||
requestWaitingForBinder = savedInstanceState?.getBoolean(KEY_WAITING_FOR_BINDER) ?: false
|
val requestWaitingForBinder = savedInstanceState?.getBoolean(KEY_WAITING_FOR_BINDER) ?: false
|
||||||
|
if (requestWaitingForBinder) {
|
||||||
|
screenState = PreviewImageActivityState.WaitingForBinder
|
||||||
|
}
|
||||||
|
|
||||||
observeWorkerState()
|
observeWorkerState()
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
if (position == 0 && !file.isDown) {
|
if (position == 0 && !file.isDown) {
|
||||||
// this is necessary because mViewPager.setCurrentItem(0) just after setting the
|
// this is necessary because mViewPager.setCurrentItem(0) just after setting the
|
||||||
// adapter does not result in a call to #onPageSelected(0)
|
// adapter does not result in a call to #onPageSelected(0)
|
||||||
requestWaitingForBinder = true
|
screenState = PreviewImageActivityState.WaitingForBinder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
outState.putBoolean(KEY_WAITING_FOR_BINDER, requestWaitingForBinder)
|
outState.putBoolean(KEY_WAITING_FOR_BINDER, screenState == PreviewImageActivityState.WaitingForBinder)
|
||||||
outState.putBoolean(KEY_SYSTEM_VISIBLE, isSystemUIVisible)
|
outState.putBoolean(KEY_SYSTEM_VISIBLE, isSystemUIVisible)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,11 +254,15 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
|
|
||||||
previewImagePagerAdapter?.let {
|
previewImagePagerAdapter?.let {
|
||||||
if (it.itemCount <= 1) {
|
if (it.itemCount <= 1) {
|
||||||
finish()
|
backToDisplayActivity()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.isPresent) {
|
||||||
|
initViewPager(user.get())
|
||||||
|
}
|
||||||
|
|
||||||
viewPager?.setCurrentItem(nextPosition, true)
|
viewPager?.setCurrentItem(nextPosition, true)
|
||||||
previewImagePagerAdapter?.delete(deletePosition)
|
previewImagePagerAdapter?.delete(deletePosition)
|
||||||
} else if (operation is SynchronizeFileOperation) {
|
} else if (operation is SynchronizeFileOperation) {
|
||||||
|
@ -274,12 +278,35 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
|
|
||||||
private fun observeWorkerState() {
|
private fun observeWorkerState() {
|
||||||
WorkerStateLiveData.instance().observe(this) { state: WorkerState? ->
|
WorkerStateLiveData.instance().observe(this) { state: WorkerState? ->
|
||||||
if (state is WorkerState.Download) {
|
when (state) {
|
||||||
|
is WorkerState.Download -> {
|
||||||
Log_OC.d(TAG, "Download worker started")
|
Log_OC.d(TAG, "Download worker started")
|
||||||
isDownloadWorkStarted = true
|
isDownloadWorkStarted = true
|
||||||
|
|
||||||
if (requestWaitingForBinder) {
|
if (screenState == PreviewImageActivityState.WaitingForBinder) {
|
||||||
requestWaitingForBinder = false
|
selectPageOnDownload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is WorkerState.Idle -> {
|
||||||
|
Log_OC.d(TAG, "Download worker stopped")
|
||||||
|
isDownloadWorkStarted = false
|
||||||
|
|
||||||
|
if (screenState == PreviewImageActivityState.Edit) {
|
||||||
|
onImageDownloadComplete(state.currentFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
Log_OC.d(TAG, "Download worker stopped")
|
||||||
|
isDownloadWorkStarted = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectPageOnDownload() {
|
||||||
|
screenState = PreviewImageActivityState.Idle
|
||||||
Log_OC.d(
|
Log_OC.d(
|
||||||
TAG,
|
TAG,
|
||||||
"Simulating reselection of current page after connection " +
|
"Simulating reselection of current page after connection " +
|
||||||
|
@ -287,11 +314,12 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
)
|
)
|
||||||
selectPage(viewPager?.currentItem)
|
selectPage(viewPager?.currentItem)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Log_OC.d(TAG, "Download worker stopped")
|
private fun onImageDownloadComplete(downloadedFile: OCFile?) {
|
||||||
isDownloadWorkStarted = false
|
dismissLoadingDialog()
|
||||||
}
|
screenState = PreviewImageActivityState.Idle
|
||||||
}
|
file = downloadedFile
|
||||||
|
startEditImageActivity()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -316,6 +344,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun backToDisplayActivity() {
|
private fun backToDisplayActivity() {
|
||||||
|
sendRefreshSearchEventBroadcast()
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +357,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
}
|
}
|
||||||
|
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
finish()
|
backToDisplayActivity()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showDetails(file: OCFile, activeTab: Int) {
|
override fun showDetails(file: OCFile, activeTab: Int) {
|
||||||
|
@ -356,7 +385,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
val currentFile = previewImagePagerAdapter?.getFileAt(position)
|
val currentFile = previewImagePagerAdapter?.getFileAt(position)
|
||||||
|
|
||||||
if (!isDownloadWorkStarted) {
|
if (!isDownloadWorkStarted) {
|
||||||
requestWaitingForBinder = true
|
screenState = PreviewImageActivityState.WaitingForBinder
|
||||||
} else {
|
} else {
|
||||||
if (currentFile != null) {
|
if (currentFile != null) {
|
||||||
if (currentFile.isEncrypted && !currentFile.isDown &&
|
if (currentFile.isEncrypted && !currentFile.isDown &&
|
||||||
|
@ -457,14 +486,31 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR
|
||||||
|
|
||||||
fun startImageEditor(file: OCFile) {
|
fun startImageEditor(file: OCFile) {
|
||||||
if (file.isDown) {
|
if (file.isDown) {
|
||||||
val editImageIntent = Intent(this, EditImageActivity::class.java)
|
startEditImageActivity()
|
||||||
editImageIntent.putExtra(EditImageActivity.EXTRA_FILE, file)
|
|
||||||
startActivity(editImageIntent)
|
|
||||||
} else {
|
} else {
|
||||||
|
showLoadingDialog(getString(R.string.preview_image_downloading_image_for_edit))
|
||||||
|
screenState = PreviewImageActivityState.Edit
|
||||||
requestForDownload(file, EditImageActivity.OPEN_IMAGE_EDITOR)
|
requestForDownload(file, EditImageActivity.OPEN_IMAGE_EDITOR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startEditImageActivity() {
|
||||||
|
if (file == null) {
|
||||||
|
DisplayUtils.showSnackMessage(this, R.string.preview_image_file_is_not_exist)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.isDown) {
|
||||||
|
DisplayUtils.showSnackMessage(this, R.string.preview_image_file_is_not_downloaded)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val intent = Intent(this, EditImageActivity::class.java).apply {
|
||||||
|
putExtra(EditImageActivity.EXTRA_FILE, file)
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBrowsedDownTo(folder: OCFile) {
|
override fun onBrowsedDownTo(folder: OCFile) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.ui.preview.model
|
||||||
|
|
||||||
|
enum class PreviewImageActivityState {
|
||||||
|
WaitingForBinder,
|
||||||
|
Edit,
|
||||||
|
Idle
|
||||||
|
}
|
|
@ -95,6 +95,10 @@ object PermissionUtil {
|
||||||
else -> checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
else -> checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkPermissions(context: Context, permissions: Array<String>): Boolean = permissions.all {
|
||||||
|
ActivityCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request relevant external storage permission depending on SDK, if needed.
|
* Request relevant external storage permission depending on SDK, if needed.
|
||||||
*
|
*
|
||||||
|
|
19
app/src/main/res/drawable/ic_find_in_page.xml
Normal file
19
app/src/main/res/drawable/ic_find_in_page.xml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!--
|
||||||
|
~ Nextcloud - Android Client
|
||||||
|
~
|
||||||
|
~ SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
|
||||||
|
~ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#FF969696"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M20,19.59V8l-6,-6H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c0.45,0 0.85,-0.15 1.19,-0.4l-4.43,-4.43c-0.8,0.52 -1.74,0.83 -2.76,0.83 -2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5c0,1.02 -0.31,1.96 -0.83,2.75L20,19.59zM9,13c0,1.66 1.34,3 3,3s3,-1.34 3,-3 -1.34,-3 -3,-3 -3,1.34 -3,3z" />
|
||||||
|
|
||||||
|
</vector>
|
|
@ -841,6 +841,7 @@ GNU yleinen lisenssi, versio 2</string>
|
||||||
<string name="upload_local_storage_full">Paikallinen tallennustila täynnä</string>
|
<string name="upload_local_storage_full">Paikallinen tallennustila täynnä</string>
|
||||||
<string name="upload_local_storage_not_copied">Tiedostoa ei voitu kopioida paikalliseen tallennustilaan</string>
|
<string name="upload_local_storage_not_copied">Tiedostoa ei voitu kopioida paikalliseen tallennustilaan</string>
|
||||||
<string name="upload_lock_failed">Kansion lukitseminen epäonnistui</string>
|
<string name="upload_lock_failed">Kansion lukitseminen epäonnistui</string>
|
||||||
|
<string name="upload_manually_cancelled">Käyttäjä peruutti latauksen</string>
|
||||||
<string name="upload_old_android">Salaus on mahdollista vain kun >= Android 5.0</string>
|
<string name="upload_old_android">Salaus on mahdollista vain kun >= Android 5.0</string>
|
||||||
<string name="upload_query_move_foreign_files">Riittämätön tila estää tiedostojen kopioinnin %1$s kansioon. Haluatko sen sijaan siirtää ne sinne?</string>
|
<string name="upload_query_move_foreign_files">Riittämätön tila estää tiedostojen kopioinnin %1$s kansioon. Haluatko sen sijaan siirtää ne sinne?</string>
|
||||||
<string name="upload_scan_doc_upload">Skannaa asiakirja kameralla</string>
|
<string name="upload_scan_doc_upload">Skannaa asiakirja kameralla</string>
|
||||||
|
|
|
@ -677,6 +677,7 @@
|
||||||
<string name="push_notifications_temp_error">Atualmente o envio de notificações está indisponível.</string>
|
<string name="push_notifications_temp_error">Atualmente o envio de notificações está indisponível.</string>
|
||||||
<string name="qr_could_not_be_read">Não foi possível ler o código QR! </string>
|
<string name="qr_could_not_be_read">Não foi possível ler o código QR! </string>
|
||||||
<string name="receive_external_files_activity_start_sync_folder_is_not_exists_message">A pasta não pode ser encontrada, a operação de sincronização foi cancelada</string>
|
<string name="receive_external_files_activity_start_sync_folder_is_not_exists_message">A pasta não pode ser encontrada, a operação de sincronização foi cancelada</string>
|
||||||
|
<string name="receive_external_files_activity_unable_to_find_file_to_upload">Não foi possível encontrar o arquivo para upload</string>
|
||||||
<string name="recommend_subject">Experimente %1$s em seu dispositivo!</string>
|
<string name="recommend_subject">Experimente %1$s em seu dispositivo!</string>
|
||||||
<string name="recommend_text">Quero convidar você a usar %1$s em seu dispositivo.\nBaixe daqui: %2$s</string>
|
<string name="recommend_text">Quero convidar você a usar %1$s em seu dispositivo.\nBaixe daqui: %2$s</string>
|
||||||
<string name="recommend_urls">%1$s ou %2$s</string>
|
<string name="recommend_urls">%1$s ou %2$s</string>
|
||||||
|
|
|
@ -129,6 +129,7 @@
|
||||||
<string name="uploader_error_message_source_file_not_found">File selected for upload not found. Please check whether the file exists.</string>
|
<string name="uploader_error_message_source_file_not_found">File selected for upload not found. Please check whether the file exists.</string>
|
||||||
<string name="uploader_error_message_source_file_not_copied">Could not copy file to a temporary folder. Try to resend it.</string>
|
<string name="uploader_error_message_source_file_not_copied">Could not copy file to a temporary folder. Try to resend it.</string>
|
||||||
<string name="uploader_upload_files_behaviour">Upload option:</string>
|
<string name="uploader_upload_files_behaviour">Upload option:</string>
|
||||||
|
<string name="max_file_count_warning_message">You have reached the maximum file upload limit. Please upload fewer than 500 files at a time.</string>
|
||||||
<string name="uploader_upload_files_behaviour_move_to_nextcloud_folder">Move file to %1$s folder</string>
|
<string name="uploader_upload_files_behaviour_move_to_nextcloud_folder">Move file to %1$s folder</string>
|
||||||
<string name="uploader_upload_files_behaviour_only_upload">Keep file in source folder</string>
|
<string name="uploader_upload_files_behaviour_only_upload">Keep file in source folder</string>
|
||||||
<string name="uploader_upload_files_behaviour_upload_and_delete_from_source">Delete file from source folder</string>
|
<string name="uploader_upload_files_behaviour_upload_and_delete_from_source">Delete file from source folder</string>
|
||||||
|
@ -419,6 +420,9 @@
|
||||||
<string name="preview_media_unhandled_http_code_message">File is currently locked by another user or process and therefore not deletable. Please try again later.</string>
|
<string name="preview_media_unhandled_http_code_message">File is currently locked by another user or process and therefore not deletable. Please try again later.</string>
|
||||||
|
|
||||||
<string name="preview_sorry">Sorry</string>
|
<string name="preview_sorry">Sorry</string>
|
||||||
|
<string name="preview_image_file_is_not_exist">File is not exist</string>
|
||||||
|
<string name="preview_image_file_is_not_downloaded">File is not downloaded</string>
|
||||||
|
<string name="preview_image_downloading_image_for_edit">Downloading image to start the edit screen, please wait…</string>
|
||||||
<string name="preview_image_description">Image preview</string>
|
<string name="preview_image_description">Image preview</string>
|
||||||
<string name="preview_image_error_unknown_format">Unable to show image</string>
|
<string name="preview_image_error_unknown_format">Unable to show image</string>
|
||||||
<string name="preview_image_error_no_local_file">There is no local file to preview</string>
|
<string name="preview_image_error_no_local_file">There is no local file to preview</string>
|
||||||
|
@ -1209,4 +1213,9 @@
|
||||||
<string name="sub_folder_rule_day">Year/Month/Day</string>
|
<string name="sub_folder_rule_day">Year/Month/Day</string>
|
||||||
<string name="secure_share_not_set_up">Secure sharing is not set up for this user</string>
|
<string name="secure_share_not_set_up">Secure sharing is not set up for this user</string>
|
||||||
<string name="share_not_allowed_when_file_drop">Resharing is not allowed during secure file drop</string>
|
<string name="share_not_allowed_when_file_drop">Resharing is not allowed during secure file drop</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="unified_search_fragment_calendar_event_not_found">Event not found, you can always sync to update. Redirecting to web…</string>
|
||||||
|
<string name="unified_search_fragment_contact_not_found">Contact not found, you can always sync to update. Redirecting to web…</string>
|
||||||
|
<string name="unified_search_fragment_permission_needed">Permissions are required to open search result otherwise it will redirected to web…</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue