From f3249dca20affb8497dfa1be7beb44885ec06ce9 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Thu, 22 Mar 2018 18:40:27 +0100 Subject: [PATCH] Cleanup, reorganization --- .../quasseldroid/service/QuasselService.kt | 31 ++-- .../quasseldroid/ui/chat/ChatActivity.kt | 168 +++++++++--------- .../chat/buffers/BufferViewConfigFragment.kt | 13 +- .../ui/chat/messages/MessageAdapter.kt | 11 +- .../ui/chat/messages/MessageListFragment.kt | 102 +++++------ .../ui/chat/nicks/NickListFragment.kt | 9 - .../compatibility/AndroidHandlerService.kt | 38 ++-- .../util/service/ServiceBoundActivity.kt | 38 +++- .../util/service/ServiceBoundFragment.kt | 13 ++ app/src/main/res/values-de/strings.xml | 6 - app/src/main/res/values-de/strings_error.xml | 6 + app/src/main/res/values-de/strings_status.xml | 9 + app/src/main/res/values/strings.xml | 6 - app/src/main/res/values/strings_error.xml | 6 + app/src/main/res/values/strings_status.xml | 9 + 15 files changed, 252 insertions(+), 213 deletions(-) create mode 100644 app/src/main/res/values-de/strings_error.xml create mode 100644 app/src/main/res/values-de/strings_status.xml create mode 100644 app/src/main/res/values/strings_error.xml create mode 100644 app/src/main/res/values/strings_status.xml diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt index 4c823409e..0ab29d260 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt @@ -16,7 +16,6 @@ import de.kuschku.quasseldroid.persistence.QuasselBacklogStorage import de.kuschku.quasseldroid.persistence.QuasselDatabase import de.kuschku.quasseldroid.settings.ConnectionSettings import de.kuschku.quasseldroid.settings.Settings -import de.kuschku.quasseldroid.util.AndroidHandlerThread import de.kuschku.quasseldroid.util.QuasseldroidNotificationManager import de.kuschku.quasseldroid.util.compatibility.AndroidHandlerService import de.kuschku.quasseldroid.util.helper.editApply @@ -98,7 +97,7 @@ class QuasselService : LifecycleService(), when (state) { ConnectionState.DISCONNECTED -> { handle.builder.setContentTitle(getString(R.string.label_status_disconnected)) - handle.builder.setProgress(0, 0, false) + handle.builder.setProgress(0, 0, true) } ConnectionState.CONNECTING -> { handle.builder.setContentTitle(getString(R.string.label_status_connecting)) @@ -117,11 +116,15 @@ class QuasselService : LifecycleService(), handle.builder.setContentTitle(getString(R.string.label_status_connected)) handle.builder.setProgress(0, 0, false) } + ConnectionState.CLOSED -> { + handle.builder.setContentTitle(getString(R.string.label_status_closed)) + handle.builder.setProgress(0, 0, false) + } } } private fun updateConnection(accountId: Long, reconnect: Boolean) { - handler.post { + handlerService.backend { val account = if (accountId != -1L && reconnect) { AccountDatabase.Creator.init(this).accounts().findById(accountId) } else { @@ -172,8 +175,7 @@ class QuasselService : LifecycleService(), override fun connect(address: SocketAddress, user: String, pass: String, reconnect: Boolean) { disconnect() sessionManager.connect( - clientData, trustManager, address, ::AndroidHandlerService, - user to pass, reconnect + clientData, trustManager, address, user to pass, reconnect ) } @@ -186,30 +188,30 @@ class QuasselService : LifecycleService(), } } - private val handler = AndroidHandlerThread("Backend") + private val handlerService = AndroidHandlerService() private val asyncBackend = object : Backend { override fun connectUnlessConnected(address: SocketAddress, user: String, pass: String, reconnect: Boolean) { - handler.post { + handlerService.backend { backendImplementation.connectUnlessConnected(address, user, pass, reconnect) } } override fun connect(address: SocketAddress, user: String, pass: String, reconnect: Boolean) { - handler.post { + handlerService.backend { backendImplementation.connect(address, user, pass, reconnect) } } override fun reconnect() { - handler.post { + handlerService.backend { backendImplementation.reconnect() } } override fun disconnect(forever: Boolean) { - handler.post { + handlerService.backend { backendImplementation.disconnect(forever) if (forever) { stopSelf() @@ -232,10 +234,9 @@ class QuasselService : LifecycleService(), private val connectivity = BehaviorSubject.createDefault(Unit) override fun onCreate() { - handler.onCreate() super.onCreate() database = QuasselDatabase.Creator.init(application) - sessionManager = SessionManager(ISession.NULL, QuasselBacklogStorage(database)) + sessionManager = SessionManager(ISession.NULL, QuasselBacklogStorage(database), handlerService) clientData = ClientData( identifier = "${resources.getString(R.string.app_name)} ${BuildConfig.VERSION_NAME}", buildDate = Instant.ofEpochSecond(BuildConfig.GIT_COMMIT_DATE), @@ -249,7 +250,7 @@ class QuasselService : LifecycleService(), sessionManager.connectionProgress.toLiveData().observe(this, Observer { if (this.progress.first != it?.first && it?.first == ConnectionState.CONNECTED) { - handler.post { + handlerService.backend { database.message().clearMessages() } } @@ -262,7 +263,7 @@ class QuasselService : LifecycleService(), }) Observable.combineLatest( - sessionManager.state.filter { it == ConnectionState.DISCONNECTED }, + sessionManager.state.filter { it == ConnectionState.DISCONNECTED || it == ConnectionState.CLOSED }, connectivity, BiFunction { a: ConnectionState, b: Unit -> a to b }) .delay(200, TimeUnit.MILLISECONDS) @@ -299,8 +300,6 @@ class QuasselService : LifecycleService(), unregisterReceiver(receiver) notificationHandle?.let { notificationManager.remove(it) } - - handler.onDestroy() super.onDestroy() } 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 f0056f480..169011eae 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 @@ -15,6 +15,7 @@ import android.support.v7.widget.DefaultItemAnimator import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.support.v7.widget.Toolbar +import android.text.Html import android.view.Gravity import android.view.Menu import android.view.MenuItem @@ -25,6 +26,7 @@ import com.afollestad.materialdialogs.MaterialDialog import com.sothree.slidinguppanel.SlidingUpPanelLayout import de.kuschku.libquassel.protocol.Message import de.kuschku.libquassel.protocol.Message_Type +import de.kuschku.libquassel.protocol.message.HandshakeMessage import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager import de.kuschku.libquassel.session.ConnectionState import de.kuschku.libquassel.util.and @@ -37,11 +39,7 @@ import de.kuschku.quasseldroid.settings.Settings import de.kuschku.quasseldroid.ui.chat.input.Editor import de.kuschku.quasseldroid.ui.chat.input.MessageHistoryAdapter import de.kuschku.quasseldroid.ui.settings.SettingsActivity -import de.kuschku.quasseldroid.ui.setup.accounts.AccountSelectionActivity -import de.kuschku.quasseldroid.util.AndroidHandlerThread -import de.kuschku.quasseldroid.util.helper.editApply import de.kuschku.quasseldroid.util.helper.invoke -import de.kuschku.quasseldroid.util.helper.sharedPreferences import de.kuschku.quasseldroid.util.service.ServiceBoundActivity import de.kuschku.quasseldroid.util.ui.MaterialContentLoadingProgressBar import de.kuschku.quasseldroid.viewmodel.QuasselViewModel @@ -68,8 +66,6 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc private lateinit var drawerToggle: ActionBarDrawerToggle - private val handler = AndroidHandlerThread("Chat") - private lateinit var viewModel: QuasselViewModel private lateinit var database: QuasselDatabase @@ -101,7 +97,6 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } override fun onCreate(savedInstanceState: Bundle?) { - handler.onCreate() super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ButterKnife.bind(this) @@ -124,7 +119,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc findViewById(R.id.formatting_toolbar), { lines -> viewModel.session { session -> - viewModel.getBuffer().value?.let { bufferId -> + viewModel.buffer { bufferId -> session.bufferSyncer?.bufferInfo(bufferId)?.also { bufferInfo -> val output = mutableListOf<IAliasManager.Command>() for ((stripped, formatted) in lines) { @@ -159,8 +154,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc setSupportActionBar(toolbar) - viewModel.getBuffer().observe( - this, Observer { + viewModel.buffer.observe(this, Observer { if (it != null && drawerLayout.isDrawerOpen(Gravity.START)) { drawerLayout.closeDrawer(Gravity.START, true) } @@ -178,19 +172,47 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc drawerToggle.syncState() } + viewModel.errors.observe(this, Observer { + when (it) { + is HandshakeMessage.ClientInitReject -> + MaterialDialog.Builder(this) + .title(R.string.label_error_init) + .content(Html.fromHtml(it.errorString)) + .neutralText(R.string.label_close) + .build() + .show() + is HandshakeMessage.CoreSetupReject -> + MaterialDialog.Builder(this) + .title(R.string.label_error_setup) + .content(Html.fromHtml(it.errorString)) + .neutralText(R.string.label_close) + .build() + .show() + is HandshakeMessage.ClientLoginReject -> + MaterialDialog.Builder(this) + .title(R.string.label_error_login) + .content(Html.fromHtml(it.errorString)) + .neutralText(R.string.label_close) + .build() + .show() + } + }) + viewModel.connectionProgress.observe(this, Observer { it -> val (state, progress, max) = it ?: Triple(ConnectionState.DISCONNECTED, 0, 0) when (state) { - ConnectionState.CONNECTED, ConnectionState.DISCONNECTED -> { + ConnectionState.CONNECTED, + ConnectionState.DISCONNECTED, + ConnectionState.CLOSED -> { progressBar.hide() } - ConnectionState.INIT -> { + ConnectionState.INIT -> { // Show indeterminate when no progress has been made yet progressBar.isIndeterminate = progress == 0 || max == 0 progressBar.progress = progress progressBar.max = max } - else -> { + else -> { progressBar.isIndeterminate = true } } @@ -209,19 +231,19 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc override fun onSaveInstanceState(outState: Bundle?) { super.onSaveInstanceState(outState) - outState?.putInt("OPEN_BUFFER", viewModel.getBuffer().value ?: -1) + outState?.putInt("OPEN_BUFFER", viewModel.buffer.value ?: -1) } override fun onSaveInstanceState(outState: Bundle?, outPersistentState: PersistableBundle?) { super.onSaveInstanceState(outState, outPersistentState) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - outPersistentState?.putInt("OPEN_BUFFER", viewModel.getBuffer().value ?: -1) + outPersistentState?.putInt("OPEN_BUFFER", viewModel.buffer.value ?: -1) } } override fun onRestoreInstanceState(savedInstanceState: Bundle?) { super.onRestoreInstanceState(savedInstanceState) - viewModel.setBuffer(savedInstanceState?.getInt("OPEN_BUFFER", -1) ?: -1) + viewModel.buffer.value = savedInstanceState?.getInt("OPEN_BUFFER", -1) ?: -1 } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @@ -230,7 +252,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc super.onRestoreInstanceState(savedInstanceState, persistentState) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val fallback = persistentState?.getInt("OPEN_BUFFER", -1) ?: -1 - viewModel.setBuffer(savedInstanceState?.getInt("OPEN_BUFFER", fallback) ?: fallback) + viewModel.buffer.value = savedInstanceState?.getInt("OPEN_BUFFER", fallback) ?: fallback } } @@ -240,83 +262,65 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } override fun onOptionsItemSelected(item: MenuItem?) = when (item?.itemId) { - android.R.id.home -> { + android.R.id.home -> { drawerToggle.onOptionsItemSelected(item) } R.id.filter_messages -> { - handler.post { - val buffer = viewModel.getBuffer().value - if (buffer != null) { - val filtered = Message_Type.of(database.filtered().get(accountId, buffer) ?: 0) - val flags = intArrayOf( - Message.MessageType.Join.bit or Message.MessageType.NetsplitJoin.bit, - Message.MessageType.Part.bit, - Message.MessageType.Quit.bit or Message.MessageType.NetsplitQuit.bit, - Message.MessageType.Nick.bit, - Message.MessageType.Mode.bit, - Message.MessageType.Topic.bit - ) - val selectedIndices = flags.withIndex().mapNotNull { (index, flag) -> - if ((filtered and flag).isNotEmpty()) { - index - } else { - null - } - }.toTypedArray() - - runOnUiThread { - MaterialDialog.Builder(this) - .title(R.string.label_filter_messages) - .items(R.array.message_filter_types) - .itemsIds(flags) - .itemsCallbackMultiChoice(selectedIndices, { _, _, _ -> false }) - .positiveText(R.string.label_select_multiple) - .negativeText(R.string.label_cancel) - .onPositive { dialog, _ -> - val selected = dialog.selectedIndices ?: emptyArray() - handler.post { - val newlyFiltered = selected - .map { flags[it] } - .fold(Message_Type.of()) { acc, i -> acc or i } - - database.filtered().replace( - QuasselDatabase.Filtered(accountId, buffer, newlyFiltered.value) - ) - } - }.negativeColorAttr(R.attr.colorTextPrimary) - .backgroundColorAttr(R.attr.colorBackgroundCard) - .contentColorAttr(R.attr.colorTextPrimary) - .build() - .show() + viewModel.buffer { buffer -> + val filtered = Message_Type.of(database.filtered().get(accountId, buffer) ?: 0) + val flags = intArrayOf( + Message.MessageType.Join.bit or Message.MessageType.NetsplitJoin.bit, + Message.MessageType.Part.bit, + Message.MessageType.Quit.bit or Message.MessageType.NetsplitQuit.bit, + Message.MessageType.Nick.bit, + Message.MessageType.Mode.bit, + Message.MessageType.Topic.bit + ) + val selectedIndices = flags.withIndex().mapNotNull { (index, flag) -> + if ((filtered and flag).isNotEmpty()) { + index + } else { + null } - } + }.toTypedArray() + + MaterialDialog.Builder(this) + .title(R.string.label_filter_messages) + .items(R.array.message_filter_types) + .itemsIds(flags) + .itemsCallbackMultiChoice(selectedIndices, { _, _, _ -> false }) + .positiveText(R.string.label_select_multiple) + .negativeText(R.string.label_cancel) + .onPositive { dialog, _ -> + val selected = dialog.selectedIndices ?: emptyArray() + runInBackground { + val newlyFiltered = selected + .map { flags[it] } + .fold(Message_Type.of()) { acc, i -> acc or i } + + database.filtered().replace( + QuasselDatabase.Filtered(accountId, buffer, newlyFiltered.value) + ) + } + }.negativeColorAttr(R.attr.colorTextPrimary) + .backgroundColorAttr(R.attr.colorBackgroundCard) + .contentColorAttr(R.attr.colorTextPrimary) + .build() + .show() } true } - R.id.settings -> { + R.id.settings -> { startActivity(Intent(applicationContext, SettingsActivity::class.java)) true } - R.id.disconnect -> { - handler.post { - sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) { - editApply { - putBoolean(Keys.Status.reconnect, false) - } - } - - val intent = Intent(this, AccountSelectionActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP - startActivityForResult(intent, REQUEST_SELECT_ACCOUNT) - } + R.id.disconnect -> { + val editor1 = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE).edit() + editor1.putBoolean(Keys.Status.reconnect, false) + editor1.commit() true } - else -> super.onOptionsItemSelected(item) - } - - override fun onDestroy() { - handler.onDestroy() - super.onDestroy() + else -> super.onOptionsItemSelected(item) } } 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 46176a771..ded3967bd 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 @@ -20,7 +20,6 @@ import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.persistence.QuasselDatabase import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.Settings -import de.kuschku.quasseldroid.util.AndroidHandlerThread import de.kuschku.quasseldroid.util.helper.map import de.kuschku.quasseldroid.util.helper.zip import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer @@ -29,8 +28,6 @@ import de.kuschku.quasseldroid.viewmodel.QuasselViewModel import de.kuschku.quasseldroid.viewmodel.data.BufferHiddenState class BufferViewConfigFragment : ServiceBoundFragment() { - private val handlerThread = AndroidHandlerThread("ChatList") - @BindView(R.id.chatListToolbar) lateinit var chatListToolbar: Toolbar @@ -158,7 +155,6 @@ class BufferViewConfigFragment : ServiceBoundFragment() { private lateinit var listAdapter: BufferListAdapter override fun onCreate(savedInstanceState: Bundle?) { - handlerThread.onCreate() super.onCreate(savedInstanceState) viewModel = ViewModelProviders.of(activity!!)[QuasselViewModel::class.java] @@ -220,7 +216,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() { }, viewModel.selectedBufferId, viewModel.collapsedNetworks, - handlerThread::post, + ::runInBackground, activity!!::runOnUiThread, clickListener, longClickListener @@ -312,16 +308,11 @@ class BufferViewConfigFragment : ServiceBoundFragment() { return view } - override fun onDestroy() { - handlerThread.onDestroy() - super.onDestroy() - } - private val clickListener: ((BufferId) -> Unit)? = { if (actionMode != null) { longClickListener?.invoke(it) } else { - viewModel.setBuffer(it) + viewModel.buffer.value = it } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt index 8728b4629..06730680b 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt @@ -53,14 +53,9 @@ class MessageAdapter( } } - override fun getItemViewType(position: Int): Int { - val item = getItem(position) - if (item != null) { - return viewType(Message_Flags.of(item.type), Message_Flags.of(item.flag)) - } else { - return 0 - } - } + override fun getItemViewType(position: Int) = getItem(position)?.let { + viewType(Message_Flags.of(it.type), Message_Flags.of(it.flag)) + } ?: 0 private fun viewType(type: Message_Types, flags: Message_Flags) = if (flags.hasFlag(Message_Flag.Highlight)) { 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 44fc8178a..7f84073c2 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 @@ -21,8 +21,10 @@ import de.kuschku.quasseldroid.persistence.QuasselDatabase import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.BacklogSettings import de.kuschku.quasseldroid.settings.Settings -import de.kuschku.quasseldroid.util.AndroidHandlerThread -import de.kuschku.quasseldroid.util.helper.* +import de.kuschku.quasseldroid.util.helper.invoke +import de.kuschku.quasseldroid.util.helper.switchMapNotNull +import de.kuschku.quasseldroid.util.helper.toggle +import de.kuschku.quasseldroid.util.helper.zip import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import de.kuschku.quasseldroid.viewmodel.QuasselViewModel @@ -36,8 +38,6 @@ class MessageListFragment : ServiceBoundFragment() { private lateinit var viewModel: QuasselViewModel private lateinit var appearanceSettings: AppearanceSettings - private val handler = AndroidHandlerThread("Chat") - private lateinit var database: QuasselDatabase private lateinit var linearLayoutManager: LinearLayoutManager @@ -49,7 +49,6 @@ class MessageListFragment : ServiceBoundFragment() { private lateinit var backlogSettings: BacklogSettings override fun onCreate(savedInstanceState: Bundle?) { - handler.onCreate() super.onCreate(savedInstanceState) viewModel = ViewModelProviders.of(activity!!)[QuasselViewModel::class.java] appearanceSettings = Settings.appearance(activity!!) @@ -87,7 +86,7 @@ class MessageListFragment : ServiceBoundFragment() { }) database = QuasselDatabase.Creator.init(context!!.applicationContext) - val data = viewModel.getBuffer().switchMapNotNull { buffer -> + val data = viewModel.buffer.switchMapNotNull { buffer -> database.filtered().listen(accountId, buffer).switchMapNotNull { filtered -> LivePagedListBuilder( database.message().findByBufferIdPaged(buffer, filtered), @@ -101,62 +100,50 @@ class MessageListFragment : ServiceBoundFragment() { } } - handler.post { - val lastMessageId = viewModel.getBuffer().switchMapNotNull { - database.message().lastMsgId(it) - } - - viewModel.sessionManager.zip(lastMessageId).observe( - this, Observer { - handler.post { - val session = it?.first - val message = it?.second - val bufferSyncer = session?.bufferSyncer - if (message != null && bufferSyncer != null && previousMessageId != message.messageId) { - markAsRead(bufferSyncer, message.bufferId, message.messageId) - previousMessageId = message.messageId - } - } - }) - - viewModel.sessionManager.zip(viewModel.getBuffer()).observe( - this, Observer { - val previous = lastBuffer ?: -1 - val firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition() - val firstVisibleMessageId = adapter[firstVisibleItemPosition]?.messageId - handler.post { - val session = it?.first - val buffer = it?.second - val bufferSyncer = session?.bufferSyncer - if (buffer != null && bufferSyncer != null) { - if (buffer != previous) { - onBufferChange(previous, buffer, firstVisibleMessageId, bufferSyncer) - } - lastBuffer = buffer - } - } - }) + val lastMessageId = viewModel.buffer.switchMapNotNull { + database.message().lastMsgId(it) } - viewModel.markerLine.observe( + viewModel.sessionManager.zip(lastMessageId).observe( this, Observer { + runInBackground { + val session = it?.first + val message = it?.second + val bufferSyncer = session?.bufferSyncer + if (message != null && bufferSyncer != null && previousMessageId != message.messageId) { + markAsRead(bufferSyncer, message.bufferId, message.messageId) + previousMessageId = message.messageId + } + } + }) + + viewModel.markerLine.observe(this, Observer { adapter.markerLinePosition = it adapter.notifyDataSetChanged() }) var lastBuffer = -1 - data.observe( - this, Observer { list -> + data.observe(this, Observer { list -> val firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition() - val buffer = viewModel.getBuffer().value ?: -1 - if (buffer != lastBuffer) { - lastBuffer = buffer - adapter.clearCache() - } - adapter.submitList(list) - if (firstVisibleItemPosition < 2) { - activity?.runOnUiThread { messageList.scrollToPosition(0) } - handler.postDelayed({ activity?.runOnUiThread { messageList.scrollToPosition(0) } }, 16) + val firstVisibleMessageId = adapter[firstVisibleItemPosition]?.messageId + runInBackground { + val buffer = viewModel.buffer.value ?: -1 + if (buffer != lastBuffer) { + backend.value?.sessionManager()?.bufferSyncer?.let { bufferSyncer -> + onBufferChange(lastBuffer, buffer, firstVisibleMessageId, bufferSyncer) + } + lastBuffer = buffer + adapter.clearCache() + } + adapter.submitList(list) + if (firstVisibleItemPosition < 2) { + activity?.runOnUiThread { messageList.scrollToPosition(0) } + runInBackgroundDelayed(16) { + activity?.runOnUiThread { + messageList.scrollToPosition(0) + } + } + } } }) scrollDown.hide() @@ -194,8 +181,8 @@ class MessageListFragment : ServiceBoundFragment() { } private fun loadMore() { - handler.post { - viewModel.getBuffer().let { bufferId -> + runInBackground { + viewModel.buffer { bufferId -> viewModel.sessionManager()?.backlogManager?.requestBacklog( bufferId = bufferId, last = database.message().findFirstByBufferId( @@ -206,9 +193,4 @@ class MessageListFragment : ServiceBoundFragment() { } } } - - override fun onDestroy() { - handler.onDestroy() - super.onDestroy() - } } \ No newline at end of file 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 8f708872b..81b6ad62a 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 @@ -15,7 +15,6 @@ import de.kuschku.libquassel.util.irc.IrcCaseMappers import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.Settings -import de.kuschku.quasseldroid.util.AndroidHandlerThread import de.kuschku.quasseldroid.util.helper.map import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.service.ServiceBoundFragment @@ -24,8 +23,6 @@ import de.kuschku.quasseldroid.viewmodel.QuasselViewModel class NickListFragment : ServiceBoundFragment() { private lateinit var viewModel: QuasselViewModel - private val handlerThread = AndroidHandlerThread("NickList") - @BindView(R.id.nickList) lateinit var nickList: RecyclerView @@ -33,7 +30,6 @@ class NickListFragment : ServiceBoundFragment() { private lateinit var appearanceSettings: AppearanceSettings override fun onCreate(savedInstanceState: Bundle?) { - handlerThread.onCreate() super.onCreate(savedInstanceState) viewModel = ViewModelProviders.of(activity!!)[QuasselViewModel::class.java] @@ -76,11 +72,6 @@ class NickListFragment : ServiceBoundFragment() { return view } - override fun onDestroy() { - handlerThread.onDestroy() - super.onDestroy() - } - private val clickListener: ((String) -> Unit)? = { // TODO } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/compatibility/AndroidHandlerService.kt b/app/src/main/java/de/kuschku/quasseldroid/util/compatibility/AndroidHandlerService.kt index 8046286da..7044fd697 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/compatibility/AndroidHandlerService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/compatibility/AndroidHandlerService.kt @@ -6,48 +6,64 @@ import android.os.Process import de.kuschku.libquassel.util.compatibility.HandlerService class AndroidHandlerService : HandlerService { - override fun parse(f: () -> Unit) { - parseHandler.post(f) + override fun serialize(f: () -> Unit) { + serializeHandler.post(f) + } + + override fun deserialize(f: () -> Unit) { + serializeHandler.post(f) } override fun write(f: () -> Unit) { writeHandler.post(f) } - override fun handle(f: () -> Unit) { + override fun backend(f: () -> Unit) { backendHandler.post(f) } - private val parseThread = HandlerThread("parse", Process.THREAD_PRIORITY_DISPLAY) + override fun backendDelayed(delayMillis: Long, f: () -> Unit) { + backendHandler.postDelayed(f, delayMillis) + } + + private val serializeThread = HandlerThread("serialize", Process.THREAD_PRIORITY_BACKGROUND) + private val deserializeThread = HandlerThread("deserialize", Process.THREAD_PRIORITY_BACKGROUND) private val writeThread = HandlerThread("write", Process.THREAD_PRIORITY_BACKGROUND) - private val backendThread = HandlerThread("backend", Process.THREAD_PRIORITY_DISPLAY) + private val backendThread = HandlerThread("backend", Process.THREAD_PRIORITY_BACKGROUND) - private val parseHandler: Handler + private val serializeHandler: Handler + private val deserializeHandler: Handler private val writeHandler: Handler private val backendHandler: Handler private val internalExceptionHandler = Thread.UncaughtExceptionHandler { thread: Thread, throwable: Throwable -> - exceptionHandler?.uncaughtException(thread, throwable) + val exceptionHandler = exceptionHandler ?: Thread.getDefaultUncaughtExceptionHandler() + exceptionHandler.uncaughtException(thread, throwable) } init { - parseThread.uncaughtExceptionHandler = internalExceptionHandler + serializeThread.uncaughtExceptionHandler = internalExceptionHandler + deserializeThread.uncaughtExceptionHandler = internalExceptionHandler writeThread.uncaughtExceptionHandler = internalExceptionHandler backendThread.uncaughtExceptionHandler = internalExceptionHandler - parseThread.start() + serializeThread.start() + deserializeThread.start() writeThread.start() backendThread.start() - parseHandler = Handler(parseThread.looper) + serializeHandler = Handler(serializeThread.looper) + deserializeHandler = Handler(deserializeThread.looper) writeHandler = Handler(writeThread.looper) backendHandler = Handler(backendThread.looper) } override var exceptionHandler: Thread.UncaughtExceptionHandler? = null + get() = field ?: Thread.getDefaultUncaughtExceptionHandler() override fun quit() { - parseThread.quit() + serializeThread.quit() + deserializeThread.quit() writeThread.quit() backendThread.quit() } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt index 37f301572..cbf32061a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt @@ -10,12 +10,15 @@ import android.support.annotation.ColorRes import android.support.annotation.DrawableRes import android.support.v7.app.AppCompatActivity import de.kuschku.libquassel.session.Backend +import de.kuschku.libquassel.util.compatibility.LoggingHandler +import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log import de.kuschku.quasseldroid.Keys import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.ConnectionSettings import de.kuschku.quasseldroid.settings.Settings import de.kuschku.quasseldroid.ui.setup.accounts.AccountSelectionActivity +import de.kuschku.quasseldroid.util.helper.invoke import de.kuschku.quasseldroid.util.helper.sharedPreferences import de.kuschku.quasseldroid.util.helper.updateRecentsHeaderIfExisting @@ -30,6 +33,18 @@ abstract class ServiceBoundActivity : AppCompatActivity(), protected val backend: LiveData<Backend?> get() = connection.backend + protected fun runInBackground(f: () -> Unit) { + connection.backend { + it.sessionManager().handlerService.backend(f) + } + } + + protected fun runInBackgroundDelayed(delayMillis: Long, f: () -> Unit) { + connection.backend { + it.sessionManager().handlerService.backendDelayed(delayMillis, f) + } + } + protected lateinit var appearanceSettings: AppearanceSettings protected lateinit var connectionSettings: ConnectionSettings protected var accountId: Long = -1 @@ -85,11 +100,20 @@ abstract class ServiceBoundActivity : AppCompatActivity(), accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 - if (!sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) { - getBoolean(Keys.Status.reconnect, false) - } || accountId == -1L) { + val reconnect = sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) { + getBoolean(Keys.Status.reconnect, false) + } + val accountIdValid = accountId != -1L + + log( + LoggingHandler.LogLevel.ERROR, "DEBUG", + "reconnect: $reconnect, accountIdValid: $accountIdValid" + ) + + if (!reconnect || !accountIdValid) { if (!startedSelection) { + log(LoggingHandler.LogLevel.ERROR, "DEBUG", "started: $REQUEST_SELECT_ACCOUNT") startActivityForResult( Intent(this, AccountSelectionActivity::class.java), REQUEST_SELECT_ACCOUNT ) @@ -102,6 +126,12 @@ abstract class ServiceBoundActivity : AppCompatActivity(), override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) + + log( + LoggingHandler.LogLevel.ERROR, "DEBUG", + "request: $requestCode result: $resultCode, data: $data" + ) + if (requestCode == REQUEST_SELECT_ACCOUNT) { startedSelection = false @@ -117,6 +147,6 @@ abstract class ServiceBoundActivity : AppCompatActivity(), } companion object { - const val REQUEST_SELECT_ACCOUNT = 0 + const val REQUEST_SELECT_ACCOUNT = 1 } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt index 2896a08a3..b6e16dbf6 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.support.v4.app.Fragment import de.kuschku.libquassel.session.Backend import de.kuschku.quasseldroid.Keys +import de.kuschku.quasseldroid.util.helper.invoke abstract class ServiceBoundFragment : Fragment() { private var connection = BackendServiceConnection() @@ -13,6 +14,18 @@ abstract class ServiceBoundFragment : Fragment() { protected val backend: LiveData<Backend?> get() = connection.backend + protected fun runInBackground(f: () -> Unit) { + connection.backend { + it.sessionManager().handlerService.backend(f) + } + } + + protected fun runInBackgroundDelayed(delayMillis: Long, f: () -> Unit) { + connection.backend { + it.sessionManager().handlerService.backendDelayed(delayMillis, f) + } + } + protected var accountId: Long = -1 override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 562cb0029..2dce73ad1 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -28,12 +28,6 @@ <string name="label_unhide">Nicht mehr ausblenden</string> <string name="label_yes">Ja</string> - <string name="label_status_disconnected">Nicht verbunden</string> - <string name="label_status_connecting">Verbindung wird hergestellt</string> - <string name="label_status_handshake">Handshake</string> - <string name="label_status_init">Initialisieren</string> - <string name="label_status_connected">Verbunden</string> - <string name="notification_channel_connection_title">Verbindung</string> <string name="notification_channel_highlight_title">Erwähnungen</string> diff --git a/app/src/main/res/values-de/strings_error.xml b/app/src/main/res/values-de/strings_error.xml new file mode 100644 index 000000000..0ff779063 --- /dev/null +++ b/app/src/main/res/values-de/strings_error.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="label_error_login">Loginfehler</string> + <string name="label_error_setup">Einrichtungsfehler</string> + <string name="label_error_init">Verbindungsfehler</string> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values-de/strings_status.xml b/app/src/main/res/values-de/strings_status.xml new file mode 100644 index 000000000..46c91580a --- /dev/null +++ b/app/src/main/res/values-de/strings_status.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="label_status_disconnected">Nicht verbunden</string> + <string name="label_status_connecting">Verbindung wird hergestellt</string> + <string name="label_status_handshake">Protokoll wird ausgehandelt</string> + <string name="label_status_init">Initialisiere Daten</string> + <string name="label_status_connected">Verbunden</string> + <string name="label_status_closed">Verbindung getrennt</string> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e0fb82803..b6e4470b9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -27,12 +27,6 @@ <string name="label_unhide">Make Visible</string> <string name="label_yes">Yes</string> - <string name="label_status_disconnected">Disconnected</string> - <string name="label_status_connecting">Connecting</string> - <string name="label_status_handshake">Handshake</string> - <string name="label_status_init">Init</string> - <string name="label_status_connected">Connected</string> - <string name="notification_channel_background" translatable="false">background</string> <string name="notification_channel_connection_title">Connection</string> <string name="notification_channel_highlight" translatable="false">highlight</string> diff --git a/app/src/main/res/values/strings_error.xml b/app/src/main/res/values/strings_error.xml new file mode 100644 index 000000000..7e9e24a25 --- /dev/null +++ b/app/src/main/res/values/strings_error.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="label_error_login">Login Error</string> + <string name="label_error_setup">Setup Error</string> + <string name="label_error_init">Connection Error</string> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings_status.xml b/app/src/main/res/values/strings_status.xml new file mode 100644 index 000000000..ec34c73fb --- /dev/null +++ b/app/src/main/res/values/strings_status.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="label_status_disconnected">Disconnected</string> + <string name="label_status_connecting">Connecting</string> + <string name="label_status_handshake">Handshake</string> + <string name="label_status_init">Init</string> + <string name="label_status_connected">Connected</string> + <string name="label_status_closed">Connection Lost</string> +</resources> \ No newline at end of file -- GitLab