diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fa10b50859672779aaf7a82b165c24e6a7c2ba50..4bc36f436e069b8cec8eb9461a151048c578486a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ android:supportsRtl="true" android:theme="@style/Theme.SplashTheme"> <activity - android:name=".ui.ChatActivity" + android:name=".ui.chat.ChatActivity" android:exported="false" android:label="@string/app_name" android:windowSoftInputMode="adjustResize" /> diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt index a1b4123ec452ddb8e8e6c225071ecb60a5c8d54c..b2f8a35c0facb7f58ca20b1f66e9325b4094e305 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt @@ -3,8 +3,6 @@ package de.kuschku.quasseldroid_ng.service import android.arch.lifecycle.LifecycleService import android.content.Intent import android.os.Binder -import android.os.Handler -import android.os.HandlerThread import de.kuschku.libquassel.protocol.* import de.kuschku.libquassel.session.Backend import de.kuschku.libquassel.session.SessionManager @@ -13,6 +11,7 @@ import de.kuschku.quasseldroid_ng.BuildConfig import de.kuschku.quasseldroid_ng.R import de.kuschku.quasseldroid_ng.persistence.PersistentSession import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase +import de.kuschku.quasseldroid_ng.util.AndroidHandlerThread import de.kuschku.quasseldroid_ng.util.compatibility.AndroidHandlerService import org.threeten.bp.Instant import java.security.cert.X509Certificate @@ -36,6 +35,12 @@ class QuasselService : LifecycleService() { private val backendImplementation = object : Backend { override fun sessionManager() = sessionManager + override fun connectUnlessConnected(address: SocketAddress, user: String, pass: String) { + sessionManager.ifDisconnected { + this.connect(address, user, pass) + } + } + override fun connect(address: SocketAddress, user: String, pass: String) { disconnect() val handlerService = AndroidHandlerService() @@ -47,10 +52,15 @@ class QuasselService : LifecycleService() { } } - private val thread = HandlerThread("BackendHandler") - private lateinit var handler: Handler + private val handler = AndroidHandlerThread("Backend") private val asyncBackend = object : Backend { + override fun connectUnlessConnected(address: SocketAddress, user: String, pass: String) { + handler.post { + backendImplementation.connectUnlessConnected(address, user, pass) + } + } + override fun connect(address: SocketAddress, user: String, pass: String) { handler.post { backendImplementation.connect(address, user, pass) @@ -67,15 +77,14 @@ class QuasselService : LifecycleService() { } override fun onDestroy() { - handler.post { thread.quit() } + handler.onDestroy() super.onDestroy() } private lateinit var database: QuasselDatabase override fun onCreate() { - thread.start() - handler = Handler(thread.looper) + handler.onCreate() super.onCreate() database = QuasselDatabase.Creator.init(application) sessionManager = SessionManager(PersistentSession()) diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt similarity index 63% rename from app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatActivity.kt rename to app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt index e9bf43aaed72769a5f83cddfe2aab6dbdcd67975..b77956e67eb352bc2e4453cf27c9698fe3049195 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt @@ -1,11 +1,9 @@ -package de.kuschku.quasseldroid_ng.ui +package de.kuschku.quasseldroid_ng.ui.chat import android.app.Activity import android.arch.lifecycle.Observer import android.content.Context import android.os.Bundle -import android.os.Handler -import android.os.HandlerThread import android.support.design.widget.Snackbar import android.support.v7.widget.Toolbar import android.util.Log @@ -17,25 +15,21 @@ import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.session.* import de.kuschku.libquassel.util.compatibility.LoggingHandler -import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.ERROR import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO -import de.kuschku.libquassel.util.compatibility.log import de.kuschku.quasseldroid_ng.Keys import de.kuschku.quasseldroid_ng.R import de.kuschku.quasseldroid_ng.persistence.AccountDatabase -import de.kuschku.quasseldroid_ng.util.helper.* +import de.kuschku.quasseldroid_ng.util.AndroidHandlerThread +import de.kuschku.quasseldroid_ng.util.helper.editApply +import de.kuschku.quasseldroid_ng.util.helper.map +import de.kuschku.quasseldroid_ng.util.helper.observeSticky +import de.kuschku.quasseldroid_ng.util.helper.switchMapRx import de.kuschku.quasseldroid_ng.util.service.ServiceBoundActivity import org.threeten.bp.ZoneOffset import org.threeten.bp.ZonedDateTime import org.threeten.bp.format.DateTimeFormatter class ChatActivity : ServiceBoundActivity() { - @BindView(R.id.connect) - lateinit var connect: Button - - @BindView(R.id.disconnect) - lateinit var disconnect: Button - @BindView(R.id.clear) lateinit var clear: Button @@ -45,12 +39,10 @@ class ChatActivity : ServiceBoundActivity() { @BindView(R.id.toolbar) lateinit var toolbar: Toolbar - private val thread = HandlerThread("Chat") - private lateinit var handler: Handler + private val handler = AndroidHandlerThread("Chat") private val sessionManager = backend.map(Backend::sessionManager) - private val state - = sessionManager.switchMapRx(SessionManager::state) + private val state = sessionManager.switchMapRx(SessionManager::state) private val bufferViewManager = sessionManager.switchMapRx(SessionManager::session).map(ISession::bufferViewManager) @@ -62,13 +54,10 @@ class ChatActivity : ServiceBoundActivity() { } } - private - var snackbar: Snackbar? = null + private var snackbar: Snackbar? = null - private - val dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ISO_TIME - private - val logHandler = object : LoggingHandler() { + private val dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ISO_TIME + private val logHandler = object : LoggingHandler() { override fun log(logLevel: LogLevel, tag: String, message: String?, throwable: Throwable?) { val time = dateTimeFormatter.format(ZonedDateTime.now(ZoneOffset.UTC)) @@ -99,46 +88,37 @@ class ChatActivity : ServiceBoundActivity() { super.onSaveInstanceState(outState) } - var account: AccountDatabase.Account? = null - override fun onCreate(savedInstanceState: Bundle?) { - thread.start() - handler = Handler(thread.looper) - + handler.onCreate() super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ButterKnife.bind(this) setSupportActionBar(toolbar) - val database = AccountDatabase.Creator.init(this) - handler.post { - val accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) - ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 - if (accountId == -1L) { - setResult(Activity.RESULT_OK) - finish() - } - val it = database.accounts().findById(accountId) - if (it == null) { - setResult(Activity.RESULT_OK) - finish() + backend.observeSticky(this, Observer { backendValue -> + if (backendValue != null) { + val database = AccountDatabase.Creator.init(this) + handler.post { + val accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) + ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 + if (accountId == -1L) { + setResult(Activity.RESULT_OK) + finish() + } + val account = database.accounts().findById(accountId) + if (account == null) { + setResult(Activity.RESULT_OK) + finish() + } else { + backendValue.connectUnlessConnected( + SocketAddress(account.host, account.port.toShort()), + account.user, + account.pass + ) + } + } } - account = it - } - - connect.setOnClickListener { - val account = account - if (account != null) - backend.value?.connect( - SocketAddress(account.host, account.port.toShort()), - account.user, - account.pass - ) - } - - disconnect.setOnClickListener { - backend.value?.disconnect() - } + }) clear.setOnClickListener { errorList.text = "" @@ -146,22 +126,11 @@ class ChatActivity : ServiceBoundActivity() { state.observeSticky(this, Observer { val status = it ?: ConnectionState.DISCONNECTED - val disconnected = status == ConnectionState.DISCONNECTED - - disconnect.isEnabled = !disconnected - connect.isEnabled = disconnected snackbar?.dismiss() snackbar = Snackbar.make(errorList, status.name, Snackbar.LENGTH_SHORT) snackbar?.show() }) - - bufferViewManager.observeSticky(this, Observer { - log(ERROR, "bufferViewManager", it.toString()) - }) - bufferViewConfigs.or(emptyList()).observeSticky(this, Observer { - log(ERROR, "bufferViewConfigs", it.toString()) - }) } override fun onCreateOptionsMenu(menu: Menu?): Boolean { @@ -171,11 +140,14 @@ class ChatActivity : ServiceBoundActivity() { override fun onOptionsItemSelected(item: MenuItem?) = when (item?.itemId) { R.id.disconnect -> { - getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE).editApply { - putBoolean(Keys.Status.reconnect, false) + handler.post { + getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE).editApply { + putBoolean(Keys.Status.reconnect, false) + } + backend.value?.disconnect() + setResult(Activity.RESULT_OK) + finish() } - setResult(Activity.RESULT_OK) - finish() true } else -> super.onOptionsItemSelected(item) @@ -186,6 +158,11 @@ class ChatActivity : ServiceBoundActivity() { LoggingHandler.loggingHandlers.add(logHandler) } + override fun onDestroy() { + handler.onDestroy() + super.onDestroy() + } + override fun onStop() { LoggingHandler.loggingHandlers.remove(logHandler) super.onStop() diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatDelegate.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatDelegate.kt new file mode 100644 index 0000000000000000000000000000000000000000..9bd77701a8d8e8703de860fd2bede8b2e3da2b52 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatDelegate.kt @@ -0,0 +1,3 @@ +package de.kuschku.quasseldroid_ng.ui.chat + +class ChatDelegate diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatListDelegate.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatListDelegate.kt new file mode 100644 index 0000000000000000000000000000000000000000..13a9fa6662bd17ef7e6b37e906d23eb000ede531 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatListDelegate.kt @@ -0,0 +1,3 @@ +package de.kuschku.quasseldroid_ng.ui.chat + +class ChatListDelegate diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/EditorDelegate.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/EditorDelegate.kt new file mode 100644 index 0000000000000000000000000000000000000000..936c10251e84a1613110a19f9c5a174f13c91005 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/EditorDelegate.kt @@ -0,0 +1,3 @@ +package de.kuschku.quasseldroid_ng.ui.chat + +class EditorDelegate diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/NickListDelegate.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/NickListDelegate.kt new file mode 100644 index 0000000000000000000000000000000000000000..1bb9509be2604a846961ea10d958e0caff38ef7f --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/NickListDelegate.kt @@ -0,0 +1,3 @@ +package de.kuschku.quasseldroid_ng.ui.chat + +class NickListDelegate diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountEditActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountEditActivity.kt index ba542dabb28e7fb112e95459a0ba371aef5e8cc0..43cfa75c73b155613afa5120f4c7fd5bd1961b58 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountEditActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountEditActivity.kt @@ -3,8 +3,6 @@ package de.kuschku.quasseldroid_ng.ui.setup.accounts import android.app.Activity import android.content.Context import android.os.Bundle -import android.os.Handler -import android.os.HandlerThread import android.support.design.widget.TextInputEditText import android.support.design.widget.TextInputLayout import android.support.v7.app.AlertDialog @@ -17,6 +15,7 @@ import butterknife.ButterKnife import de.kuschku.quasseldroid_ng.Keys import de.kuschku.quasseldroid_ng.R import de.kuschku.quasseldroid_ng.persistence.AccountDatabase +import de.kuschku.quasseldroid_ng.util.AndroidHandlerThread import de.kuschku.quasseldroid_ng.util.Patterns import de.kuschku.quasseldroid_ng.util.TextValidator import de.kuschku.quasseldroid_ng.util.helper.editCommit @@ -51,13 +50,10 @@ class AccountEditActivity : AppCompatActivity() { private var account: AccountDatabase.Account? = null lateinit var database: AccountDatabase - private val thread = HandlerThread("AccountEdit") - private lateinit var handler: Handler + private val handler = AndroidHandlerThread("AccountEdit") override fun onCreate(savedInstanceState: Bundle?) { - thread.start() - handler = Handler(thread.looper) - + handler.onCreate() setTheme(R.style.Theme_AppTheme_Light) super.onCreate(savedInstanceState) setContentView(R.layout.setup_account_edit) @@ -136,7 +132,7 @@ class AccountEditActivity : AppCompatActivity() { } override fun onDestroy() { - handler.post { thread.quit() } + handler.onDestroy() super.onDestroy() } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountSelectionActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountSelectionActivity.kt index e446387990f08a35c71e00a9ed2ce963abb0db6e..ec5e12e1cc664ea2d12ebfffb2c1920e0655690c 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountSelectionActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountSelectionActivity.kt @@ -6,7 +6,7 @@ import android.content.Intent import android.content.SharedPreferences import android.os.Bundle import de.kuschku.quasseldroid_ng.Keys -import de.kuschku.quasseldroid_ng.ui.ChatActivity +import de.kuschku.quasseldroid_ng.ui.chat.ChatActivity import de.kuschku.quasseldroid_ng.ui.setup.SetupActivity import de.kuschku.quasseldroid_ng.util.helper.editCommit @@ -27,7 +27,7 @@ class AccountSelectionActivity : SetupActivity() { putLong(Keys.Status.selectedAccount, data.getLong(Keys.Status.selectedAccount, -1)) putBoolean(Keys.Status.reconnect, true) } - startActivity(Intent(this, ChatActivity::class.java)) + startActivityForResult(Intent(this, ChatActivity::class.java), REQUEST_CHAT) } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountSetupActivity.kt index 35921bb0e213bd8184ec9659366fbfee835805d0..816786b5893dfe70bb03af07748abb3d4746a8e4 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountSetupActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountSetupActivity.kt @@ -2,15 +2,13 @@ package de.kuschku.quasseldroid_ng.ui.setup.accounts import android.app.Activity import android.os.Bundle -import android.os.Handler -import android.os.HandlerThread import de.kuschku.quasseldroid_ng.persistence.AccountDatabase import de.kuschku.quasseldroid_ng.ui.setup.SetupActivity +import de.kuschku.quasseldroid_ng.util.AndroidHandlerThread import org.threeten.bp.Instant class AccountSetupActivity : SetupActivity() { - private val thread = HandlerThread("Setup") - private lateinit var handler: Handler + private val handler = AndroidHandlerThread("Setup") override fun onDone(data: Bundle) { val account = AccountDatabase.Account( @@ -23,7 +21,7 @@ class AccountSetupActivity : SetupActivity() { lastUsed = Instant.now().epochSecond ) handler.post { - val (id) = AccountDatabase.Creator.init(this).accounts().create(account) + AccountDatabase.Creator.init(this).accounts().create(account) runOnUiThread { setResult(Activity.RESULT_OK) finish() @@ -32,13 +30,12 @@ class AccountSetupActivity : SetupActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - thread.start() - handler = Handler(thread.looper) + handler.onCreate() super.onCreate(savedInstanceState) } override fun onDestroy() { - handler.post { thread.quit() } + handler.onDestroy() super.onDestroy() } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidHandlerThread.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidHandlerThread.kt new file mode 100644 index 0000000000000000000000000000000000000000..4471116224ca3fb2ecd863c16df9a86a92687ae2 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/AndroidHandlerThread.kt @@ -0,0 +1,135 @@ +package de.kuschku.quasseldroid_ng.util + +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper +import android.os.Message +import android.util.Printer + +class AndroidHandlerThread(name: String) : HandlerThread(name) { + @Volatile + private var handler: Handler? = null + + fun started(): AndroidHandlerThread { + onCreate() + return this + } + + fun onCreate() { + if (handler == null) { + synchronized(this@AndroidHandlerThread) { + if (handler == null) { + start() + handler = Handler(looper) + } + } + } + } + + fun onDestroy() { + if (handler != null) { + post { + quit() + if (handler != null) { + synchronized(this@AndroidHandlerThread) { + if (handler != null) { + handler = null + } + } + } + } + } + } + + fun handleMessage(msg: Message) + = handler?.handleMessage(msg) ?: throw RuntimeException("Thread not started") + + fun dispatchMessage(msg: Message) + = handler?.dispatchMessage(msg) ?: throw RuntimeException("Thread not started") + + fun getMessageName(message: Message): String + = handler?.getMessageName(message) ?: throw RuntimeException("Thread not started") + + fun obtainMessage(): Message + = handler?.obtainMessage() ?: throw RuntimeException("Thread not started") + + fun obtainMessage(what: Int): Message + = handler?.obtainMessage(what) ?: throw RuntimeException("Thread not started") + + fun obtainMessage(what: Int, obj: Any): Message + = handler?.obtainMessage(what, obj) ?: throw RuntimeException("Thread not started") + + fun obtainMessage(what: Int, arg1: Int, arg2: Int): Message + = handler?.obtainMessage(what, arg1, arg2) ?: throw RuntimeException("Thread not started") + + fun obtainMessage(what: Int, arg1: Int, arg2: Int, obj: Any): Message + = handler?.obtainMessage(what, arg1, arg2, obj) ?: throw RuntimeException("Thread not started") + + fun post(r: () -> Unit): Boolean + = handler?.post(r) ?: throw RuntimeException("Thread not started") + + fun postAtTime(r: () -> Unit, uptimeMillis: Long): Boolean + = handler?.postAtTime(r, uptimeMillis) ?: throw RuntimeException("Thread not started") + + fun postAtTime(r: () -> Unit, token: Any, uptimeMillis: Long): Boolean + = handler?.postAtTime(r, token, uptimeMillis) ?: throw RuntimeException("Thread not started") + + fun postDelayed(r: () -> Unit, delayMillis: Long): Boolean + = handler?.postDelayed(r, delayMillis) ?: throw RuntimeException("Thread not started") + + fun postAtFrontOfQueue(r: () -> Unit): Boolean + = handler?.postAtFrontOfQueue(r) ?: throw RuntimeException("Thread not started") + + fun removeCallbacks(r: () -> Unit) + = handler?.removeCallbacks(r) ?: throw RuntimeException("Thread not started") + + fun removeCallbacks(r: () -> Unit, token: Any) + = handler?.removeCallbacks(r, token) ?: throw RuntimeException("Thread not started") + + fun sendMessage(msg: Message): Boolean + = handler?.sendMessage(msg) ?: throw RuntimeException("Thread not started") + + fun sendEmptyMessage(what: Int): Boolean + = handler?.sendEmptyMessage(what) ?: throw RuntimeException("Thread not started") + + fun sendEmptyMessageDelayed(what: Int, delayMillis: Long): Boolean + = handler?.sendEmptyMessageDelayed(what, delayMillis) ?: throw RuntimeException( + "Thread not started") + + fun sendEmptyMessageAtTime(what: Int, uptimeMillis: Long): Boolean + = handler?.sendEmptyMessageAtTime(what, uptimeMillis) ?: throw RuntimeException( + "Thread not started") + + fun sendMessageDelayed(msg: Message, delayMillis: Long): Boolean + = handler?.sendMessageDelayed(msg, delayMillis) ?: throw RuntimeException("Thread not started") + + fun sendMessageAtTime(msg: Message, uptimeMillis: Long): Boolean + = handler?.sendMessageAtTime(msg, uptimeMillis) ?: throw RuntimeException("Thread not started") + + fun sendMessageAtFrontOfQueue(msg: Message): Boolean + = handler?.sendMessageAtFrontOfQueue(msg) ?: throw RuntimeException("Thread not started") + + fun removeMessages(what: Int) + = handler?.removeMessages(what) ?: throw RuntimeException("Thread not started") + + fun removeMessages(what: Int, `object`: Any) + = handler?.removeMessages(what, `object`) ?: throw RuntimeException("Thread not started") + + fun removeCallbacksAndMessages(token: Any) + = handler?.removeCallbacksAndMessages(token) ?: throw RuntimeException("Thread not started") + + fun hasMessages(what: Int): Boolean + = handler?.hasMessages(what) ?: throw RuntimeException("Thread not started") + + fun hasMessages(what: Int, `object`: Any): Boolean + = handler?.hasMessages(what, `object`) ?: throw RuntimeException("Thread not started") + + val handlerLooper: Looper + get() = handler?.looper ?: throw RuntimeException("Thread not started") + + fun dump(pw: Printer, prefix: String) + = handler?.dump(pw, prefix) ?: throw RuntimeException("Thread not started") + + override fun toString(): String + = handler?.toString() ?: throw RuntimeException("Thread not started") +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ebbea8d560a06dab78280fd5c9a774237fbfbc85..c5aac80009882180102c38b38ae04d4f8f2e9b7c 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,57 +7,89 @@ android:fitsSystemWindows="true" tools:openDrawer="start"> - <include layout="@layout/content_main" /> - - <de.kuschku.quasseldroid_ng.util.ui.DrawerRecyclerView - android:id="@+id/nickList" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="end" - android:background="?android:attr/windowBackground" - android:clipToPadding="false" - android:fitsSystemWindows="true" /> - - <de.kuschku.quasseldroid_ng.util.ui.NavigationDrawerLayout + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_gravity="start" - android:background="?android:attr/windowBackground" android:fitsSystemWindows="true" - app:insetForeground="?attr/colorPrimaryDark"> + android:orientation="vertical"> - <LinearLayout + <android.support.design.widget.AppBarLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> + android:layout_height="wrap_content" + android:theme="?attr/actionBarTheme"> - <android.support.design.widget.AppBarLayout + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:theme="?attr/actionBarTheme"> + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + app:contentInsetStartWithNavigation="0dp"> - <android.support.v7.widget.Toolbar - android:id="@+id/toolbar" - android:layout_width="match_parent" - android:layout_height="?attr/actionBarSize" - android:background="?attr/colorPrimary" - app:contentInsetStartWithNavigation="0dp"> + <LinearLayout + android:id="@+id/toolbar_action_area" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="?attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:focusable="true" + android:focusableInTouchMode="false" + android:gravity="center_vertical|start" + android:minHeight="?attr/actionBarSize" + android:orientation="vertical"> - <android.support.v7.widget.AppCompatSpinner - android:id="@+id/chatListSpinner" - android:layout_width="fill_parent" - android:layout_height="match_parent" - android:theme="?attr/actionBarTheme" /> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginTop="-2dp" + android:baselineAligned="false" + android:gravity="center_vertical"> - </android.support.v7.widget.Toolbar> + <TextView + android:id="@+id/key" + style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_marginEnd="2dp" + android:layout_marginRight="2dp" + android:layout_marginTop="2dp" + android:gravity="center" + android:textSize="16sp" + android:visibility="gone" /> - </android.support.design.widget.AppBarLayout> + <TextView + android:id="@+id/toolbar_title" + style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:gravity="center_vertical" + android:singleLine="true" + android:text="@string/app_name" /> + </LinearLayout> - <android.support.v7.widget.RecyclerView - android:id="@+id/chatList" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - </LinearLayout> - </de.kuschku.quasseldroid_ng.util.ui.NavigationDrawerLayout> + <TextView + android:id="@+id/toolbar_subtitle" + style="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="-3dp" + android:ellipsize="end" + android:singleLine="true" + android:visibility="gone" /> + </LinearLayout> + + </android.support.v7.widget.Toolbar> + + </android.support.design.widget.AppBarLayout> + + <include layout="@layout/content_main" /> + + </LinearLayout> + + <include layout="@layout/content_nicklist" /> + + <include layout="@layout/content_chatlist" /> </android.support.v4.widget.DrawerLayout> diff --git a/app/src/main/res/layout/content_chatlist.xml b/app/src/main/res/layout/content_chatlist.xml new file mode 100644 index 0000000000000000000000000000000000000000..76715e8e3ce75d4254750d61c3bce2cdffcf2a8c --- /dev/null +++ b/app/src/main/res/layout/content_chatlist.xml @@ -0,0 +1,43 @@ +<de.kuschku.quasseldroid_ng.util.ui.NavigationDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="start" + android:background="?android:attr/windowBackground" + android:fitsSystemWindows="true" + app:insetForeground="?attr/colorPrimaryDark"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.design.widget.AppBarLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:theme="?attr/actionBarTheme"> + + <android.support.v7.widget.Toolbar + android:id="@+id/chatListToolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + app:contentInsetStartWithNavigation="0dp"> + + <android.support.v7.widget.AppCompatSpinner + android:id="@+id/chatListSpinner" + android:layout_width="fill_parent" + android:layout_height="match_parent" + android:theme="?attr/actionBarTheme" /> + + </android.support.v7.widget.Toolbar> + + </android.support.design.widget.AppBarLayout> + + + <android.support.v7.widget.RecyclerView + android:id="@+id/chatList" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + </LinearLayout> +</de.kuschku.quasseldroid_ng.util.ui.NavigationDrawerLayout> diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index a4984d3789756b6101de591c7cd1159a7ddf92b1..8151d2669b8cc7c546e73ad70c3871e569665bc3 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -1,128 +1,33 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" - android:fitsSystemWindows="true" - android:orientation="vertical"> + android:layout_height="match_parent"> - <android.support.design.widget.AppBarLayout + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:theme="?attr/actionBarTheme"> - - <android.support.v7.widget.Toolbar - android:id="@+id/toolbar" - android:layout_width="match_parent" - android:layout_height="?attr/actionBarSize" - android:background="?attr/colorPrimary" - app:contentInsetStartWithNavigation="0dp"> - - <LinearLayout - android:id="@+id/toolbar_action_area" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:background="?attr/selectableItemBackgroundBorderless" - android:clickable="true" - android:focusable="true" - android:focusableInTouchMode="false" - android:gravity="center_vertical|start" - android:minHeight="?attr/actionBarSize" - android:orientation="vertical"> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:layout_marginTop="-2dp" - android:baselineAligned="false" - android:gravity="center_vertical"> - - <TextView - android:id="@+id/key" - style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_marginEnd="2dp" - android:layout_marginRight="2dp" - android:layout_marginTop="2dp" - android:gravity="center" - android:textSize="16sp" - android:visibility="gone" /> - - <TextView - android:id="@+id/toolbar_title" - style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ellipsize="end" - android:gravity="center_vertical" - android:singleLine="true" - android:text="@string/app_name" /> - - </LinearLayout> - - <TextView - android:id="@+id/toolbar_subtitle" - style="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="-3dp" - android:ellipsize="end" - android:singleLine="true" - android:visibility="gone" /> - </LinearLayout> - - </android.support.v7.widget.Toolbar> - - </android.support.design.widget.AppBarLayout> - - <ScrollView - android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:orientation="vertical" + android:paddingLeft="24dp" + android:paddingRight="24dp"> <LinearLayout android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="8dp" - android:layout_marginTop="8dp" - android:orientation="vertical" - android:paddingLeft="24dp" - android:paddingRight="24dp"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <Button - android:id="@+id/connect" - style="@style/Widget.Button.Colored" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:minWidth="88dp" - android:text="Connect" /> + android:layout_height="wrap_content"> - <Button - android:id="@+id/disconnect" - style="@style/Widget.Button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:minWidth="88dp" - android:text="Disconnect" /> - - <Button - android:id="@+id/clear" - style="@style/Widget.Button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:minWidth="88dp" - android:text="Clear" /> - </LinearLayout> - - <TextView - android:id="@+id/errorList" - android:layout_width="match_parent" + <Button + android:id="@+id/clear" + style="@style/Widget.Button" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textIsSelectable="true" /> + android:minWidth="88dp" + android:text="Clear" /> </LinearLayout> - </ScrollView> -</LinearLayout> + + <TextView + android:id="@+id/errorList" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textIsSelectable="true" /> + </LinearLayout> +</ScrollView> diff --git a/app/src/main/res/layout/content_nicklist.xml b/app/src/main/res/layout/content_nicklist.xml new file mode 100644 index 0000000000000000000000000000000000000000..d056659f040debc25c381349d4aefcfcfdde0787 --- /dev/null +++ b/app/src/main/res/layout/content_nicklist.xml @@ -0,0 +1,8 @@ +<de.kuschku.quasseldroid_ng.util.ui.DrawerRecyclerView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/nickList" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="end" + android:background="?android:attr/windowBackground" + android:clipToPadding="false" + android:fitsSystemWindows="true" /> diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt b/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt index a0535645b8d50bedab512b1252272b23e6f96d93..634d1da4583fe2748fb170377024c004f373c365 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt @@ -1,6 +1,7 @@ package de.kuschku.libquassel.session interface Backend { + fun connectUnlessConnected(address: SocketAddress, user: String, pass: String) fun connect(address: SocketAddress, user: String, pass: String) fun disconnect() fun sessionManager(): SessionManager diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt index a90363ffa07ebab255cac266048141097b32a96a..935c503d775b384b1efb168e85145cb435aabf3c 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt @@ -49,6 +49,12 @@ class SessionManager( Invokers } + fun ifDisconnected(closure: () -> Unit) { + if (state.or(ConnectionState.DISCONNECTED) == ConnectionState.DISCONNECTED) { + closure() + } + } + fun connect( clientData: ClientData, trustManager: X509TrustManager, @@ -61,6 +67,7 @@ class SessionManager( } fun disconnect() { + inProgressSession.value inProgressSession.value.close() inProgressSession.onNext(offlineSession) }