From 7d9c88b173a92c8749ac897e6db897a6707bff47 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 18 Jun 2024 15:02:16 +0200 Subject: [PATCH 01/34] Test Signed-off-by: alperozturk --- .../ui/activity/FileDisplayActivity.java | 29 +++++++++++++++++++ .../ui/adapter/UnifiedSearchItemViewHolder.kt | 12 ++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index b9105cb833..83668fd170 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -27,6 +28,7 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources.NotFoundException; +import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -35,6 +37,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Parcelable; +import android.provider.ContactsContract; import android.text.TextUtils; import android.view.Menu; import android.view.MenuInflater; @@ -131,6 +134,8 @@ import com.owncloud.android.utils.PushUtils; import com.owncloud.android.utils.StringUtils; import com.owncloud.android.utils.theme.CapabilityUtils; +import net.fortuna.ical4j.model.property.Contact; + import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -278,6 +283,30 @@ public class FileDisplayActivity extends FileActivity initSyncBroadcastReceiver(); observeWorkerState(); registerRefreshFolderEventReceiver(); + getAllContacts(); + } + + public void getAllContacts() { + ContentResolver contentResolver = getContentResolver(); + Uri uri = ContactsContract.Contacts.CONTENT_URI; + String[] projection = new String[]{ + ContactsContract.Contacts._ID, + ContactsContract.Contacts.DISPLAY_NAME + }; + Cursor cursor = contentResolver.query(uri, projection, null, null, null); + + if (cursor != null && cursor.getCount() > 0) { + while (cursor.moveToNext()) { + String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); + String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); + Log_OC.d(TAG, "CONTACT_ID: " + id); + Log_OC.d(TAG, "CONTACT_NAME: " + name); + + } + cursor.close(); + } + + // Do something with the list of contacts } @SuppressWarnings("unchecked") diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt index b6a58f4726..5ce2ddf1b8 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt @@ -8,8 +8,11 @@ package com.owncloud.android.ui.adapter import android.content.Context +import android.content.Intent import android.graphics.Bitmap import android.graphics.drawable.Drawable +import android.net.Uri +import android.provider.ContactsContract import android.view.View import androidx.core.content.res.ResourcesCompat import com.afollestad.sectionedrecyclerview.SectionedViewHolder @@ -70,12 +73,17 @@ class UnifiedSearchItemViewHolder( if (entry.isFile) { binding.more.visibility = View.VISIBLE - binding.more.setOnClickListener { filesAction.showFilesAction(entry) } + binding.more.setOnClickListener { + filesAction.showFilesAction(entry) + } } else { binding.more.visibility = View.GONE } - binding.unifiedSearchItemLayout.setOnClickListener { listInterface.onSearchResultClicked(entry) } + binding.unifiedSearchItemLayout.setOnClickListener { + + // listInterface.onSearchResultClicked(entry) + } } private fun getPlaceholder(entry: SearchResultEntry, mimetype: String?): Drawable { From 6f62d39944279e530411039cca92348a6cc04f15 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 18 Jun 2024 16:17:34 +0200 Subject: [PATCH 02/34] Add event managers for unified search Signed-off-by: alperozturk --- .../nextcloud/utils/CalendarEventManager.kt | 67 +++++++++++++++++++ .../com/nextcloud/utils/ContactManager.kt | 64 ++++++++++++++++++ .../extensions/SearchResultEntryExtensions.kt | 18 +++++ .../ui/activity/FileDisplayActivity.java | 24 ------- .../ui/adapter/UnifiedSearchItemViewHolder.kt | 24 +++++-- 5 files changed, 166 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt create mode 100644 app/src/main/java/com/nextcloud/utils/ContactManager.kt create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt new file mode 100644 index 0000000000..be567a2d17 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -0,0 +1,67 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +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.parseDateTimeToMillis +import com.owncloud.android.lib.common.SearchResultEntry +import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface + +class CalendarEventManager(private val context: Context) { + + fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { + val eventStartDate = searchResult.parseDateTimeToMillis() + val eventId: Long? = getCalendarEventId(searchResult.title, eventStartDate) + if (eventId == null) { + 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 cursor = context.contentResolver.query( + CalendarContract.Events.CONTENT_URI, + projection, + null, + null, + "${CalendarContract.Events.DTSTART} ASC" + ) + + cursor?.use { + val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID) + val titleIndex = cursor.getColumnIndex(CalendarContract.Events.TITLE) + val dtstartIndex = cursor.getColumnIndex(CalendarContract.Events.DTSTART) + + while (cursor.moveToNext()) { + val title = cursor.getString(titleIndex) + val startDate = cursor.getLong(dtstartIndex) + + // Jun 19, 2024 9:30 AM - 10:00 AM + if (eventTitle == title && startDate == eventStartDate) { + return cursor.getLong(idIndex) + } + } + } + + return null + } + +} diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt new file mode 100644 index 0000000000..63a713d043 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -0,0 +1,64 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.ContactsContract +import com.owncloud.android.lib.common.SearchResultEntry +import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface + +class ContactManager(private val context: Context) { + + fun openContact(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { + val contactIds = getContactIds(searchResult.title) + if (contactIds.isEmpty()) { + listInterface.onSearchResultClicked(searchResult) + } else { + val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactIds.first().toString()) + val intent = Intent(Intent.ACTION_VIEW).apply { + setData(uri) + } + context.startActivity(intent) + } + } + + private fun getContactIds(contactName: String): List { + val result = arrayListOf() + + val projection = arrayOf( + ContactsContract.Contacts._ID, + ContactsContract.Contacts.DISPLAY_NAME + ) + + val cursor = context.contentResolver.query( + ContactsContract.Contacts.CONTENT_URI, + projection, + null, + null, + null + ) + + cursor?.use { + val idIndex = cursor.getColumnIndex(ContactsContract.Contacts._ID) + val nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME) + + while (cursor.moveToNext()) { + val id = cursor.getLong(idIndex) + val title = cursor.getString(nameIndex) + + if (title == contactName) { + result.add(id) + } + } + } + + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt new file mode 100644 index 0000000000..533d3fa729 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -0,0 +1,18 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.lib.common.SearchResultEntry +import java.text.SimpleDateFormat +import java.util.Locale + +fun SearchResultEntry.parseDateTimeToMillis(): Long { + val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) + val date = sdf.parse(subline) + return date?.time ?: 0 +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 83668fd170..28bf81cf0f 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -283,30 +283,6 @@ public class FileDisplayActivity extends FileActivity initSyncBroadcastReceiver(); observeWorkerState(); registerRefreshFolderEventReceiver(); - getAllContacts(); - } - - public void getAllContacts() { - ContentResolver contentResolver = getContentResolver(); - Uri uri = ContactsContract.Contacts.CONTENT_URI; - String[] projection = new String[]{ - ContactsContract.Contacts._ID, - ContactsContract.Contacts.DISPLAY_NAME - }; - Cursor cursor = contentResolver.query(uri, projection, null, null, null); - - if (cursor != null && cursor.getCount() > 0) { - while (cursor.moveToNext()) { - String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); - String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); - Log_OC.d(TAG, "CONTACT_ID: " + id); - Log_OC.d(TAG, "CONTACT_NAME: " + name); - - } - cursor.close(); - } - - // Do something with the list of contacts } @SuppressWarnings("unchecked") diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt index 5ce2ddf1b8..bda0dc45e1 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt @@ -8,11 +8,8 @@ package com.owncloud.android.ui.adapter import android.content.Context -import android.content.Intent import android.graphics.Bitmap import android.graphics.drawable.Drawable -import android.net.Uri -import android.provider.ContactsContract import android.view.View import androidx.core.content.res.ResourcesCompat import com.afollestad.sectionedrecyclerview.SectionedViewHolder @@ -27,6 +24,8 @@ import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface import com.owncloud.android.utils.BitmapUtils +import com.nextcloud.utils.CalendarEventManager +import com.nextcloud.utils.ContactManager import com.owncloud.android.utils.MimeTypeUtil import com.owncloud.android.utils.glide.CustomGlideStreamLoader import com.owncloud.android.utils.theme.ViewThemeUtils @@ -41,13 +40,15 @@ class UnifiedSearchItemViewHolder( private val filesAction: FilesAction, val context: Context, private val viewThemeUtils: ViewThemeUtils -) : - SectionedViewHolder(binding.root) { +) : SectionedViewHolder(binding.root) { interface FilesAction { fun showFilesAction(searchResultEntry: SearchResultEntry) } + private val contactManager = ContactManager(context) + private val calendarEventManager = CalendarEventManager(context) + fun bind(entry: SearchResultEntry) { binding.title.text = entry.title binding.subline.text = entry.subline @@ -81,8 +82,17 @@ class UnifiedSearchItemViewHolder( } binding.unifiedSearchItemLayout.setOnClickListener { - - // listInterface.onSearchResultClicked(entry) + when (entry.icon) { + "icon-contacts" -> { + contactManager.openContact(entry, listInterface) + } + "icon-calendar" -> { + calendarEventManager.openCalendarEvent(entry, listInterface) + } + else -> { + listInterface.onSearchResultClicked(entry) + } + } } } From f07ef57a216e3b4c02adc0a8769d9e2cdfd1d6e9 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 18 Jun 2024 16:20:33 +0200 Subject: [PATCH 03/34] Add parseDateTimeRange Signed-off-by: alperozturk --- .../com/nextcloud/utils/CalendarEventManager.kt | 12 +++++++++++- .../extensions/SearchResultEntryExtensions.kt | 17 +++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index be567a2d17..5b93ae9af9 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -12,6 +12,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.CalendarContract +import com.nextcloud.utils.extensions.parseDateTimeRange import com.nextcloud.utils.extensions.parseDateTimeToMillis import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface @@ -19,7 +20,16 @@ import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface class CalendarEventManager(private val context: Context) { fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { - val eventStartDate = searchResult.parseDateTimeToMillis() + var eventStartDate = searchResult.parseDateTimeToMillis() + if (eventStartDate == null) { + eventStartDate = searchResult.parseDateTimeRange() + } + + if (eventStartDate == null) { + listInterface.onSearchResultClicked(searchResult) + return + } + val eventId: Long? = getCalendarEventId(searchResult.title, eventStartDate) if (eventId == null) { listInterface.onSearchResultClicked(searchResult) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index 533d3fa729..e47ebf08de 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -11,8 +11,21 @@ import com.owncloud.android.lib.common.SearchResultEntry import java.text.SimpleDateFormat import java.util.Locale -fun SearchResultEntry.parseDateTimeToMillis(): Long { +fun SearchResultEntry.parseDateTimeToMillis(): Long? { val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) val date = sdf.parse(subline) - return date?.time ?: 0 + return date?.time } + +fun SearchResultEntry.parseDateTimeRange(): Long? { + val regex = Regex("""(\w+ \d{1,2}, \d{4} \d{1,2}:\d{2} [AP]M) - (\d{1,2}:\d{2} [AP]M)""") + val matchResult = regex.find(subline) + + if (matchResult != null) { + val (startDateTimeString, endTimeString) = matchResult.destructured + val dateFormat = SimpleDateFormat("MMM d, yyyy h:mm a", Locale.getDefault()) + return dateFormat.parse(startDateTimeString)?.time + } + + return null +} \ No newline at end of file From 485a859ca21730d4b637cff44d6409ef83450556 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 18 Jun 2024 17:05:22 +0200 Subject: [PATCH 04/34] Split subline Signed-off-by: alperozturk --- .../nextcloud/utils/CalendarEventManager.kt | 14 ++------ .../extensions/SearchResultEntryExtensions.kt | 36 +++++++++++-------- .../ui/activity/FileDisplayActivity.java | 17 +++++++++ .../ui/adapter/UnifiedSearchItemViewHolder.kt | 16 ++++----- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 5b93ae9af9..b723db0660 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -13,23 +13,13 @@ import android.content.Intent import android.net.Uri import android.provider.CalendarContract import com.nextcloud.utils.extensions.parseDateTimeRange -import com.nextcloud.utils.extensions.parseDateTimeToMillis import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface class CalendarEventManager(private val context: Context) { fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { - var eventStartDate = searchResult.parseDateTimeToMillis() - if (eventStartDate == null) { - eventStartDate = searchResult.parseDateTimeRange() - } - - if (eventStartDate == null) { - listInterface.onSearchResultClicked(searchResult) - return - } - + val eventStartDate = searchResult.parseDateTimeRange()!! val eventId: Long? = getCalendarEventId(searchResult.title, eventStartDate) if (eventId == null) { listInterface.onSearchResultClicked(searchResult) @@ -58,7 +48,7 @@ class CalendarEventManager(private val context: Context) { cursor?.use { val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID) val titleIndex = cursor.getColumnIndex(CalendarContract.Events.TITLE) - val dtstartIndex = cursor.getColumnIndex(CalendarContract.Events.DTSTART) + val dtstartIndex = cursor.getColumnIndex(CalendarContract.Events.DTEND) while (cursor.moveToNext()) { val title = cursor.getString(titleIndex) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index e47ebf08de..888c48a876 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -7,25 +7,31 @@ package com.nextcloud.utils.extensions +import android.annotation.SuppressLint import com.owncloud.android.lib.common.SearchResultEntry +import java.text.ParseException import java.text.SimpleDateFormat -import java.util.Locale - -fun SearchResultEntry.parseDateTimeToMillis(): Long? { - val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) - val date = sdf.parse(subline) - return date?.time -} +import java.util.TimeZone +@SuppressLint("SimpleDateFormat") fun SearchResultEntry.parseDateTimeRange(): Long? { - val regex = Regex("""(\w+ \d{1,2}, \d{4} \d{1,2}:\d{2} [AP]M) - (\d{1,2}:\d{2} [AP]M)""") - val matchResult = regex.find(subline) + // Define the input and output date formats + val inputFormat = SimpleDateFormat("MMM d, yyyy h:mm a") + val outputFormat = SimpleDateFormat("MMM d, yyyy HH:mm a") - if (matchResult != null) { - val (startDateTimeString, endTimeString) = matchResult.destructured - val dateFormat = SimpleDateFormat("MMM d, yyyy h:mm a", Locale.getDefault()) - return dateFormat.parse(startDateTimeString)?.time + // Parse the input date string + val startDateTime = inputFormat.parse(subline.split(" - ")[0]) + + // Format the date to the desired output format + val result = outputFormat.format(startDateTime) + + + val formatter = SimpleDateFormat("MMM dd, yyyy HH:MM") + try { + val date = formatter.parse(result) + formatter.timeZone = TimeZone.getTimeZone("UTC") + return date?.time + } catch (e: ParseException) { + return null } - - return null } \ No newline at end of file diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 28bf81cf0f..7f118f7214 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -141,10 +141,14 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.io.File; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.TimeZone; import javax.inject.Inject; @@ -283,6 +287,19 @@ public class FileDisplayActivity extends FileActivity initSyncBroadcastReceiver(); observeWorkerState(); registerRefreshFolderEventReceiver(); + + String aa = "Jun 19, 2024 9:30 AM - 10:00 AM"; + SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy HH:mm a - HH:mm a"); + try { + Date date = formatter.parse(aa); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + + Log_OC.d("TAGGGGG", "UTC time: " + date.getTime()); + + } catch (ParseException e) { + throw new RuntimeException(e); + } + } @SuppressWarnings("unchecked") diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt index bda0dc45e1..a52eb8838f 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt @@ -82,16 +82,12 @@ class UnifiedSearchItemViewHolder( } binding.unifiedSearchItemLayout.setOnClickListener { - when (entry.icon) { - "icon-contacts" -> { - contactManager.openContact(entry, listInterface) - } - "icon-calendar" -> { - calendarEventManager.openCalendarEvent(entry, listInterface) - } - else -> { - listInterface.onSearchResultClicked(entry) - } + if (entry.icon.startsWith("icon-contacts")) { + contactManager.openContact(entry, listInterface) + } else if (entry.icon.startsWith("icon-calendar")) { + calendarEventManager.openCalendarEvent(entry, listInterface) + } else { + listInterface.onSearchResultClicked(entry) } } } From bc893388df9983a93784fd977762ef10b608834f Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Jul 2024 10:16:46 +0200 Subject: [PATCH 05/34] solve git conflicts Signed-off-by: alperozturk --- .../nextcloud/model/SearchResultEntryType.kt | 25 +++++++++ .../nextcloud/utils/CalendarEventManager.kt | 32 +++++------ .../com/nextcloud/utils/ContactManager.kt | 18 +++--- .../extensions/SearchResultEntryExtensions.kt | 18 ++++++ .../ui/activity/FileDisplayActivity.java | 13 ----- .../ui/adapter/UnifiedSearchItemViewHolder.kt | 55 +++++++++++-------- 6 files changed, 100 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt diff --git a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt new file mode 100644 index 0000000000..6d9e56c165 --- /dev/null +++ b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt @@ -0,0 +1,25 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * 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 getIconId(): 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 -> null + } + } +} diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index b723db0660..8bcb1977ff 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -19,8 +19,15 @@ import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface class CalendarEventManager(private val context: Context) { fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { - val eventStartDate = searchResult.parseDateTimeRange()!! + val eventStartDate = searchResult.parseDateTimeRange() + + if (eventStartDate == null) { + listInterface.onSearchResultClicked(searchResult) + return + } + val eventId: Long? = getCalendarEventId(searchResult.title, eventStartDate) + if (eventId == null) { listInterface.onSearchResultClicked(searchResult) } else { @@ -37,31 +44,24 @@ class CalendarEventManager(private val context: Context) { 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, - null, - null, + selection, + selectionArgs, "${CalendarContract.Events.DTSTART} ASC" ) cursor?.use { - val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID) - val titleIndex = cursor.getColumnIndex(CalendarContract.Events.TITLE) - val dtstartIndex = cursor.getColumnIndex(CalendarContract.Events.DTEND) - - while (cursor.moveToNext()) { - val title = cursor.getString(titleIndex) - val startDate = cursor.getLong(dtstartIndex) - - // Jun 19, 2024 9:30 AM - 10:00 AM - if (eventTitle == title && startDate == eventStartDate) { - return cursor.getLong(idIndex) - } + if (cursor.moveToFirst()) { + val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID) + return cursor.getLong(idIndex) } } return null } - } diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index 63a713d043..6691595391 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -37,28 +37,26 @@ class ContactManager(private val context: Context) { ContactsContract.Contacts.DISPLAY_NAME ) + val selection = "${ContactsContract.Contacts.DISPLAY_NAME} = ?" + val selectionArgs = arrayOf(contactName) + val cursor = context.contentResolver.query( ContactsContract.Contacts.CONTENT_URI, projection, - null, - null, + selection, + selectionArgs, null ) cursor?.use { val idIndex = cursor.getColumnIndex(ContactsContract.Contacts._ID) - val nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME) while (cursor.moveToNext()) { val id = cursor.getLong(idIndex) - val title = cursor.getString(nameIndex) - - if (title == contactName) { - result.add(id) - } + result.add(id) } } return result } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index 888c48a876..0081b83b3a 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -8,11 +8,29 @@ package com.nextcloud.utils.extensions import android.annotation.SuppressLint +import com.nextcloud.model.SearchResultEntryType import com.owncloud.android.lib.common.SearchResultEntry import java.text.ParseException import java.text.SimpleDateFormat import java.util.TimeZone +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 + } +} + +// FIXME @SuppressLint("SimpleDateFormat") fun SearchResultEntry.parseDateTimeRange(): Long? { // Define the input and output date formats diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 7f118f7214..71fb8d8df6 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -287,19 +287,6 @@ public class FileDisplayActivity extends FileActivity initSyncBroadcastReceiver(); observeWorkerState(); registerRefreshFolderEventReceiver(); - - String aa = "Jun 19, 2024 9:30 AM - 10:00 AM"; - SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy HH:mm a - HH:mm a"); - try { - Date date = formatter.parse(aa); - formatter.setTimeZone(TimeZone.getTimeZone("UTC")); - - Log_OC.d("TAGGGGG", "UTC time: " + date.getTime()); - - } catch (ParseException e) { - throw new RuntimeException(e); - } - } @SuppressWarnings("unchecked") diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt index a52eb8838f..09bc0625b2 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt @@ -16,9 +16,10 @@ import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestListener 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.network.ClientFactory -import com.owncloud.android.R +import com.nextcloud.model.SearchResultEntryType import com.owncloud.android.databinding.UnifiedSearchItemBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.lib.common.SearchResultEntry @@ -26,6 +27,7 @@ import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface import com.owncloud.android.utils.BitmapUtils import com.nextcloud.utils.CalendarEventManager import com.nextcloud.utils.ContactManager +import com.nextcloud.utils.extensions.getType import com.owncloud.android.utils.MimeTypeUtil import com.owncloud.android.utils.glide.CustomGlideStreamLoader import com.owncloud.android.utils.theme.ViewThemeUtils @@ -61,7 +63,8 @@ class UnifiedSearchItemViewHolder( 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)) .load(entry.thumbnailUrl) @@ -82,34 +85,42 @@ class UnifiedSearchItemViewHolder( } binding.unifiedSearchItemLayout.setOnClickListener { - if (entry.icon.startsWith("icon-contacts")) { + searchEntryOnClick(entry, entryType) + } + } + + private fun searchEntryOnClick(entry: SearchResultEntry, entryType: SearchResultEntryType) { + when (entryType) { + SearchResultEntryType.Contact -> { contactManager.openContact(entry, listInterface) - } else if (entry.icon.startsWith("icon-calendar")) { + } + SearchResultEntryType.CalendarEvent -> { calendarEventManager.openCalendarEvent(entry, listInterface) - } else { + } + else -> { listInterface.onSearchResultClicked(entry) } } } - private fun getPlaceholder(entry: SearchResultEntry, mimetype: String?): Drawable { - val drawable = with(entry.icon) { - when { - equals("icon-folder") -> - ResourcesCompat.getDrawable(context.resources, R.drawable.folder, null) - startsWith("icon-note") -> - ResourcesCompat.getDrawable(context.resources, R.drawable.ic_edit, null) - startsWith("icon-contacts") -> - ResourcesCompat.getDrawable(context.resources, R.drawable.file_vcard, null) - startsWith("icon-calendar") -> - ResourcesCompat.getDrawable(context.resources, R.drawable.file_calendar, null) - startsWith("icon-deck") -> - ResourcesCompat.getDrawable(context.resources, R.drawable.ic_deck, null) - else -> - MimeTypeUtil.getFileTypeIcon(mimetype, entry.title, context, viewThemeUtils) - } + private fun getPlaceholder( + entry: SearchResultEntry, + entryType: SearchResultEntryType, + mimetype: String? + ): Drawable { + val iconId = entryType.run { + getIconId() } - return viewThemeUtils.platform.tintPrimaryDrawable(context, drawable)!! + + val defaultDrawable = MimeTypeUtil.getFileTypeIcon(mimetype, entry.title, context, viewThemeUtils) + + val drawable: Drawable = if (iconId == null) { + defaultDrawable + } else { + ResourcesCompat.getDrawable(context.resources, iconId, null) ?: defaultDrawable + } + + return viewThemeUtils.platform.tintDrawable(context, drawable, ColorRole.PRIMARY) } private inner class RoundIfNeededListener(private val entry: SearchResultEntry) : From 274132a12dc96c6c3606ade323bddd266c6e5d44 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 19 Jun 2024 09:57:52 +0200 Subject: [PATCH 06/34] Fix dateformat Signed-off-by: alperozturk --- .../nextcloud/utils/CalendarEventManager.kt | 21 +++++++++++------- .../extensions/SearchResultEntryExtensions.kt | 22 ++++++------------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 8bcb1977ff..188ba127c5 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -44,21 +44,26 @@ class CalendarEventManager(private val context: Context) { 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, + null, + null, "${CalendarContract.Events.DTSTART} ASC" ) cursor?.use { - if (cursor.moveToFirst()) { - val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID) - return cursor.getLong(idIndex) + val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID) + val titleIndex = cursor.getColumnIndex(CalendarContract.Events.TITLE) + val startDateIndex = cursor.getColumnIndex(CalendarContract.Events.DTSTART) + + while (cursor.moveToNext()) { + val title = cursor.getString(titleIndex) + val startDate = cursor.getLong(startDateIndex) + + if (eventTitle == title && startDate == eventStartDate) { + return cursor.getLong(idIndex) + } } } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index 0081b83b3a..4f3639f35f 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -12,6 +12,7 @@ import com.nextcloud.model.SearchResultEntryType import com.owncloud.android.lib.common.SearchResultEntry import java.text.ParseException import java.text.SimpleDateFormat +import java.util.Date import java.util.TimeZone fun SearchResultEntry.getType(): SearchResultEntryType { @@ -30,26 +31,17 @@ fun SearchResultEntry.getType(): SearchResultEntryType { } } -// FIXME @SuppressLint("SimpleDateFormat") fun SearchResultEntry.parseDateTimeRange(): Long? { - // Define the input and output date formats - val inputFormat = SimpleDateFormat("MMM d, yyyy h:mm a") - val outputFormat = SimpleDateFormat("MMM d, yyyy HH:mm a") + val cleanedSubline: String = subline.replace('\u202F', ' ') + val formatter = SimpleDateFormat("MMM d, yyyy h:mm a") + val startDate = cleanedSubline.substringBefore(" -") - // Parse the input date string - val startDateTime = inputFormat.parse(subline.split(" - ")[0]) - - // Format the date to the desired output format - val result = outputFormat.format(startDateTime) - - - val formatter = SimpleDateFormat("MMM dd, yyyy HH:MM") try { - val date = formatter.parse(result) + val date: Date? = formatter.parse(startDate) formatter.timeZone = TimeZone.getTimeZone("UTC") return date?.time } catch (e: ParseException) { - return null + return null } -} \ No newline at end of file +} From b3ab911c498711c9d4c412d58e6622cfa7a0c8fb Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 19 Jun 2024 10:03:32 +0200 Subject: [PATCH 07/34] Fix code analytics Signed-off-by: alperozturk --- app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt | 2 +- app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt index 6d9e56c165..1de05af7f7 100644 --- a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt +++ b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt @@ -13,7 +13,7 @@ enum class SearchResultEntryType { CalendarEvent, Folder, Note, Contact, Deck, Unknown; fun getIconId(): Int? { - return when(this) { + return when (this) { Folder -> R.drawable.folder Note -> R.drawable.ic_edit Contact -> R.drawable.file_vcard diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 188ba127c5..3c2869a432 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -41,7 +41,7 @@ class CalendarEventManager(private val context: Context) { val projection = arrayOf( CalendarContract.Events._ID, CalendarContract.Events.TITLE, - CalendarContract.Events.DTSTART, + CalendarContract.Events.DTSTART ) val cursor = context.contentResolver.query( From e036ec6826f10cb03790b065223c62e4a34c5490 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 19 Jun 2024 10:23:28 +0200 Subject: [PATCH 08/34] Show toast message for fallback scenario Signed-off-by: alperozturk --- .../nextcloud/utils/CalendarEventManager.kt | 25 +++++++++---------- .../com/nextcloud/utils/ContactManager.kt | 4 +++ .../ui/activity/FileDisplayActivity.java | 9 ------- app/src/main/res/values/strings.xml | 3 +++ 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 3c2869a432..5788af00fc 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -13,6 +13,8 @@ import android.content.Intent import android.net.Uri import android.provider.CalendarContract import com.nextcloud.utils.extensions.parseDateTimeRange +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 @@ -22,6 +24,7 @@ class CalendarEventManager(private val context: Context) { val eventStartDate = searchResult.parseDateTimeRange() if (eventStartDate == null) { + context.showToast(R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device) listInterface.onSearchResultClicked(searchResult) return } @@ -29,6 +32,7 @@ class CalendarEventManager(private val context: Context) { val eventId: Long? = getCalendarEventId(searchResult.title, eventStartDate) if (eventId == null) { + context.showToast(R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device) listInterface.onSearchResultClicked(searchResult) } else { val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventId) @@ -44,26 +48,21 @@ class CalendarEventManager(private val context: Context) { 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, - null, - null, + selection, + selectionArgs, "${CalendarContract.Events.DTSTART} ASC" ) cursor?.use { - val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID) - val titleIndex = cursor.getColumnIndex(CalendarContract.Events.TITLE) - val startDateIndex = cursor.getColumnIndex(CalendarContract.Events.DTSTART) - - while (cursor.moveToNext()) { - val title = cursor.getString(titleIndex) - val startDate = cursor.getLong(startDateIndex) - - if (eventTitle == title && startDate == eventStartDate) { - return cursor.getLong(idIndex) - } + if (cursor.moveToFirst()) { + val idIndex = cursor.getColumnIndex(CalendarContract.Events._ID) + return cursor.getLong(idIndex) } } diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index 6691595391..38b7a66b05 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -11,6 +11,8 @@ 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 @@ -18,7 +20,9 @@ class ContactManager(private val context: Context) { fun openContact(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { val contactIds = getContactIds(searchResult.title) + if (contactIds.isEmpty()) { + context.showToast(R.string.unified_search_fragment_contact_cannot_be_found_on_device) listInterface.onSearchResultClicked(searchResult) } else { val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactIds.first().toString()) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 71fb8d8df6..b9105cb833 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -20,7 +20,6 @@ import android.app.Activity; import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -28,7 +27,6 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources.NotFoundException; -import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -37,7 +35,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Parcelable; -import android.provider.ContactsContract; import android.text.TextUtils; import android.view.Menu; import android.view.MenuInflater; @@ -134,21 +131,15 @@ import com.owncloud.android.utils.PushUtils; import com.owncloud.android.utils.StringUtils; import com.owncloud.android.utils.theme.CapabilityUtils; -import net.fortuna.ical4j.model.property.Contact; - import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.io.File; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.List; import java.util.Optional; -import java.util.TimeZone; import javax.inject.Inject; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08c9921cc3..e3222c52d8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -298,6 +298,9 @@ Incorrect credentials Credentials disabled + The calendar event cannot be found on your device. Redirecting to web… + Contact cannot be found on your device. Redirecting to web… + Enter your passcode The passcode will be requested every time the app is started Please reenter your passcode From 2d1a06b034461305070162b26601cdb25e06ad7b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Jul 2024 11:31:43 +0200 Subject: [PATCH 09/34] Add permission checks Signed-off-by: alperozturk --- .../nextcloud/utils/CalendarEventManager.kt | 1 + .../com/nextcloud/utils/ContactManager.kt | 22 ++++++++++++---- .../ui/fragment/UnifiedSearchFragment.kt | 26 +++++++++++++++++++ app/src/main/res/values/strings.xml | 10 ++++--- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 5788af00fc..99130ad100 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -21,6 +21,7 @@ import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface class CalendarEventManager(private val context: Context) { fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { + val createdAt = searchResult.attributes["createdAt"] val eventStartDate = searchResult.parseDateTimeRange() if (eventStartDate == null) { diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index 38b7a66b05..5d6c9ecfba 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -7,6 +7,7 @@ package com.nextcloud.utils +import android.Manifest import android.content.Context import android.content.Intent import android.net.Uri @@ -15,14 +16,26 @@ 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 contactIds = getContactIds(searchResult.title) + val contactId = searchResult.attributes["uid"] + val haveReadContactsPermission = checkSelfPermission(context, Manifest.permission.READ_CONTACTS) + val contactIds = if (haveReadContactsPermission) { + getContactId(contactId!!) + } else { + listOf() + } if (contactIds.isEmpty()) { - context.showToast(R.string.unified_search_fragment_contact_cannot_be_found_on_device) + val messageId = if (haveReadContactsPermission) { + R.string.unified_search_fragment_contact_cannot_be_found_on_device + } else { + R.string.unified_search_fragment_contact_permission_needed_redirecting_web + } + context.showToast(messageId) listInterface.onSearchResultClicked(searchResult) } else { val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactIds.first().toString()) @@ -33,15 +46,14 @@ class ContactManager(private val context: Context) { } } - private fun getContactIds(contactName: String): List { + private fun getContactId(contactName: String): List { val result = arrayListOf() val projection = arrayOf( ContactsContract.Contacts._ID, - ContactsContract.Contacts.DISPLAY_NAME ) - val selection = "${ContactsContract.Contacts.DISPLAY_NAME} = ?" + val selection = "${ContactsContract.Contacts._ID} = ?" val selectionArgs = arrayOf(contactName) val cursor = context.contentResolver.query( diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index 234e49995f..960e8c0300 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -7,6 +7,7 @@ */ package com.owncloud.android.ui.fragment +import android.Manifest import android.content.Intent import android.os.Bundle import android.view.LayoutInflater @@ -16,6 +17,7 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.ImageView +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.VisibleForTesting import androidx.appcompat.widget.SearchView import androidx.core.view.updatePadding @@ -44,6 +46,7 @@ import com.owncloud.android.ui.unifiedsearch.UnifiedSearchSection import com.owncloud.android.ui.unifiedsearch.UnifiedSearchViewModel import com.owncloud.android.ui.unifiedsearch.filterOutHiddenFiles import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.PermissionUtil.checkSelfPermission import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject @@ -132,6 +135,7 @@ class UnifiedSearchFragment : setupFileDisplayActivity() setupAdapter() + checkPermissions() } @Deprecated("Deprecated in Java") @@ -140,6 +144,28 @@ class UnifiedSearchFragment : setupSearchView(item) } + private val contactPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> + if (!isGranted) { + DisplayUtils.showSnackMessage(binding.root, R.string.unified_search_fragment_contact_permission_needed) + } + } + + private val calendarPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> + if (!isGranted) { + DisplayUtils.showSnackMessage(binding.root, R.string.unified_search_fragment_calendar_permission_needed) + } + } + + private fun checkPermissions() { + if (!checkSelfPermission(requireActivity(), Manifest.permission.READ_CONTACTS)) { + contactPermissionLauncher.launch(Manifest.permission.READ_CONTACTS) + } + + if (!checkSelfPermission(requireActivity(), Manifest.permission.READ_CALENDAR)) { + calendarPermissionLauncher.launch(Manifest.permission.READ_CALENDAR) + } + } + private fun setupSearchView(item: MenuItem) { (item.actionView as? SearchView?)?.run { // Required to align with TextView width. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e3222c52d8..3a1929bf71 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -298,9 +298,6 @@ Incorrect credentials Credentials disabled - The calendar event cannot be found on your device. Redirecting to web… - Contact cannot be found on your device. Redirecting to web… - Enter your passcode The passcode will be requested every time the app is started Please reenter your passcode @@ -1212,4 +1209,11 @@ Year/Month/Day Secure sharing is not set up for this user Resharing is not allowed during secure file drop + + + The calendar event cannot be found on your device. Redirecting to web… + Contact cannot be found on your device. Redirecting to web… + Permission to access contacts is required to open them. Redirecting to web… + Permission to access contacts is required to open them. + Permission to access calendar is required to view events. From 69b477063f5a98fcaaaba4626a9c0efba42c5033 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Jul 2024 13:23:56 +0200 Subject: [PATCH 10/34] Use displayName Signed-off-by: alperozturk --- .../com/nextcloud/utils/ContactManager.kt | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index 5d6c9ecfba..ae0798093a 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -21,10 +21,10 @@ import com.owncloud.android.utils.PermissionUtil.checkSelfPermission class ContactManager(private val context: Context) { fun openContact(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { - val contactId = searchResult.attributes["uid"] + val displayName = searchResult.attributes["displayName"] val haveReadContactsPermission = checkSelfPermission(context, Manifest.permission.READ_CONTACTS) - val contactIds = if (haveReadContactsPermission) { - getContactId(contactId!!) + val contactIds = if (haveReadContactsPermission && displayName != null) { + getContactId(displayName) } else { listOf() } @@ -46,15 +46,11 @@ class ContactManager(private val context: Context) { } } - private fun getContactId(contactName: String): List { + private fun getContactId(displayName: String): List { val result = arrayListOf() - - val projection = arrayOf( - ContactsContract.Contacts._ID, - ) - - val selection = "${ContactsContract.Contacts._ID} = ?" - val selectionArgs = arrayOf(contactName) + 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, @@ -66,7 +62,6 @@ class ContactManager(private val context: Context) { cursor?.use { val idIndex = cursor.getColumnIndex(ContactsContract.Contacts._ID) - while (cursor.moveToNext()) { val id = cursor.getLong(idIndex) result.add(id) From 042583c683a64869e21d1560788be037001abbab Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Jul 2024 13:31:33 +0200 Subject: [PATCH 11/34] Add permission checks for calendar event Signed-off-by: alperozturk --- .../nextcloud/utils/CalendarEventManager.kt | 25 +++++++++++-------- .../extensions/SearchResultEntryExtensions.kt | 20 --------------- app/src/main/res/values/strings.xml | 1 + 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 99130ad100..eabc1a5da2 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -7,33 +7,36 @@ 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.parseDateTimeRange 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 createdAt = searchResult.attributes["createdAt"] - val eventStartDate = searchResult.parseDateTimeRange() - - if (eventStartDate == null) { - context.showToast(R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device) - listInterface.onSearchResultClicked(searchResult) - return + val createdAt = searchResult.attributes["createdAt"]?.toLongOrNull() + val haveReadCalendarPermission = checkSelfPermission(context, Manifest.permission.READ_CALENDAR) + val eventId: Long? = if (haveReadCalendarPermission && createdAt != null) { + getCalendarEventId(searchResult.title, createdAt) + } else { + null } - val eventId: Long? = getCalendarEventId(searchResult.title, eventStartDate) - if (eventId == null) { - context.showToast(R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device) + val messageId = if (haveReadCalendarPermission) { + R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device + } else { + R.string.unified_search_fragment_calendar_permission_needed_redirecting_web + } + context.showToast(messageId) listInterface.onSearchResultClicked(searchResult) } else { val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventId) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index 4f3639f35f..802c836565 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -7,13 +7,8 @@ package com.nextcloud.utils.extensions -import android.annotation.SuppressLint import com.nextcloud.model.SearchResultEntryType import com.owncloud.android.lib.common.SearchResultEntry -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.Date -import java.util.TimeZone fun SearchResultEntry.getType(): SearchResultEntryType { return if (icon == "icon-folder") { @@ -30,18 +25,3 @@ fun SearchResultEntry.getType(): SearchResultEntryType { SearchResultEntryType.Unknown } } - -@SuppressLint("SimpleDateFormat") -fun SearchResultEntry.parseDateTimeRange(): Long? { - val cleanedSubline: String = subline.replace('\u202F', ' ') - val formatter = SimpleDateFormat("MMM d, yyyy h:mm a") - val startDate = cleanedSubline.substringBefore(" -") - - try { - val date: Date? = formatter.parse(startDate) - formatter.timeZone = TimeZone.getTimeZone("UTC") - return date?.time - } catch (e: ParseException) { - return null - } -} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3a1929bf71..8dc02cca6d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1214,6 +1214,7 @@ The calendar event cannot be found on your device. Redirecting to web… Contact cannot be found on your device. Redirecting to web… Permission to access contacts is required to open them. Redirecting to web… + Permission to access calendar is required to view events. Redirecting to web… Permission to access contacts is required to open them. Permission to access calendar is required to view events. From d596d2f11e99817e26dc2898307640a0fb48bc13 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Jul 2024 13:36:34 +0200 Subject: [PATCH 12/34] Use createdAt Signed-off-by: alperozturk --- app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index eabc1a5da2..3504b02b0c 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -22,7 +22,7 @@ import com.owncloud.android.utils.PermissionUtil.checkSelfPermission class CalendarEventManager(private val context: Context) { fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { - val createdAt = searchResult.attributes["createdAt"]?.toLongOrNull() + val createdAt = searchResult.attributes["createdAt"]?.toLongOrNull()?.times(1000L) val haveReadCalendarPermission = checkSelfPermission(context, Manifest.permission.READ_CALENDAR) val eventId: Long? = if (haveReadCalendarPermission && createdAt != null) { getCalendarEventId(searchResult.title, createdAt) From 11a27eb74a9954e199fd3753fa469eaeb792cd80 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Jul 2024 13:55:14 +0200 Subject: [PATCH 13/34] Refactor Signed-off-by: alperozturk --- .../nextcloud/utils/CalendarEventManager.kt | 17 +++--- .../com/nextcloud/utils/ContactManager.kt | 17 +++--- .../extensions/SearchResultEntryExtensions.kt | 4 ++ .../ui/fragment/UnifiedSearchFragment.kt | 52 ++++++++++++++----- .../interfaces/UnifiedSearchListInterface.kt | 2 +- 5 files changed, 61 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 3504b02b0c..3c62565ead 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -13,6 +13,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.CalendarContract +import com.nextcloud.utils.extensions.createdAt import com.nextcloud.utils.extensions.showToast import com.owncloud.android.R import com.owncloud.android.lib.common.SearchResultEntry @@ -22,21 +23,21 @@ import com.owncloud.android.utils.PermissionUtil.checkSelfPermission class CalendarEventManager(private val context: Context) { fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { - val createdAt = searchResult.attributes["createdAt"]?.toLongOrNull()?.times(1000L) val haveReadCalendarPermission = checkSelfPermission(context, Manifest.permission.READ_CALENDAR) - val eventId: Long? = if (haveReadCalendarPermission && createdAt != null) { + if (!haveReadCalendarPermission) { + listInterface.checkPermission(searchResult) + return + } + + val createdAt = searchResult.createdAt() + val eventId: Long? = if (createdAt != null) { getCalendarEventId(searchResult.title, createdAt) } else { null } if (eventId == null) { - val messageId = if (haveReadCalendarPermission) { - R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device - } else { - R.string.unified_search_fragment_calendar_permission_needed_redirecting_web - } - context.showToast(messageId) + context.showToast(R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device) listInterface.onSearchResultClicked(searchResult) } else { val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventId) diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index ae0798093a..8b1afc8531 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -12,6 +12,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.ContactsContract +import com.nextcloud.utils.extensions.displayName import com.nextcloud.utils.extensions.showToast import com.owncloud.android.R import com.owncloud.android.lib.common.SearchResultEntry @@ -21,21 +22,21 @@ import com.owncloud.android.utils.PermissionUtil.checkSelfPermission class ContactManager(private val context: Context) { fun openContact(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { - val displayName = searchResult.attributes["displayName"] val haveReadContactsPermission = checkSelfPermission(context, Manifest.permission.READ_CONTACTS) - val contactIds = if (haveReadContactsPermission && displayName != null) { + if (!haveReadContactsPermission) { + listInterface.checkPermission(searchResult) + return + } + + val displayName = searchResult.displayName() + val contactIds = if (displayName != null) { getContactId(displayName) } else { listOf() } if (contactIds.isEmpty()) { - val messageId = if (haveReadContactsPermission) { - R.string.unified_search_fragment_contact_cannot_be_found_on_device - } else { - R.string.unified_search_fragment_contact_permission_needed_redirecting_web - } - context.showToast(messageId) + context.showToast(R.string.unified_search_fragment_contact_cannot_be_found_on_device) listInterface.onSearchResultClicked(searchResult) } else { val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactIds.first().toString()) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index 802c836565..6855a65e0f 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -25,3 +25,7 @@ fun SearchResultEntry.getType(): SearchResultEntryType { SearchResultEntryType.Unknown } } + +fun SearchResultEntry.createdAt(): Long? = attributes["createdAt"]?.toLongOrNull()?.times(1000L) + +fun SearchResultEntry.displayName(): String? = attributes["displayName"] diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index 960e8c0300..2745d2a84d 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -10,6 +10,8 @@ package com.owncloud.android.ui.fragment import android.Manifest import android.content.Intent import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -101,7 +103,7 @@ class UnifiedSearchFragment : lateinit var viewThemeUtils: ViewThemeUtils private var listOfHiddenFiles = ArrayList() - + private var searchResultEntry: SearchResultEntry? = null private var showMoreActions = false override fun onCreate(savedInstanceState: Bundle?) { @@ -135,7 +137,6 @@ class UnifiedSearchFragment : setupFileDisplayActivity() setupAdapter() - checkPermissions() } @Deprecated("Deprecated in Java") @@ -144,28 +145,51 @@ class UnifiedSearchFragment : setupSearchView(item) } - private val contactPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> - if (!isGranted) { - DisplayUtils.showSnackMessage(binding.root, R.string.unified_search_fragment_contact_permission_needed) - } - } + override fun checkPermission(searchResultEntry: SearchResultEntry) { + this.searchResultEntry = searchResultEntry - private val calendarPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> - if (!isGranted) { - DisplayUtils.showSnackMessage(binding.root, R.string.unified_search_fragment_calendar_permission_needed) - } - } - - private fun checkPermissions() { if (!checkSelfPermission(requireActivity(), Manifest.permission.READ_CONTACTS)) { + DisplayUtils.showSnackMessage( + binding.root, + R.string.unified_search_fragment_contact_permission_needed_redirecting_web + ) contactPermissionLauncher.launch(Manifest.permission.READ_CONTACTS) } if (!checkSelfPermission(requireActivity(), Manifest.permission.READ_CALENDAR)) { + DisplayUtils.showSnackMessage( + binding.root, + R.string.unified_search_fragment_calendar_permission_needed_redirecting_web + ) calendarPermissionLauncher.launch(Manifest.permission.READ_CALENDAR) } } + @Suppress("MagicNumber") + private fun triggerOnSearchClick() { + searchResultEntry?.let { + Handler(Looper.getMainLooper()).postDelayed({ + onSearchResultClicked(it) + }, 1500) + } + } + + private val contactPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> + if (!isGranted) { + DisplayUtils.showSnackMessage(binding.root, R.string.unified_search_fragment_contact_permission_needed) + triggerOnSearchClick() + } + } + + private val calendarPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> + if (!isGranted) { + DisplayUtils.showSnackMessage(binding.root, R.string.unified_search_fragment_calendar_permission_needed) + triggerOnSearchClick() + } + } + private fun setupSearchView(item: MenuItem) { (item.actionView as? SearchView?)?.run { // Required to align with TextView width. diff --git a/app/src/main/java/com/owncloud/android/ui/interfaces/UnifiedSearchListInterface.kt b/app/src/main/java/com/owncloud/android/ui/interfaces/UnifiedSearchListInterface.kt index e407d92b8e..190c707dc6 100644 --- a/app/src/main/java/com/owncloud/android/ui/interfaces/UnifiedSearchListInterface.kt +++ b/app/src/main/java/com/owncloud/android/ui/interfaces/UnifiedSearchListInterface.kt @@ -11,7 +11,7 @@ import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.ui.unifiedsearch.ProviderID interface UnifiedSearchListInterface { - + fun checkPermission(searchResultEntry: SearchResultEntry) fun onSearchResultClicked(searchResultEntry: SearchResultEntry) fun onLoadMoreClicked(providerID: ProviderID) } From b66aeaba2207002f0f874fb2eeaf769399ffc009 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Jul 2024 15:11:19 +0200 Subject: [PATCH 14/34] Permission check on create Signed-off-by: alperozturk --- .../nextcloud/utils/CalendarEventManager.kt | 16 +++--- .../com/nextcloud/utils/ContactManager.kt | 24 ++++---- .../ui/fragment/UnifiedSearchFragment.kt | 57 +++++-------------- .../interfaces/UnifiedSearchListInterface.kt | 1 - .../owncloud/android/utils/PermissionUtil.kt | 4 ++ app/src/main/res/values/strings.xml | 3 +- 6 files changed, 39 insertions(+), 66 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 3c62565ead..243211f082 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -23,21 +23,21 @@ import com.owncloud.android.utils.PermissionUtil.checkSelfPermission class CalendarEventManager(private val context: Context) { fun openCalendarEvent(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { - val haveReadCalendarPermission = checkSelfPermission(context, Manifest.permission.READ_CALENDAR) - if (!haveReadCalendarPermission) { - listInterface.checkPermission(searchResult) - return - } - + val havePermission = checkSelfPermission(context, Manifest.permission.READ_CALENDAR) val createdAt = searchResult.createdAt() - val eventId: Long? = if (createdAt != null) { + val eventId: Long? = if (havePermission && createdAt != null) { getCalendarEventId(searchResult.title, createdAt) } else { null } if (eventId == null) { - context.showToast(R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device) + val messageId = if (havePermission) { + R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device + } 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) diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index 8b1afc8531..106241674b 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -22,24 +22,24 @@ import com.owncloud.android.utils.PermissionUtil.checkSelfPermission class ContactManager(private val context: Context) { fun openContact(searchResult: SearchResultEntry, listInterface: UnifiedSearchListInterface) { - val haveReadContactsPermission = checkSelfPermission(context, Manifest.permission.READ_CONTACTS) - if (!haveReadContactsPermission) { - listInterface.checkPermission(searchResult) - return - } - + val havePermission = checkSelfPermission(context, Manifest.permission.READ_CONTACTS) val displayName = searchResult.displayName() - val contactIds = if (displayName != null) { - getContactId(displayName) + val contactId: Long? = if (havePermission && displayName != null) { + getContactId(displayName).firstOrNull() } else { - listOf() + null } - if (contactIds.isEmpty()) { - context.showToast(R.string.unified_search_fragment_contact_cannot_be_found_on_device) + if (contactId == null) { + val messageId = if (havePermission) { + R.string.unified_search_fragment_contact_cannot_be_found_on_device + } else { + R.string.unified_search_fragment_permission_needed + } + context.showToast(messageId) listInterface.onSearchResultClicked(searchResult) } else { - val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactIds.first().toString()) + val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactId.toString()) val intent = Intent(Intent.ACTION_VIEW).apply { setData(uri) } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index 2745d2a84d..461df24650 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -10,8 +10,6 @@ package com.owncloud.android.ui.fragment import android.Manifest import android.content.Intent import android.os.Bundle -import android.os.Handler -import android.os.Looper import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -48,7 +46,7 @@ import com.owncloud.android.ui.unifiedsearch.UnifiedSearchSection import com.owncloud.android.ui.unifiedsearch.UnifiedSearchViewModel import com.owncloud.android.ui.unifiedsearch.filterOutHiddenFiles import com.owncloud.android.utils.DisplayUtils -import com.owncloud.android.utils.PermissionUtil.checkSelfPermission +import com.owncloud.android.utils.PermissionUtil import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject @@ -103,8 +101,11 @@ class UnifiedSearchFragment : lateinit var viewThemeUtils: ViewThemeUtils private var listOfHiddenFiles = ArrayList() - private var searchResultEntry: SearchResultEntry? = null private var showMoreActions = false + private val permissions = arrayOf( + Manifest.permission.READ_CONTACTS, + Manifest.permission.READ_CALENDAR + ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -137,6 +138,7 @@ class UnifiedSearchFragment : setupFileDisplayActivity() setupAdapter() + checkPermissions() } @Deprecated("Deprecated in Java") @@ -145,48 +147,17 @@ class UnifiedSearchFragment : setupSearchView(item) } - override fun checkPermission(searchResultEntry: SearchResultEntry) { - this.searchResultEntry = searchResultEntry - - if (!checkSelfPermission(requireActivity(), Manifest.permission.READ_CONTACTS)) { - DisplayUtils.showSnackMessage( - binding.root, - R.string.unified_search_fragment_contact_permission_needed_redirecting_web - ) - contactPermissionLauncher.launch(Manifest.permission.READ_CONTACTS) - } - - if (!checkSelfPermission(requireActivity(), Manifest.permission.READ_CALENDAR)) { - DisplayUtils.showSnackMessage( - binding.root, - R.string.unified_search_fragment_calendar_permission_needed_redirecting_web - ) - calendarPermissionLauncher.launch(Manifest.permission.READ_CALENDAR) + private fun checkPermissions() { + if (!PermissionUtil.checkPermissions(requireContext(), permissions)) { + permissionLauncher.launch(permissions) } } - @Suppress("MagicNumber") - private fun triggerOnSearchClick() { - searchResultEntry?.let { - Handler(Looper.getMainLooper()).postDelayed({ - onSearchResultClicked(it) - }, 1500) - } - } - - private val contactPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> - if (!isGranted) { - DisplayUtils.showSnackMessage(binding.root, R.string.unified_search_fragment_contact_permission_needed) - triggerOnSearchClick() - } - } - - private val calendarPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> - if (!isGranted) { - DisplayUtils.showSnackMessage(binding.root, R.string.unified_search_fragment_calendar_permission_needed) - triggerOnSearchClick() + 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) } } diff --git a/app/src/main/java/com/owncloud/android/ui/interfaces/UnifiedSearchListInterface.kt b/app/src/main/java/com/owncloud/android/ui/interfaces/UnifiedSearchListInterface.kt index 190c707dc6..e6751abc28 100644 --- a/app/src/main/java/com/owncloud/android/ui/interfaces/UnifiedSearchListInterface.kt +++ b/app/src/main/java/com/owncloud/android/ui/interfaces/UnifiedSearchListInterface.kt @@ -11,7 +11,6 @@ import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.ui.unifiedsearch.ProviderID interface UnifiedSearchListInterface { - fun checkPermission(searchResultEntry: SearchResultEntry) fun onSearchResultClicked(searchResultEntry: SearchResultEntry) fun onLoadMoreClicked(providerID: ProviderID) } diff --git a/app/src/main/java/com/owncloud/android/utils/PermissionUtil.kt b/app/src/main/java/com/owncloud/android/utils/PermissionUtil.kt index e6e6f7b77f..1d85050e18 100644 --- a/app/src/main/java/com/owncloud/android/utils/PermissionUtil.kt +++ b/app/src/main/java/com/owncloud/android/utils/PermissionUtil.kt @@ -95,6 +95,10 @@ object PermissionUtil { else -> checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) } + fun checkPermissions(context: Context, permissions: Array): Boolean = permissions.all { + ActivityCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED + } + /** * Request relevant external storage permission depending on SDK, if needed. * diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8dc02cca6d..e1e6d3d627 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1213,8 +1213,7 @@ The calendar event cannot be found on your device. Redirecting to web… Contact cannot be found on your device. Redirecting to web… - Permission to access contacts is required to open them. Redirecting to web… - Permission to access calendar is required to view events. Redirecting to web… + Permissions are required to open search result otherwise it will redirecting to web… Permission to access contacts is required to open them. Permission to access calendar is required to view events. From 99f8390f280f7ca5c8c1ec7470ffb7c3d431a570 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Jul 2024 15:24:42 +0200 Subject: [PATCH 15/34] Fix code analytics Signed-off-by: alperozturk --- .../java/com/nextcloud/model/SearchResultEntryType.kt | 7 ++++++- .../java/com/nextcloud/utils/CalendarEventManager.kt | 2 +- .../main/java/com/nextcloud/utils/ContactManager.kt | 2 +- .../utils/extensions/SearchResultEntryExtensions.kt | 1 + .../android/ui/fragment/UnifiedSearchFragment.kt | 5 +---- app/src/main/res/values/strings.xml | 10 +++++----- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt index 1de05af7f7..de7fc2f850 100644 --- a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt +++ b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt @@ -10,7 +10,12 @@ package com.nextcloud.model import com.owncloud.android.R enum class SearchResultEntryType { - CalendarEvent, Folder, Note, Contact, Deck, Unknown; + CalendarEvent, + Folder, + Note, + Contact, + Deck, + Unknown; fun getIconId(): Int? { return when (this) { diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 243211f082..9f90847a15 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -33,7 +33,7 @@ class CalendarEventManager(private val context: Context) { if (eventId == null) { val messageId = if (havePermission) { - R.string.unified_search_fragment_calendar_event_cannot_be_found_on_device + R.string.unified_search_fragment_calendar_event_not_found } else { R.string.unified_search_fragment_permission_needed } diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index 106241674b..803beafbff 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -32,7 +32,7 @@ class ContactManager(private val context: Context) { if (contactId == null) { val messageId = if (havePermission) { - R.string.unified_search_fragment_contact_cannot_be_found_on_device + R.string.unified_search_fragment_contact_not_found } else { R.string.unified_search_fragment_permission_needed } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index 6855a65e0f..6042c13d73 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -26,6 +26,7 @@ fun SearchResultEntry.getType(): SearchResultEntryType { } } +@Suppress("MagicNumber") fun SearchResultEntry.createdAt(): Long? = attributes["createdAt"]?.toLongOrNull()?.times(1000L) fun SearchResultEntry.displayName(): String? = attributes["displayName"] diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index 461df24650..0297b5c717 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -102,10 +102,6 @@ class UnifiedSearchFragment : private var listOfHiddenFiles = ArrayList() private var showMoreActions = false - private val permissions = arrayOf( - Manifest.permission.READ_CONTACTS, - Manifest.permission.READ_CALENDAR - ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -148,6 +144,7 @@ class UnifiedSearchFragment : } private fun checkPermissions() { + val permissions = arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.READ_CALENDAR) if (!PermissionUtil.checkPermissions(requireContext(), permissions)) { permissionLauncher.launch(permissions) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e1e6d3d627..39e9e6261c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1211,9 +1211,9 @@ Resharing is not allowed during secure file drop - The calendar event cannot be found on your device. Redirecting to web… - Contact cannot be found on your device. Redirecting to web… - Permissions are required to open search result otherwise it will redirecting to web… - Permission to access contacts is required to open them. - Permission to access calendar is required to view events. + Event not found. Redirecting to web… + Contact not found. Redirecting to web… + Permissions are required to open search result otherwise it will redirected to web… + Contact permission needed to open contact in device + Calendar permission needed to open calendar event in device From 2043abf87d63a7c9d804eb32f461389fab3b26f8 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 15 Jul 2024 09:03:24 +0200 Subject: [PATCH 16/34] Fix code analytics Signed-off-by: alperozturk --- app/src/main/res/values/strings.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 39e9e6261c..c566e5a485 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1214,6 +1214,4 @@ Event not found. Redirecting to web… Contact not found. Redirecting to web… Permissions are required to open search result otherwise it will redirected to web… - Contact permission needed to open contact in device - Calendar permission needed to open calendar event in device From 8b2f281b41f1bd39789abdbb16d8eb3b4c0bf6f4 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Jul 2024 14:14:03 +0200 Subject: [PATCH 17/34] Use feature only v30 and above Signed-off-by: alperozturk --- .../utils/extensions/AccountExtensions.kt | 6 ++++ .../ui/adapter/UnifiedSearchItemViewHolder.kt | 29 +++++++++++-------- .../ui/adapter/UnifiedSearchListAdapter.kt | 2 ++ .../ui/fragment/UnifiedSearchFragment.kt | 14 ++++++++- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt index 2f70ada3c9..cc7ce75981 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt @@ -9,6 +9,12 @@ package com.nextcloud.utils.extensions import android.accounts.Account import android.content.Context +import com.nextcloud.client.account.UserAccountManager import com.owncloud.android.R +import com.owncloud.android.lib.resources.status.NextcloudVersion fun Account.isAnonymous(context: Context): Boolean = type.equals(context.getString(R.string.anonymous_account_type)) + +fun UserAccountManager.isServerVersionNewerOrEqual(version: NextcloudVersion): Boolean { + return user.server.version.isNewerOrEqual(version) +} diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt index 09bc0625b2..39f96ffcdb 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt @@ -20,20 +20,21 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.User import com.nextcloud.client.network.ClientFactory 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.datamodel.FileDataStorageManager import com.owncloud.android.lib.common.SearchResultEntry import com.owncloud.android.ui.interfaces.UnifiedSearchListInterface import com.owncloud.android.utils.BitmapUtils -import com.nextcloud.utils.CalendarEventManager -import com.nextcloud.utils.ContactManager -import com.nextcloud.utils.extensions.getType import com.owncloud.android.utils.MimeTypeUtil import com.owncloud.android.utils.glide.CustomGlideStreamLoader import com.owncloud.android.utils.theme.ViewThemeUtils @Suppress("LongParameterList") class UnifiedSearchItemViewHolder( + private val isServerVersionThirtyOrAbove: Boolean, val binding: UnifiedSearchItemBinding, val user: User, val clientFactory: ClientFactory, @@ -90,16 +91,20 @@ class UnifiedSearchItemViewHolder( } private fun searchEntryOnClick(entry: SearchResultEntry, entryType: SearchResultEntryType) { - when (entryType) { - SearchResultEntryType.Contact -> { - contactManager.openContact(entry, listInterface) - } - SearchResultEntryType.CalendarEvent -> { - calendarEventManager.openCalendarEvent(entry, listInterface) - } - else -> { - listInterface.onSearchResultClicked(entry) + if (isServerVersionThirtyOrAbove) { + when (entryType) { + SearchResultEntryType.Contact -> { + contactManager.openContact(entry, listInterface) + } + SearchResultEntryType.CalendarEvent -> { + calendarEventManager.openCalendarEvent(entry, listInterface) + } + else -> { + listInterface.onSearchResultClicked(entry) + } } + } else { + listInterface.onSearchResultClicked(entry) } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt index d88e47c73c..9ff02e939f 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt @@ -33,6 +33,7 @@ import com.owncloud.android.utils.theme.ViewThemeUtils */ @Suppress("LongParameterList") class UnifiedSearchListAdapter( + private val isServerVersionThirtyOrAbove: Boolean, private val storageManager: FileDataStorageManager, private val listInterface: UnifiedSearchListInterface, private val filesAction: UnifiedSearchItemViewHolder.FilesAction, @@ -73,6 +74,7 @@ class UnifiedSearchListAdapter( false ) UnifiedSearchItemViewHolder( + isServerVersionThirtyOrAbove, binding, user, clientFactory, diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index 0297b5c717..7bb14bf51b 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -25,16 +25,19 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager import com.nextcloud.client.account.CurrentAccountProvider +import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.core.AsyncRunner import com.nextcloud.client.di.Injectable import com.nextcloud.client.di.ViewModelFactory import com.nextcloud.client.network.ClientFactory +import com.nextcloud.utils.extensions.isServerVersionNewerOrEqual import com.owncloud.android.R import com.owncloud.android.databinding.ListFragmentBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.SearchResultEntry 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.adapter.UnifiedSearchItemViewHolder import com.owncloud.android.ui.adapter.UnifiedSearchListAdapter @@ -100,6 +103,9 @@ class UnifiedSearchFragment : @Inject lateinit var viewThemeUtils: ViewThemeUtils + @Inject + lateinit var accountManager: UserAccountManager + private var listOfHiddenFiles = ArrayList() private var showMoreActions = false @@ -134,9 +140,14 @@ class UnifiedSearchFragment : setupFileDisplayActivity() setupAdapter() - checkPermissions() + if (isServerVersionThirtyOrAbove()) { + checkPermissions() + } } + private fun isServerVersionThirtyOrAbove(): Boolean = + accountManager.isServerVersionNewerOrEqual(NextcloudVersion.nextcloud_30) + @Deprecated("Deprecated in Java") override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { val item = menu.findItem(R.id.action_search) @@ -248,6 +259,7 @@ class UnifiedSearchFragment : private fun setupAdapter() { val gridLayoutManager = GridLayoutManager(requireContext(), 1) adapter = UnifiedSearchListAdapter( + isServerVersionThirtyOrAbove(), storageManager, this, this, From 4bddc4f35102fd6ec9cdc278e292e79f87a65ac7 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Jul 2024 14:25:31 +0200 Subject: [PATCH 18/34] Give user to the better feedback Signed-off-by: alperozturk --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c566e5a485..d601172bff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1211,7 +1211,7 @@ Resharing is not allowed during secure file drop - Event not found. Redirecting to web… - Contact not found. Redirecting to web… + Event not found, you can always sync to update. Redirecting to web… + Contact not found, you can always sync to update. Redirecting to web… Permissions are required to open search result otherwise it will redirected to web… From 6e91559e64c08dbaf8f32881db572dfd27b31b70 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Jul 2024 14:42:04 +0200 Subject: [PATCH 19/34] Add getPhoneNumberById and getEmailById Signed-off-by: alperozturk --- .../com/nextcloud/utils/ContactManager.kt | 68 ++++++++++++++++++- .../extensions/SearchResultEntryExtensions.kt | 5 ++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index 803beafbff..369ad53c8b 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -13,6 +13,8 @@ import android.content.Intent import android.net.Uri import android.provider.ContactsContract import com.nextcloud.utils.extensions.displayName +import com.nextcloud.utils.extensions.email +import com.nextcloud.utils.extensions.phoneNumber import com.nextcloud.utils.extensions.showToast import com.owncloud.android.R import com.owncloud.android.lib.common.SearchResultEntry @@ -25,7 +27,12 @@ class ContactManager(private val context: Context) { val havePermission = checkSelfPermission(context, Manifest.permission.READ_CONTACTS) val displayName = searchResult.displayName() val contactId: Long? = if (havePermission && displayName != null) { - getContactId(displayName).firstOrNull() + val contactIds = getContactIds(displayName) + if (contactIds.size > 1) { + getContactId(searchResult, contactIds) + } else { + contactIds.firstOrNull() + } } else { null } @@ -47,7 +54,64 @@ class ContactManager(private val context: Context) { } } - private fun getContactId(displayName: String): List { + private fun getContactId(searchResult: SearchResultEntry, contactIds: List): Long? { + val email = searchResult.email() + val phoneNumber = searchResult.phoneNumber() + + for (contactId in contactIds) { + val targetEmail = getEmailById(contactId) + val targetPhoneNumber = getPhoneNumberById(contactId) + if (targetEmail == email && targetPhoneNumber == phoneNumber) { + return contactId + } + } + + return null + } + + private fun getEmailById(contactId: Long): String? { + var email: String? = null + val cursor = context.contentResolver.query( + ContactsContract.CommonDataKinds.Email.CONTENT_URI, + arrayOf(ContactsContract.CommonDataKinds.Email.ADDRESS), + "${ContactsContract.CommonDataKinds.Email.CONTACT_ID} = ?", + arrayOf(contactId.toString()), + null + ) + + cursor?.use { + if (it.moveToFirst()) { + val emailIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS) + if (emailIndex != -1) { + email = it.getString(emailIndex) + } + } + } + return email + } + + private fun getPhoneNumberById(contactId: Long): String? { + var phoneNumber: String? = null + val cursor = context.contentResolver.query( + ContactsContract.CommonDataKinds.Phone.CONTENT_URI, + arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER), + "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?", + arrayOf(contactId.toString()), + null + ) + + cursor?.use { + if (it.moveToFirst()) { + val phoneIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER) + if (phoneIndex != -1) { + phoneNumber = it.getString(phoneIndex) + } + } + } + return phoneNumber + } + + private fun getContactIds(displayName: String): List { val result = arrayListOf() val projection = arrayOf(ContactsContract.Contacts._ID) val selection = "${ContactsContract.Contacts.DISPLAY_NAME} = ?" diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index 6042c13d73..81ec5b9251 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -30,3 +30,8 @@ fun SearchResultEntry.getType(): SearchResultEntryType { fun SearchResultEntry.createdAt(): Long? = attributes["createdAt"]?.toLongOrNull()?.times(1000L) fun SearchResultEntry.displayName(): String? = attributes["displayName"] + +fun SearchResultEntry.email(): String? = attributes["email"] + +fun SearchResultEntry.phoneNumber(): String? = attributes["phoneNumber"] + From 62c2c6fb0fcf9ad0316e3d6f7106cbc922d98491 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Jul 2024 14:51:03 +0200 Subject: [PATCH 20/34] Fix code analytics Signed-off-by: alperozturk --- .../nextcloud/utils/extensions/SearchResultEntryExtensions.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index 81ec5b9251..7b18253dfd 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -34,4 +34,3 @@ fun SearchResultEntry.displayName(): String? = attributes["displayName"] fun SearchResultEntry.email(): String? = attributes["email"] fun SearchResultEntry.phoneNumber(): String? = attributes["phoneNumber"] - From 10550b53e90cb44c9f0436c91c7b0c0b77976f32 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Jul 2024 15:05:39 +0200 Subject: [PATCH 21/34] Refactor Signed-off-by: alperozturk --- .../com/nextcloud/utils/ContactManager.kt | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index 369ad53c8b..c2b81c0288 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -27,11 +27,8 @@ class ContactManager(private val context: Context) { val havePermission = checkSelfPermission(context, Manifest.permission.READ_CONTACTS) val displayName = searchResult.displayName() val contactId: Long? = if (havePermission && displayName != null) { - val contactIds = getContactIds(displayName) - if (contactIds.size > 1) { - getContactId(searchResult, contactIds) - } else { - contactIds.firstOrNull() + getContactIds(displayName).let { contactIds -> + if (contactIds.size > 1) getContactId(searchResult, contactIds) else contactIds.firstOrNull() } } else { null @@ -58,11 +55,11 @@ class ContactManager(private val context: Context) { val email = searchResult.email() val phoneNumber = searchResult.phoneNumber() - for (contactId in contactIds) { - val targetEmail = getEmailById(contactId) - val targetPhoneNumber = getPhoneNumberById(contactId) + contactIds.forEach { + val targetEmail = getEmailById(it) + val targetPhoneNumber = getPhoneNumberById(it) if (targetEmail == email && targetPhoneNumber == phoneNumber) { - return contactId + return it } } @@ -70,45 +67,51 @@ class ContactManager(private val context: Context) { } private fun getEmailById(contactId: Long): String? { - var email: String? = null + 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, - arrayOf(ContactsContract.CommonDataKinds.Email.ADDRESS), - "${ContactsContract.CommonDataKinds.Email.CONTACT_ID} = ?", - arrayOf(contactId.toString()), + projection, + selection, + selectionArgs, null ) cursor?.use { - if (it.moveToFirst()) { - val emailIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS) - if (emailIndex != -1) { - email = it.getString(emailIndex) - } + val emailIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS) + while (cursor.moveToNext()) { + result = cursor.getString(emailIndex) } } - return email + + return result } private fun getPhoneNumberById(contactId: Long): String? { - var phoneNumber: String? = null + 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, - arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER), - "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?", - arrayOf(contactId.toString()), + projection, + selection, + selectionArgs, null ) cursor?.use { - if (it.moveToFirst()) { - val phoneIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER) - if (phoneIndex != -1) { - phoneNumber = it.getString(phoneIndex) - } + val phoneIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER) + while (cursor.moveToNext()) { + result = cursor.getString(phoneIndex) } } - return phoneNumber + + return result } private fun getContactIds(displayName: String): List { From 97c6c95de5c0722866b0284d9f936890a73dee37 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 16 Jul 2024 15:08:31 +0200 Subject: [PATCH 22/34] Cover edge case for empty email and phone number for multiple contact that have same display_name Signed-off-by: alperozturk --- app/src/main/java/com/nextcloud/utils/ContactManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index c2b81c0288..491aca066c 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -56,8 +56,8 @@ class ContactManager(private val context: Context) { val phoneNumber = searchResult.phoneNumber() contactIds.forEach { - val targetEmail = getEmailById(it) - val targetPhoneNumber = getPhoneNumberById(it) + val targetEmail = getEmailById(it) ?: "" + val targetPhoneNumber = getPhoneNumberById(it) ?: "" if (targetEmail == email && targetPhoneNumber == phoneNumber) { return it } From 033c0734301838fcde2517a07a242137595ee63d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 26 Jul 2024 10:58:02 +0200 Subject: [PATCH 23/34] Add fallback icon for events, make SearchResultEntry extensions private Signed-off-by: alperozturk --- .../nextcloud/model/SearchResultEntryType.kt | 4 ++-- .../nextcloud/utils/CalendarEventManager.kt | 4 +++- .../com/nextcloud/utils/ContactManager.kt | 9 ++++++--- .../extensions/SearchResultEntryExtensions.kt | 9 --------- .../ui/adapter/UnifiedSearchItemViewHolder.kt | 16 ++++++---------- app/src/main/res/drawable/ic_find_in_page.xml | 19 +++++++++++++++++++ 6 files changed, 36 insertions(+), 25 deletions(-) create mode 100644 app/src/main/res/drawable/ic_find_in_page.xml diff --git a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt index de7fc2f850..75e1529160 100644 --- a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt +++ b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt @@ -17,14 +17,14 @@ enum class SearchResultEntryType { Deck, Unknown; - fun getIconId(): Int? { + 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 -> null + else -> R.drawable.ic_find_in_page } } } diff --git a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt index 9f90847a15..a609071d26 100644 --- a/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt +++ b/app/src/main/java/com/nextcloud/utils/CalendarEventManager.kt @@ -13,7 +13,6 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.CalendarContract -import com.nextcloud.utils.extensions.createdAt import com.nextcloud.utils.extensions.showToast import com.owncloud.android.R import com.owncloud.android.lib.common.SearchResultEntry @@ -74,3 +73,6 @@ class CalendarEventManager(private val context: Context) { return null } } + +@Suppress("MagicNumber") +private fun SearchResultEntry.createdAt(): Long? = attributes["createdAt"]?.toLongOrNull()?.times(1000L) diff --git a/app/src/main/java/com/nextcloud/utils/ContactManager.kt b/app/src/main/java/com/nextcloud/utils/ContactManager.kt index 491aca066c..c956e17f64 100644 --- a/app/src/main/java/com/nextcloud/utils/ContactManager.kt +++ b/app/src/main/java/com/nextcloud/utils/ContactManager.kt @@ -12,9 +12,6 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.ContactsContract -import com.nextcloud.utils.extensions.displayName -import com.nextcloud.utils.extensions.email -import com.nextcloud.utils.extensions.phoneNumber import com.nextcloud.utils.extensions.showToast import com.owncloud.android.R import com.owncloud.android.lib.common.SearchResultEntry @@ -139,3 +136,9 @@ class ContactManager(private val context: Context) { return result } } + +private fun SearchResultEntry.displayName(): String? = attributes["displayName"] + +private fun SearchResultEntry.email(): String? = attributes["email"] + +private fun SearchResultEntry.phoneNumber(): String? = attributes["phoneNumber"] diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index 7b18253dfd..802c836565 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -25,12 +25,3 @@ fun SearchResultEntry.getType(): SearchResultEntryType { SearchResultEntryType.Unknown } } - -@Suppress("MagicNumber") -fun SearchResultEntry.createdAt(): Long? = attributes["createdAt"]?.toLongOrNull()?.times(1000L) - -fun SearchResultEntry.displayName(): String? = attributes["displayName"] - -fun SearchResultEntry.email(): String? = attributes["email"] - -fun SearchResultEntry.phoneNumber(): String? = attributes["phoneNumber"] diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt index 39f96ffcdb..69054c3ed2 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt @@ -34,7 +34,7 @@ import com.owncloud.android.utils.theme.ViewThemeUtils @Suppress("LongParameterList") class UnifiedSearchItemViewHolder( - private val isServerVersionThirtyOrAbove: Boolean, + private val supportsOpeningCalendarContactsLocally: Boolean, val binding: UnifiedSearchItemBinding, val user: User, val clientFactory: ClientFactory, @@ -91,14 +91,16 @@ class UnifiedSearchItemViewHolder( } private fun searchEntryOnClick(entry: SearchResultEntry, entryType: SearchResultEntryType) { - if (isServerVersionThirtyOrAbove) { + if (supportsOpeningCalendarContactsLocally) { when (entryType) { SearchResultEntryType.Contact -> { contactManager.openContact(entry, listInterface) } + SearchResultEntryType.CalendarEvent -> { calendarEventManager.openCalendarEvent(entry, listInterface) } + else -> { listInterface.onSearchResultClicked(entry) } @@ -114,17 +116,11 @@ class UnifiedSearchItemViewHolder( mimetype: String? ): Drawable { val iconId = entryType.run { - getIconId() + iconId() } val defaultDrawable = MimeTypeUtil.getFileTypeIcon(mimetype, entry.title, context, viewThemeUtils) - - val drawable: Drawable = if (iconId == null) { - defaultDrawable - } else { - ResourcesCompat.getDrawable(context.resources, iconId, null) ?: defaultDrawable - } - + val drawable: Drawable = ResourcesCompat.getDrawable(context.resources, iconId, null) ?: defaultDrawable return viewThemeUtils.platform.tintDrawable(context, drawable, ColorRole.PRIMARY) } diff --git a/app/src/main/res/drawable/ic_find_in_page.xml b/app/src/main/res/drawable/ic_find_in_page.xml new file mode 100644 index 0000000000..aba6de3c4e --- /dev/null +++ b/app/src/main/res/drawable/ic_find_in_page.xml @@ -0,0 +1,19 @@ + + + + + + + From 730af29fe39e44bbe2bbe9838a6d3d8b3c652925 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 29 Jul 2024 14:27:44 +0200 Subject: [PATCH 24/34] Remove extension for version check Signed-off-by: alperozturk --- .../nextcloud/utils/extensions/AccountExtensions.kt | 6 ------ .../android/ui/adapter/UnifiedSearchListAdapter.kt | 4 ++-- .../android/ui/fragment/UnifiedSearchFragment.kt | 12 +++++++----- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt index cc7ce75981..2f70ada3c9 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/AccountExtensions.kt @@ -9,12 +9,6 @@ package com.nextcloud.utils.extensions import android.accounts.Account import android.content.Context -import com.nextcloud.client.account.UserAccountManager import com.owncloud.android.R -import com.owncloud.android.lib.resources.status.NextcloudVersion fun Account.isAnonymous(context: Context): Boolean = type.equals(context.getString(R.string.anonymous_account_type)) - -fun UserAccountManager.isServerVersionNewerOrEqual(version: NextcloudVersion): Boolean { - return user.server.version.isNewerOrEqual(version) -} diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt index 9ff02e939f..4e269eabca 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchListAdapter.kt @@ -33,7 +33,7 @@ import com.owncloud.android.utils.theme.ViewThemeUtils */ @Suppress("LongParameterList") class UnifiedSearchListAdapter( - private val isServerVersionThirtyOrAbove: Boolean, + private val supportsOpeningCalendarContactsLocally: Boolean, private val storageManager: FileDataStorageManager, private val listInterface: UnifiedSearchListInterface, private val filesAction: UnifiedSearchItemViewHolder.FilesAction, @@ -74,7 +74,7 @@ class UnifiedSearchListAdapter( false ) UnifiedSearchItemViewHolder( - isServerVersionThirtyOrAbove, + supportsOpeningCalendarContactsLocally, binding, user, clientFactory, diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index 7bb14bf51b..782f3049dc 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -30,7 +30,6 @@ import com.nextcloud.client.core.AsyncRunner import com.nextcloud.client.di.Injectable import com.nextcloud.client.di.ViewModelFactory import com.nextcloud.client.network.ClientFactory -import com.nextcloud.utils.extensions.isServerVersionNewerOrEqual import com.owncloud.android.R import com.owncloud.android.databinding.ListFragmentBinding import com.owncloud.android.datamodel.FileDataStorageManager @@ -140,13 +139,16 @@ class UnifiedSearchFragment : setupFileDisplayActivity() setupAdapter() - if (isServerVersionThirtyOrAbove()) { + if (supportsOpeningCalendarContactsLocally()) { checkPermissions() } } - private fun isServerVersionThirtyOrAbove(): Boolean = - accountManager.isServerVersionNewerOrEqual(NextcloudVersion.nextcloud_30) + private fun supportsOpeningCalendarContactsLocally(): Boolean = + storageManager + .getCapability(accountManager.user) + .version + .isNewerOrEqual(NextcloudVersion.nextcloud_30) @Deprecated("Deprecated in Java") override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -259,7 +261,7 @@ class UnifiedSearchFragment : private fun setupAdapter() { val gridLayoutManager = GridLayoutManager(requireContext(), 1) adapter = UnifiedSearchListAdapter( - isServerVersionThirtyOrAbove(), + supportsOpeningCalendarContactsLocally(), storageManager, this, this, From 2775f1c84b3d5fd92f7bd8b39d650a1511562e88 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 29 Jul 2024 14:28:41 +0200 Subject: [PATCH 25/34] Fix code analytics Signed-off-by: alperozturk --- .../android/ui/fragment/UnifiedSearchFragment.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index 782f3049dc..a158c80529 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -144,11 +144,10 @@ class UnifiedSearchFragment : } } - private fun supportsOpeningCalendarContactsLocally(): Boolean = - storageManager - .getCapability(accountManager.user) - .version - .isNewerOrEqual(NextcloudVersion.nextcloud_30) + private fun supportsOpeningCalendarContactsLocally(): Boolean = storageManager + .getCapability(accountManager.user) + .version + .isNewerOrEqual(NextcloudVersion.nextcloud_30) @Deprecated("Deprecated in Java") override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { From f72c37be29ba248ab0da7cdb00a39c57190e521b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 29 Jul 2024 15:03:22 +0200 Subject: [PATCH 26/34] Fix Signed-off-by: alperozturk --- .../com/nextcloud/client/jobs/upload/FileUploadHelper.kt | 3 +++ .../android/ui/activity/ReceiveExternalFilesActivity.java | 7 ++++++- .../owncloud/android/ui/activity/UploadFilesActivity.java | 8 +++++++- app/src/main/res/values/strings.xml | 1 + 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index c97fe56641..05d9bd1d55 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -64,6 +64,9 @@ class FileUploadHelper { companion object { private val TAG = FileUploadWorker::class.java.simpleName + @Suppress("MagicNumber") + const val MAX_FILE_COUNT = 500 + val mBoundListeners = HashMap() private var instance: FileUploadHelper? = null diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index aab5490acf..60d8c944a7 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -880,7 +880,7 @@ public class ReceiveExternalFilesActivity extends FileActivity } private boolean somethingToUpload() { - return (mStreamsToUpload != null && mStreamsToUpload.size() > 0 && mStreamsToUpload.get(0) != null || + return (mStreamsToUpload != null && !mStreamsToUpload.isEmpty() && mStreamsToUpload.get(0) != null || mUploadFromTmpFile); } @@ -904,6 +904,11 @@ public class ReceiveExternalFilesActivity extends FileActivity return; } + if (mStreamsToUpload.size() > FileUploadHelper.MAX_FILE_COUNT) { + DisplayUtils.showSnackMessage(this, R.string.max_file_count_warning_message); + return; + } + UriUploader uploader = new UriUploader( this, mStreamsToUpload, diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java index da59551317..a2c36b8824 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -30,6 +30,7 @@ import android.widget.TextView; import com.nextcloud.client.account.User; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.utils.extensions.ActivityExtensionsKt; @@ -486,7 +487,7 @@ public class UploadFilesActivity extends DrawerActivity implements LocalFileList mCurrentDialog = null; } - if (hasEnoughSpaceAvailable) { + if (!hasEnoughSpaceAvailable) { // return the list of files (success) Intent data = new Intent(); @@ -653,6 +654,11 @@ public class UploadFilesActivity extends DrawerActivity implements LocalFileList @Override public void onConfirmation(String 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)) { // return the list of selected files to the caller activity (success), // signaling that they should be moved to the ownCloud folder, instead of copied diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08c9921cc3..b8eca07ba9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -129,6 +129,7 @@ File selected for upload not found. Please check whether the file exists. Could not copy file to a temporary folder. Try to resend it. Upload option: + You have reached the maximum file upload limit. Please upload fewer than 500 files at a time. Move file to %1$s folder Keep file in source folder Delete file from source folder From fac75084ae79a1f85b4039d235197ff199af70e5 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Tue, 30 Jul 2024 02:44:58 +0000 Subject: [PATCH 27/34] Fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-fi-rFI/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 19f6f7ea78..4ae5ba3800 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -841,6 +841,7 @@ GNU yleinen lisenssi, versio 2 Paikallinen tallennustila täynnä Tiedostoa ei voitu kopioida paikalliseen tallennustilaan Kansion lukitseminen epäonnistui + Käyttäjä peruutti latauksen Salaus on mahdollista vain kun >= Android 5.0 Riittämätön tila estää tiedostojen kopioinnin %1$s kansioon. Haluatko sen sijaan siirtää ne sinne? Skannaa asiakirja kameralla diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index c012619c93..013e07d3a1 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -677,6 +677,7 @@ Atualmente o envio de notificações está indisponível. Não foi possível ler o código QR! A pasta não pode ser encontrada, a operação de sincronização foi cancelada + Não foi possível encontrar o arquivo para upload Experimente %1$s em seu dispositivo! Quero convidar você a usar %1$s em seu dispositivo.\nBaixe daqui: %2$s %1$s ou %2$s From e2caaa182ebd0aa4a69dcc921748c1a6995391a0 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 30 Jul 2024 09:49:22 +0200 Subject: [PATCH 28/34] Remove test check Signed-off-by: alperozturk --- .../com/owncloud/android/ui/activity/UploadFilesActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java index a2c36b8824..3bbdfe7752 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -487,7 +487,7 @@ public class UploadFilesActivity extends DrawerActivity implements LocalFileList mCurrentDialog = null; } - if (!hasEnoughSpaceAvailable) { + if (hasEnoughSpaceAvailable) { // return the list of files (success) Intent data = new Intent(); From 7eac3e66cc8835742b0db4504881d02698535b55 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 30 Jul 2024 14:43:43 +0200 Subject: [PATCH 29/34] Fix Signed-off-by: alperozturk --- .../jobs/download/FileDownloadWorker.kt | 2 +- .../client/jobs/upload/FileUploadWorker.kt | 2 +- .../java/com/nextcloud/model/WorkerState.kt | 7 +- .../ui/preview/PreviewImageActivity.kt | 92 ++++++++++++------- .../model/PreviewImageActivityState.kt | 12 +++ app/src/main/res/values/strings.xml | 1 + 6 files changed, 80 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/com/owncloud/android/ui/preview/model/PreviewImageActivityState.kt diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt index 089a094c6d..1e9a264fb3 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt @@ -159,7 +159,7 @@ class FileDownloadWorker( } private fun setIdleWorkerState() { - WorkerStateLiveData.instance().setWorkState(WorkerState.Idle) + WorkerStateLiveData.instance().setWorkState(WorkerState.Idle(getCurrentFile())) } private fun removePendingDownload(accountName: String?) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt index fb4fe4b7e0..ccafb20f50 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt @@ -124,7 +124,7 @@ class FileUploadWorker( } private fun setIdleWorkerState() { - WorkerStateLiveData.instance().setWorkState(WorkerState.Idle) + WorkerStateLiveData.instance().setWorkState(WorkerState.Idle(currentUploadFileOperation?.file)) } @Suppress("ReturnCount") diff --git a/app/src/main/java/com/nextcloud/model/WorkerState.kt b/app/src/main/java/com/nextcloud/model/WorkerState.kt index ebca644172..998f601f3f 100644 --- a/app/src/main/java/com/nextcloud/model/WorkerState.kt +++ b/app/src/main/java/com/nextcloud/model/WorkerState.kt @@ -8,11 +8,12 @@ package com.nextcloud.model import com.nextcloud.client.account.User +import com.owncloud.android.datamodel.OCFile import com.owncloud.android.db.OCUpload import com.owncloud.android.operations.DownloadFileOperation sealed class WorkerState { - object Idle : WorkerState() - class Download(var user: User?, var currentDownload: DownloadFileOperation?) : WorkerState() - class Upload(var user: User?, var uploads: List) : WorkerState() + data class Idle(var currentFile: OCFile?) : WorkerState() + data class Download(var user: User?, var currentDownload: DownloadFileOperation?) : WorkerState() + data class Upload(var user: User?, var uploads: List) : WorkerState() } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index e535519213..5671b84ad1 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -1,15 +1,8 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2020-2024 Andy Scherzinger - * SPDX-FileCopyrightText: 2023 Alper Ozturk - * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-FileCopyrightText: 2019 Tobias Kaminsky - * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-FileCopyrightText: 2016 ownCloud Inc. - * SPDX-FileCopyrightText: 2015 María Asensio Valverde - * SPDX-FileCopyrightText: 2013 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) + * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.owncloud.android.ui.preview @@ -56,6 +49,7 @@ import com.owncloud.android.ui.activity.FileDisplayActivity import com.owncloud.android.ui.fragment.FileFragment import com.owncloud.android.ui.fragment.GalleryFragment import com.owncloud.android.ui.fragment.OCFileListFragment +import com.owncloud.android.ui.preview.model.PreviewImageActivityState import com.owncloud.android.utils.MimeTypeUtil import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import java.io.Serializable @@ -65,16 +59,18 @@ import kotlin.math.max /** * Holds a swiping gallery where image files contained in an Nextcloud directory are shown. */ +@Suppress("TooManyFunctions") class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnRemoteOperationListener, Injectable { private var livePhotoFile: OCFile? = null private var viewPager: ViewPager2? = null private var previewImagePagerAdapter: PreviewImagePagerAdapter? = null private var savedPosition = 0 private var hasSavedPosition = false - private var requestWaitingForBinder = false private var downloadFinishReceiver: DownloadFinishReceiver? = null private var fullScreenAnchorView: View? = null + private var isDownloadWorkStarted = false + private var screenState = PreviewImageActivityState.Idle @Inject lateinit var preferences: AppPreferences @@ -115,7 +111,10 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR // to keep our UI controls visibility in line with system bars visibility 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() } @@ -181,7 +180,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR if (position == 0 && !file.isDown) { // this is necessary because mViewPager.setCurrentItem(0) just after setting the // adapter does not result in a call to #onPageSelected(0) - requestWaitingForBinder = true + screenState = PreviewImageActivityState.WaitingForBinder } } @@ -241,7 +240,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR override fun onSaveInstanceState(outState: Bundle) { 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) } @@ -274,26 +273,50 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR private fun observeWorkerState() { WorkerStateLiveData.instance().observe(this) { state: WorkerState? -> - if (state is WorkerState.Download) { - Log_OC.d(TAG, "Download worker started") - isDownloadWorkStarted = true + when(state) { + is WorkerState.Download -> { + Log_OC.d(TAG, "Download worker started") + isDownloadWorkStarted = true - if (requestWaitingForBinder) { - requestWaitingForBinder = false - Log_OC.d( - TAG, - "Simulating reselection of current page after connection " + - "of download binder" - ) - selectPage(viewPager?.currentItem) + if (screenState == PreviewImageActivityState.WaitingForBinder) { + 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 } - } else { - Log_OC.d(TAG, "Download worker stopped") - isDownloadWorkStarted = false } } } + private fun selectPageOnDownload() { + screenState = PreviewImageActivityState.Idle + Log_OC.d( + TAG, + "Simulating reselection of current page after connection " + + "of download binder" + ) + selectPage(viewPager?.currentItem) + } + + private fun onImageDownloadComplete(downloadedFile: OCFile?) { + dismissLoadingDialog() + screenState = PreviewImageActivityState.Idle + file = downloadedFile + startEditImageActivity() + } + override fun onResume() { super.onResume() @@ -356,7 +379,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR val currentFile = previewImagePagerAdapter?.getFileAt(position) if (!isDownloadWorkStarted) { - requestWaitingForBinder = true + screenState = PreviewImageActivityState.WaitingForBinder } else { if (currentFile != null) { if (currentFile.isEncrypted && !currentFile.isDown && @@ -457,14 +480,21 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR fun startImageEditor(file: OCFile) { if (file.isDown) { - val editImageIntent = Intent(this, EditImageActivity::class.java) - editImageIntent.putExtra(EditImageActivity.EXTRA_FILE, file) - startActivity(editImageIntent) + startEditImageActivity() } else { + showLoadingDialog(getString(R.string.preview_image_downloading_image_for_edit)) + screenState = PreviewImageActivityState.Edit requestForDownload(file, EditImageActivity.OPEN_IMAGE_EDITOR) } } + private fun startEditImageActivity() { + val intent = Intent(this, EditImageActivity::class.java).apply { + putExtra(EditImageActivity.EXTRA_FILE, file) + } + startActivity(intent) + } + override fun onBrowsedDownTo(folder: OCFile) { // TODO Auto-generated method stub } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/model/PreviewImageActivityState.kt b/app/src/main/java/com/owncloud/android/ui/preview/model/PreviewImageActivityState.kt new file mode 100644 index 0000000000..6db7e83e68 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/preview/model/PreviewImageActivityState.kt @@ -0,0 +1,12 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.preview.model + +enum class PreviewImageActivityState { + WaitingForBinder, Edit, Idle +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d601172bff..5781babe23 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -419,6 +419,7 @@ File is currently locked by another user or process and therefore not deletable. Please try again later. Sorry + Downloading image to start the edit screen, please wait… Image preview Unable to show image There is no local file to preview From 7dfc56c7e217a3b7ce36da4d1538b0c41af2e055 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 30 Jul 2024 14:45:01 +0200 Subject: [PATCH 30/34] Fix code analytics Signed-off-by: alperozturk --- .../com/owncloud/android/ui/preview/PreviewImageActivity.kt | 2 +- .../android/ui/preview/model/PreviewImageActivityState.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 5671b84ad1..8859d8a0ea 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -273,7 +273,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR private fun observeWorkerState() { WorkerStateLiveData.instance().observe(this) { state: WorkerState? -> - when(state) { + when (state) { is WorkerState.Download -> { Log_OC.d(TAG, "Download worker started") isDownloadWorkStarted = true diff --git a/app/src/main/java/com/owncloud/android/ui/preview/model/PreviewImageActivityState.kt b/app/src/main/java/com/owncloud/android/ui/preview/model/PreviewImageActivityState.kt index 6db7e83e68..cce5191d43 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/model/PreviewImageActivityState.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/model/PreviewImageActivityState.kt @@ -8,5 +8,7 @@ package com.owncloud.android.ui.preview.model enum class PreviewImageActivityState { - WaitingForBinder, Edit, Idle + WaitingForBinder, + Edit, + Idle } From 60571635f4e04dbffbf86fc8c439213939c51c60 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 30 Jul 2024 16:08:42 +0200 Subject: [PATCH 31/34] Fix remove and adapter position Signed-off-by: alperozturk --- .../com/owncloud/android/ui/preview/PreviewImageActivity.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 8859d8a0ea..6393bc95bb 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -258,8 +258,12 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR } } - viewPager?.setCurrentItem(nextPosition, true) + if (user.isPresent) { + initViewPager(user.get()) + } + previewImagePagerAdapter?.delete(deletePosition) + viewPager?.setCurrentItem(nextPosition, true) } else if (operation is SynchronizeFileOperation) { onSynchronizeFileOperationFinish(result) } From f5011c31400297cab1eb1d01f26ae05187c9ce41 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 30 Jul 2024 16:23:13 +0200 Subject: [PATCH 32/34] When delete last image refresh previous screen Signed-off-by: alperozturk --- .../owncloud/android/ui/preview/PreviewImageActivity.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 6393bc95bb..34f3297994 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -253,7 +253,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR previewImagePagerAdapter?.let { if (it.itemCount <= 1) { - finish() + backToDisplayActivity() return } } @@ -262,8 +262,8 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR initViewPager(user.get()) } - previewImagePagerAdapter?.delete(deletePosition) viewPager?.setCurrentItem(nextPosition, true) + previewImagePagerAdapter?.delete(deletePosition) } else if (operation is SynchronizeFileOperation) { onSynchronizeFileOperationFinish(result) } @@ -343,6 +343,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR } private fun backToDisplayActivity() { + sendRefreshSearchEventBroadcast() finish() } @@ -355,7 +356,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR } startActivity(intent) - finish() + backToDisplayActivity() } override fun showDetails(file: OCFile, activeTab: Int) { From 8a42462c0c32d41d422abe3b31692d7cd3062476 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 30 Jul 2024 16:33:25 +0200 Subject: [PATCH 33/34] Update license header Signed-off-by: alperozturk --- .../com/owncloud/android/ui/preview/PreviewImageActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 34f3297994..e4fefd7bf9 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.owncloud.android.ui.preview From ed7d0aaa11fa841d575ef743d3b74ed44daa448c Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 30 Jul 2024 16:40:05 +0200 Subject: [PATCH 34/34] Give feedback to user for file existence and readiness Signed-off-by: alperozturk --- .../android/ui/preview/PreviewImageActivity.kt | 11 +++++++++++ app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 13 insertions(+) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index e4fefd7bf9..7ebd4af61d 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -50,6 +50,7 @@ import com.owncloud.android.ui.fragment.FileFragment import com.owncloud.android.ui.fragment.GalleryFragment 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings import java.io.Serializable @@ -494,6 +495,16 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR } 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) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5781babe23..765d842628 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -419,6 +419,8 @@ File is currently locked by another user or process and therefore not deletable. Please try again later. Sorry + File is not exist + File is not downloaded Downloading image to start the edit screen, please wait… Image preview Unable to show image