diff --git a/build.gradle b/build.gradle index 63f2345579..b26989083c 100644 --- a/build.gradle +++ b/build.gradle @@ -17,8 +17,8 @@ buildscript { allprojects { ext { projectGroupId = 'org.mariotaku.twidere' - projectVersionCode = 509 - projectVersionName = '4.1.0' + projectVersionCode = 510 + projectVersionName = '4.1.1' globalCompileSdkVersion = 29 globalBuildToolsVersion = "29.0.3" diff --git a/twidere/build.gradle b/twidere/build.gradle index ee1612385b..cf04d627a5 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -185,8 +185,6 @@ dependencies { implementation 'com.commonsware.cwac:layouts:0.4.3' implementation 'com.rengwuxian.materialedittext:library:2.1.4' implementation 'com.pnikosis:materialish-progress:1.7' - implementation "com.github.mariotaku:MessageBubbleView:${libVersions['MessageBubbleView']}" - implementation 'com.github.mariotaku:DragSortListView:0.6.1' implementation 'com.github.uucky:ColorPicker-Android:0.9.7@aar' implementation "pl.droidsonroids.gif:android-gif-drawable:${libVersions['AndroidGIFDrawable']}" implementation 'com.sprylab.android.texturevideoview:texturevideoview:1.2.1' @@ -216,7 +214,8 @@ dependencies { /** Custom dependencies **/ - implementation 'com.github.mariotaku:MessageBubbleView:1.6' + implementation 'com.github.mariotaku:DragSortListView:0.6.1' + implementation "com.github.mariotaku:MessageBubbleView:${libVersions['MessageBubbleView']}" implementation 'com.github.mariotaku:DragSortListView:0.6.1' implementation "com.github.mariotaku.MediaViewerLibrary:base:${libVersions['MediaViewerLibrary']}" implementation "com.github.mariotaku.MediaViewerLibrary:subsample-image-view:${libVersions['MediaViewerLibrary']}" diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt index 67ece98e42..170307f4fb 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt @@ -1032,6 +1032,12 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, } return true } + R.id.copy_url -> { + val uri = LinkCreator.getUserWebLink(user) + ClipboardUtils.setText(context, uri.toString()) + Toast.makeText(context, R.string.message_toast_link_copied_to_clipboard, Toast.LENGTH_SHORT).show() + return true + } R.id.qr_code -> { executeAfterFragmentResumed { val df = UserQrDialogFragment() diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/ConversationLoader.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/ConversationLoader.kt index bfd66daeac..b95c2eb9a5 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/ConversationLoader.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/ConversationLoader.kt @@ -38,7 +38,6 @@ import org.mariotaku.twidere.extension.atto.filter import org.mariotaku.twidere.extension.atto.firstElementOrNull import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable import org.mariotaku.twidere.extension.model.api.toParcelable -import org.mariotaku.twidere.extension.model.isOfficial import org.mariotaku.twidere.extension.model.makeOriginal import org.mariotaku.twidere.extension.model.newMicroBlogInstance import org.mariotaku.twidere.model.AccountDetails @@ -153,39 +152,34 @@ class ConversationLoader( } if (loadReplies || noSinceMaxId || sinceId != null && sinceSortId > status.sort_id) { // Load replies - var repliesLoaded = false -// try { -// if (details.type == AccountType.TWITTER) { -// if (noSinceMaxId) { -// statuses.addAll(loadTwitterWebReplies(details, twitter)) -// } -// repliesLoaded = true -// } -// } catch (e: MicroBlogException) { -// // Ignore -// } - if (!repliesLoaded) { - val query = SearchQuery() - query.count(100) + try { if (details.type == AccountType.TWITTER) { - query.query("to:${status.user_screen_name} since_id:${status.id}") - } else { - query.query("@${status.user_screen_name}") + // try to load thread + statuses.addAll(loadTwitterWebReplies(details, twitter)) } - query.sinceId(sinceId ?: status.id) - try { - val queryResult = twitter.search(query) - val firstId = queryResult.firstOrNull()?.id - if (firstId != null) { - nextPagination = SinceMaxPagination.sinceId(firstId, 0) - } - queryResult.filterTo(statuses) { it.inReplyToStatusId == status.id } - } catch (e: MicroBlogException) { - // Ignore for now + } catch (e: MicroBlogException) { + // Ignore + } + val query = SearchQuery() + query.count(100) + if (details.type == AccountType.TWITTER) { + query.query("to:${status.user_screen_name} since_id:${status.id}") + } else { + query.query("@${status.user_screen_name}") + } + query.sinceId(sinceId ?: status.id) + try { + val queryResult = twitter.search(query) + val firstId = queryResult.firstOrNull()?.id + if (firstId != null) { + nextPagination = SinceMaxPagination.sinceId(firstId, 0) } + queryResult.filterTo(statuses) { it.inReplyToStatusId == status.id } + } catch (e: MicroBlogException) { + // Ignore for now } } - return statuses.mapTo(PaginatedArrayList()) { + return statuses.distinctBy { it.id }.mapTo(PaginatedArrayList()) { it.toParcelable(details, profileImageSize) }.apply { this.nextPage = nextPagination diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/GetMessagesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/GetMessagesTask.kt index b0eaf1d5d3..3e9dd431f2 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/GetMessagesTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/GetMessagesTask.kt @@ -60,6 +60,7 @@ import org.mariotaku.twidere.util.DataStoreUtils import org.mariotaku.twidere.util.UriUtils import org.mariotaku.twidere.util.content.ContentResolverUtils import java.util.* +import kotlin.collections.ArrayList import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.filter @@ -171,19 +172,27 @@ class GetMessagesTask( }.filter { it.sender != null && it.recipient != null } val insertMessages = arrayListOf() - val conversations = hashMapOf() - val conversationIds = hashSetOf() - result.forEach { - conversationIds.add(ParcelableMessageUtils.incomingConversationId(it.senderId, it.recipientId)) - } + val conversationIds = result.map { + if (it.senderId == details.key.id) { + ParcelableMessageUtils.outgoingConversationId(it.senderId, it.recipientId) + } else { + ParcelableMessageUtils.incomingConversationId(it.senderId, it.recipientId) + } + }.distinct().toHashSet() + + val conversations = hashMapOf() conversations.addLocalConversations(context, accountKey, conversationIds) + // remove duplicate conversations upgrade from version 4.0.9 + val distinct = distinctLocalConversations(context, accountKey, result.map { it.id }.toSet()) + .distinct() + .filter { !it.startsWith(details.key.id) || it == details.key.id } result.forEachIndexed { i, dm -> addConversationMessage(insertMessages, conversations, details, dm, i, list.size, - false, profileImageSize, updateLastRead) + dm.senderId == details.key.id, profileImageSize, updateLastRead) } - return DatabaseUpdateData(conversations.values, insertMessages) + return DatabaseUpdateData(conversations.values, insertMessages, distinct) } @@ -490,6 +499,26 @@ class GetMessagesTask( } } + + internal fun distinctLocalConversations(context: Context, accountKey: UserKey, messageIds: Set): ArrayList { + val where = Expression.and(Expression.inArgs(Messages.MESSAGE_ID, messageIds.size), + Expression.equalsArgs(Conversations.ACCOUNT_KEY)).sql + val whereArgs = messageIds.toTypedArray() + accountKey.toString() + val result = arrayListOf() + context.contentResolver.queryReference(Messages.CONTENT_URI, Messages.COLUMNS, + where, whereArgs, null)?.use { (cur) -> + val indices = ObjectCursor.indicesFrom(cur, ParcelableMessage::class.java) + cur.moveToFirst() + while (!cur.isAfterLast) { + val conversationId = cur.getString(indices[Messages.CONVERSATION_ID]) + result.add(conversationId) + indices.newObject(cur) + cur.moveToNext() + } + } + return result + } + internal fun MutableMap.addLocalConversations(context: Context, accountKey: UserKey, conversationIds: Set) { val newIds = conversationIds.filterNot { it in this.keys } diff --git a/twidere/src/main/res/menu/menu_user_profile.xml b/twidere/src/main/res/menu/menu_user_profile.xml index fef2060414..e60e79de39 100644 --- a/twidere/src/main/res/menu/menu_user_profile.xml +++ b/twidere/src/main/res/menu/menu_user_profile.xml @@ -86,6 +86,10 @@ android:id="@id/open_in_browser" android:icon="@drawable/ic_action_web" android:title="@string/action_open_in_browser"/> + @android:color/transparent true - shortEdges true @style/Widget.Twidere.Viewer.ActionBar