From f2c612be79f801fc2e87b8062db3e8b8a5deb1a9 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski <mail@justjanne.de> Date: Wed, 27 Nov 2024 13:53:38 +0100 Subject: [PATCH] fix: navigation between chats --- .../de/kuschku/quasseldroid/Quasseldroid.kt | 3 +- .../quasseldroid/dagger/ActivityModule.kt | 80 ++-- .../quasseldroid/dagger/AppComponent.kt | 9 +- .../service/QuasselNotificationBackend.kt | 4 +- .../service/QuasselServiceModule.kt | 6 +- .../quasseldroid/ui/chat/ChatActivity.kt | 352 +++++++++--------- .../ui/chat/ChatActivityModule.kt | 4 +- .../ui/chat/ChatFragmentProvider.kt | 14 +- .../quasseldroid/ui/chat/ToolbarFragment.kt | 28 +- .../chat/add/create/ChannelCreateFragment.kt | 33 +- .../create/ChannelCreateFragmentProvider.kt | 6 +- .../ui/chat/add/join/ChannelJoinFragment.kt | 40 +- .../add/join/ChannelJoinFragmentProvider.kt | 6 +- .../ui/chat/add/query/QueryCreateFragment.kt | 49 ++- .../add/query/QueryCreateFragmentProvider.kt | 6 +- .../ui/chat/archive/ArchiveFragment.kt | 38 +- .../chat/archive/ArchiveFragmentProvider.kt | 6 +- .../chat/buffers/BufferViewConfigFragment.kt | 78 ++-- .../ui/chat/input/AutoCompleteHelper.kt | 48 +-- .../ui/chat/input/ChatlineFragment.kt | 26 +- .../ui/chat/messages/MessageListFragment.kt | 118 +++--- .../chat/messages/QuasselMessageRenderer.kt | 76 +++- .../ui/chat/nicks/NickListFragment.kt | 36 +- .../ui/chat/topic/TopicFragment.kt | 33 +- .../ui/chat/topic/TopicFragmentProvider.kt | 6 +- .../about/AboutFragmentProvider.kt | 6 +- .../client/ClientSettingsFragmentProvider.kt | 6 +- .../crash/CrashFragmentProvider.kt | 6 +- .../license/LicenseFragmentProvider.kt | 6 +- .../whitelist/WhitelistFragmentProvider.kt | 6 +- .../ui/coresettings/CoreSettingsFragment.kt | 26 +- .../CoreSettingsFragmentProvider.kt | 6 +- .../aliasitem/AliasItemFragment.kt | 28 +- .../aliasitem/AliasItemFragmentProvider.kt | 6 +- .../aliaslist/AliasListAdapter.kt | 8 +- .../aliaslist/AliasListFragment.kt | 5 +- .../aliaslist/AliasListFragmentProvider.kt | 6 +- .../chatlist/ChatListBaseFragment.kt | 21 +- .../ChatlistCreateFragmentProvider.kt | 6 +- .../chatlist/ChatlistEditFragmentProvider.kt | 6 +- .../highlightlist/HighlightListFragment.kt | 30 +- .../HighlightListFragmentProvider.kt | 6 +- .../HighlightRuleFragmentProvider.kt | 6 +- .../identity/IdentityBaseFragment.kt | 9 +- .../IdentityCreateFragmentProvider.kt | 6 +- .../identity/IdentityEditFragmentProvider.kt | 6 +- .../ignoreitem/IgnoreItemFragmentProvider.kt | 6 +- .../ignorelist/IgnoreListFragment.kt | 5 +- .../ignorelist/IgnoreListFragmentProvider.kt | 6 +- .../network/NetworkBaseFragment.kt | 17 +- .../network/NetworkCreateFragmentProvider.kt | 6 +- .../network/NetworkEditFragmentProvider.kt | 6 +- .../networkconfig/NetworkConfigFragment.kt | 5 +- .../NetworkConfigFragmentProvider.kt | 6 +- .../NetworkServerFragmentProvider.kt | 6 +- .../passwordchange/PasswordChangeFragment.kt | 5 +- .../PasswordChangeFragmentProvider.kt | 6 +- .../certificate/CertificateInfoFragment.kt | 5 +- .../CertificateInfoFragmentProvider.kt | 6 +- .../ui/info/channel/ChannelInfoFragment.kt | 33 +- .../channel/ChannelInfoFragmentProvider.kt | 6 +- .../ui/info/channellist/ChannelListAdapter.kt | 18 +- .../info/channellist/ChannelListFragment.kt | 106 ++++-- .../ChannelListFragmentProvider.kt | 6 +- .../ui/info/core/CoreInfoFragment.kt | 50 ++- .../ui/info/core/CoreInfoFragmentProvider.kt | 6 +- .../ui/info/user/ChannelAdapter.kt | 11 +- .../ui/info/user/UserInfoFragment.kt | 57 ++- .../ui/info/user/UserInfoFragmentProvider.kt | 6 +- .../edit/AccountEditFragmentProvider.kt | 6 +- .../AccountSelectionFragmentProvider.kt | 6 +- .../setup/AccountSetupFragmentProvider.kt | 10 +- .../setup/core/CoreSetupFragmentProvider.kt | 12 +- .../network/NetworkSetupFragmentProvider.kt | 8 +- .../setup/network/NetworkSetupNetworkSlide.kt | 9 +- .../ui/setup/user/UserSetupActivity.kt | 8 +- .../setup/user/UserSetupFragmentProvider.kt | 10 +- .../ui/setup/user/UserSetupIdentitySlide.kt | 7 +- .../util/irc/format/ContentFormatter.kt | 15 +- .../util/irc/format/IrcFormatDeserializer.kt | 4 +- .../irc/format/model/FormatDescription.kt | 5 +- .../util/irc/format/model/FormatInfo.kt | 5 +- .../util/irc/format/model/IrcFormat.kt | 45 ++- .../util/irc/format/spans/ChannelLinkSpan.kt | 8 +- .../util/irc/format/spans/QuasselURLSpan.kt | 19 +- .../util/listener/AutocompleteTextHandler.kt | 25 ++ .../util/listener/AutocompleteTextListener.kt | 8 + .../util/listener/LinkClickHandler.kt | 61 +++ .../util/listener/LinkClickListener.kt | 27 ++ .../util/listener/ListenerModule.kt | 13 + .../util/listener/QuasselLinkClickListener.kt | 27 ++ .../util/ui/BetterLinkMovementMethod.java | 12 +- .../util/ui/presenter/BufferPresenter.kt | 7 +- .../irc/format/IrcFormatDeserializerTest.kt | 5 + 94 files changed, 1306 insertions(+), 756 deletions(-) create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/listener/AutocompleteTextHandler.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/listener/AutocompleteTextListener.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/listener/LinkClickHandler.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/listener/LinkClickListener.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/listener/ListenerModule.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/listener/QuasselLinkClickListener.kt diff --git a/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt b/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt index 79b88d15d..f7e1a76f0 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/Quasseldroid.kt @@ -20,6 +20,7 @@ package de.kuschku.quasseldroid import android.content.Context +import dagger.android.AndroidInjector import dagger.android.support.DaggerApplication import de.kuschku.quasseldroid.app.AppDelegate import de.kuschku.quasseldroid.app.QuasseldroidReleaseDelegate @@ -27,7 +28,7 @@ import de.kuschku.quasseldroid.dagger.DaggerAppComponent import de.kuschku.quasseldroid.util.ui.LocaleHelper open class Quasseldroid : DaggerApplication() { - override fun applicationInjector() = DaggerAppComponent.builder().create(this) + override fun applicationInjector(): AndroidInjector<Quasseldroid> = DaggerAppComponent.factory().create(this) open val delegate: AppDelegate = QuasseldroidReleaseDelegate(this) override fun onCreate() { diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt index 841c00314..381e5bfac 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt @@ -102,172 +102,172 @@ import de.kuschku.quasseldroid.ui.setup.user.UserSetupActivity import de.kuschku.quasseldroid.ui.setup.user.UserSetupFragmentProvider @Module -abstract class ActivityModule { +interface ActivityModule { @ActivityScope @ContributesAndroidInjector(modules = [ChatActivityModule::class, ChatFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindChatActivity(): ChatActivity + fun bindChatActivity(): ChatActivity @ActivityScope @ContributesAndroidInjector(modules = [ArchiveFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindArchiveActivity(): ArchiveActivity + fun bindArchiveActivity(): ArchiveActivity // Info @ActivityScope @ContributesAndroidInjector(modules = [UserInfoFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindUserInfoActivity(): UserInfoActivity + fun bindUserInfoActivity(): UserInfoActivity @ActivityScope @ContributesAndroidInjector(modules = [ChannelInfoFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindChannelInfoActivity(): ChannelInfoActivity + fun bindChannelInfoActivity(): ChannelInfoActivity @ActivityScope @ContributesAndroidInjector(modules = [CoreInfoFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindCoreInfoActivity(): CoreInfoActivity + fun bindCoreInfoActivity(): CoreInfoActivity @ActivityScope @ContributesAndroidInjector(modules = [TopicFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindTopicActivity(): TopicActivity + fun bindTopicActivity(): TopicActivity @ActivityScope @ContributesAndroidInjector(modules = [ChannelListFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindChannelListActivity(): ChannelListActivity + fun bindChannelListActivity(): ChannelListActivity @ActivityScope @ContributesAndroidInjector(modules = [CertificateInfoFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindCertificateInfoActivity(): CertificateInfoActivity + fun bindCertificateInfoActivity(): CertificateInfoActivity // Add @ActivityScope @ContributesAndroidInjector(modules = [ChannelCreateFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindChannelCreateActivity(): ChannelCreateActivity + fun bindChannelCreateActivity(): ChannelCreateActivity @ActivityScope @ContributesAndroidInjector(modules = [ChannelJoinFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindChannelJoinActivity(): ChannelJoinActivity + fun bindChannelJoinActivity(): ChannelJoinActivity @ActivityScope @ContributesAndroidInjector(modules = [QueryCreateFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindQueryCreateActivity(): QueryCreateActivity + fun bindQueryCreateActivity(): QueryCreateActivity // Client Settings @ActivityScope @ContributesAndroidInjector(modules = [ClientSettingsFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindClientSettingsActivity(): ClientSettingsActivity + fun bindClientSettingsActivity(): ClientSettingsActivity @ActivityScope @ContributesAndroidInjector(modules = [WhitelistFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindWhitelistActivity(): WhitelistActivity + fun bindWhitelistActivity(): WhitelistActivity @ActivityScope @ContributesAndroidInjector(modules = [CrashFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindCrashActivity(): CrashActivity + fun bindCrashActivity(): CrashActivity @ActivityScope @ContributesAndroidInjector(modules = [AboutFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindAboutActivity(): AboutActivity + fun bindAboutActivity(): AboutActivity @ActivityScope @ContributesAndroidInjector(modules = [LicenseFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindLicenseActivity(): LicenseActivity + fun bindLicenseActivity(): LicenseActivity // Core Settings @ActivityScope @ContributesAndroidInjector(modules = [CoreSettingsFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindCoreSettingsActivity(): CoreSettingsActivity + fun bindCoreSettingsActivity(): CoreSettingsActivity @ActivityScope @ContributesAndroidInjector(modules = [NetworkCreateFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindNetworkCreateActivity(): NetworkCreateActivity + fun bindNetworkCreateActivity(): NetworkCreateActivity @ActivityScope @ContributesAndroidInjector(modules = [NetworkEditFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindNetworkEditActivity(): NetworkEditActivity + fun bindNetworkEditActivity(): NetworkEditActivity @ActivityScope @ContributesAndroidInjector(modules = [NetworkServerFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindNetworkServerActivity(): NetworkServerActivity + fun bindNetworkServerActivity(): NetworkServerActivity @ActivityScope @ContributesAndroidInjector(modules = [IdentityCreateFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindIdentityCreateActivity(): IdentityCreateActivity + fun bindIdentityCreateActivity(): IdentityCreateActivity @ActivityScope @ContributesAndroidInjector(modules = [IdentityEditFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindIdentityEditActivity(): IdentityEditActivity + fun bindIdentityEditActivity(): IdentityEditActivity @ActivityScope @ContributesAndroidInjector(modules = [ChatlistCreateFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindChatListCreateActivity(): ChatlistCreateActivity + fun bindChatListCreateActivity(): ChatlistCreateActivity @ActivityScope @ContributesAndroidInjector(modules = [ChatlistEditFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindChatListEditActivity(): ChatlistEditActivity + fun bindChatListEditActivity(): ChatlistEditActivity @ActivityScope @ContributesAndroidInjector(modules = [IgnoreListFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindIgnoreListActivity(): IgnoreListActivity + fun bindIgnoreListActivity(): IgnoreListActivity @ActivityScope @ContributesAndroidInjector(modules = [IgnoreItemFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindIgnoreItemActivity(): IgnoreItemActivity + fun bindIgnoreItemActivity(): IgnoreItemActivity @ActivityScope @ContributesAndroidInjector(modules = [HighlightListFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindHighlightListActivity(): HighlightListActivity + fun bindHighlightListActivity(): HighlightListActivity @ActivityScope @ContributesAndroidInjector(modules = [HighlightRuleFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindHighlightRuleActivity(): HighlightRuleActivity + fun bindHighlightRuleActivity(): HighlightRuleActivity @ActivityScope @ContributesAndroidInjector(modules = [AliasListFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindAliasListActivity(): AliasListActivity + fun bindAliasListActivity(): AliasListActivity @ActivityScope @ContributesAndroidInjector(modules = [AliasItemFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindAliasItemActivity(): AliasItemActivity + fun bindAliasItemActivity(): AliasItemActivity @ActivityScope @ContributesAndroidInjector(modules = [NetworkConfigFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindNetworkConfigActivity(): NetworkConfigActivity + fun bindNetworkConfigActivity(): NetworkConfigActivity @ActivityScope @ContributesAndroidInjector(modules = [PasswordChangeFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindPasswordChangeActivity(): PasswordChangeActivity + fun bindPasswordChangeActivity(): PasswordChangeActivity // Setup @ActivityScope @ContributesAndroidInjector(modules = [AccountSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindAccountSetupActivity(): AccountSetupActivity + fun bindAccountSetupActivity(): AccountSetupActivity @ActivityScope @ContributesAndroidInjector(modules = [AccountSelectionFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindAccountSelectionActivity(): AccountSelectionActivity + fun bindAccountSelectionActivity(): AccountSelectionActivity @ActivityScope @ContributesAndroidInjector(modules = [AccountEditFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindAccountEditActivity(): AccountEditActivity + fun bindAccountEditActivity(): AccountEditActivity @ActivityScope @ContributesAndroidInjector(modules = [UserSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindUserSetupActivity(): UserSetupActivity + fun bindUserSetupActivity(): UserSetupActivity @ActivityScope @ContributesAndroidInjector(modules = [NetworkSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindNetworkSetupActivity(): NetworkSetupActivity + fun bindNetworkSetupActivity(): NetworkSetupActivity @ActivityScope @ContributesAndroidInjector(modules = [CoreSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) - abstract fun bindCoreSetupActivity(): CoreSetupActivity + fun bindCoreSetupActivity(): CoreSetupActivity // Service @ActivityScope @ContributesAndroidInjector(modules = [QuasselServiceModule::class, SettingsModule::class, DatabaseModule::class]) - abstract fun bindQuasselService(): QuasselService + fun bindQuasselService(): QuasselService } diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/AppComponent.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/AppComponent.kt index e070a7bd6..e3a6fdc8f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/dagger/AppComponent.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/AppComponent.kt @@ -23,17 +23,20 @@ import dagger.Component import dagger.android.AndroidInjector import dagger.android.support.AndroidSupportInjectionModule import de.kuschku.quasseldroid.Quasseldroid +import de.kuschku.quasseldroid.util.listener.ListenerModule import javax.inject.Singleton + @Singleton @Component( modules = [ AndroidSupportInjectionModule::class, AppModule::class, - ActivityModule::class + ActivityModule::class, + ListenerModule::class, ] ) interface AppComponent : AndroidInjector<Quasseldroid> { - @Component.Builder - abstract class Builder : AndroidInjector.Builder<Quasseldroid>() + @Component.Factory + interface Factory : AndroidInjector.Factory<Quasseldroid> } diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt index 883ca16ac..225b2ab49 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt @@ -55,6 +55,7 @@ import de.kuschku.quasseldroid.util.helper.letIf import de.kuschku.quasseldroid.util.helper.loadWithFallbacks import de.kuschku.quasseldroid.util.helper.styledAttributes import de.kuschku.quasseldroid.util.irc.format.ContentFormatter +import de.kuschku.quasseldroid.util.listener.LinkClickListener import de.kuschku.quasseldroid.util.ui.drawable.TextDrawable import de.kuschku.quasseldroid.viewmodel.helper.EditorViewModelHelper.Companion.IGNORED_CHARS import org.threeten.bp.Instant @@ -66,6 +67,7 @@ class QuasselNotificationBackend @Inject constructor( private val context: Context, private val database: QuasselDatabase, private val contentFormatter: ContentFormatter, + private val linkClickListener: LinkClickListener, private val notificationHandler: QuasseldroidNotificationManager ) : NotificationManager { private var notificationSettings: NotificationSettings @@ -353,7 +355,7 @@ class QuasselNotificationBackend @Inject constructor( selfColor = selfColor )) } - val (content, _) = contentFormatter.formatContent(it.content, false, false, it.networkId) + val (content, _) = contentFormatter.formatContent(it.content, false, false, it.networkId, linkClickListener) NotificationMessage( messageId = it.messageId, diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselServiceModule.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselServiceModule.kt index bdb1a9e68..f18948de9 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselServiceModule.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselServiceModule.kt @@ -26,10 +26,10 @@ import de.kuschku.quasseldroid.ui.chat.messages.MessageRenderer import de.kuschku.quasseldroid.ui.chat.messages.QuasselMessageRenderer @Module -abstract class QuasselServiceModule { +interface QuasselServiceModule { @Binds - abstract fun bindContext(service: QuasselService): Context + fun bindContext(service: QuasselService): Context @Binds - abstract fun bindMessageRenderer(messageRenderer: QuasselMessageRenderer): MessageRenderer + fun bindMessageRenderer(messageRenderer: QuasselMessageRenderer): MessageRenderer } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt index 59c206299..c1b9366ad 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt @@ -22,14 +22,17 @@ package de.kuschku.quasseldroid.ui.chat import android.Manifest import android.annotation.SuppressLint import android.app.Activity +import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.content.SharedPreferences import android.content.pm.PackageManager +import android.net.Uri import android.os.Build import android.os.Bundle import android.system.ErrnoException import android.text.Html +import android.util.Log import android.view.ActionMode import android.view.Menu import android.view.MenuItem @@ -41,8 +44,6 @@ import androidx.appcompat.app.ActionBarDrawerToggle import androidx.core.app.ActivityCompat import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import com.afollestad.materialdialogs.MaterialDialog @@ -95,6 +96,10 @@ import de.kuschku.quasseldroid.util.backport.OsConstants import de.kuschku.quasseldroid.util.deceptive_networks.DeceptiveNetworkDialog import de.kuschku.quasseldroid.util.helper.* import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer +import de.kuschku.quasseldroid.util.listener.AutocompleteTextHandler +import de.kuschku.quasseldroid.util.listener.AutocompleteTextListener +import de.kuschku.quasseldroid.util.listener.LinkClickHandler +import de.kuschku.quasseldroid.util.listener.LinkClickListener import de.kuschku.quasseldroid.util.missingfeatures.MissingFeature import de.kuschku.quasseldroid.util.missingfeatures.MissingFeaturesDialog import de.kuschku.quasseldroid.util.missingfeatures.RequiredFeatures @@ -110,7 +115,6 @@ import de.kuschku.quasseldroid.viewmodel.helper.ChatViewModelHelper import io.reactivex.BackpressureStrategy import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject -import io.reactivex.subjects.PublishSubject import org.threeten.bp.Instant import org.threeten.bp.ZoneId import org.threeten.bp.format.DateTimeFormatter @@ -122,7 +126,8 @@ import java.security.cert.CertificateNotYetValidException import javax.inject.Inject @SuppressLint("ResourceType") -class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenceChangeListener { +class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenceChangeListener, + AutocompleteTextListener, LinkClickListener { lateinit var binding: ActivityMainBinding @Inject @@ -155,6 +160,12 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc @Inject lateinit var notificationBackend: QuasselNotificationBackend + @Inject + lateinit var autocompleteTextHandler: AutocompleteTextHandler + + @Inject + lateinit var linkClickHandler: LinkClickHandler + lateinit var editorBottomSheet: DragInterceptBottomSheetBehavior<View> private val dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) @@ -180,111 +191,17 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } intent.hasExtra(KEY_BUFFER_ID) -> { - chatViewModel.bufferId.onNext(BufferId(intent.getIntExtra(KEY_BUFFER_ID, -1))) - chatViewModel.bufferOpened.onNext(Unit) + val bufferId = BufferId(intent.getIntExtra(KEY_BUFFER_ID, -1)) if (intent.hasExtra(KEY_ACCOUNT_ID)) { - val accountId = AccountId(intent.getLongExtra(KEY_ACCOUNT_ID, -1L)) - if (accountId != this.accountId) { - resetAccount() - connectToAccount(accountId) - startedSelection = false - connectedAccount = AccountId(-1L) - checkConnection() - recreate() - } + openBuffer( + bufferId, + AccountId(intent.getLongExtra(KEY_ACCOUNT_ID, -1L)) + ) + } else { + openBuffer(bufferId) } } - intent.hasExtra(KEY_AUTOCOMPLETE_TEXT) -> { - chatlineFragment?.editorHelper?.appendText( - intent.getStringExtra(KEY_AUTOCOMPLETE_TEXT), - intent.getStringExtra(KEY_AUTOCOMPLETE_SUFFIX) - ) - binding.drawerLayout.closeDrawers() - } - - intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_CHANNEL) -> { - val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1)) - val channel = intent.getStringExtra(KEY_CHANNEL) ?: "" - - val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false) - - modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement() - .subscribe { - it.orNull()?.also { session -> - val info = session.bufferSyncer.find( - bufferName = channel, - networkId = networkId, - type = Buffer_Type.of(Buffer_Type.ChannelBuffer) - ) - - if (info != null && !forceJoin) { - ChatActivity.launch(this, bufferId = info.bufferId) - } else { - modelHelper.chat.chatToJoin.onNext( - Optional.of( - Pair(networkId, channel) - ) - ) - - session.bufferSyncer.find( - networkId = networkId, - type = Buffer_Type.of(Buffer_Type.StatusBuffer) - )?.let { statusInfo -> - session.rpcHandler.sendInput( - statusInfo, "/join $channel" - ) - } - } - } - } - } - - intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_NICK_NAME) -> { - val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1)) - val channel = intent.getStringExtra(KEY_NICK_NAME) - - val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false) - - modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement() - .subscribe { - it.orNull()?.also { session -> - val info = session.bufferSyncer.find( - bufferName = channel, - networkId = networkId, - type = Buffer_Type.of(Buffer_Type.QueryBuffer) - ) - - if (info != null && !forceJoin) { - ChatActivity.launch(this, bufferId = info.bufferId) - } else { - modelHelper.allBuffers.map { - listOfNotNull(it.find { - it.networkId == networkId && - it.bufferName == channel && - it.type.hasFlag(Buffer_Type.QueryBuffer) - }) - }.filter { - it.isNotEmpty() - }.firstElement().toLiveData().observeForever { - it?.firstOrNull()?.let { info -> - ChatActivity.launch(this, bufferId = info.bufferId) - } - } - - session.bufferSyncer.find( - networkId = networkId, - type = Buffer_Type.of(Buffer_Type.StatusBuffer) - )?.let { statusInfo -> - session.rpcHandler.sendInput( - statusInfo, "/query $channel" - ) - } - } - } - } - } - intent.scheme == "irc" || intent.scheme == "ircs" -> { val uri = intent.data @@ -316,6 +233,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + linkClickHandler.register(this) + autocompleteTextHandler.register(this) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { permissionGranted.onNext( PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission( @@ -420,7 +340,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc R.attr.colorTintNotification ) maxBufferActivity.toLiveData() - .observe(this@ChatActivity, Observer { (activity, hasNotifications) -> + .observe(this@ChatActivity) { (activity, hasNotifications) -> setHomeAsUpIndicator( when { notificationSettings.showAllActivitiesInToolbar && @@ -442,7 +362,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc toggleDefault } ) - }) + } } if (autoCompleteSettings.prefix || autoCompleteSettings.auto) { @@ -467,14 +387,14 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc else Optional.ofNullable(buffers.firstOrNull { it.networkId == NetworkId(-current.id) && it.type.hasFlag(Buffer_Type.StatusBuffer) }) - }.toLiveData().observe(this, Observer { info -> + }.toLiveData().observe(this) { info -> info?.orNull()?.let { - ChatActivity.launch(this, bufferId = it.bufferId) + openBuffer(bufferId = it.bufferId) } - }) + } // User-actionable errors that require immediate action, and should show up as dialog - modelHelper.errors.toLiveData(BackpressureStrategy.BUFFER).observe(this, Observer { error -> + modelHelper.errors.toLiveData(BackpressureStrategy.BUFFER).observe(this) { error -> error?.let { when (it) { is Error.HandshakeError -> it.message.let { @@ -801,7 +721,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } } } - }) + } val isConnected: Observable<Boolean> = modelHelper.connectionProgress.map { (state, _, _) -> state == ConnectionState.CONNECTED @@ -898,7 +818,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc // Show Connection Progress Bar combineLatest(modelHelper.connectionProgress, modelHelper.deceptiveNetwork) - .toLiveData().observe(this, Observer { + .toLiveData().observe(this) { val (connection, deceptive) = it ?: Pair(Triple(ConnectionState.DISCONNECTED, 0, 0), false) val (state, progress, max) = connection when (state) { @@ -949,10 +869,10 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } } } - }) + } // Only show nick list when we’re in a channel bufferId - modelHelper.bufferDataThrottled.toLiveData().observe(this, Observer { + modelHelper.bufferDataThrottled.toLiveData().observe(this) { bufferData = it if (bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) { binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END) @@ -964,7 +884,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } invalidateOptionsMenu() - }) + } editorBottomSheet = DragInterceptBottomSheetBehavior.from(binding.root.findViewById(R.id.fragment_chatline)) @@ -1003,11 +923,17 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc it.isNotEmpty() }.firstElement().toLiveData().observeForever { it?.firstOrNull()?.let { info -> - launch(this, bufferId = info.bufferId) + openBuffer(bufferId = info.bufferId) } } } + override fun onDestroy() { + linkClickHandler.unregister(this) + autocompleteTextHandler.unregister(this) + super.onDestroy() + } + override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) setIntent(intent) @@ -1255,7 +1181,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc startedSelection = true connectedAccount = AccountId(-1L) restoredDrawerState = false - ChatActivity.launch(this, bufferId = BufferId.MAX_VALUE) + openBuffer(bufferId = BufferId.MAX_VALUE) chatViewModel.resetAccount() } @@ -1266,16 +1192,133 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } } + override fun autocompleteText( + text: CharSequence, + suffix: String? + ) { + Log.i("ChatActivity", "autocompleteText text=\"$text\", suffix=\"$suffix\"") + chatlineFragment?.editorHelper?.appendText( + text, + suffix, + ) + binding.drawerLayout.closeDrawers() + } + + override fun openBuffer( + bufferId: BufferId, + accountId: AccountId?, + forceJoin: Boolean + ) { + Log.i("ChatActivity", "openBuffer bufferId=$bufferId, accountId=$accountId, forceJoin=$forceJoin") + chatViewModel.bufferId.onNext(bufferId) + chatViewModel.bufferOpened.onNext(Unit) + if (accountId != null && accountId != this.accountId) { + resetAccount() + connectToAccount(accountId) + startedSelection = false + connectedAccount = AccountId(-1L) + checkConnection() + recreate() + } + } + + override fun openChannel( + networkId: NetworkId, + channel: String, + forceJoin: Boolean + ) { + Log.i("ChatActivity", "openChannel networkId=$networkId, channel=$channel, forceJoin=$forceJoin") + modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement() + .subscribe { + it.orNull()?.also { session -> + val info = session.bufferSyncer.find( + bufferName = channel, + networkId = networkId, + type = Buffer_Type.of(Buffer_Type.ChannelBuffer) + ) + + if (info != null && !forceJoin) { + openBuffer(bufferId = info.bufferId) + } else { + modelHelper.chat.chatToJoin.onNext( + Optional.of( + Pair(networkId, channel) + ) + ) + + session.bufferSyncer.find( + networkId = networkId, + type = Buffer_Type.of(Buffer_Type.StatusBuffer) + )?.let { statusInfo -> + session.rpcHandler.sendInput( + statusInfo, "/join $channel" + ) + } + } + } + } + } + + override fun openDirectMessage( + networkId: NetworkId, + nickName: String, + forceJoin: Boolean + ) { + Log.i("ChatActivity", "openDirectMessage networkId=$networkId, nickName=$nickName, forceJoin=$forceJoin") + modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement() + .subscribe { + it.orNull()?.also { session -> + val info = session.bufferSyncer.find( + bufferName = nickName, + networkId = networkId, + type = Buffer_Type.of(Buffer_Type.QueryBuffer) + ) + + if (info != null && !forceJoin) { + openBuffer(bufferId = info.bufferId) + } else { + modelHelper.allBuffers.map { + listOfNotNull(it.find { + it.networkId == networkId && + it.bufferName == nickName && + it.type.hasFlag(Buffer_Type.QueryBuffer) + }) + }.filter { + it.isNotEmpty() + }.firstElement().toLiveData().observeForever { + it?.firstOrNull()?.let { info -> + openBuffer(bufferId = info.bufferId) + } + } + + session.bufferSyncer.find( + networkId = networkId, + type = Buffer_Type.of(Buffer_Type.StatusBuffer) + )?.let { statusInfo -> + session.rpcHandler.sendInput( + statusInfo, "/query $nickName" + ) + } + } + } + } + } + + override fun openUrl(url: String) { + Log.i("ChatActivity", "openUrl url=$url") + try { + startActivity(Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(url) + }) + } catch (e: ActivityNotFoundException) { + Log.w("ChatActivity", "Unable to open URL: Activity was not found for $url") + } + } + companion object { // Intent keys - private const val KEY_AUTOCOMPLETE_TEXT = "autocomplete_text" - private const val KEY_AUTOCOMPLETE_SUFFIX = "autocomplete_suffix" private const val KEY_BUFFER_ID = "buffer_id" private const val KEY_ACCOUNT_ID = "account_id" - private const val KEY_NETWORK_ID = "network_id" - private const val KEY_CHANNEL = "channel" - private const val KEY_NICK_NAME = "nick_name" - private const val KEY_FORCE_JOIN = "force_join" // Instance state keys private const val KEY_OPEN_BUFFER = "open_buffer" @@ -1284,78 +1327,25 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc private const val KEY_OPEN_DRAWER_START = "open_drawer_start" private const val KEY_OPEN_DRAWER_END = "open_drawer_end" - fun launch( - context: Context, - sharedText: CharSequence? = null, - autoCompleteText: CharSequence? = null, - autoCompleteSuffix: String? = null, - channel: String? = null, - nickName: String? = null, - networkId: NetworkId? = null, - bufferId: BufferId? = null, - accountId: Long? = null, - forceJoin: Boolean? = null - ) = context.startActivity( - intent( - context, - sharedText, - autoCompleteText, - autoCompleteSuffix, - channel, - nickName, - networkId, - bufferId, - accountId, - forceJoin - ) - ) - fun intent( context: Context, - sharedText: CharSequence? = null, - autoCompleteText: CharSequence? = null, - autoCompleteSuffix: String? = null, - channel: String? = null, - nickName: String? = null, - networkId: NetworkId? = null, bufferId: BufferId? = null, accountId: Long? = null, - forceJoin: Boolean? = null ) = Intent(context, ChatActivity::class.java).apply { - if (sharedText != null) { - type = "text/plain" - putExtra(Intent.EXTRA_TEXT, sharedText) - } - if (autoCompleteText != null) { - putExtra(KEY_AUTOCOMPLETE_TEXT, autoCompleteText) - if (autoCompleteSuffix != null) { - putExtra(KEY_AUTOCOMPLETE_SUFFIX, autoCompleteSuffix) - } - } if (bufferId != null) { putExtra(KEY_BUFFER_ID, bufferId.id) if (accountId != null) { putExtra(KEY_ACCOUNT_ID, accountId) } } - if (networkId != null && channel != null) { - putExtra(KEY_NETWORK_ID, networkId.id) - putExtra(KEY_CHANNEL, channel) - } else if (networkId != null && nickName != null) { - putExtra(KEY_NETWORK_ID, networkId.id) - putExtra(KEY_NICK_NAME, nickName) - if (forceJoin != null) { - putExtra(KEY_NICK_NAME, nickName) - } - } } } sealed class PostConnectionState { - object Connecting : PostConnectionState() - object SetupIdentity : PostConnectionState() + data object Connecting : PostConnectionState() + data object SetupIdentity : PostConnectionState() data class MissingFeatures(val features: List<MissingFeature>) : PostConnectionState() - object RequestingPermissions : PostConnectionState() - object Done : PostConnectionState() + data object RequestingPermissions : PostConnectionState() + data object Done : PostConnectionState() } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivityModule.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivityModule.kt index a29bd4342..c088b3657 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivityModule.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivityModule.kt @@ -25,7 +25,7 @@ import de.kuschku.quasseldroid.ui.chat.messages.MessageRenderer import de.kuschku.quasseldroid.ui.chat.messages.QuasselMessageRenderer @Module -abstract class ChatActivityModule { +interface ChatActivityModule { @Binds - abstract fun bindMessageRenderer(messageRenderer: QuasselMessageRenderer): MessageRenderer + fun bindMessageRenderer(messageRenderer: QuasselMessageRenderer): MessageRenderer } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatFragmentProvider.kt index a38d77d20..e1c97f8cd 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatFragmentProvider.kt @@ -29,22 +29,22 @@ import de.kuschku.quasseldroid.ui.chat.messages.MessageListFragment import de.kuschku.quasseldroid.ui.chat.nicks.NickListFragment @Module -abstract class ChatFragmentProvider { +interface ChatFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: ChatActivity): FragmentActivity + fun bindFragmentActivity(activity: ChatActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindBufferViewConfigFragment(): BufferViewConfigFragment + fun bindBufferViewConfigFragment(): BufferViewConfigFragment @ContributesAndroidInjector - abstract fun bindMessageListFragment(): MessageListFragment + fun bindMessageListFragment(): MessageListFragment @ContributesAndroidInjector - abstract fun bindNickListFragment(): NickListFragment + fun bindNickListFragment(): NickListFragment @ContributesAndroidInjector - abstract fun bindToolbarFragment(): ToolbarFragment + fun bindToolbarFragment(): ToolbarFragment @ContributesAndroidInjector - abstract fun bindChatlineFragment(): ChatlineFragment + fun bindChatlineFragment(): ChatlineFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt index 10292d879..fa2b5fe77 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ToolbarFragment.kt @@ -19,15 +19,14 @@ package de.kuschku.quasseldroid.ui.chat +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.widget.AppCompatImageView -import androidx.lifecycle.Observer import com.bumptech.glide.Glide - import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.util.flag.hasFlag @@ -45,6 +44,8 @@ import de.kuschku.quasseldroid.util.helper.setTooltip import de.kuschku.quasseldroid.util.helper.toLiveData import de.kuschku.quasseldroid.util.helper.visibleIf import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.util.ui.SpanFormatter import de.kuschku.quasseldroid.viewmodel.helper.EditorViewModelHelper @@ -71,6 +72,10 @@ class ToolbarFragment : ServiceBoundFragment() { @Inject lateinit var messageSettings: MessageSettings + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + var title: CharSequence? get() = toolbarTitle.text set(value) { @@ -87,6 +92,17 @@ class ToolbarFragment : ServiceBoundFragment() { toolbarSubtitle.visibleIf(value?.isNotEmpty() == true) } + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -99,7 +115,9 @@ class ToolbarFragment : ServiceBoundFragment() { this.actionArea = view.findViewById(R.id.toolbar_action_area) fun colorizeDescription(description: String?) = ircFormatDeserializer.formatString( - description, messageSettings.colorizeMirc + description, + messageSettings.colorizeMirc, + linkClickListener, ) val avatarSize = resources.getDimensionPixelSize(R.dimen.avatar_size_buffer) @@ -125,7 +143,7 @@ class ToolbarFragment : ServiceBoundFragment() { Triple(it.first, it.second, avatarInfo) }.toLiveData() - .observe(viewLifecycleOwner, Observer { + .observe(viewLifecycleOwner) { if (it != null) { val (data, lag, avatarInfo) = it @@ -159,7 +177,7 @@ class ToolbarFragment : ServiceBoundFragment() { } } } - }) + } actionArea.setOnClickListener { val bufferData = modelHelper.bufferData.value diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt index b51d438e6..1686b8622 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt @@ -19,6 +19,7 @@ package de.kuschku.quasseldroid.ui.chat.add.create +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -28,7 +29,6 @@ import android.widget.Button import android.widget.EditText import androidx.appcompat.widget.AppCompatSpinner import androidx.appcompat.widget.SwitchCompat -import androidx.lifecycle.Observer import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.syncables.IrcChannel @@ -43,10 +43,11 @@ import de.kuschku.quasseldroid.ui.chat.add.NetworkAdapter import de.kuschku.quasseldroid.ui.chat.add.NetworkItem import de.kuschku.quasseldroid.util.helper.setDependent import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment import de.kuschku.quasseldroid.viewmodel.helper.QuasselViewModelHelper import io.reactivex.Observable -import io.reactivex.disposables.Disposable import javax.inject.Inject class ChannelCreateFragment : ServiceBoundSettingsFragment() { @@ -62,9 +63,24 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() { @Inject lateinit var modelHelper: QuasselViewModelHelper + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + private var hasSelectedNetwork = false private var networkId = NetworkId(0) + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.add_create, container, false) @@ -105,7 +121,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() { NetworkItem(it.networkId, it.networkName) }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, NetworkItem::name)) } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { if (it != null) { networkAdapter.submitList(it) if (!hasSetNetwork && networkId.isValidId() && it.isNotEmpty()) { @@ -119,7 +135,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() { hasSetNetwork = true } } - }) + } passwordProtected.setDependent(passwordGroup) @@ -156,12 +172,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() { } } - activity?.let { - it.finish() - ChatActivity.launch(it, - bufferId = existingBuffer.bufferId - ) - } + linkClickListener.openBuffer(bufferId = existingBuffer.bufferId) } else { bufferSyncer.find( networkId = selectedNetworkId, @@ -189,7 +200,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() { activity?.let { it.finish() - ChatActivity.launch(it, + linkClickListener.openChannel( networkId = selectedNetworkId, channel = channelName ) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragmentProvider.kt index be6c9a8f3..dc8dfc67c 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class ChannelCreateFragmentProvider { +interface ChannelCreateFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: ChannelCreateActivity): FragmentActivity + fun bindFragmentActivity(activity: ChannelCreateActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindChannelCreateFragment(): ChannelCreateFragment + fun bindChannelCreateFragment(): ChannelCreateFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt index c03d2d1e5..c03e74b5e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt @@ -19,6 +19,7 @@ package de.kuschku.quasseldroid.ui.chat.add.join +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -27,7 +28,6 @@ import android.widget.AdapterView import android.widget.Button import android.widget.EditText import androidx.appcompat.widget.AppCompatSpinner -import androidx.lifecycle.Observer import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.syncables.Network import de.kuschku.libquassel.util.helper.combineLatest @@ -37,6 +37,8 @@ import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.ui.chat.add.NetworkAdapter import de.kuschku.quasseldroid.ui.chat.add.NetworkItem import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.viewmodel.helper.QuasselViewModelHelper import javax.inject.Inject @@ -49,9 +51,24 @@ class ChannelJoinFragment : ServiceBoundFragment() { @Inject lateinit var modelHelper: QuasselViewModelHelper + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + private var hasSelectedNetwork = false private var networkId = NetworkId(0) + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.add_join, container, false) @@ -87,7 +104,7 @@ class ChannelJoinFragment : ServiceBoundFragment() { NetworkItem(it.networkId, it.networkName) }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, NetworkItem::name)) } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { if (it != null) { networkAdapter.submitList(it) if (!hasSetNetwork && networkId.isValidId() && it.isNotEmpty()) { @@ -101,24 +118,17 @@ class ChannelJoinFragment : ServiceBoundFragment() { hasSetNetwork = true } } - }) + } join.setOnClickListener { join.setText(R.string.label_saving) join.isEnabled = false - val selectedNetworkId = NetworkId(network.selectedItemId.toInt()) - val channelName = name.text.toString().trim() - - activity?.let { - it.finish() - ChatActivity.launch( - it, - networkId = selectedNetworkId, - channel = channelName, - forceJoin = true - ) - } + linkClickListener.openChannel( + networkId = NetworkId(network.selectedItemId.toInt()), + channel = name.text.toString().trim(), + forceJoin = true + ) } return view diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragmentProvider.kt index e031f56cb..77c5b3d24 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class ChannelJoinFragmentProvider { +interface ChannelJoinFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: ChannelJoinActivity): FragmentActivity + fun bindFragmentActivity(activity: ChannelJoinActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindChannelJoinFragment(): ChannelJoinFragment + fun bindChannelJoinFragment(): ChannelJoinFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt index 6a4e78aaa..3cd4cded6 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt @@ -19,6 +19,7 @@ package de.kuschku.quasseldroid.ui.chat.add.query +import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -29,7 +30,6 @@ import android.widget.AdapterView import android.widget.Button import android.widget.EditText import androidx.appcompat.widget.AppCompatSpinner -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -60,6 +60,8 @@ import de.kuschku.quasseldroid.util.helper.styledAttributes import de.kuschku.quasseldroid.util.helper.toLiveData import de.kuschku.quasseldroid.util.irc.format.ContentFormatter import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.viewmodel.data.Avatar import de.kuschku.quasseldroid.viewmodel.data.IrcUserItem @@ -69,6 +71,7 @@ import de.kuschku.quasseldroid.viewmodel.helper.QueryCreateViewModelHelper import io.reactivex.Observable import java.util.concurrent.TimeUnit import javax.inject.Inject +import kotlin.math.min class QueryCreateFragment : ServiceBoundFragment() { lateinit var network: AppCompatSpinner @@ -88,9 +91,24 @@ class QueryCreateFragment : ServiceBoundFragment() { @Inject lateinit var contentFormatter: ContentFormatter + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + private var hasSelectedNetwork = false private var networkId = NetworkId(0) + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.add_query, container, false) @@ -129,7 +147,7 @@ class QueryCreateFragment : ServiceBoundFragment() { NetworkItem(it.networkId, it.networkName) }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, NetworkItem::name)) } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { if (it != null) { networkAdapter.submitList(it) if (!hasSetNetwork && networkId.isValidId() && it.isNotEmpty()) { @@ -143,7 +161,7 @@ class QueryCreateFragment : ServiceBoundFragment() { hasSetNetwork = true } } - }) + } name.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) { @@ -266,10 +284,12 @@ class QueryCreateFragment : ServiceBoundFragment() { MessageSettings.ShowPrefixMode.ALL -> it.modes else -> - it.modes.substring(0, Math.min(it.modes.length, 1)) + it.modes.substring(0, min(it.modes.length, 1)) }, realname = ircFormatDeserializer.formatString( - it.realname.toString(), messageSettings.colorizeMirc + it.realname.toString(), + messageSettings.colorizeMirc, + linkClickListener, ), avatarUrls = AvatarHelper.avatar(messageSettings, it, avatarSize) ) @@ -279,9 +299,9 @@ class QueryCreateFragment : ServiceBoundFragment() { }.sortedBy { it.lowestMode }.toList() - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { nickListAdapter.submitList(it) - }) + } query.setOnClickListener { val selectedNetworkId = NetworkId(network.selectedItemId.toInt()) @@ -297,15 +317,12 @@ class QueryCreateFragment : ServiceBoundFragment() { query.setText(R.string.label_saving) query.isEnabled = false - activity?.let { - it.finish() - ChatActivity.launch( - it, - networkId = selectedNetworkId, - nickName = nickName, - forceJoin = true - ) - } + linkClickListener.openDirectMessage( + networkId = selectedNetworkId, + nickName = nickName, + forceJoin = true + ) + activity?.finish() } override fun onSaveInstanceState(outState: Bundle) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragmentProvider.kt index 3f8158210..f000e75b3 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class QueryCreateFragmentProvider { +interface QueryCreateFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: QueryCreateActivity): FragmentActivity + fun bindFragmentActivity(activity: QueryCreateActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindQueryCreateFragment(): QueryCreateFragment + fun bindQueryCreateFragment(): QueryCreateFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/archive/ArchiveFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/archive/ArchiveFragment.kt index 9df3fcca9..3ef8f321f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/archive/ArchiveFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/archive/ArchiveFragment.kt @@ -19,10 +19,15 @@ package de.kuschku.quasseldroid.ui.chat.archive +import android.content.Context import android.os.Bundle -import android.view.* +import android.view.ActionMode +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -39,6 +44,8 @@ import de.kuschku.quasseldroid.persistence.models.Filtered import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.util.ui.presenter.BufferContextPresenter import de.kuschku.quasseldroid.util.ui.presenter.BufferPresenter @@ -64,6 +71,10 @@ class ArchiveFragment : ServiceBoundFragment() { @Inject lateinit var messageSettings: MessageSettings + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + private lateinit var listAdapter: ArchiveListAdapter private var actionMode: ActionMode? = null @@ -110,6 +121,17 @@ class ArchiveFragment : ServiceBoundFragment() { } } + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.chat_archive, container, false) @@ -163,15 +185,15 @@ class ArchiveFragment : ServiceBoundFragment() { content = getString(R.string.label_permanently_archived_empty) )) } - }.toLiveData().observe(viewLifecycleOwner, Observer { processedList -> + }.toLiveData().observe(viewLifecycleOwner) { processedList -> listAdapter.submitList(processedList) - }) + } - modelHelper.selectedBuffer.toLiveData().observe(viewLifecycleOwner, Observer { buffer -> + modelHelper.selectedBuffer.toLiveData().observe(viewLifecycleOwner) { buffer -> actionMode?.let { BufferContextPresenter.present(it, buffer) } - }) + } return view } @@ -188,9 +210,7 @@ class ArchiveFragment : ServiceBoundFragment() { if (actionMode != null) { longClickListener(bufferId) } else { - context?.let { - ChatActivity.launch(it, bufferId = bufferId) - } + linkClickListener.openBuffer(bufferId = bufferId) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/archive/ArchiveFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/archive/ArchiveFragmentProvider.kt index 0f00b8244..4cbf6582d 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/archive/ArchiveFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/archive/ArchiveFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class ArchiveFragmentProvider { +interface ArchiveFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: ArchiveActivity): FragmentActivity + fun bindFragmentActivity(activity: ArchiveActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindArchiveFragment(): ArchiveFragment + fun bindArchiveFragment(): ArchiveFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt index 77293d7c5..0da794fb9 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt @@ -19,18 +19,23 @@ package de.kuschku.quasseldroid.ui.chat.buffers +import android.content.Context import android.os.Bundle import android.os.Parcelable import android.text.Editable import android.text.TextWatcher -import android.view.* +import android.view.ActionMode +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import android.widget.AdapterView import android.widget.EditText import androidx.annotation.ColorInt import androidx.appcompat.widget.AppCompatImageButton import androidx.appcompat.widget.AppCompatSpinner import androidx.appcompat.widget.Toolbar -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -40,7 +45,14 @@ import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.ExtendedFeature import de.kuschku.libquassel.quassel.syncables.BufferViewConfig -import de.kuschku.libquassel.util.helper.* +import de.kuschku.libquassel.util.helper.combineLatest +import de.kuschku.libquassel.util.helper.mapMap +import de.kuschku.libquassel.util.helper.mapOrElse +import de.kuschku.libquassel.util.helper.nullIf +import de.kuschku.libquassel.util.helper.or +import de.kuschku.libquassel.util.helper.safeSwitchMap +import de.kuschku.libquassel.util.helper.safeValue +import de.kuschku.libquassel.util.helper.value import de.kuschku.quasseldroid.BuildConfig import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.persistence.dao.listenDefaultFiltered @@ -61,6 +73,8 @@ import de.kuschku.quasseldroid.util.helper.styledAttributes import de.kuschku.quasseldroid.util.helper.toLiveData import de.kuschku.quasseldroid.util.helper.visibleIf import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.util.ui.presenter.BufferContextPresenter import de.kuschku.quasseldroid.util.ui.presenter.BufferPresenter @@ -102,6 +116,10 @@ class BufferViewConfigFragment : ServiceBoundFragment() { @Inject lateinit var bufferPresenter: BufferPresenter + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + private var actionMode: ActionMode? = null private val actionModeCallback = object : ActionMode.Callback { @@ -147,6 +165,17 @@ class BufferViewConfigFragment : ServiceBoundFragment() { private lateinit var listAdapter: BufferListAdapter + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -164,11 +193,11 @@ class BufferViewConfigFragment : ServiceBoundFragment() { val adapter = BufferViewConfigAdapter() modelHelper.bufferViewConfigs.safeSwitchMap { combineLatest(it.map(BufferViewConfig::liveUpdates)) - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { if (it != null) { adapter.submitList(it) } - }) + } var hasSetBufferViewConfigId = false adapter.setOnUpdateFinishedListener { @@ -212,14 +241,15 @@ class BufferViewConfigFragment : ServiceBoundFragment() { } } - modelHelper.negotiatedFeatures.toLiveData().observe(viewLifecycleOwner, - Observer { (connected, features) -> - featureContextBufferActivitySync.setMode( - if (!connected || features.hasFeature( - ExtendedFeature.BufferActivitySync)) WarningBarView.MODE_NONE - else WarningBarView.MODE_ICON - ) - }) + modelHelper.negotiatedFeatures.toLiveData().observe(viewLifecycleOwner) { (connected, features) -> + featureContextBufferActivitySync.setMode( + if (!connected || features.hasFeature( + ExtendedFeature.BufferActivitySync + ) + ) WarningBarView.MODE_NONE + else WarningBarView.MODE_ICON + ) + } val filtered = combineLatest( database.filtered().listenRx(accountId).toObservable().map { @@ -230,23 +260,23 @@ class BufferViewConfigFragment : ServiceBoundFragment() { modelHelper.processChatBufferList(filtered).map { buffers -> bufferPresenter.render(buffers) - }.toLiveData().observe(viewLifecycleOwner, Observer { processedList -> + }.toLiveData().observe(viewLifecycleOwner) { processedList -> if (hasRestoredChatListState) { chatListState = chatList.layoutManager?.onSaveInstanceState() hasRestoredChatListState = false } listAdapter.submitList(processedList) - }) + } listAdapter.setOnClickListener(this@BufferViewConfigFragment::clickListener) listAdapter.setOnLongClickListener(this@BufferViewConfigFragment::longClickListener) chatList.adapter = listAdapter - modelHelper.selectedBuffer.toLiveData().observe(viewLifecycleOwner, Observer { buffer -> + modelHelper.selectedBuffer.toLiveData().observe(viewLifecycleOwner) { buffer -> actionMode?.let { BufferContextPresenter.present(it, buffer) } - }) + } chatListToolbar.inflateMenu(R.menu.context_bufferlist) chatListToolbar.menu.findItem(R.id.action_search).isChecked = modelHelper.chat.bufferSearchTemporarilyVisible.or( @@ -276,10 +306,10 @@ class BufferViewConfigFragment : ServiceBoundFragment() { chatList.itemAnimator = DefaultItemAnimator() chatList.setItemViewCacheSize(10) - modelHelper.chat.stateReset.toLiveData().observe(viewLifecycleOwner, Observer { + modelHelper.chat.stateReset.toLiveData().observe(viewLifecycleOwner) { listAdapter.submitList(emptyList()) hasSetBufferViewConfigId = false - }) + } val bufferSearchPermanentlyVisible = modelHelper.bufferViewConfig .mapMap(BufferViewConfig::showSearch) @@ -287,7 +317,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() { combineLatest(modelHelper.chat.bufferSearchTemporarilyVisible.distinctUntilChanged(), bufferSearchPermanentlyVisible) - .toLiveData().observe(viewLifecycleOwner, Observer { (temporarily, permanently) -> + .toLiveData().observe(viewLifecycleOwner) { (temporarily, permanently) -> val visible = temporarily || permanently val menuItem = chatListToolbar.menu.findItem(R.id.action_search) @@ -297,7 +327,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() { bufferSearchContainer.visibleIf(visible) if (!visible) bufferSearch.setText("") - }) + } bufferSearch.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable) { @@ -411,10 +441,8 @@ class BufferViewConfigFragment : ServiceBoundFragment() { if (actionMode != null) { longClickListener(bufferId) } else { - context?.let { - modelHelper.chat.bufferSearchTemporarilyVisible.onNext(false) - ChatActivity.launch(it, bufferId = bufferId) - } + modelHelper.chat.bufferSearchTemporarilyVisible.onNext(false) + linkClickListener.openBuffer(bufferId = bufferId) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt index bdfe45ac2..f9eb517b7 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt @@ -20,7 +20,6 @@ package de.kuschku.quasseldroid.ui.chat.input import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.Observer import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.quassel.syncables.IrcChannel @@ -43,6 +42,7 @@ import de.kuschku.quasseldroid.util.helper.styledAttributes import de.kuschku.quasseldroid.util.helper.toLiveData import de.kuschku.quasseldroid.util.irc.format.ContentFormatter import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener import de.kuschku.quasseldroid.viewmodel.data.AutoCompleteItem import de.kuschku.quasseldroid.viewmodel.data.BufferStatus import de.kuschku.quasseldroid.viewmodel.helper.EditorViewModelHelper @@ -53,6 +53,7 @@ class AutoCompleteHelper( private val autoCompleteSettings: AutoCompleteSettings, private val messageSettings: MessageSettings, private val ircFormatDeserializer: IrcFormatDeserializer, + private val linkClickListener: LinkClickListener, private val contentFormatter: ContentFormatter, private val helper: EditorViewModelHelper, private val emojiHandler: EmojiHandler, @@ -86,32 +87,32 @@ class AutoCompleteHelper( private val colorContext = ColorContext(activity, messageSettings) init { - helper.autoCompleteData.toLiveData().observe(activity, Observer { + helper.autoCompleteData.toLiveData().observe(activity) { val query = it?.first ?: "" val shouldShowResults = (autoCompleteSettings.auto && query.length >= 3) || - (autoCompleteSettings.prefix && autoCompleteSettings.nicks && query.startsWith('@')) || - (autoCompleteSettings.prefix && autoCompleteSettings.buffers && query.startsWith('#')) || - (autoCompleteSettings.prefix && autoCompleteSettings.aliases && query.startsWith('/')) || - (autoCompleteSettings.prefix && autoCompleteSettings.emoji && query.startsWith(':')) + (autoCompleteSettings.prefix && autoCompleteSettings.nicks && query.startsWith('@')) || + (autoCompleteSettings.prefix && autoCompleteSettings.buffers && query.startsWith('#')) || + (autoCompleteSettings.prefix && autoCompleteSettings.aliases && query.startsWith('/')) || + (autoCompleteSettings.prefix && autoCompleteSettings.emoji && query.startsWith(':')) val list = if (shouldShowResults) it?.second.orEmpty() else emptyList() val data = list.filter { it is AutoCompleteItem.AliasItem && autoCompleteSettings.aliases || - it is AutoCompleteItem.UserItem && autoCompleteSettings.nicks || - it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers || - it is AutoCompleteItem.EmojiItem && autoCompleteSettings.emoji + it is AutoCompleteItem.UserItem && autoCompleteSettings.nicks || + it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers || + it is AutoCompleteItem.EmojiItem && autoCompleteSettings.emoji }.map { when (it) { - is AutoCompleteItem.UserItem -> { + is AutoCompleteItem.UserItem -> { val nickName = it.nick val senderColorIndex = SenderColorUtil.senderColor(nickName) val rawInitial = nickName.trimStart(*IGNORED_CHARS).firstOrNull() - ?: nickName.firstOrNull() + ?: nickName.firstOrNull() val initial = rawInitial?.uppercase().toString() val useSelfColor = when (messageSettings.colorizeNicknames) { - MessageSettings.SenderColorMode.ALL -> false + MessageSettings.SenderColorMode.ALL -> false MessageSettings.SenderColorMode.ALL_BUT_MINE -> it.self - MessageSettings.SenderColorMode.NONE -> true + MessageSettings.SenderColorMode.NONE -> true } val senderColor = if (useSelfColor) selfColor else senderColors[senderColorIndex] it.copy( @@ -120,15 +121,19 @@ class AutoCompleteHelper( modes = when (messageSettings.showPrefix) { MessageSettings.ShowPrefixMode.ALL -> it.modes - else -> + + else -> it.modes.substring(0, Math.min(it.modes.length, 1)) }, realname = ircFormatDeserializer.formatString( - it.realname.toString(), messageSettings.colorizeMirc + it.realname.toString(), + messageSettings.colorizeMirc, + linkClickListener, ), avatarUrls = AvatarHelper.avatar(messageSettings, it) ) } + is AutoCompleteItem.ChannelItem -> { val color = if (it.bufferStatus == BufferStatus.ONLINE) colorAccent else colorAway @@ -137,13 +142,14 @@ class AutoCompleteHelper( icon = colorContext.buildTextDrawable("#", color) ) } - else -> it + + else -> it } } for (dataListener in dataListeners) { dataListener(data) } - }) + } } fun setAutocompleteListener(listener: ((AutoCompletionState) -> Unit)?) { @@ -233,7 +239,7 @@ class AutoCompleteHelper( val prefixModes = network.prefixModes() val lowestMode = userModes.mapNotNull(prefixModes::indexOf).minOrNull() - ?: prefixModes.size + ?: prefixModes.size AutoCompleteItem.UserItem( user.nick(), @@ -271,9 +277,9 @@ class AutoCompleteHelper( fullAutoComplete(sessionOptional, id, lastWord) }?.filter { it is AutoCompleteItem.AliasItem && autoCompleteSettings.aliases || - it is AutoCompleteItem.UserItem && autoCompleteSettings.nicks || - it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers || - it is AutoCompleteItem.EmojiItem && autoCompleteSettings.emoji + it is AutoCompleteItem.UserItem && autoCompleteSettings.nicks || + it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers || + it is AutoCompleteItem.EmojiItem && autoCompleteSettings.emoji }.orEmpty() if (previous != null && originalWord.first == previous.originalWord && originalWord.second.start == previous.range.start) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt index d00526885..6ada9bd27 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt @@ -19,6 +19,7 @@ package de.kuschku.quasseldroid.ui.chat.input +import android.content.Context import android.os.Bundle import android.text.SpannableString import android.text.Spanned @@ -38,11 +39,18 @@ import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.AutoCompleteSettings import de.kuschku.quasseldroid.settings.MessageSettings +import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.util.emoji.EmojiHandler -import de.kuschku.quasseldroid.util.helper.* +import de.kuschku.quasseldroid.util.helper.lineSequence +import de.kuschku.quasseldroid.util.helper.retint +import de.kuschku.quasseldroid.util.helper.setTooltip +import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.helper.visibleIf import de.kuschku.quasseldroid.util.irc.format.ContentFormatter import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.viewmodel.helper.EditorViewModelHelper import javax.inject.Inject @@ -76,6 +84,10 @@ class ChatlineFragment : ServiceBoundFragment() { @Inject lateinit var ircFormatSerializer: IrcFormatSerializer + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + @Inject lateinit var autoCompleteAdapter: AutoCompleteAdapter @@ -99,6 +111,17 @@ class ChatlineFragment : ServiceBoundFragment() { } } + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.chat_chatline, container, false) @@ -117,6 +140,7 @@ class ChatlineFragment : ServiceBoundFragment() { autoCompleteSettings, messageSettings, ircFormatDeserializer, + linkClickListener, contentFormatter, modelHelper, emojiHandler diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt index 1545b6ec3..af87047a2 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt @@ -26,8 +26,12 @@ import android.content.Intent import android.os.Bundle import android.text.SpannableStringBuilder import android.util.TypedValue -import android.view.* -import androidx.lifecycle.Observer +import android.view.ActionMode +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.paging.LivePagedListBuilder import androidx.paging.PagedList import androidx.recyclerview.widget.LinearLayoutManager @@ -39,25 +43,51 @@ import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader import com.bumptech.glide.util.FixedPreloadSizeProvider import com.google.android.material.floatingactionbutton.FloatingActionButton import de.kuschku.libquassel.connection.ConnectionState -import de.kuschku.libquassel.protocol.* +import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.protocol.Buffer_Type +import de.kuschku.libquassel.protocol.Message_Type +import de.kuschku.libquassel.protocol.MsgId +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.quassel.syncables.BufferSyncer import de.kuschku.libquassel.session.SessionManager import de.kuschku.libquassel.util.flag.hasFlag -import de.kuschku.libquassel.util.helper.* +import de.kuschku.libquassel.util.helper.combineLatest +import de.kuschku.libquassel.util.helper.invoke +import de.kuschku.libquassel.util.helper.mapSwitchMap +import de.kuschku.libquassel.util.helper.nullIf +import de.kuschku.libquassel.util.helper.safeValue +import de.kuschku.libquassel.util.helper.value import de.kuschku.libquassel.util.irc.HostmaskHelper import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.persistence.dao.* +import de.kuschku.quasseldroid.persistence.dao.findByBufferIdPaged +import de.kuschku.quasseldroid.persistence.dao.findById +import de.kuschku.quasseldroid.persistence.dao.findFirstByBufferId +import de.kuschku.quasseldroid.persistence.dao.get +import de.kuschku.quasseldroid.persistence.dao.hasVisibleMessages +import de.kuschku.quasseldroid.persistence.dao.lastMsgId +import de.kuschku.quasseldroid.persistence.dao.listen import de.kuschku.quasseldroid.persistence.db.AccountDatabase import de.kuschku.quasseldroid.persistence.db.QuasselDatabase import de.kuschku.quasseldroid.persistence.models.MessageData import de.kuschku.quasseldroid.service.BacklogRequester -import de.kuschku.quasseldroid.settings.* -import de.kuschku.quasseldroid.ui.chat.ChatActivity +import de.kuschku.quasseldroid.settings.AppearanceSettings +import de.kuschku.quasseldroid.settings.AutoCompleteSettings +import de.kuschku.quasseldroid.settings.BacklogSettings +import de.kuschku.quasseldroid.settings.MessageSettings +import de.kuschku.quasseldroid.settings.RedirectionSettings import de.kuschku.quasseldroid.ui.info.user.UserInfoActivity import de.kuschku.quasseldroid.util.Patterns import de.kuschku.quasseldroid.util.avatars.AvatarHelper -import de.kuschku.quasseldroid.util.helper.* +import de.kuschku.quasseldroid.util.helper.loadWithFallbacks +import de.kuschku.quasseldroid.util.helper.mapReverse +import de.kuschku.quasseldroid.util.helper.safeSwitchMap +import de.kuschku.quasseldroid.util.helper.styledAttributes +import de.kuschku.quasseldroid.util.helper.switchMapNotNull +import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.helper.toggle +import de.kuschku.quasseldroid.util.helper.zip +import de.kuschku.quasseldroid.util.listener.AutocompleteTextListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.util.ui.LinkLongClickMenuHelper import de.kuschku.quasseldroid.util.ui.SpanFormatter @@ -105,6 +135,9 @@ class MessageListFragment : ServiceBoundFragment() { @Inject lateinit var modelHelper: ChatViewModelHelper + @Inject + lateinit var autocompleteTextListener: AutocompleteTextListener + private lateinit var linearLayoutManager: LinearLayoutManager private lateinit var backlogRequester: BacklogRequester @@ -270,11 +303,7 @@ class MessageListFragment : ServiceBoundFragment() { } if (autoCompleteSettings.senderDoubleClick) adapter.setOnDoubleClickListener { msg -> - ChatActivity.launch( - requireContext(), - autoCompleteText = HostmaskHelper.nick(msg.sender), - autoCompleteSuffix = ": " - ) + autocompleteTextListener.autocompleteText(HostmaskHelper.nick(msg.sender), ": ") } adapter.setOnSenderIconClickListener { msg -> modelHelper.connectedSession.value?.orNull()?.bufferSyncer?.let { bufferSyncer -> @@ -402,35 +431,39 @@ class MessageListFragment : ServiceBoundFragment() { database.message().lastMsgId(it) } - modelHelper.chat.bufferId.toLiveData().observe(viewLifecycleOwner, Observer { bufferId -> + modelHelper.chat.bufferId.toLiveData().observe(viewLifecycleOwner) { bufferId -> swipeRefreshLayout.isEnabled = (bufferId != null || bufferId?.isValidId() == true) - }) + } combineLatest(modelHelper.bufferSyncer, modelHelper.sessionManager.mapSwitchMap(SessionManager::state).distinctUntilChanged()) - .toLiveData().observe(viewLifecycleOwner, Observer { (bufferSyncer, state) -> + .toLiveData().observe(viewLifecycleOwner) { (bufferSyncer, state) -> if (state?.orNull() == ConnectionState.CONNECTED) { runInBackgroundDelayed(16) { modelHelper.chat.bufferId { bufferId -> val currentNetwork = bufferSyncer.orNull()?.bufferInfo(bufferId)?.networkId - ?: NetworkId(0) + ?: NetworkId(0) val currentServerBuffer = bufferSyncer.orNull()?.find( networkId = currentNetwork, type = Buffer_Type.of(Buffer_Type.StatusBuffer) )?.bufferId ?: BufferId(0) - val filtered = database.filtered().get(accountId, - bufferId, - accountDatabase.accounts().findById(accountId)?.defaultFiltered - ?: 0) + val filtered = database.filtered().get( + accountId, + bufferId, + accountDatabase.accounts().findById(accountId)?.defaultFiltered + ?: 0 + ) // Try loading messages when switching to isEmpty bufferId - val hasVisibleMessages = database.message().hasVisibleMessages(currentNetwork, - currentServerBuffer, - bufferId, - filtered, - redirectionSettings.userNotices, - redirectionSettings.serverNotices, - redirectionSettings.errors) + val hasVisibleMessages = database.message().hasVisibleMessages( + currentNetwork, + currentServerBuffer, + bufferId, + filtered, + redirectionSettings.userNotices, + redirectionSettings.serverNotices, + redirectionSettings.errors + ) if (!hasVisibleMessages) { if (bufferId.isValidId() && bufferId != BufferId.MAX_VALUE) { loadMore(initial = true) @@ -439,10 +472,9 @@ class MessageListFragment : ServiceBoundFragment() { } } } - }) + } - modelHelper.connectedSession.toLiveData().zip(lastMessageId).observe( - viewLifecycleOwner, Observer { + modelHelper.connectedSession.toLiveData().zip(lastMessageId).observe(viewLifecycleOwner) { runInBackground { val session = it?.first?.orNull() val message = it?.second @@ -452,7 +484,7 @@ class MessageListFragment : ServiceBoundFragment() { previousMessageId = message.messageId } } - }) + } var hasLoaded = false fun checkScroll() { @@ -512,7 +544,7 @@ class MessageListFragment : ServiceBoundFragment() { lastBuffer = BufferId(getInt(KEY_STATE_BUFFER)).nullIf { !it.isValidId() } } - data.observe(viewLifecycleOwner, Observer { list -> + data.observe(viewLifecycleOwner) { list -> previousLoadKey = list?.lastKey as? Int val firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition() val firstVisibleMessageId = adapter[firstVisibleItemPosition]?.content?.messageId @@ -522,9 +554,9 @@ class MessageListFragment : ServiceBoundFragment() { } val buffer = modelHelper.chat.bufferId.safeValue - ?: BufferId(-1) + ?: BufferId(-1) val network = modelHelper.bufferSyncer.value?.orNull()?.bufferInfo(buffer)?.networkId - ?: NetworkId(0) + ?: NetworkId(0) val serverBuffer = modelHelper.bufferSyncer.value?.orNull()?.find( networkId = network, type = Buffer_Type.of(Buffer_Type.StatusBuffer) @@ -532,18 +564,20 @@ class MessageListFragment : ServiceBoundFragment() { if (buffer != lastBuffer) { adapter.clearCache() modelHelper.connectedSession.value?.orNull()?.bufferSyncer?.let { bufferSyncer -> - onBufferChange(lastBuffer, - network, - buffer, - serverBuffer, - firstVisibleMessageId, - bufferSyncer) + onBufferChange( + lastBuffer, + network, + buffer, + serverBuffer, + firstVisibleMessageId, + bufferSyncer + ) } modelHelper.backend.value?.orNull()?.setCurrentBuffer(buffer) lastBuffer = buffer } } - }) + } return view } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt index 66d2c3ece..615f51db4 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt @@ -29,7 +29,24 @@ import android.view.Gravity import android.view.View import android.widget.FrameLayout import android.widget.LinearLayout -import de.kuschku.libquassel.protocol.Message.MessageType.* +import de.kuschku.libquassel.protocol.Message.MessageType.Action +import de.kuschku.libquassel.protocol.Message.MessageType.DayChange +import de.kuschku.libquassel.protocol.Message.MessageType.Error +import de.kuschku.libquassel.protocol.Message.MessageType.Info +import de.kuschku.libquassel.protocol.Message.MessageType.Invite +import de.kuschku.libquassel.protocol.Message.MessageType.Join +import de.kuschku.libquassel.protocol.Message.MessageType.Kick +import de.kuschku.libquassel.protocol.Message.MessageType.Kill +import de.kuschku.libquassel.protocol.Message.MessageType.Mode +import de.kuschku.libquassel.protocol.Message.MessageType.NetsplitJoin +import de.kuschku.libquassel.protocol.Message.MessageType.NetsplitQuit +import de.kuschku.libquassel.protocol.Message.MessageType.Nick +import de.kuschku.libquassel.protocol.Message.MessageType.Notice +import de.kuschku.libquassel.protocol.Message.MessageType.Part +import de.kuschku.libquassel.protocol.Message.MessageType.Plain +import de.kuschku.libquassel.protocol.Message.MessageType.Quit +import de.kuschku.libquassel.protocol.Message.MessageType.Server +import de.kuschku.libquassel.protocol.Message.MessageType.Topic import de.kuschku.libquassel.protocol.Message_Flag import de.kuschku.libquassel.protocol.Message_Type import de.kuschku.libquassel.util.flag.hasFlag @@ -44,6 +61,7 @@ import de.kuschku.quasseldroid.util.helper.styledAttributes import de.kuschku.quasseldroid.util.helper.visibleIf import de.kuschku.quasseldroid.util.irc.format.ContentFormatter import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener import de.kuschku.quasseldroid.util.ui.SpanFormatter import de.kuschku.quasseldroid.viewmodel.data.FormattedMessage import de.kuschku.quasseldroid.viewmodel.helper.EditorViewModelHelper.Companion.IGNORED_CHARS @@ -58,7 +76,8 @@ class QuasselMessageRenderer @Inject constructor( context: Context, private val messageSettings: MessageSettings, private val contentFormatter: ContentFormatter, - private val ircFormatDeserializer: IrcFormatDeserializer + private val ircFormatDeserializer: IrcFormatDeserializer, + private val linkClickListener: LinkClickListener, ) : MessageRenderer { private val timeFormatter = DateTimeFormatter.ofPattern( timePattern(messageSettings.showSeconds, messageSettings.use24hClock) @@ -240,8 +259,11 @@ class QuasselMessageRenderer @Inject constructor( val monochromeForeground = highlight && monochromeHighlights return when (message.content.type.enabledValues().firstOrNull()) { Message_Type.Plain -> { - val realName = ircFormatDeserializer.formatString(message.content.realName, - !monochromeForeground) + val realName = ircFormatDeserializer.formatString( + message.content.realName, + !monochromeForeground, + linkClickListener, + ) val nick = SpannableStringBuilder().apply { append(contentFormatter.formatPrefix(message.content.senderPrefixes)) append(contentFormatter.formatNick( @@ -251,10 +273,13 @@ class QuasselMessageRenderer @Inject constructor( false )) } - val (content, hasSpoilers) = contentFormatter.formatContent(message.content.content, - monochromeForeground, - message.isExpanded, - message.content.networkId) + val (content, hasSpoilers) = contentFormatter.formatContent( + message.content.content, + monochromeForeground, + message.isExpanded, + message.content.networkId, + linkClickListener + ) val nickName = HostmaskHelper.nick(message.content.sender) val senderColorIndex = SenderColorUtil.senderColor(nickName) val rawInitial = nickName.trimStart(*IGNORED_CHARS) @@ -303,10 +328,13 @@ class QuasselMessageRenderer @Inject constructor( } val senderColor = if (useSelfColor) selfColor else senderColors[senderColorIndex] - val (content, hasSpoilers) = contentFormatter.formatContent(message.content.content, - monochromeForeground, - message.isExpanded, - message.content.networkId) + val (content, hasSpoilers) = contentFormatter.formatContent( + message.content.content, + monochromeForeground, + message.isExpanded, + message.content.networkId, + linkClickListener + ) FormattedMessage( original = message.content, @@ -331,7 +359,8 @@ class QuasselMessageRenderer @Inject constructor( val (content, hasSpoilers) = contentFormatter.formatContent(message.content.content, monochromeForeground, message.isExpanded, - message.content.networkId) + message.content.networkId, linkClickListener + ) FormattedMessage( original = message.content, time = timeFormatter.format(message.content.time.atZone(zoneId)), @@ -448,7 +477,8 @@ class QuasselMessageRenderer @Inject constructor( val (content, hasSpoilers) = contentFormatter.formatContent(message.content.content, monochromeForeground, message.isExpanded, - message.content.networkId) + message.content.networkId, linkClickListener + ) Pair( SpanFormatter.format( context.getString(R.string.message_format_part_2), @@ -496,7 +526,8 @@ class QuasselMessageRenderer @Inject constructor( val (content, hasSpoilers) = contentFormatter.formatContent(message.content.content, monochromeForeground, message.isExpanded, - message.content.networkId) + message.content.networkId, linkClickListener + ) Pair( SpanFormatter.format( context.getString(R.string.message_format_quit_2), @@ -544,7 +575,8 @@ class QuasselMessageRenderer @Inject constructor( val (content, hasSpoilers) = contentFormatter.formatContent(reason, monochromeForeground, message.isExpanded, - message.content.networkId) + message.content.networkId, linkClickListener + ) Pair( SpanFormatter.format( context.getString(R.string.message_format_kick_2), @@ -591,7 +623,8 @@ class QuasselMessageRenderer @Inject constructor( val (content, hasSpoilers) = contentFormatter.formatContent(reason, monochromeForeground, message.isExpanded, - message.content.networkId) + message.content.networkId, linkClickListener + ) Pair( SpanFormatter.format( context.getString(R.string.message_format_kill_2), @@ -679,7 +712,8 @@ class QuasselMessageRenderer @Inject constructor( val (content, hasSpoilers) = contentFormatter.formatContent(message.content.content, monochromeForeground, message.isExpanded, - message.content.networkId) + message.content.networkId, linkClickListener + ) FormattedMessage( original = message.content, time = timeFormatter.format(message.content.time.atZone(zoneId)), @@ -696,7 +730,8 @@ class QuasselMessageRenderer @Inject constructor( val (content, hasSpoilers) = contentFormatter.formatContent(message.content.content, monochromeForeground, message.isExpanded, - message.content.networkId) + message.content.networkId, linkClickListener + ) FormattedMessage( original = message.content, time = timeFormatter.format(message.content.time.atZone(zoneId)), @@ -724,7 +759,8 @@ class QuasselMessageRenderer @Inject constructor( val (content, hasSpoilers) = contentFormatter.formatContent(message.content.content, monochromeForeground, message.isExpanded, - message.content.networkId) + message.content.networkId, linkClickListener + ) FormattedMessage( original = message.content, time = timeFormatter.format(message.content.time.atZone(zoneId)), diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt index 0d0c11f0f..2d01cd466 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt @@ -19,11 +19,11 @@ package de.kuschku.quasseldroid.ui.chat.nicks +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -40,6 +40,7 @@ import de.kuschku.libquassel.util.irc.SenderColorUtil import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.MessageSettings +import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.ui.info.user.UserInfoActivity import de.kuschku.quasseldroid.util.ColorContext import de.kuschku.quasseldroid.util.avatars.AvatarHelper @@ -48,6 +49,8 @@ import de.kuschku.quasseldroid.util.helper.styledAttributes import de.kuschku.quasseldroid.util.helper.toLiveData import de.kuschku.quasseldroid.util.irc.format.ContentFormatter import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.viewmodel.data.Avatar import de.kuschku.quasseldroid.viewmodel.helper.ChatViewModelHelper @@ -66,12 +69,27 @@ class NickListFragment : ServiceBoundFragment() { @Inject lateinit var ircFormatDeserializer: IrcFormatDeserializer + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + @Inject lateinit var contentFormatter: ContentFormatter @Inject lateinit var modelHelper: ChatViewModelHelper + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.chat_nicklist, container, false) @@ -102,18 +120,18 @@ class NickListFragment : ServiceBoundFragment() { val colorContext = ColorContext(requireContext(), messageSettings) val avatarSize = resources.getDimensionPixelSize(R.dimen.avatar_size) - modelHelper.nickDataThrottled.toLiveData().observe(viewLifecycleOwner, Observer { + modelHelper.nickDataThrottled.toLiveData().observe(viewLifecycleOwner) { runInBackground { it?.asSequence()?.map { val nickName = it.nick val senderColorIndex = SenderColorUtil.senderColor(nickName) val rawInitial = nickName.trimStart(*IGNORED_CHARS) - .firstOrNull() ?: nickName.firstOrNull() + .firstOrNull() ?: nickName.firstOrNull() val initial = rawInitial?.uppercase().toString() val useSelfColor = when (messageSettings.colorizeNicknames) { - MessageSettings.SenderColorMode.ALL -> false + MessageSettings.SenderColorMode.ALL -> false MessageSettings.SenderColorMode.ALL_BUT_MINE -> it.self - MessageSettings.SenderColorMode.NONE -> true + MessageSettings.SenderColorMode.NONE -> true } val senderColor = if (useSelfColor) selfColor else senderColors[senderColorIndex] @@ -124,11 +142,13 @@ class NickListFragment : ServiceBoundFragment() { modes = when (messageSettings.showPrefix) { MessageSettings.ShowPrefixMode.ALL -> it.modes - else -> + else -> it.modes.substring(0, Math.min(it.modes.length, 1)) }, realname = ircFormatDeserializer.formatString( - it.realname.toString(), messageSettings.colorizeMirc + it.realname.toString(), + messageSettings.colorizeMirc, + linkClickListener, ), avatarUrls = AvatarHelper.avatar(messageSettings, it, avatarSize) ) @@ -143,7 +163,7 @@ class NickListFragment : ServiceBoundFragment() { } } } - }) + } savedInstanceState?.run { (nickList.layoutManager as RecyclerView.LayoutManager) .onRestoreInstanceState(getParcelable(KEY_STATE_LIST)) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt index d90e0a8b6..ad2e85f8f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt @@ -19,11 +19,11 @@ package de.kuschku.quasseldroid.ui.chat.topic +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -34,12 +34,19 @@ import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.AutoCompleteSettings import de.kuschku.quasseldroid.settings.MessageSettings -import de.kuschku.quasseldroid.ui.chat.input.* +import de.kuschku.quasseldroid.ui.chat.ChatActivity +import de.kuschku.quasseldroid.ui.chat.input.AutoCompleteAdapter +import de.kuschku.quasseldroid.ui.chat.input.AutoCompleteHelper +import de.kuschku.quasseldroid.ui.chat.input.EditorHelper +import de.kuschku.quasseldroid.ui.chat.input.RichEditText +import de.kuschku.quasseldroid.ui.chat.input.RichToolbar import de.kuschku.quasseldroid.util.emoji.EmojiHandler import de.kuschku.quasseldroid.util.helper.toLiveData import de.kuschku.quasseldroid.util.irc.format.ContentFormatter import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment import de.kuschku.quasseldroid.viewmodel.helper.EditorViewModelHelper @@ -77,8 +84,23 @@ class TopicFragment : ServiceBoundSettingsFragment(), Savable { @Inject lateinit var emojiHandler: EmojiHandler + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + private lateinit var editorHelper: EditorHelper + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.info_topic, container, false) @@ -91,6 +113,7 @@ class TopicFragment : ServiceBoundSettingsFragment(), Savable { autoCompleteSettings, messageSettings, formatDeserializer, + linkClickListener, contentFormatter, modelHelper, emojiHandler @@ -125,9 +148,9 @@ class TopicFragment : ServiceBoundSettingsFragment(), Savable { modelHelper.chat.bufferId.onNext(bufferId) modelHelper.bufferData.filter { it.info != null - }.firstElement().toLiveData().observe(viewLifecycleOwner, Observer { - chatline.setText(formatDeserializer.formatString(it?.description, true)) - }) + }.firstElement().toLiveData().observe(viewLifecycleOwner) { + chatline.setText(formatDeserializer.formatString(it?.description, true, linkClickListener)) + } return view } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragmentProvider.kt index 94132e346..6db8784a9 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class TopicFragmentProvider { +interface TopicFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: TopicActivity): FragmentActivity + fun bindFragmentActivity(activity: TopicActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindTopicFragment(): TopicFragment + fun bindTopicFragment(): TopicFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragmentProvider.kt index ad0827441..eb70b2634 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/about/AboutFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class AboutFragmentProvider { +interface AboutFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: AboutActivity): FragmentActivity + fun bindFragmentActivity(activity: AboutActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindAboutFragment(): AboutFragment + fun bindAboutFragment(): AboutFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/client/ClientSettingsFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/client/ClientSettingsFragmentProvider.kt index 41e06ae35..68d5c2b3e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/client/ClientSettingsFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/client/ClientSettingsFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class ClientSettingsFragmentProvider { +interface ClientSettingsFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: ClientSettingsActivity): FragmentActivity + fun bindFragmentActivity(activity: ClientSettingsActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindClientSettingsFragment(): ClientSettingsFragment + fun bindClientSettingsFragment(): ClientSettingsFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/crash/CrashFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/crash/CrashFragmentProvider.kt index f20f6f460..6dbc13da7 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/crash/CrashFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/crash/CrashFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class CrashFragmentProvider { +interface CrashFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: CrashActivity): FragmentActivity + fun bindFragmentActivity(activity: CrashActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindClientSettingsFragment(): CrashFragment + fun bindClientSettingsFragment(): CrashFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/license/LicenseFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/license/LicenseFragmentProvider.kt index 07cf71735..8fbd4bf60 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/license/LicenseFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/license/LicenseFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class LicenseFragmentProvider { +interface LicenseFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: LicenseActivity): FragmentActivity + fun bindFragmentActivity(activity: LicenseActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindLicenseFragment(): LicenseFragment + fun bindLicenseFragment(): LicenseFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/whitelist/WhitelistFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/whitelist/WhitelistFragmentProvider.kt index 0dd6a2efe..f50149656 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/whitelist/WhitelistFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/clientsettings/whitelist/WhitelistFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class WhitelistFragmentProvider { +interface WhitelistFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: WhitelistActivity): FragmentActivity + fun bindFragmentActivity(activity: WhitelistActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindWhitelistFragment(): WhitelistFragment + fun bindWhitelistFragment(): WhitelistFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt index 27f33b01c..24c6d6d7f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt @@ -25,7 +25,6 @@ import android.view.View import android.view.ViewGroup import android.widget.Button import androidx.core.view.ViewCompat -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -132,9 +131,9 @@ class CoreSettingsFragment : ServiceBoundFragment() { }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, SettingsItem<NetworkId>::name)) } } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { networkAdapter.submitList(it.orEmpty()) - }) + } identities.adapter = identityAdapter identities.layoutManager = LinearLayoutManager(context) @@ -151,9 +150,9 @@ class CoreSettingsFragment : ServiceBoundFragment() { }.sortedBy(SettingsItem<IdentityId>::name) } } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { identityAdapter.submitList(it.orEmpty()) - }) + } chatlists.adapter = chatListAdapter chatlists.layoutManager = LinearLayoutManager(context) @@ -166,18 +165,17 @@ class CoreSettingsFragment : ServiceBoundFragment() { SettingsItem(it.bufferViewId(), it.bufferViewName()) }.sortedBy(SettingsItem<Int>::name) } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { chatListAdapter.submitList(it.orEmpty()) - }) + } var missingFeatureList: List<MissingFeature> = emptyList() - modelHelper.negotiatedFeatures.toLiveData().observe(viewLifecycleOwner, - Observer { (connected, features) -> - missingFeatureList = RequiredFeatures.features.filter { - it.feature !in features.enabledFeatures - } - featureContextMissing.visibleIf(connected && missingFeatureList.isNotEmpty()) - }) + modelHelper.negotiatedFeatures.toLiveData().observe(viewLifecycleOwner) { (connected, features) -> + missingFeatureList = RequiredFeatures.features.filter { + it.feature !in features.enabledFeatures + } + featureContextMissing.visibleIf(connected && missingFeatureList.isNotEmpty()) + } featureContextMissing.setOnClickListener { MissingFeaturesDialog.Builder(requireActivity()) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragmentProvider.kt index dd0be146c..5a4d370e1 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class CoreSettingsFragmentProvider { +interface CoreSettingsFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: CoreSettingsActivity): FragmentActivity + fun bindFragmentActivity(activity: CoreSettingsActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindCoreSettingsFragment(): CoreSettingsFragment + fun bindCoreSettingsFragment(): CoreSettingsFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt index 4c149a24a..802830e26 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragment.kt @@ -20,6 +20,7 @@ package de.kuschku.quasseldroid.ui.coresettings.aliasitem import android.app.Activity +import android.content.Context import android.content.Intent import android.os.Bundle import android.view.LayoutInflater @@ -35,11 +36,18 @@ import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.AutoCompleteSettings import de.kuschku.quasseldroid.settings.MessageSettings -import de.kuschku.quasseldroid.ui.chat.input.* +import de.kuschku.quasseldroid.ui.chat.ChatActivity +import de.kuschku.quasseldroid.ui.chat.input.AutoCompleteAdapter +import de.kuschku.quasseldroid.ui.chat.input.AutoCompleteHelper +import de.kuschku.quasseldroid.ui.chat.input.EditorHelper +import de.kuschku.quasseldroid.ui.chat.input.RichEditText +import de.kuschku.quasseldroid.ui.chat.input.RichToolbar import de.kuschku.quasseldroid.util.emoji.EmojiHandler import de.kuschku.quasseldroid.util.irc.format.ContentFormatter import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.ui.settings.fragment.Changeable import de.kuschku.quasseldroid.util.ui.settings.fragment.Savable import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment @@ -64,6 +72,10 @@ class AliasItemFragment : ServiceBoundSettingsFragment(), Savable, Changeable { @Inject lateinit var formatDeserializer: IrcFormatDeserializer + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + @Inject lateinit var contentFormatter: ContentFormatter @@ -83,6 +95,17 @@ class AliasItemFragment : ServiceBoundSettingsFragment(), Savable, Changeable { private var rule: IAliasManager.Alias? = null + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -101,6 +124,7 @@ class AliasItemFragment : ServiceBoundSettingsFragment(), Savable, Changeable { autoCompleteSettings, messageSettings, formatDeserializer, + linkClickListener, contentFormatter, modelHelper, emojiHandler @@ -133,7 +157,7 @@ class AliasItemFragment : ServiceBoundSettingsFragment(), Savable, Changeable { rule?.let { data -> name.setText(data.name ?: "") - expansion.setText(formatDeserializer.formatString(data.expansion, true)) + expansion.setText(formatDeserializer.formatString(data.expansion, true, linkClickListener)) } return view diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragmentProvider.kt index 012df443f..ff4d38c5f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliasitem/AliasItemFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class AliasItemFragmentProvider { +interface AliasItemFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: AliasItemActivity): FragmentActivity + fun bindFragmentActivity(activity: AliasItemActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindAliasItemFragment(): AliasItemFragment + fun bindAliasItemFragment(): AliasItemFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListAdapter.kt index 4c885fbaa..3279fc2b5 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListAdapter.kt @@ -27,11 +27,13 @@ import androidx.recyclerview.widget.RecyclerView import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager import de.kuschku.quasseldroid.databinding.SettingsAliaslistItemBinding import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener import java.util.* import javax.inject.Inject class AliasListAdapter @Inject constructor( - private val formatDeserializer: IrcFormatDeserializer + private val formatDeserializer: IrcFormatDeserializer, + private val linkClickListener: LinkClickListener, ) : RecyclerView.Adapter<AliasListAdapter.AliasItemViewHolder>() { private var clickListener: ((IAliasManager.Alias) -> Unit)? = null private var dragListener: ((AliasItemViewHolder) -> Unit)? = null @@ -83,6 +85,7 @@ class AliasListAdapter @Inject constructor( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = AliasItemViewHolder( SettingsAliaslistItemBinding.inflate(LayoutInflater.from(parent.context), parent, false), formatDeserializer, + linkClickListener, clickListener, dragListener ) @@ -95,6 +98,7 @@ class AliasListAdapter @Inject constructor( class AliasItemViewHolder( private val binding: SettingsAliaslistItemBinding, private val formatDeserializer: IrcFormatDeserializer, + private val linkClickListener: LinkClickListener, clickListener: ((IAliasManager.Alias) -> Unit)?, dragListener: ((AliasItemViewHolder) -> Unit)? ) : RecyclerView.ViewHolder(binding.root) { @@ -117,7 +121,7 @@ class AliasListAdapter @Inject constructor( fun bind(item: IAliasManager.Alias) { this.item = item binding.name.text = item.name - binding.expansion.text = formatDeserializer.formatString(item.expansion, true) + binding.expansion.text = formatDeserializer.formatString(item.expansion, true, linkClickListener) } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragment.kt index 0b49fdfc8..f6d1f3aae 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragment.kt @@ -25,7 +25,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.lifecycle.Observer import androidx.recyclerview.widget.* import com.google.android.material.floatingactionbutton.FloatingActionButton import de.kuschku.libquassel.quassel.syncables.AliasManager @@ -79,7 +78,7 @@ class AliasListFragment : ServiceBoundSettingsFragment(), Savable, Changeable { modelHelper.aliasManager .filter(Optional<AliasManager>::isPresent) .map(Optional<AliasManager>::get) - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { if (it != null) { if (this.aliasManager == null) { this.aliasManager = Pair(it, it.copy()) @@ -88,7 +87,7 @@ class AliasListFragment : ServiceBoundSettingsFragment(), Savable, Changeable { } } } - }) + } return view } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragmentProvider.kt index ae9d501b6..3594fecbd 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/aliaslist/AliasListFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class AliasListFragmentProvider { +interface AliasListFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: AliasListActivity): FragmentActivity + fun bindFragmentActivity(activity: AliasListActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindAliasListFragment(): AliasListFragment + fun bindAliasListFragment(): AliasListFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt index 7715ba1b6..ac91b17a7 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt @@ -27,7 +27,6 @@ import android.widget.AdapterView import android.widget.EditText import android.widget.Spinner import androidx.appcompat.widget.SwitchCompat -import androidx.lifecycle.Observer import de.kuschku.libquassel.protocol.Buffer_Activity import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.protocol.NetworkId @@ -113,7 +112,7 @@ abstract class ChatListBaseFragment(private val initDefault: Boolean) : combineLatest(it.values.map(Network::liveNetworkInfo)).map { it.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, INetwork.NetworkInfo::networkName)) } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { if (it != null) { val selectOriginal = networkId.selectedItemId == Spinner.INVALID_ROW_ID networkAdapter.submitList(listOf(null) + it) @@ -123,30 +122,32 @@ abstract class ChatListBaseFragment(private val initDefault: Boolean) : } } } - }) + } if (initDefault) { modelHelper.connectedSession .filter(Optional<ISession>::isPresent) .map(Optional<ISession>::get) .firstElement() - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { it?.let { - update(Defaults.bufferViewConfig(requireContext(), it.proxy), - minimumActivityAdapter, - networkAdapter) + update( + Defaults.bufferViewConfig(requireContext(), it.proxy), + minimumActivityAdapter, + networkAdapter + ) } - }) + } } else { modelHelper.bufferViewConfigMap.map { Optional.ofNullable(it[chatlistId]) } .filter(Optional<BufferViewConfig>::isPresent) .map(Optional<BufferViewConfig>::get) .firstElement() - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { it?.let { update(it, minimumActivityAdapter, networkAdapter) } - }) + } } networkId.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatlistCreateFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatlistCreateFragmentProvider.kt index 7fbe2bbb3..39f06d0b3 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatlistCreateFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatlistCreateFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class ChatlistCreateFragmentProvider { +interface ChatlistCreateFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: ChatlistCreateActivity): FragmentActivity + fun bindFragmentActivity(activity: ChatlistCreateActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindChatListCreateFragment(): ChatListCreateFragment + fun bindChatListCreateFragment(): ChatListCreateFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatlistEditFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatlistEditFragmentProvider.kt index ac0db70e4..4a0a43f4f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatlistEditFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatlistEditFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class ChatlistEditFragmentProvider { +interface ChatlistEditFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: ChatlistEditActivity): FragmentActivity + fun bindFragmentActivity(activity: ChatlistEditActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindChatListEditFragment(): ChatListEditFragment + fun bindChatListEditFragment(): ChatListEditFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragment.kt index f2b9f3c9c..336a6e4a5 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragment.kt @@ -28,7 +28,6 @@ import android.view.ViewGroup import android.widget.Button import android.widget.Spinner import androidx.appcompat.widget.SwitchCompat -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager @@ -127,29 +126,32 @@ class HighlightListFragment : ServiceBoundSettingsFragment(), Savable, Changeabl modelHelper.highlightRuleManager .filter(Optional<HighlightRuleManager>::isPresent) .map(Optional<HighlightRuleManager>::get) - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { if (it != null) { if (this.ruleManager == null) { this.ruleManager = Pair(it, it.copy()) this.ruleManager?.let { (_, data) -> rulesAdapter.list = data.highlightRuleList().filter { !it.isInverse } ignoreRulesAdapter.list = data.highlightRuleList().filter { it.isInverse } - highlightNickType.setSelection(highlightNickTypeAdapter.indexOf(data.highlightNick()) - ?: 0) + highlightNickType.setSelection( + highlightNickTypeAdapter.indexOf(data.highlightNick()) + ?: 0 + ) isCaseSensitive.isChecked = data.nicksCaseSensitive() } } } - }) - - modelHelper.negotiatedFeatures.toLiveData().observe(viewLifecycleOwner, - Observer { (connected, features) -> - featureContextCoreSideHighlights.setMode( - if (!connected || features.hasFeature( - ExtendedFeature.CoreSideHighlights)) WarningBarView.MODE_NONE - else WarningBarView.MODE_ICON - ) - }) + } + + modelHelper.negotiatedFeatures.toLiveData().observe(viewLifecycleOwner) { (connected, features) -> + featureContextCoreSideHighlights.setMode( + if (!connected || features.hasFeature( + ExtendedFeature.CoreSideHighlights + ) + ) WarningBarView.MODE_NONE + else WarningBarView.MODE_ICON + ) + } return view } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragmentProvider.kt index d7804d7c6..da8baf2d0 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightlist/HighlightListFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class HighlightListFragmentProvider { +interface HighlightListFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: HighlightListActivity): FragmentActivity + fun bindFragmentActivity(activity: HighlightListActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindHighlightListFragment(): HighlightListFragment + fun bindHighlightListFragment(): HighlightListFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightrule/HighlightRuleFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightrule/HighlightRuleFragmentProvider.kt index a2659f0be..57754aab5 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightrule/HighlightRuleFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/highlightrule/HighlightRuleFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class HighlightRuleFragmentProvider { +interface HighlightRuleFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: HighlightRuleActivity): FragmentActivity + fun bindFragmentActivity(activity: HighlightRuleActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindHighlightRuleFragment(): HighlightRuleFragment + fun bindHighlightRuleFragment(): HighlightRuleFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt index 38763e5ba..255cb8857 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt @@ -27,7 +27,6 @@ import android.widget.Button import android.widget.EditText import androidx.appcompat.widget.SwitchCompat import androidx.core.view.ViewCompat -import androidx.lifecycle.Observer import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -116,21 +115,21 @@ abstract class IdentityBaseFragment(private val initDefault: Boolean) : .filter(Optional<ISession>::isPresent) .map(Optional<ISession>::get) .firstElement() - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { it?.let { update(Defaults.identity(requireContext(), it.proxy)) } - }) + } } else { modelHelper.identities.map { Optional.ofNullable(it[identityId]) } .filter(Optional<Identity>::isPresent) .map(Optional<Identity>::get) .firstElement() - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { it?.let { update(it) } - }) + } } detachAway.setDependent(detachAwayGroup) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityCreateFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityCreateFragmentProvider.kt index 2f93ae0cd..9d4d56ffd 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityCreateFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityCreateFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class IdentityCreateFragmentProvider { +interface IdentityCreateFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: IdentityCreateActivity): FragmentActivity + fun bindFragmentActivity(activity: IdentityCreateActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindIdentityCreateFragment(): IdentityCreateFragment + fun bindIdentityCreateFragment(): IdentityCreateFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditFragmentProvider.kt index 7c7a4f455..5f4a04a73 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class IdentityEditFragmentProvider { +interface IdentityEditFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: IdentityEditActivity): FragmentActivity + fun bindFragmentActivity(activity: IdentityEditActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindIdentityEditFragment(): IdentityEditFragment + fun bindIdentityEditFragment(): IdentityEditFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignoreitem/IgnoreItemFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignoreitem/IgnoreItemFragmentProvider.kt index b9131763f..c86b4319b 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignoreitem/IgnoreItemFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignoreitem/IgnoreItemFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class IgnoreItemFragmentProvider { +interface IgnoreItemFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: IgnoreItemActivity): FragmentActivity + fun bindFragmentActivity(activity: IgnoreItemActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindIgnoreItemFragment(): IgnoreItemFragment + fun bindIgnoreItemFragment(): IgnoreItemFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragment.kt index 76b0c429b..54acd8b40 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragment.kt @@ -25,7 +25,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager @@ -86,7 +85,7 @@ class IgnoreListFragment : ServiceBoundSettingsFragment(), Savable, modelHelper.ignoreListManager .filter(Optional<IgnoreListManager>::isPresent) .map(Optional<IgnoreListManager>::get) - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { if (it != null) { if (this.ignoreListManager == null) { this.ignoreListManager = Pair(it, it.copy()) @@ -95,7 +94,7 @@ class IgnoreListFragment : ServiceBoundSettingsFragment(), Savable, } } } - }) + } return view } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragmentProvider.kt index ecac100e6..db19f8fca 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/ignorelist/IgnoreListFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class IgnoreListFragmentProvider { +interface IgnoreListFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: IgnoreListActivity): FragmentActivity + fun bindFragmentActivity(activity: IgnoreListActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindIgnoreFragment(): IgnoreListFragment + fun bindIgnoreFragment(): IgnoreListFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt index 39bd8cc55..9381eeacb 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt @@ -30,7 +30,6 @@ import android.widget.EditText import android.widget.Spinner import androidx.appcompat.widget.SwitchCompat import androidx.core.view.ViewCompat -import androidx.lifecycle.Observer import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -149,7 +148,7 @@ abstract class NetworkBaseFragment(private val initDefault: Boolean) : combineLatest(it.values.map(Identity::liveUpdates)).map { it.sortedBy(Identity::identityName) } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { if (it != null) { val selectOriginal = identity.selectedItemId == Spinner.INVALID_ROW_ID identityAdapter.submitList(it) @@ -159,37 +158,37 @@ abstract class NetworkBaseFragment(private val initDefault: Boolean) : } } } - }) + } if (initDefault) { modelHelper.connectedSession .filter(Optional<ISession>::isPresent) .map(Optional<ISession>::get) .firstElement() - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { it?.let { update(Defaults.network(requireContext(), it.proxy), identityAdapter) } - }) + } } else { modelHelper.networks.map { Optional.ofNullable(it[networkId]) } .filter(Optional<Network>::isPresent) .map(Optional<Network>::get) .firstElement() .toLiveData() - .observe(viewLifecycleOwner, Observer { + .observe(viewLifecycleOwner) { it?.let { update(it, identityAdapter) } - }) + } modelHelper.networks.map { Optional.ofNullable(it[networkId]) } .filter(Optional<Network>::isPresent) .map(Optional<Network>::get) .safeSwitchMap(Network::liveCaps) .toLiveData() - .observe(viewLifecycleOwner, Observer { + .observe(viewLifecycleOwner) { autoidentifyWarning.visibleIf(it.contains("sasl")) - }) + } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateFragmentProvider.kt index 9c84fc30c..0dca07a5f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkCreateFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class NetworkCreateFragmentProvider { +interface NetworkCreateFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: NetworkCreateActivity): FragmentActivity + fun bindFragmentActivity(activity: NetworkCreateActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindNetworkCreateFragment(): NetworkCreateFragment + fun bindNetworkCreateFragment(): NetworkCreateFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditFragmentProvider.kt index fc9218f1a..0bbebbc96 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class NetworkEditFragmentProvider { +interface NetworkEditFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: NetworkEditActivity): FragmentActivity + fun bindFragmentActivity(activity: NetworkEditActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindNetworkEditFragment(): NetworkEditFragment + fun bindNetworkEditFragment(): NetworkEditFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragment.kt index 6de953ceb..c1e3d5b0b 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragment.kt @@ -25,7 +25,6 @@ import android.view.View import android.view.ViewGroup import android.widget.EditText import androidx.appcompat.widget.SwitchCompat -import androidx.lifecycle.Observer import de.kuschku.libquassel.quassel.syncables.NetworkConfig import de.kuschku.libquassel.util.Optional import de.kuschku.quasseldroid.R @@ -73,7 +72,7 @@ class NetworkConfigFragment : ServiceBoundSettingsFragment(), Savable, .filter(Optional<NetworkConfig>::isPresent) .map(Optional<NetworkConfig>::get) .firstElement() - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { it?.let { if (this.networkConfig == null) { this.networkConfig = Pair(it, it.copy()) @@ -91,7 +90,7 @@ class NetworkConfigFragment : ServiceBoundSettingsFragment(), Savable, } } } - }) + } pingTimeoutEnabled.setDependent(pingTimeoutGroup) autoWhoEnabled.setDependent(autoWhoGroup) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragmentProvider.kt index 312d14e15..d59ebede1 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkconfig/NetworkConfigFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class NetworkConfigFragmentProvider { +interface NetworkConfigFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: NetworkConfigActivity): FragmentActivity + fun bindFragmentActivity(activity: NetworkConfigActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindNetworkConfigFragment(): NetworkConfigFragment + fun bindNetworkConfigFragment(): NetworkConfigFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragmentProvider.kt index d7f234d07..598d398ae 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/networkserver/NetworkServerFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class NetworkServerFragmentProvider { +interface NetworkServerFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: NetworkServerActivity): FragmentActivity + fun bindFragmentActivity(activity: NetworkServerActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindNetworkServerFragment(): NetworkServerFragment + fun bindNetworkServerFragment(): NetworkServerFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragment.kt index 90eb2db3b..cb6981b80 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragment.kt @@ -27,7 +27,6 @@ import android.view.ViewGroup import android.widget.Button import android.widget.EditText import android.widget.TextView -import androidx.lifecycle.Observer import com.google.android.material.textfield.TextInputLayout import de.kuschku.libquassel.quassel.syncables.RpcHandler import de.kuschku.libquassel.session.ISession @@ -87,7 +86,7 @@ class PasswordChangeFragment : ServiceBoundFragment() { .mapSwitchMap(RpcHandler::passwordChanged) .filter(Optional<Boolean>::isPresent) .map(Optional<Boolean>::get) - .toLiveData().observe(viewLifecycleOwner, Observer { + .toLiveData().observe(viewLifecycleOwner) { val waiting = this.waiting if (waiting != null) { if (it) { @@ -105,7 +104,7 @@ class PasswordChangeFragment : ServiceBoundFragment() { error.visibility = View.VISIBLE } } - }) + } oldPassword.addTextChangedListener(object : TextValidator( activity, diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragmentProvider.kt index 4145d74de..aaef99ae9 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/passwordchange/PasswordChangeFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class PasswordChangeFragmentProvider { +interface PasswordChangeFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: PasswordChangeActivity): FragmentActivity + fun bindFragmentActivity(activity: PasswordChangeActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindPasswordChangeFragment(): PasswordChangeFragment + fun bindPasswordChangeFragment(): PasswordChangeFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragment.kt index ca16bd675..09f18ce96 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragment.kt @@ -24,7 +24,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView -import androidx.lifecycle.Observer import de.kuschku.libquassel.ssl.X509Helper import de.kuschku.libquassel.ssl.commonName import de.kuschku.libquassel.ssl.organization @@ -93,7 +92,7 @@ class CertificateInfoFragment : ServiceBoundSettingsFragment() { val dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) - modelHelper.peerCertificateChain.toLiveData().observe(viewLifecycleOwner, Observer { + modelHelper.peerCertificateChain.toLiveData().observe(viewLifecycleOwner) { val leafCertificate = it.firstOrNull() if (leafCertificate != null) { content.visibility = View.VISIBLE @@ -135,7 +134,7 @@ class CertificateInfoFragment : ServiceBoundSettingsFragment() { content.visibility = View.GONE error.visibility = View.VISIBLE } - }) + } return view } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragmentProvider.kt index 79610c535..28cc0b521 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/certificate/CertificateInfoFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class CertificateInfoFragmentProvider { +interface CertificateInfoFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: CertificateInfoActivity): FragmentActivity + fun bindFragmentActivity(activity: CertificateInfoActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindCertificateInfoFragment(): CertificateInfoFragment + fun bindCertificateInfoFragment(): CertificateInfoFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt index 867d9b18a..ba87307b2 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt @@ -19,6 +19,7 @@ package de.kuschku.quasseldroid.ui.info.channel +import android.content.Context import android.os.Build import android.os.Bundle import android.view.LayoutInflater @@ -26,7 +27,6 @@ import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.TextView -import androidx.lifecycle.Observer import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.protocol.NetworkId @@ -37,10 +37,17 @@ import de.kuschku.libquassel.util.helper.safeSwitchMap import de.kuschku.libquassel.util.helper.value import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.settings.MessageSettings +import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.ui.chat.topic.TopicActivity import de.kuschku.quasseldroid.util.ShortcutCreationHelper -import de.kuschku.quasseldroid.util.helper.* +import de.kuschku.quasseldroid.util.helper.getVectorDrawableCompat +import de.kuschku.quasseldroid.util.helper.retint +import de.kuschku.quasseldroid.util.helper.setTooltip +import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.helper.visibleIf import de.kuschku.quasseldroid.util.irc.format.ContentFormatter +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.util.ui.BetterLinkMovementMethod import de.kuschku.quasseldroid.util.ui.LinkLongClickMenuHelper @@ -62,9 +69,24 @@ class ChannelInfoFragment : ServiceBoundFragment() { @Inject lateinit var messageSettings: MessageSettings + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + @Inject lateinit var modelHelper: EditorViewModelHelper + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.info_channel, container, false) @@ -104,11 +126,12 @@ class ChannelInfoFragment : ServiceBoundFragment() { channel.updates().map { Pair(info, it) } - }.toLiveData().observe(viewLifecycleOwner, Observer { (info, channel) -> + }.toLiveData().observe(viewLifecycleOwner) { (info, channel) -> name.text = channel.name() val (content, hasSpoilers) = contentFormatter.formatContent( channel.topic(), - networkId = channel.network().networkId() + networkId = channel.network().networkId(), + linkClickListener = linkClickListener, ) topic.text = content @@ -155,7 +178,7 @@ class ChannelInfoFragment : ServiceBoundFragment() { } } } - }) + } val movementMethod = BetterLinkMovementMethod.newInstance() movementMethod.setOnLinkLongClickListener(LinkLongClickMenuHelper()) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragmentProvider.kt index f87d5ca29..8e4155050 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class ChannelInfoFragmentProvider { +interface ChannelInfoFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: ChannelInfoActivity): FragmentActivity + fun bindFragmentActivity(activity: ChannelInfoActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindChannelInfoFragment(): ChannelInfoFragment + fun bindChannelInfoFragment(): ChannelInfoFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt index e9352e809..f5d1c4415 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt @@ -29,14 +29,15 @@ import androidx.recyclerview.widget.RecyclerView import de.kuschku.libquassel.quassel.syncables.IrcListHelper import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.databinding.WidgetChannelSearchBinding -import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.util.ColorContext import de.kuschku.quasseldroid.util.helper.styledAttributes import de.kuschku.quasseldroid.util.irc.format.ContentFormatter +import de.kuschku.quasseldroid.util.listener.LinkClickListener import javax.inject.Inject class ChannelListAdapter @Inject constructor( private val contentFormatter: ContentFormatter, + private var linkClickListener: LinkClickListener, context: Context, colorContext: ColorContext ) : @@ -55,12 +56,17 @@ class ChannelListAdapter @Inject constructor( getColor(0, 0) } + fun setOnClickListener(listener: LinkClickListener) { + this.linkClickListener = listener + } + private val fallbackDrawable = colorContext.buildTextDrawable("#", colorAccent) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelViewHolder { return ChannelViewHolder( WidgetChannelSearchBinding.inflate(LayoutInflater.from(parent.context), parent, false), contentFormatter, + linkClickListener, fallbackDrawable ) } @@ -72,6 +78,7 @@ class ChannelListAdapter @Inject constructor( class ChannelViewHolder( private val binding: WidgetChannelSearchBinding, private val contentFormatter: ContentFormatter, + private val linkClickListener: LinkClickListener, fallbackDrawable: Drawable ) : RecyclerView.ViewHolder(binding.root) { private var data: IrcListHelper.ChannelDescription? = null @@ -80,11 +87,7 @@ class ChannelListAdapter @Inject constructor( binding.status.setImageDrawable(fallbackDrawable) itemView.setOnClickListener { data?.let { - ChatActivity.launch( - itemView.context, - networkId = it.netId, - channel = it.channelName - ) + linkClickListener.openChannel(it.netId, it.channelName) } } } @@ -93,7 +96,8 @@ class ChannelListAdapter @Inject constructor( binding.name.text = data.channelName val (content, hasSpoilers) = contentFormatter.formatContent( data.topic, - networkId = data.netId + networkId = data.netId, + linkClickListener = linkClickListener ) binding.topic.text = content binding.users.text = itemView.context.resources.getQuantityString(R.plurals.label_user_count, diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt index 8df314a05..fa73457b3 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt @@ -19,12 +19,12 @@ package de.kuschku.quasseldroid.ui.info.channellist +import android.content.Context import android.os.Bundle import android.view.* import android.widget.EditText import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.AppCompatImageButton -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -36,8 +36,11 @@ import de.kuschku.libquassel.util.helper.combineLatest import de.kuschku.libquassel.util.helper.mapSwitchMap import de.kuschku.libquassel.util.helper.value import de.kuschku.quasseldroid.R +import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.util.helper.retint import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment import de.kuschku.quasseldroid.util.ui.view.MaterialContentLoadingProgressBar import de.kuschku.quasseldroid.util.ui.view.WarningBarView @@ -59,6 +62,10 @@ class ChannelListFragment : ServiceBoundSettingsFragment() { @Inject lateinit var modelHelper: EditorViewModelHelper + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + private var query: Query? = null private var state: State = State() @@ -98,6 +105,17 @@ class ChannelListFragment : ServiceBoundSettingsFragment() { val results = BehaviorSubject.createDefault(emptyList<IrcListHelper.ChannelDescription>()) val sort = BehaviorSubject.createDefault(Sort(Sort.Field.CHANNEL_NAME, Sort.Direction.ASC)) + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.info_channellist, container, false) @@ -109,66 +127,76 @@ class ChannelListFragment : ServiceBoundSettingsFragment() { val networkId = NetworkId(arguments?.getInt("network_id", -1) ?: -1) + adapter.setOnClickListener(linkClickListener) searchResults.adapter = adapter searchResults.layoutManager = LinearLayoutManager(view.context) searchResults.itemAnimator = DefaultItemAnimator() - combineLatest(results, sort).toLiveData().observe(viewLifecycleOwner, - Observer { (results, sort) -> - adapter.submitList(results.let { - when (sort.field) { - Sort.Field.CHANNEL_NAME -> { - when (sort.direction) { - Sort.Direction.ASC -> - it.sortedBy(IrcListHelper.ChannelDescription::channelName) - Sort.Direction.DESC -> - it.sortedByDescending( - IrcListHelper.ChannelDescription::channelName) - } - } - Sort.Field.USER_COUNT -> { - when (sort.direction) { - Sort.Direction.ASC -> - it.sortedBy(IrcListHelper.ChannelDescription::userCount) - Sort.Direction.DESC -> - it.sortedByDescending( - IrcListHelper.ChannelDescription::userCount) - } - } - Sort.Field.TOPIC -> { - when (sort.direction) { - Sort.Direction.ASC -> - it.sortedBy(IrcListHelper.ChannelDescription::topic) - Sort.Direction.DESC -> - it.sortedByDescending( - IrcListHelper.ChannelDescription::topic) - } - } - } - }) - }) + combineLatest(results, sort).toLiveData().observe(viewLifecycleOwner) { (results, sort) -> + adapter.submitList(results.let { + when (sort.field) { + Sort.Field.CHANNEL_NAME -> { + when (sort.direction) { + Sort.Direction.ASC -> + it.sortedBy(IrcListHelper.ChannelDescription::channelName) + + Sort.Direction.DESC -> + it.sortedByDescending( + IrcListHelper.ChannelDescription::channelName + ) + } + } + + Sort.Field.USER_COUNT -> { + when (sort.direction) { + Sort.Direction.ASC -> + it.sortedBy(IrcListHelper.ChannelDescription::userCount) + + Sort.Direction.DESC -> + it.sortedByDescending( + IrcListHelper.ChannelDescription::userCount + ) + } + } + + Sort.Field.TOPIC -> { + when (sort.direction) { + Sort.Direction.ASC -> + it.sortedBy(IrcListHelper.ChannelDescription::topic) + + Sort.Direction.DESC -> + it.sortedByDescending( + IrcListHelper.ChannelDescription::topic + ) + } + } + } + }) + } modelHelper.ircListHelper .mapSwitchMap(IrcListHelper::observable) .filter(Optional<IrcListHelper.Event>::isPresent) .map(Optional<IrcListHelper.Event>::get) - .toLiveData(BackpressureStrategy.BUFFER).observe(viewLifecycleOwner, Observer { + .toLiveData(BackpressureStrategy.BUFFER).observe(viewLifecycleOwner) { when (it) { is IrcListHelper.Event.ChannelList -> { if (it.netId == query?.networkId) { results.onNext(it.data) } } - is IrcListHelper.Event.Finished -> { + + is IrcListHelper.Event.Finished -> { if (it.netId == query?.networkId) { updateState(false, null) } } - is IrcListHelper.Event.Error -> { + + is IrcListHelper.Event.Error -> { updateState(false, it.error) } } - }) + } searchButton.setOnClickListener { modelHelper.ircListHelper.value?.orNull()?.let { ircListHelper -> diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragmentProvider.kt index 6844f76b6..cd1f1f664 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class ChannelListFragmentProvider { +interface ChannelListFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: ChannelListActivity): FragmentActivity + fun bindFragmentActivity(activity: ChannelListActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindChannelListFragment(): ChannelListFragment + fun bindChannelListFragment(): ChannelListFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt index e1ce42197..16093c07f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragment.kt @@ -27,7 +27,6 @@ import android.view.ViewGroup import android.widget.Button import android.widget.ImageView import android.widget.TextView -import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import de.kuschku.libquassel.quassel.QuasselFeatures @@ -38,7 +37,12 @@ import de.kuschku.libquassel.util.helper.combineLatest import de.kuschku.libquassel.util.helper.value import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.ui.info.certificate.CertificateInfoActivity -import de.kuschku.quasseldroid.util.helper.* +import de.kuschku.quasseldroid.util.helper.getVectorDrawableCompat +import de.kuschku.quasseldroid.util.helper.isValid +import de.kuschku.quasseldroid.util.helper.styledAttributes +import de.kuschku.quasseldroid.util.helper.tint +import de.kuschku.quasseldroid.util.helper.toLiveData +import de.kuschku.quasseldroid.util.helper.visibleIf import de.kuschku.quasseldroid.util.missingfeatures.MissingFeature import de.kuschku.quasseldroid.util.missingfeatures.MissingFeaturesDialog import de.kuschku.quasseldroid.util.missingfeatures.RequiredFeatures @@ -100,12 +104,12 @@ class CoreInfoFragment : ServiceBoundFragment() { var missingFeatureList: List<MissingFeature> = emptyList() combineLatest(modelHelper.coreInfo, modelHelper.coreFeatures).toLiveData() - .observe(viewLifecycleOwner, Observer { + .observe(viewLifecycleOwner) { val data = it?.first?.orNull() val connected = it?.second?.first - ?: false + ?: false val features = it?.second?.second - ?: QuasselFeatures.empty() + ?: QuasselFeatures.empty() version.text = data?.quasselVersion?.let(Html::fromHtml) val versionTime = data?.quasselBuildDate?.toLongOrNull() @@ -120,10 +124,12 @@ class CoreInfoFragment : ServiceBoundFragment() { missingFeatures.visibleIf(connected && missingFeatureList.isNotEmpty()) val startTime = data?.startTime?.atZone(ZoneId.systemDefault())?.let(dateTimeFormatter::format) - uptime.text = requireContext().getString(R.string.label_core_online_since, - startTime.toString()) + uptime.text = requireContext().getString( + R.string.label_core_online_since, + startTime.toString() + ) uptimeContainer.visibleIf(startTime != null) - }) + } missingFeatures.setOnClickListener { MissingFeaturesDialog.Builder(requireActivity()) .missingFeatures(missingFeatureList) @@ -150,7 +156,7 @@ class CoreInfoFragment : ServiceBoundFragment() { CertificateInfoActivity.launch(it.context) } - modelHelper.sslSession.toLiveData().observe(viewLifecycleOwner, Observer { + modelHelper.sslSession.toLiveData().observe(viewLifecycleOwner) { val peerCertificateChain = try { it?.orNull()?.peerCertificateChain } catch (ignored: SSLPeerUnverifiedException) { @@ -190,22 +196,28 @@ class CoreInfoFragment : ServiceBoundFragment() { if (cipherSuite != null && keyExchangeMechanism != null && protocol != null) { // TLSv1.3 has no key exchange mechanism in the ciphersuite if (keyExchangeMechanism.isEmpty()) { - secureConnectionCiphersuite.text = context?.getString(R.string.label_core_connection_ciphersuite_13, - cipherSuite) + secureConnectionCiphersuite.text = context?.getString( + R.string.label_core_connection_ciphersuite_13, + cipherSuite + ) } else { - secureConnectionCiphersuite.text = context?.getString(R.string.label_core_connection_ciphersuite, - cipherSuite, - keyExchangeMechanism) + secureConnectionCiphersuite.text = context?.getString( + R.string.label_core_connection_ciphersuite, + cipherSuite, + keyExchangeMechanism + ) } - secureConnectionProtocol.text = context?.getString(R.string.label_core_connection_protocol, - protocol) + secureConnectionProtocol.text = context?.getString( + R.string.label_core_connection_protocol, + protocol + ) secureConnectionProtocol.visibility = View.VISIBLE secureConnectionCiphersuite.visibility = View.VISIBLE } else { secureConnectionProtocol.visibility = View.GONE secureConnectionCiphersuite.visibility = View.GONE } - }) + } clients.layoutManager = LinearLayoutManager(requireContext()) val adapter = ClientAdapter() @@ -216,10 +228,10 @@ class CoreInfoFragment : ServiceBoundFragment() { rpcHandler?.requestKickClient(it) } clients.adapter = adapter - modelHelper.coreInfoClients.toLiveData().observe(viewLifecycleOwner, Observer { + modelHelper.coreInfoClients.toLiveData().observe(viewLifecycleOwner) { clientsTitle.visibleIf(it?.isNotEmpty() == true) adapter.submitList(it) - }) + } return view } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragmentProvider.kt index 441573d02..d30add9e6 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/core/CoreInfoFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class CoreInfoFragmentProvider { +interface CoreInfoFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: CoreInfoActivity): FragmentActivity + fun bindFragmentActivity(activity: CoreInfoActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindCoreInfoFragment(): CoreInfoFragment + fun bindCoreInfoFragment(): CoreInfoFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/ChannelAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/ChannelAdapter.kt index 365450c1d..399cb5729 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/ChannelAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/ChannelAdapter.kt @@ -26,7 +26,6 @@ import androidx.recyclerview.widget.RecyclerView import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.quasseldroid.databinding.WidgetBufferBinding -import de.kuschku.quasseldroid.ui.chat.ChatActivity import de.kuschku.quasseldroid.util.helper.visibleIf import de.kuschku.quasseldroid.util.lists.ListAdapter import de.kuschku.quasseldroid.viewmodel.data.BufferProps @@ -61,12 +60,10 @@ class ChannelAdapter : ListAdapter<BufferProps, ChannelAdapter.ChannelViewHolder init { itemView.setOnClickListener { - info?.let { - ChatActivity.launch( - itemView.context, - networkId = it.networkId, - channel = it.bufferName - ) + info?.let { bufferInfo -> + bufferInfo.bufferName?.let { bufferName -> + clickListener?.invoke(bufferInfo.networkId, bufferName) + } } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt index 5dc6b3626..b79caaecb 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt @@ -33,7 +33,6 @@ import android.widget.Button import android.widget.ImageView import android.widget.TextView import androidx.appcompat.widget.PopupMenu -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -63,6 +62,9 @@ import de.kuschku.quasseldroid.util.helper.* import de.kuschku.quasseldroid.util.irc.format.ContentFormatter import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.irc.format.spans.IrcItalicSpan +import de.kuschku.quasseldroid.util.listener.AutocompleteTextListener +import de.kuschku.quasseldroid.util.listener.LinkClickListener +import de.kuschku.quasseldroid.util.listener.QuasselLinkClickListener import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.util.ui.BetterLinkMovementMethod import de.kuschku.quasseldroid.util.ui.LinkLongClickMenuHelper @@ -110,8 +112,26 @@ class UserInfoFragment : ServiceBoundFragment() { @Inject lateinit var modelHelper: EditorViewModelHelper + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + + @Inject + lateinit var autocompleteTextListener: AutocompleteTextListener + private var actualUrl: String? = null + override fun onAttach(context: Context) { + super.onAttach(context) + linkClickListener = QuasselLinkClickListener(internalLinkClickListener) { + activity?.let { + if (it !is ChatActivity) { + it.finish() + } + } + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.info_user, container, false) @@ -146,6 +166,9 @@ class UserInfoFragment : ServiceBoundFragment() { var currentIrcUser: IrcUser? val commonChannelsAdapter = ChannelAdapter() + commonChannelsAdapter.setOnClickListener { networkId, channel -> + linkClickListener.openChannel(networkId, channel) + } commonChannels.layoutManager = LinearLayoutManager(context) commonChannels.itemAnimator = DefaultItemAnimator() commonChannels.adapter = commonChannelsAdapter @@ -256,7 +279,7 @@ class UserInfoFragment : ServiceBoundFragment() { ) } } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { val live = it val user = it.meta @@ -275,6 +298,7 @@ class UserInfoFragment : ServiceBoundFragment() { is String -> { actualUrl = model } + is Avatar.MatrixAvatar -> { runInBackground { matrixApi.avatarUrl(model.userId).execute().body() @@ -295,10 +319,11 @@ class UserInfoFragment : ServiceBoundFragment() { } } } - nick.text = ircFormatDeserializer.formatString(user.nick, messageSettings.colorizeMirc) + nick.text = ircFormatDeserializer.formatString(user.nick, messageSettings.colorizeMirc, linkClickListener) val (content, _) = contentFormatter.formatContent( user.realName ?: "", - networkId = user.networkId + networkId = user.networkId, + linkClickListener = linkClickListener, ) realName.text = content realName.visibleIf(!user.realName.isNullOrBlank() && user.realName != user.nick) @@ -314,13 +339,15 @@ class UserInfoFragment : ServiceBoundFragment() { accountContainer.visibleIf(!user.account.isNullOrBlank()) val (userIdent, _) = contentFormatter.formatContent( user.user ?: "", - networkId = user.networkId + networkId = user.networkId, + linkClickListener = linkClickListener, ) ident.text = userIdent identContainer.visibleIf(userIdent.isNotBlank()) val (userHost, _) = contentFormatter.formatContent( user.host ?: "", - networkId = user.networkId + networkId = user.networkId, + linkClickListener = linkClickListener, ) host.text = userHost hostContainer.visibleIf(userHost.isNotBlank()) @@ -336,7 +363,8 @@ class UserInfoFragment : ServiceBoundFragment() { ) if (info != null) { - ChatActivity.launch(view.context, bufferId = info.bufferId) + linkClickListener.openBuffer(bufferId = info.bufferId) + activity?.finish() } else { modelHelper.allBuffers.map { listOfNotNull(it.find { @@ -344,11 +372,12 @@ class UserInfoFragment : ServiceBoundFragment() { }) }.filter { it.isNotEmpty() - }.firstElement().toLiveData().observe(viewLifecycleOwner, Observer { + }.firstElement().toLiveData().observe(viewLifecycleOwner) { it?.firstOrNull()?.let { info -> - ChatActivity.launch(view.context, bufferId = info.bufferId) + linkClickListener.openBuffer(bufferId = info.bufferId) + activity?.finish() } - }) + } session.bufferSyncer.find( networkId = user.networkId, @@ -381,6 +410,7 @@ class UserInfoFragment : ServiceBoundFragment() { ignoreMenu = null true } + it.itemId == R.id.action_show -> { IgnoreListActivity.launch( view.context @@ -389,11 +419,13 @@ class UserInfoFragment : ServiceBoundFragment() { ignoreMenu = null true } + it.isCheckable -> { modelHelper.ignoreListManager.value?.orNull() ?.requestToggleIgnoreRule(it.title.toString()) true } + else -> false } } @@ -404,7 +436,8 @@ class UserInfoFragment : ServiceBoundFragment() { } } actionMention.setOnClickListener { view -> - ChatActivity.launch(view.context, sharedText = "${user.nick}: ") + autocompleteTextListener.autocompleteText(user.nick, ": ") + activity?.finish() } actionWhois.setOnClickListener { view -> modelHelper.connectedSession { @@ -432,7 +465,7 @@ class UserInfoFragment : ServiceBoundFragment() { } } commonChannelsAdapter.submitList(live.channels) - }) + } avatar.setOnClickListener { actualUrl?.let { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragmentProvider.kt index 73b41ea10..62163c89c 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class UserInfoFragmentProvider { +interface UserInfoFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: UserInfoActivity): FragmentActivity + fun bindFragmentActivity(activity: UserInfoActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindUserInfoFragment(): UserInfoFragment + fun bindUserInfoFragment(): UserInfoFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragmentProvider.kt index 292bee3a1..5fae3486a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/edit/AccountEditFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class AccountEditFragmentProvider { +interface AccountEditFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: AccountEditActivity): FragmentActivity + fun bindFragmentActivity(activity: AccountEditActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindAccountEditFragment(): AccountEditFragment + fun bindAccountEditFragment(): AccountEditFragment } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/selection/AccountSelectionFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/selection/AccountSelectionFragmentProvider.kt index db97a1407..ba38bd171 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/selection/AccountSelectionFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/selection/AccountSelectionFragmentProvider.kt @@ -25,10 +25,10 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class AccountSelectionFragmentProvider { +interface AccountSelectionFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: AccountSelectionActivity): FragmentActivity + fun bindFragmentActivity(activity: AccountSelectionActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindAccountSelectionSlide(): AccountSelectionSlide + fun bindAccountSelectionSlide(): AccountSelectionSlide } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupFragmentProvider.kt index d78ec563c..7df489c06 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/accounts/setup/AccountSetupFragmentProvider.kt @@ -25,16 +25,16 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class AccountSetupFragmentProvider { +interface AccountSetupFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: AccountSetupActivity): FragmentActivity + fun bindFragmentActivity(activity: AccountSetupActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindAccountSetupConnectionSlide(): AccountSetupConnectionSlide + fun bindAccountSetupConnectionSlide(): AccountSetupConnectionSlide @ContributesAndroidInjector - abstract fun bindAccountSetupNameSlide(): AccountSetupNameSlide + fun bindAccountSetupNameSlide(): AccountSetupNameSlide @ContributesAndroidInjector - abstract fun bindAccountSetupUserSlide(): AccountSetupUserSlide + fun bindAccountSetupUserSlide(): AccountSetupUserSlide } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupFragmentProvider.kt index a4da3a29d..4728ee1e5 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupFragmentProvider.kt @@ -25,19 +25,19 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class CoreSetupFragmentProvider { +interface CoreSetupFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: CoreSetupActivity): FragmentActivity + fun bindFragmentActivity(activity: CoreSetupActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindCoreStorageBackendChooseSlide(): CoreStorageBackendChooseSlide + fun bindCoreStorageBackendChooseSlide(): CoreStorageBackendChooseSlide @ContributesAndroidInjector - abstract fun bindCoreStorageBackendSetupSlide(): CoreStorageBackendSetupSlide + fun bindCoreStorageBackendSetupSlide(): CoreStorageBackendSetupSlide @ContributesAndroidInjector - abstract fun bindCoreAuthenticatorBackendChooseSlide(): CoreAuthenticatorBackendChooseSlide + fun bindCoreAuthenticatorBackendChooseSlide(): CoreAuthenticatorBackendChooseSlide @ContributesAndroidInjector - abstract fun bindCoreAuthenticatorBackendSetupSlide(): CoreAuthenticatorBackendSetupSlide + fun bindCoreAuthenticatorBackendSetupSlide(): CoreAuthenticatorBackendSetupSlide } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupFragmentProvider.kt index 923317f7f..8f1c4ce54 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupFragmentProvider.kt @@ -25,13 +25,13 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class NetworkSetupFragmentProvider { +interface NetworkSetupFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: NetworkSetupActivity): FragmentActivity + fun bindFragmentActivity(activity: NetworkSetupActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindNetworkSetupNetworkSlide(): NetworkSetupNetworkSlide + fun bindNetworkSetupNetworkSlide(): NetworkSetupNetworkSlide @ContributesAndroidInjector - abstract fun bindNetworkSetupChannelsSlide(): NetworkSetupChannelsSlide + fun bindNetworkSetupChannelsSlide(): NetworkSetupChannelsSlide } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt index 3acb9c93f..4edd9857e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt @@ -28,7 +28,6 @@ import android.widget.AdapterView import android.widget.EditText import android.widget.Spinner import androidx.appcompat.widget.SwitchCompat -import androidx.lifecycle.Observer import com.google.android.material.textfield.TextInputLayout import de.kuschku.libquassel.protocol.IdentityId import de.kuschku.libquassel.protocol.NetworkId @@ -180,22 +179,22 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() { combineLatest(it.values.map(Identity::liveUpdates)).map { it.sortedBy(Identity::identityName) } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { if (it != null) { identityAdapter.submitList(it) } - }) + } modelHelper.networks.safeSwitchMap { combineLatest(it.values.map(Network::liveNetworkInfo)).map { it.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, INetwork.NetworkInfo::networkName)) } - }.toLiveData().observe(viewLifecycleOwner, Observer { + }.toLiveData().observe(viewLifecycleOwner) { if (it != null) { this.networks = it update() } - }) + } identity.adapter = identityAdapter diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt index 58641d0aa..71b4d8ead 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupActivity.kt @@ -23,7 +23,6 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle -import androidx.lifecycle.Observer import de.kuschku.libquassel.protocol.IdentityId import de.kuschku.libquassel.quassel.syncables.Identity import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork @@ -63,9 +62,10 @@ class UserSetupActivity : ServiceBoundSetupActivity() { .filter(Collection<Identity>::isNotEmpty) .map(Collection<Identity>::first) .firstElement() - .toLiveData().observe(this@UserSetupActivity, Observer { + .toLiveData().observe(this@UserSetupActivity) { if (it != null) { - createNetwork(INetwork.NetworkInfo( + createNetwork( + INetwork.NetworkInfo( networkName = network.name, identity = it.id(), serverList = network.servers.map { @@ -79,7 +79,7 @@ class UserSetupActivity : ServiceBoundSetupActivity() { backend.requestConnectNewNetwork() } - }) + } } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupFragmentProvider.kt index e76e3f416..88fe5f44a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupFragmentProvider.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupFragmentProvider.kt @@ -25,16 +25,16 @@ import dagger.Module import dagger.android.ContributesAndroidInjector @Module -abstract class UserSetupFragmentProvider { +interface UserSetupFragmentProvider { @Binds - abstract fun bindFragmentActivity(activity: UserSetupActivity): FragmentActivity + fun bindFragmentActivity(activity: UserSetupActivity): FragmentActivity @ContributesAndroidInjector - abstract fun bindUserSetupIdentitySlide(): UserSetupIdentitySlide + fun bindUserSetupIdentitySlide(): UserSetupIdentitySlide @ContributesAndroidInjector - abstract fun bindUserSetupNetworkSlide(): UserSetupNetworkSlide + fun bindUserSetupNetworkSlide(): UserSetupNetworkSlide @ContributesAndroidInjector - abstract fun bindUserSetupChannelsSlide(): UserSetupChannelsSlide + fun bindUserSetupChannelsSlide(): UserSetupChannelsSlide } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupIdentitySlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupIdentitySlide.kt index de98aa393..47081bddc 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupIdentitySlide.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/user/UserSetupIdentitySlide.kt @@ -32,6 +32,7 @@ import de.kuschku.quasseldroid.util.Patterns import de.kuschku.quasseldroid.util.TextValidator import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener import javax.inject.Inject class UserSetupIdentitySlide : SlideFragment() { @@ -46,6 +47,10 @@ class UserSetupIdentitySlide : SlideFragment() { @Inject lateinit var ircFormatDeserializer: IrcFormatDeserializer + @Inject + lateinit var internalLinkClickListener: LinkClickListener + lateinit var linkClickListener: LinkClickListener + override fun isValid(): Boolean { return nickValidator.isValid } @@ -57,7 +62,7 @@ class UserSetupIdentitySlide : SlideFragment() { if (data.containsKey("nick")) nickField.setText(data.getString("nick")) if (data.containsKey("realname")) - realnameField.setText(ircFormatDeserializer.formatString(data.getString("realname"), true)) + realnameField.setText(ircFormatDeserializer.formatString(data.getString("realname"), true, linkClickListener)) updateValidity() } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/ContentFormatter.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/ContentFormatter.kt index 8a50b32c5..ef88427b9 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/ContentFormatter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/ContentFormatter.kt @@ -33,6 +33,7 @@ import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.util.helper.styledAttributes import de.kuschku.quasseldroid.util.irc.format.model.FormatInfo import de.kuschku.quasseldroid.util.irc.format.model.IrcFormat +import de.kuschku.quasseldroid.util.listener.LinkClickListener import de.kuschku.quasseldroid.util.ui.SpanFormatter import org.intellij.lang.annotations.Language import javax.inject.Inject @@ -77,15 +78,19 @@ class ContentFormatter @Inject constructor( getColor(0, 0) } - fun formatContent(content: String, - highlight: Boolean = false, - unhideSpoilers: Boolean = false, - networkId: NetworkId?): Pair<CharSequence, Boolean> { + fun formatContent( + content: String, + highlight: Boolean = false, + unhideSpoilers: Boolean = false, + networkId: NetworkId?, + linkClickListener: LinkClickListener, + ): Pair<CharSequence, Boolean> { val spans = mutableListOf<FormatInfo>() val formattedText = SpannableString( ircFormatDeserializer.formatString( content, messageSettings.colorizeMirc, + linkClickListener, spans ) ) @@ -139,7 +144,7 @@ class ContentFormatter @Inject constructor( } for (span in spans) { - span.apply(formattedText) + span.apply(formattedText, linkClickListener) } return Pair(formattedText, hasSpoilers) diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializer.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializer.kt index 30619f43e..796a2d54a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializer.kt @@ -26,6 +26,7 @@ import de.kuschku.quasseldroid.util.helper.getColorCompat import de.kuschku.quasseldroid.util.irc.format.model.FormatDescription import de.kuschku.quasseldroid.util.irc.format.model.FormatInfo import de.kuschku.quasseldroid.util.irc.format.model.IrcFormat +import de.kuschku.quasseldroid.util.listener.LinkClickListener import javax.inject.Inject /** @@ -71,6 +72,7 @@ class IrcFormatDeserializer(private val mircColors: IntArray) { * @return a CharSequence with Android’s span format representing the input string */ fun formatString(str: String?, colorize: Boolean, + onClickListener: LinkClickListener, output: MutableList<FormatInfo>? = null): CharSequence { if (str == null) return "" @@ -87,7 +89,7 @@ class IrcFormatDeserializer(private val mircColors: IntArray) { if (output != null) { output.add(FormatInfo(desc.start, plainText.length, desc.format)) } else { - desc.apply(plainText, plainText.length) + desc.apply(plainText, plainText.length, onClickListener) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/FormatDescription.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/FormatDescription.kt index 47f3328de..7826bd733 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/FormatDescription.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/FormatDescription.kt @@ -20,9 +20,10 @@ package de.kuschku.quasseldroid.util.irc.format.model import android.text.SpannableStringBuilder +import de.kuschku.quasseldroid.util.listener.LinkClickListener class FormatDescription<out U : IrcFormat>(val start: Int, val format: U) { - fun apply(editable: SpannableStringBuilder, end: Int) { - format.applyTo(editable, start, end) + fun apply(editable: SpannableStringBuilder, end: Int, onClickListener: LinkClickListener) { + format.applyTo(editable, start, end, onClickListener) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/FormatInfo.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/FormatInfo.kt index 60abcb748..d8ea4ffc8 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/FormatInfo.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/FormatInfo.kt @@ -20,9 +20,10 @@ package de.kuschku.quasseldroid.util.irc.format.model import android.text.Spannable +import de.kuschku.quasseldroid.util.listener.LinkClickListener data class FormatInfo(val start: Int, val end: Int, val format: IrcFormat) { - fun apply(editable: Spannable) { - format.applyTo(editable, start, end) + fun apply(editable: Spannable, onClickListener: LinkClickListener) { + format.applyTo(editable, start, end, onClickListener) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/IrcFormat.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/IrcFormat.kt index 63fc1135a..23f59564d 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/IrcFormat.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/model/IrcFormat.kt @@ -22,48 +22,57 @@ package de.kuschku.quasseldroid.util.irc.format.model import android.text.Spannable import android.text.Spanned import de.kuschku.libquassel.protocol.NetworkId -import de.kuschku.quasseldroid.util.irc.format.spans.* +import de.kuschku.quasseldroid.util.irc.format.spans.ChannelLinkSpan +import de.kuschku.quasseldroid.util.irc.format.spans.IrcBackgroundColorSpan +import de.kuschku.quasseldroid.util.irc.format.spans.IrcBoldSpan +import de.kuschku.quasseldroid.util.irc.format.spans.IrcForegroundColorSpan +import de.kuschku.quasseldroid.util.irc.format.spans.IrcItalicSpan +import de.kuschku.quasseldroid.util.irc.format.spans.IrcMonospaceSpan +import de.kuschku.quasseldroid.util.irc.format.spans.IrcStrikethroughSpan +import de.kuschku.quasseldroid.util.irc.format.spans.IrcUnderlineSpan +import de.kuschku.quasseldroid.util.irc.format.spans.QuasselURLSpan +import de.kuschku.quasseldroid.util.listener.LinkClickListener sealed class IrcFormat { - abstract fun applyTo(editable: Spannable, from: Int, to: Int) + abstract fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) - object Italic : IrcFormat() { - override fun applyTo(editable: Spannable, from: Int, to: Int) { + data object Italic : IrcFormat() { + override fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) { editable.setSpan(IrcItalicSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } } - object Underline : IrcFormat() { - override fun applyTo(editable: Spannable, from: Int, to: Int) { + data object Underline : IrcFormat() { + override fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) { editable.setSpan(IrcUnderlineSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } } - object Strikethrough : IrcFormat() { - override fun applyTo(editable: Spannable, from: Int, to: Int) { + data object Strikethrough : IrcFormat() { + override fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) { editable.setSpan(IrcStrikethroughSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } } - object Monospace : IrcFormat() { - override fun applyTo(editable: Spannable, from: Int, to: Int) { + data object Monospace : IrcFormat() { + override fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) { editable.setSpan(IrcMonospaceSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } } - object Bold : IrcFormat() { - override fun applyTo(editable: Spannable, from: Int, to: Int) { + data object Bold : IrcFormat() { + override fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) { editable.setSpan(IrcBoldSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) } } data class Hex(val foreground: Int, val background: Int) : IrcFormat() { - override fun applyTo(editable: Spannable, from: Int, to: Int) { + override fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) { if (foreground >= 0) { editable.setSpan( IrcForegroundColorSpan.HEX(foreground or 0xFFFFFF.inv()), from, to, @@ -81,7 +90,7 @@ sealed class IrcFormat { data class Color(val foreground: Byte, val background: Byte, private val mircColors: IntArray) : IrcFormat() { - override fun applyTo(editable: Spannable, from: Int, to: Int) { + override fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) { if (foreground.toInt() >= 0 && foreground.toInt() < mircColors.size) { editable.setSpan( IrcForegroundColorSpan.MIRC(foreground.toInt(), @@ -130,18 +139,18 @@ sealed class IrcFormat { } data class Url(val target: String, val highlight: Boolean) : IrcFormat() { - override fun applyTo(editable: Spannable, from: Int, to: Int) { + override fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) { editable.setSpan( - QuasselURLSpan(target, highlight), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE + QuasselURLSpan(target, highlight, onClickListener), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE ) } } data class Channel(val networkId: NetworkId, val target: String, val highlight: Boolean) : IrcFormat() { - override fun applyTo(editable: Spannable, from: Int, to: Int) { + override fun applyTo(editable: Spannable, from: Int, to: Int, onClickListener: LinkClickListener) { editable.setSpan( - ChannelLinkSpan(networkId, target, highlight), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE + ChannelLinkSpan(networkId, target, highlight, onClickListener), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE ) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/ChannelLinkSpan.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/ChannelLinkSpan.kt index 81f23cc15..f866e8829 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/ChannelLinkSpan.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/ChannelLinkSpan.kt @@ -23,12 +23,13 @@ import android.text.TextPaint import android.text.style.ClickableSpan import android.view.View import de.kuschku.libquassel.protocol.NetworkId -import de.kuschku.quasseldroid.ui.chat.ChatActivity +import de.kuschku.quasseldroid.util.listener.LinkClickListener class ChannelLinkSpan( private val networkId: NetworkId, private val text: String, - private val highlight: Boolean + private val highlight: Boolean, + private val onClickListener: LinkClickListener ) : ClickableSpan() { override fun updateDrawState(ds: TextPaint) { if (!highlight) ds.color = ds.linkColor @@ -36,8 +37,7 @@ class ChannelLinkSpan( } override fun onClick(widget: View) { - ChatActivity.launch( - widget.context, + onClickListener.openChannel( networkId = networkId, channel = text ) diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/QuasselURLSpan.kt b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/QuasselURLSpan.kt index d13cffac9..bab01a2cc 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/QuasselURLSpan.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/irc/format/spans/QuasselURLSpan.kt @@ -19,27 +19,22 @@ package de.kuschku.quasseldroid.util.irc.format.spans -import android.content.ActivityNotFoundException -import android.content.Intent -import android.net.Uri import android.text.TextPaint import android.text.style.URLSpan -import android.util.Log import android.view.View +import de.kuschku.quasseldroid.util.listener.LinkClickListener -class QuasselURLSpan(text: String, private val highlight: Boolean) : URLSpan(text) { +class QuasselURLSpan( + text: String, + private val highlight: Boolean, + private val onClickListener: LinkClickListener +) : URLSpan(text) { override fun updateDrawState(ds: TextPaint) { if (!highlight) ds.color = ds.linkColor ds.isUnderlineText = true } override fun onClick(widget: View) { - try { - widget.context?.startActivity(Intent(Intent.ACTION_VIEW).apply { - data = Uri.parse(url) - }) - } catch (e: ActivityNotFoundException) { - Log.w("QuasselURLSpan", "Actvity was not found for $url") - } + onClickListener.openUrl(url) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/listener/AutocompleteTextHandler.kt b/app/src/main/java/de/kuschku/quasseldroid/util/listener/AutocompleteTextHandler.kt new file mode 100644 index 000000000..39c72d890 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/listener/AutocompleteTextHandler.kt @@ -0,0 +1,25 @@ +package de.kuschku.quasseldroid.util.listener + +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AutocompleteTextHandler @Inject constructor() : AutocompleteTextListener { + private val listeners = mutableListOf<AutocompleteTextListener>() + fun register(listener: AutocompleteTextListener) { + listeners.add(listener) + } + + fun unregister(listener: AutocompleteTextListener) { + listeners.remove(listener) + } + + override fun autocompleteText( + text: CharSequence, + suffix: String?, + ) { + for (listener in listeners) { + listener.autocompleteText(text, suffix) + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/listener/AutocompleteTextListener.kt b/app/src/main/java/de/kuschku/quasseldroid/util/listener/AutocompleteTextListener.kt new file mode 100644 index 000000000..b74457ef5 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/listener/AutocompleteTextListener.kt @@ -0,0 +1,8 @@ +package de.kuschku.quasseldroid.util.listener + +interface AutocompleteTextListener { + fun autocompleteText( + text: CharSequence, + suffix: String? + ) +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/listener/LinkClickHandler.kt b/app/src/main/java/de/kuschku/quasseldroid/util/listener/LinkClickHandler.kt new file mode 100644 index 000000000..035a3200c --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/listener/LinkClickHandler.kt @@ -0,0 +1,61 @@ +package de.kuschku.quasseldroid.util.listener + +import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.protocol.NetworkId +import de.kuschku.quasseldroid.persistence.util.AccountId +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LinkClickHandler @Inject constructor() : LinkClickListener { + private val listeners = mutableListOf<LinkClickListener>() + fun register(listener: LinkClickListener) { + listeners.add(listener) + } + + fun unregister(listener: LinkClickListener) { + listeners.remove(listener) + } + + override fun openBuffer( + bufferId: BufferId, + accountId: AccountId?, + forceJoin: Boolean + ) { + for (listener in listeners) { + listener.openBuffer( + bufferId, accountId, forceJoin + ) + } + } + + override fun openDirectMessage( + networkId: NetworkId, + nickName: String, + forceJoin: Boolean + ) { + for (listener in listeners) { + listener.openDirectMessage( + networkId, nickName, forceJoin + ) + } + } + + override fun openChannel( + networkId: NetworkId, + channel: String, + forceJoin: Boolean + ) { + for (listener in listeners) { + listener.openChannel( + networkId, channel, forceJoin + ) + } + } + + override fun openUrl(url: String) { + for (listener in listeners) { + listener.openUrl(url) + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/listener/LinkClickListener.kt b/app/src/main/java/de/kuschku/quasseldroid/util/listener/LinkClickListener.kt new file mode 100644 index 000000000..04ed879b2 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/listener/LinkClickListener.kt @@ -0,0 +1,27 @@ +package de.kuschku.quasseldroid.util.listener + +import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.protocol.NetworkId +import de.kuschku.quasseldroid.persistence.util.AccountId + +interface LinkClickListener { + fun openBuffer( + bufferId: BufferId, + accountId: AccountId? = null, + forceJoin: Boolean = false + ) + + fun openDirectMessage( + networkId: NetworkId, + nickName: String, + forceJoin: Boolean = false + ) + + fun openChannel( + networkId: NetworkId, + channel: String, + forceJoin: Boolean = false + ) + + fun openUrl(url: String) +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/listener/ListenerModule.kt b/app/src/main/java/de/kuschku/quasseldroid/util/listener/ListenerModule.kt new file mode 100644 index 000000000..d4b3d3e03 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/listener/ListenerModule.kt @@ -0,0 +1,13 @@ +package de.kuschku.quasseldroid.util.listener + +import dagger.Binds +import dagger.Module + +@Module +interface ListenerModule { + @Binds + fun bindAutocompleteTextListener(handler: AutocompleteTextHandler): AutocompleteTextListener + + @Binds + fun bindlinkClickListener(handler: LinkClickHandler): LinkClickListener +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/listener/QuasselLinkClickListener.kt b/app/src/main/java/de/kuschku/quasseldroid/util/listener/QuasselLinkClickListener.kt new file mode 100644 index 000000000..74b85299a --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/listener/QuasselLinkClickListener.kt @@ -0,0 +1,27 @@ +package de.kuschku.quasseldroid.util.listener + +import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.protocol.NetworkId +import de.kuschku.quasseldroid.persistence.util.AccountId + +class QuasselLinkClickListener( + private val wrapped: LinkClickListener, + private val onClickQuasselLink: () -> Unit, +) : LinkClickListener { + override fun openBuffer(bufferId: BufferId, accountId: AccountId?, forceJoin: Boolean) { + wrapped.openBuffer(bufferId, accountId, forceJoin) + onClickQuasselLink() + } + + override fun openDirectMessage(networkId: NetworkId, nickName: String, forceJoin: Boolean) { + wrapped.openDirectMessage(networkId, nickName, forceJoin) + onClickQuasselLink() + } + + override fun openChannel(networkId: NetworkId, channel: String, forceJoin: Boolean) { + wrapped.openChannel(networkId, channel, forceJoin) + onClickQuasselLink() + } + + override fun openUrl(url: String) = wrapped.openUrl(url) +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/BetterLinkMovementMethod.java b/app/src/main/java/de/kuschku/quasseldroid/util/ui/BetterLinkMovementMethod.java index 572ebfd44..f9f587cae 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/BetterLinkMovementMethod.java +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/BetterLinkMovementMethod.java @@ -53,7 +53,7 @@ public class BetterLinkMovementMethod extends LinkMovementMethod { private static final int LINKIFY_NONE = -2; private static BetterLinkMovementMethod singleInstance; private final RectF touchedLineBounds = new RectF(); - private OnLinkClickListener onLinkClickListener; + private OnlinkClickListener onlinkClickListener; private OnLinkLongClickListener onLinkLongClickListener; private boolean isUrlHighlighted; private ClickableSpan clickableSpanUnderTouchOnActionDown; @@ -178,13 +178,13 @@ public class BetterLinkMovementMethod extends LinkMovementMethod { /** * Set a listener that will get called whenever any link is clicked on the TextView. */ - public BetterLinkMovementMethod setOnLinkClickListener(OnLinkClickListener clickListener) { + public BetterLinkMovementMethod setOnlinkClickListener(OnlinkClickListener clickListener) { if (this == singleInstance) { throw new UnsupportedOperationException("Setting a click listener on the instance returned by getInstance() is not supported to avoid memory " + "leaks. Please use newInstance() or any of the linkify() methods instead."); } - this.onLinkClickListener = clickListener; + this.onlinkClickListener = clickListener; return this; } @@ -383,7 +383,9 @@ public class BetterLinkMovementMethod extends LinkMovementMethod { protected void dispatchUrlClick(TextView textView, ClickableSpan clickableSpan) { ClickableSpanWithText clickableSpanWithText = ClickableSpanWithText.ofSpan(textView, clickableSpan); - boolean handled = onLinkClickListener != null && onLinkClickListener.onClick(textView, clickableSpanWithText.text()); + boolean handled = onlinkClickListener != null && onlinkClickListener.onClick( + textView, + clickableSpanWithText.text()); if (!handled) { // Let Android handle this click. @@ -401,7 +403,7 @@ public class BetterLinkMovementMethod extends LinkMovementMethod { } } - public interface OnLinkClickListener { + public interface OnlinkClickListener { /** * @param textView The TextView on which a click was registered. * @param url The clicked URL. diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/ui/presenter/BufferPresenter.kt b/app/src/main/java/de/kuschku/quasseldroid/util/ui/presenter/BufferPresenter.kt index 0bfa57680..5c8656860 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/ui/presenter/BufferPresenter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/ui/presenter/BufferPresenter.kt @@ -27,6 +27,7 @@ import de.kuschku.quasseldroid.util.ColorContext import de.kuschku.quasseldroid.util.avatars.AvatarHelper import de.kuschku.quasseldroid.util.irc.format.ContentFormatter import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer +import de.kuschku.quasseldroid.util.listener.LinkClickListener import de.kuschku.quasseldroid.viewmodel.data.BufferListItem import de.kuschku.quasseldroid.viewmodel.data.BufferProps import de.kuschku.quasseldroid.viewmodel.data.BufferStatus @@ -36,6 +37,7 @@ class BufferPresenter @Inject constructor( val appearanceSettings: AppearanceSettings, val messageSettings: MessageSettings, val ircFormatDeserializer: IrcFormatDeserializer, + val linkClickListener: LinkClickListener, val contentFormatter: ContentFormatter, val colorContext: ColorContext ) { @@ -43,7 +45,7 @@ class BufferPresenter @Inject constructor( return props.copy( name = when { props.info.type.hasFlag(Buffer_Type.QueryBuffer) -> - ircFormatDeserializer.formatString(props.info.bufferName, messageSettings.colorizeMirc) + ircFormatDeserializer.formatString(props.info.bufferName, messageSettings.colorizeMirc, linkClickListener) props.info.type.hasFlag(Buffer_Type.StatusBuffer) -> props.network.networkName else -> @@ -51,7 +53,8 @@ class BufferPresenter @Inject constructor( }, description = ircFormatDeserializer.formatString( props.description.toString(), - colorize = messageSettings.colorizeMirc + messageSettings.colorizeMirc, + linkClickListener ), fallbackDrawable = if (props.info.type.hasFlag(Buffer_Type.QueryBuffer)) { props.ircUser?.let { diff --git a/app/src/test/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializerTest.kt b/app/src/test/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializerTest.kt index 63c0ee68f..5403b23f2 100644 --- a/app/src/test/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializerTest.kt +++ b/app/src/test/java/de/kuschku/quasseldroid/util/irc/format/IrcFormatDeserializerTest.kt @@ -23,6 +23,8 @@ import android.os.Build import de.kuschku.quasseldroid.QuasseldroidTest import de.kuschku.quasseldroid.util.irc.format.model.FormatInfo import de.kuschku.quasseldroid.util.irc.format.model.IrcFormat +import de.kuschku.quasseldroid.util.listener.LinkClickHandler +import de.kuschku.quasseldroid.util.listener.LinkClickListener import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -34,10 +36,12 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) class IrcFormatDeserializerTest { private lateinit var deserializer: IrcFormatDeserializer + private lateinit var linkClickListener: LinkClickListener @Before fun setUp() { deserializer = IrcFormatDeserializer(mircColors = colors) + linkClickListener = LinkClickHandler() } @Test @@ -46,6 +50,7 @@ class IrcFormatDeserializerTest { val text = deserializer.formatString( str = "\u000301,01weeeeeeeeee", colorize = true, + onClickListener = linkClickListener, output = spans ) assertEquals("weeeeeeeeee", text.toString()) -- GitLab