diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..388691cea9255c689c428d7d1641a172f439f0d3 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt @@ -0,0 +1,71 @@ +package de.kuschku.quasseldroid_ng.ui.chat + +import android.arch.lifecycle.LifecycleOwner +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.Observer +import android.support.v7.util.DiffUtil +import android.support.v7.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.libquassel.protocol.BufferId + +class BufferListAdapter( + lifecycleOwner: LifecycleOwner, + liveData: LiveData<List<BufferId>?>, + runInBackground: (() -> Unit) -> Any, + runOnUiThread: (Runnable) -> Any +) : RecyclerView.Adapter<BufferListAdapter.BufferViewHolder>() { + var data = mutableListOf<BufferId>() + + init { + liveData.observe(lifecycleOwner, Observer { list: List<BufferId>? -> + runInBackground { + val old = data + val new = list ?: emptyList() + val result = DiffUtil.calculateDiff( + object : DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) + = old[oldItemPosition] == new[newItemPosition] + + override fun getOldListSize() = old.size + override fun getNewListSize() = new.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) + = old[oldItemPosition] == new[newItemPosition] + }, true) + runOnUiThread(Runnable { + data.clear() + data.addAll(new) + result.dispatchUpdatesTo(this@BufferListAdapter) + }) + } + }) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = BufferViewHolder( + LayoutInflater.from(parent.context) + .inflate(android.R.layout.simple_list_item_1, parent, false) + ) + + override fun onBindViewHolder(holder: BufferViewHolder, position: Int) + = holder.bind(data[position]) + + override fun getItemCount() = data.size + + class BufferViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + @BindView(android.R.id.text1) + lateinit var text: TextView + + init { + ButterKnife.bind(this, itemView) + } + + fun bind(bufferId: BufferId) { + text.text = "$bufferId" + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..007573f626c2546f8f88cbdbc9b46a9ef0688edd --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferViewConfigAdapter.kt @@ -0,0 +1,76 @@ +package de.kuschku.quasseldroid_ng.ui.chat + +import android.arch.lifecycle.LifecycleOwner +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.Observer +import android.support.v7.widget.RecyclerView +import android.support.v7.widget.ThemedSpinnerAdapter +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.libquassel.quassel.syncables.BufferViewConfig +import de.kuschku.quasseldroid_ng.R +import de.kuschku.quasseldroid_ng.util.ui.ContextThemeWrapper +import de.kuschku.quasseldroid_ng.util.ui.RecyclerSpinnerAdapter + +class BufferViewConfigAdapter( + lifecycleOwner: LifecycleOwner, + liveData: LiveData<List<BufferViewConfig>> +) : RecyclerSpinnerAdapter<BufferViewConfigAdapter.BufferViewConfigViewHolder>(), + ThemedSpinnerAdapter { + val data = mutableListOf<BufferViewConfig>() + + init { + liveData.observe(lifecycleOwner, Observer { list: List<BufferViewConfig>? -> + data.clear() + if (list != null) { + data.addAll(list) + } + notifyDataSetChanged() + }) + } + + override fun isEmpty() = data.isEmpty() + + override fun onBindViewHolder(holder: BufferViewConfigViewHolder, position: Int) + = holder.bind(getItem(position)) + + override fun onCreateViewHolder(parent: ViewGroup, dropDown: Boolean) + : BufferViewConfigViewHolder { + val inflater = LayoutInflater.from( + if (dropDown) + ContextThemeWrapper(parent.context, dropDownViewTheme) + else + parent.context + ) + val view = inflater.inflate(R.layout.widget_spinner_item_toolbar, parent, false) + return BufferViewConfigViewHolder(view) + } + + override fun getItem(position: Int): BufferViewConfig? = when (position) { + in (0 until data.size) -> data[position] + else -> null + } + + override fun getItemId(position: Int) = getItem(position)?.bufferViewId()?.toLong() ?: -1L + + override fun hasStableIds() = true + + override fun getCount() = data.size + + class BufferViewConfigViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + @BindView(android.R.id.text1) + lateinit var text: TextView + + init { + ButterKnife.bind(this, itemView) + } + + fun bind(bufferViewConfig: BufferViewConfig?) { + text.text = bufferViewConfig?.bufferViewName() ?: "" + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt index 1357f85f23066bea3c707dbffe08c70757e9c868..ba7818a4fc59b58ef8e7f2a422560b0524bf1931 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt @@ -14,8 +14,10 @@ import android.widget.Button import android.widget.TextView import butterknife.BindView import butterknife.ButterKnife -import de.kuschku.libquassel.quassel.syncables.BufferViewManager -import de.kuschku.libquassel.session.* +import de.kuschku.libquassel.session.Backend +import de.kuschku.libquassel.session.ConnectionState +import de.kuschku.libquassel.session.SessionManager +import de.kuschku.libquassel.session.SocketAddress import de.kuschku.libquassel.util.compatibility.LoggingHandler import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO import de.kuschku.quasseldroid_ng.Keys @@ -46,16 +48,6 @@ class ChatActivity : ServiceBoundActivity() { private val sessionManager: LiveData<SessionManager?> = backend.map(Backend::sessionManager) private val state = sessionManager.switchMapRx(SessionManager::state) - private val bufferViewManager: LiveData<BufferViewManager?> - = sessionManager.switchMapRx(SessionManager::session).map(ISession::bufferViewManager) - private val bufferViewConfigs = bufferViewManager.switchMapRx { manager -> - manager.bufferViewConfigIds.map { ids -> - ids.map { id -> - manager.bufferViewConfig(id) - }.sortedBy { it?.bufferViewName() } - } - } - private var snackbar: Snackbar? = null private val dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ISO_TIME 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 deleted file mode 100644 index 13a9fa6662bd17ef7e6b37e906d23eb000ede531..0000000000000000000000000000000000000000 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatListDelegate.kt +++ /dev/null @@ -1,3 +0,0 @@ -package de.kuschku.quasseldroid_ng.ui.chat - -class ChatListDelegate diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatListFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..d128a9486337a97815466df9e729b5587d99f6a5 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatListFragment.kt @@ -0,0 +1,90 @@ +package de.kuschku.quasseldroid_ng.ui.chat + +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MutableLiveData +import android.os.Bundle +import android.support.v7.widget.* +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.libquassel.quassel.syncables.BufferViewConfig +import de.kuschku.libquassel.quassel.syncables.BufferViewManager +import de.kuschku.libquassel.session.Backend +import de.kuschku.libquassel.session.ISession +import de.kuschku.libquassel.session.SessionManager +import de.kuschku.quasseldroid_ng.R +import de.kuschku.quasseldroid_ng.util.AndroidHandlerThread +import de.kuschku.quasseldroid_ng.util.helper.map +import de.kuschku.quasseldroid_ng.util.helper.or +import de.kuschku.quasseldroid_ng.util.helper.switchMapRx +import de.kuschku.quasseldroid_ng.util.service.ServiceBoundFragment + +class ChatListFragment : ServiceBoundFragment() { + private val handlerThread = AndroidHandlerThread("ChatList") + + @BindView(R.id.chatListToolbar) + lateinit var chatListToolbar: Toolbar + + @BindView(R.id.chatListSpinner) + lateinit var chatListSpinner: AppCompatSpinner + + @BindView(R.id.chatList) + lateinit var chatList: RecyclerView + + private val sessionManager: LiveData<SessionManager?> + = backend.map(Backend::sessionManager) + private val bufferViewManager: LiveData<BufferViewManager?> + = sessionManager.switchMapRx(SessionManager::session).map(ISession::bufferViewManager) + private val bufferViewConfigs = bufferViewManager.switchMapRx { manager -> + manager.live_bufferViewConfigs.map { ids -> + ids.mapNotNull { id -> + manager.bufferViewConfig(id) + }.sortedWith(Comparator { a, b -> + (a?.bufferViewName() ?: "").compareTo((b?.bufferViewName() ?: ""), true) + }) + } + }.or(emptyList()) + + private val selectedBufferViewConfig = MutableLiveData<BufferViewConfig>() + + private val itemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(p0: AdapterView<*>?) { + selectedBufferViewConfig.value = null + } + + override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { + selectedBufferViewConfig.value = adapter.getItem(p2) + } + } + + private val adapter = BufferViewConfigAdapter(this, bufferViewConfigs) + + private val bufferList = selectedBufferViewConfig.switchMapRx(BufferViewConfig::live_buffers) + + override fun onCreate(savedInstanceState: Bundle?) { + handlerThread.onCreate() + super.onCreate(savedInstanceState) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.content_chat_list, container, false) + ButterKnife.bind(this, view) + chatListSpinner.adapter = adapter + chatListSpinner.onItemSelectedListener = itemSelectedListener + + chatList.adapter = BufferListAdapter(this, bufferList, handlerThread::post, + activity::runOnUiThread) + chatList.layoutManager = LinearLayoutManager(context) + chatList.itemAnimator = DefaultItemAnimator() + return view + } + + override fun onDestroy() { + handlerThread.onDestroy() + super.onDestroy() + } +} 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 deleted file mode 100644 index 936c10251e84a1613110a19f9c5a174f13c91005..0000000000000000000000000000000000000000 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/EditorDelegate.kt +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 1bb9509be2604a846961ea10d958e0caff38ef7f..0000000000000000000000000000000000000000 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/NickListDelegate.kt +++ /dev/null @@ -1,3 +0,0 @@ -package de.kuschku.quasseldroid_ng.ui.chat - -class NickListDelegate diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountAdapter.kt index ce4bdbb4ea6f51b98c1625becced4d00d597a44c..1d68c90a2b4e233318ee45eb64145bd84897b4d2 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountAdapter.kt @@ -28,6 +28,10 @@ class AccountAdapter : } } + init { + setHasStableIds(true) + } + private val actionListener = object : ItemListener { override fun onAction(id: Long, pos: Int) { for (actionListener in actionListeners) { @@ -77,8 +81,8 @@ class AccountAdapter : selectionListeners.remove(f) } - override fun onBindViewHolder(holder: AccountViewHolder, @SuppressLint( - "RecyclerView") position: Int) { + override fun onBindViewHolder(holder: AccountViewHolder, + @SuppressLint("RecyclerView") position: Int) { when (holder) { is AccountViewHolder.Item -> { val account = getItem(position) diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/LiveDataHelper.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/LiveDataHelper.kt index ddfdb78758459205cc02f5356b764d9dc05e4920..8b0f9998205dfe8c2285b74dcb1ed6b44ab1002e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/LiveDataHelper.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/helper/LiveDataHelper.kt @@ -90,7 +90,7 @@ inline fun <X> LiveData<X>.orElse( } @MainThread -inline fun <X> LiveData<X>.or( +inline fun <X> LiveData<X?>.or( default: X ): LiveData<X> { val result = object : MediatorLiveData<X>() { diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..87c260f11ad0bbbb6047a1306d7c26acc1bb1269 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/service/ServiceBoundFragment.kt @@ -0,0 +1,49 @@ +package de.kuschku.quasseldroid_ng.util.service + +import android.arch.lifecycle.MutableLiveData +import android.content.ComponentName +import android.content.Intent +import android.content.ServiceConnection +import android.os.Bundle +import android.os.IBinder +import android.support.v4.app.Fragment +import de.kuschku.libquassel.session.Backend +import de.kuschku.quasseldroid_ng.service.QuasselService + +abstract class ServiceBoundFragment : Fragment() { + protected val backend = MutableLiveData<Backend?>() + + private val connection = object : ServiceConnection { + override fun onServiceDisconnected(component: ComponentName?) { + when (component) { + ComponentName(context.applicationContext, QuasselService::class.java) -> { + backend.value = null + } + } + } + + override fun onServiceConnected(component: ComponentName?, binder: IBinder?) { + when (component) { + ComponentName(context.applicationContext, QuasselService::class.java) -> + if (binder is QuasselService.QuasselBinder) { + backend.value = binder.backend + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + context.startService(Intent(context, QuasselService::class.java)) + } + + override fun onStart() { + context.bindService(Intent(context, QuasselService::class.java), connection, 0) + super.onStart() + } + + override fun onStop() { + super.onStop() + context.unbindService(connection) + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/ui/ContextThemeWrapper.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ui/ContextThemeWrapper.kt new file mode 100644 index 0000000000000000000000000000000000000000..39996a3caaa0b0d9734cc0c0b59ed4935d58dd6c --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ui/ContextThemeWrapper.kt @@ -0,0 +1,177 @@ +package de.kuschku.quasseldroid_ng.util.ui + +import android.content.Context +import android.content.ContextWrapper +import android.content.res.AssetManager +import android.content.res.Configuration +import android.content.res.Resources +import android.os.Build +import android.support.annotation.StyleRes +import android.support.v7.appcompat.R +import android.view.LayoutInflater + +/** + * A ContextWrapper that allows you to modify the theme from what is in the + * wrapped context. + * + * @hide + */ +class ContextThemeWrapper : ContextWrapper { + var themeResId: Int = 0 + private set + private var mTheme: Resources.Theme? = null + private var mInflater: LayoutInflater? = null + /** + * Used by ActivityThread to apply the overridden configuration to onConfigurationChange + * callbacks. + * @hide + */ + var overrideConfiguration: Configuration? = null + private set + private var mResources: Resources? = null + + /** + * Creates a new context wrapper with no theme and no base context. + * + * + * **Note:** A base context **must** be attached + * using [.attachBaseContext] before calling any other + * method on the newly constructed context wrapper. + */ + constructor() : super(null) + + /** + * Creates a new context wrapper with the specified theme. + * + * + * The specified theme will be applied on top of the base context's theme. + * Any attributes not explicitly defined in the theme identified by + * <var>themeResId</var> will retain their original values. + * + * @param base the base context + * @param themeResId the resource ID of the theme to be applied on top of + * the base context's theme + */ + constructor(base: Context, @StyleRes themeResId: Int) : super(base) { + this.themeResId = themeResId + } + + /** + * Creates a new context wrapper with the specified theme. + * + * + * Unlike [.ContextThemeWrapper], the theme passed to + * this constructor will completely replace the base context's theme. + * + * @param base the base context + * @param theme the theme against which resources should be inflated + */ + constructor(base: Context, theme: Resources.Theme?) : super(base) { + mTheme = theme + } + + override fun attachBaseContext(newBase: Context) { + super.attachBaseContext(newBase) + } + + /** + * Call to set an "override configuration" on this context -- this is + * a configuration that replies one or more values of the standard + * configuration that is applied to the context. See + * [Context.createConfigurationContext] for more + * information. + * + * + * This method can only be called once, and must be called before any + * calls to [.getResources] or [.getAssets] are made. + */ + fun applyOverrideConfiguration(overrideConfiguration: Configuration) { + if (mResources != null) { + throw IllegalStateException( + "getResources() or getAssets() has already been called") + } + if (this.overrideConfiguration != null) { + throw IllegalStateException("Override configuration has already been set") + } + this.overrideConfiguration = Configuration(overrideConfiguration) + } + + override fun getResources(): Resources? { + return resourcesInternal + } + + private val resourcesInternal: Resources? + get() { + if (mResources == null) { + if (overrideConfiguration == null) { + mResources = super.getResources() + } else if (Build.VERSION.SDK_INT >= 17) { + val resContext = createConfigurationContext(overrideConfiguration) + mResources = resContext.resources + } + } + return mResources + } + + override fun setTheme(resid: Int) { + if (themeResId != resid) { + themeResId = resid + initializeTheme() + } + } + + override fun getTheme(): Resources.Theme? { + if (mTheme != null) { + return mTheme + } + + if (themeResId == 0) { + themeResId = R.style.Theme_AppCompat_Light + } + initializeTheme() + + return mTheme + } + + override fun getSystemService(name: String): Any? { + if (Context.LAYOUT_INFLATER_SERVICE == name) { + if (mInflater == null) { + mInflater = LayoutInflater.from(baseContext).cloneInContext(this) + } + return mInflater + } + return baseContext.getSystemService(name) + } + + /** + * Called by [.setTheme] and [.getTheme] to apply a theme + * resource to the current Theme object. Can override to change the + * default (simple) behavior. This method will not be called in multiple + * threads simultaneously. + * + * @param theme The Theme object being modified. + * @param resid The theme style resource being applied to <var>theme</var>. + * @param first Set to true if this is the first time a style is being + * applied to <var>theme</var>. + */ + protected fun onApplyThemeResource(theme: Resources.Theme, resid: Int, first: Boolean) { + theme.applyStyle(resid, true) + } + + private fun initializeTheme() { + val first = mTheme == null + if (first) { + mTheme = resources!!.newTheme() + val theme = baseContext.theme + if (theme != null) { + mTheme!!.setTo(theme) + } + } + onApplyThemeResource(mTheme!!, themeResId, first) + } + + override fun getAssets(): AssetManager { + // Ensure we're returning assets with the correct configuration. + return resources!!.assets + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/ui/RecyclerSpinnerAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ui/RecyclerSpinnerAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..368b5c791598241bfc240be2b9ca2608e69d6ea0 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/ui/RecyclerSpinnerAdapter.kt @@ -0,0 +1,37 @@ +package de.kuschku.quasseldroid_ng.util.ui + +import android.content.res.Resources +import android.support.v7.widget.RecyclerView +import android.support.v7.widget.ThemedSpinnerAdapter +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter + +abstract class RecyclerSpinnerAdapter<VH : RecyclerView.ViewHolder> : BaseAdapter(), + ThemedSpinnerAdapter { + private var dropDownViewTheme: Resources.Theme? = null + override fun getDropDownViewTheme() = dropDownViewTheme + override fun setDropDownViewTheme(theme: Resources.Theme?) { + dropDownViewTheme = theme + } + + override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { + val tag = convertView?.tag + val holder: VH = tag as? VH ?: onCreateViewHolder(parent, true) + holder.itemView.tag = holder + onBindViewHolder(holder, position) + return holder.itemView + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val tag = convertView?.tag + val holder = tag as? VH ?: onCreateViewHolder(parent, false) + holder.itemView.tag = holder + onBindViewHolder(holder, position) + return holder.itemView + } + + + protected abstract fun onBindViewHolder(holder: VH, position: Int) + protected abstract fun onCreateViewHolder(parent: ViewGroup, dropDown: Boolean): VH +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f81b88965a24284ac96c715cfc4b775626643783..0c505022b8b5aca928d6c2617ec12b1fac38a367 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -22,7 +22,8 @@ android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" - app:contentInsetStartWithNavigation="0dp"> + app:contentInsetStartWithNavigation="0dp" + app:popupTheme="@style/Widget.PopupOverlay"> <LinearLayout android:id="@+id/toolbar_action_area" @@ -89,5 +90,18 @@ <include layout="@layout/content_nick_list" /> - <include layout="@layout/content_chat_list" /> + <de.kuschku.quasseldroid_ng.util.ui.NavigationDrawerLayout + 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"> + + <fragment + android:id="@+id/chatListFragment" + android:name="de.kuschku.quasseldroid_ng.ui.chat.ChatListFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + </de.kuschku.quasseldroid_ng.util.ui.NavigationDrawerLayout> </android.support.v4.widget.DrawerLayout> diff --git a/app/src/main/res/layout/content_chat_list.xml b/app/src/main/res/layout/content_chat_list.xml index 7c7cba9cb62882854db66a64d1f46edb328558bb..a5bf5a98ef227351033b387322190ebf47cd8bae 100644 --- a/app/src/main/res/layout/content_chat_list.xml +++ b/app/src/main/res/layout/content_chat_list.xml @@ -1,45 +1,37 @@ -<de.kuschku.quasseldroid_ng.util.ui.NavigationDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" 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" - tools:showIn="@layout/activity_main"> + 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/chatListToolbar" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:theme="?attr/actionBarTheme"> + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + android:theme="?attr/actionBarTheme" + app:contentInsetStartWithNavigation="0dp" + app:popupTheme="@style/Widget.PopupOverlay"> - <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" + app:popupTheme="@style/Widget.PopupOverlay" /> - <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.v7.widget.Toolbar> + </android.support.design.widget.AppBarLayout> - </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> + <android.support.v7.widget.RecyclerView + android:id="@+id/chatList" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/app/src/main/res/layout/widget_spinner_item_inline.xml b/app/src/main/res/layout/widget_spinner_item_inline.xml new file mode 100644 index 0000000000000000000000000000000000000000..b21fc81584630bf85d2771c9dd2c164f7013e61c --- /dev/null +++ b/app/src/main/res/layout/widget_spinner_item_inline.xml @@ -0,0 +1,7 @@ +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:minHeight="48dp" + android:textAppearance="?android:attr/textAppearanceListItemSmall" /> diff --git a/app/src/main/res/layout/widget_spinner_item_toolbar.xml b/app/src/main/res/layout/widget_spinner_item_toolbar.xml new file mode 100644 index 0000000000000000000000000000000000000000..fa2ad16dc1baf60651bce7e96127b01ba6600e77 --- /dev/null +++ b/app/src/main/res/layout/widget_spinner_item_toolbar.xml @@ -0,0 +1,11 @@ +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@android:id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:minHeight="?attr/actionBarSize" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:textAppearance="?android:attr/textAppearanceListItemSmall" + tools:text="All Chats" /> diff --git a/app/src/main/res/values/styles_widgets.xml b/app/src/main/res/values/styles_widgets.xml index f10b077c7d58296629c4a67f69298a5ab73e4da8..7c72974099ec0791346f033dd16aa62775e7d814 100644 --- a/app/src/main/res/values/styles_widgets.xml +++ b/app/src/main/res/values/styles_widgets.xml @@ -35,4 +35,6 @@ <style name="Widget.DrawerArrowToggle.Light" parent="Widget.AppCompat.DrawerArrowToggle"> <item name="color">?attr/colorControlNormal</item> </style> + + <style name="Widget.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> </resources> diff --git a/app/src/main/res/values/themes_base.xml b/app/src/main/res/values/themes_base.xml index 1929c6d5a5bb2291628cb0523db12e4901735fe0..9ca5f8a3c00abd278ac97f5fb1bd4bde883c3f61 100644 --- a/app/src/main/res/values/themes_base.xml +++ b/app/src/main/res/values/themes_base.xml @@ -12,16 +12,14 @@ <item name="colorAccent">@color/colorAccent</item> </style> - <style name="Theme.AppTheme.Light.NoActionBar" parent="Theme.AppCompat.Light.NoActionBar"> - <item name="colorPrimary">@color/colorPrimary</item> - <item name="colorPrimaryDark">@color/colorPrimaryDark</item> - <item name="colorAccent">@color/colorAccent</item> + <style name="Theme.AppTheme.NoActionBar" parent="Theme.AppTheme"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> </style> - <style name="Theme.AppTheme.NoActionBar" parent="Theme.AppCompat.NoActionBar"> - <item name="colorPrimary">@color/colorPrimary</item> - <item name="colorPrimaryDark">@color/colorPrimaryDark</item> - <item name="colorAccent">@color/colorAccent</item> + <style name="Theme.AppTheme.Light.NoActionBar" parent="Theme.AppTheme.Light"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> </style> <style name="Theme.Base.ChatTheme" parent="Theme.AppTheme.NoActionBar" /> diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt index e96c1a90b933d65a84b3b0caeecd17698a1fe52e..2ed66474cf78211ba3089cb39fb58f4884cb4083 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt @@ -5,6 +5,7 @@ import de.kuschku.libquassel.protocol.* import de.kuschku.libquassel.protocol.Type import de.kuschku.libquassel.quassel.syncables.interfaces.IBufferViewConfig import de.kuschku.libquassel.session.SignalProxy +import io.reactivex.subjects.BehaviorSubject class BufferViewConfig constructor( bufferViewId: Int, @@ -54,14 +55,17 @@ class BufferViewConfig constructor( override fun initSetBufferList(buffers: QVariantList) { _buffers = buffers.mapNotNull { it.value<BufferId?>() }.toMutableList() + live_buffers.onNext(_buffers) } override fun initSetRemovedBuffers(buffers: QVariantList) { _removedBuffers = buffers.mapNotNull { it.value<BufferId?>() }.toMutableSet() + live_removedBuffers.onNext(_removedBuffers) } override fun initSetTemporarilyRemovedBuffers(buffers: QVariantList) { _temporarilyRemovedBuffers = buffers.mapNotNull { it.value<BufferId?>() }.toMutableSet() + live_temporarilyRemovedBuffers.onNext(_temporarilyRemovedBuffers) } override fun initSetProperties(properties: QVariantMap) { @@ -82,13 +86,18 @@ class BufferViewConfig constructor( if (_buffers.contains(bufferId)) return - if (_removedBuffers.contains(bufferId)) + if (_removedBuffers.contains(bufferId)) { _removedBuffers.remove(bufferId) + live_removedBuffers.onNext(_removedBuffers) + } - if (_temporarilyRemovedBuffers.contains(bufferId)) + if (_temporarilyRemovedBuffers.contains(bufferId)) { _temporarilyRemovedBuffers.remove(bufferId) + live_temporarilyRemovedBuffers.onNext(_temporarilyRemovedBuffers) + } _buffers.add(minOf(maxOf(pos, 0), _buffers.size), bufferId) + live_buffers.onNext(_buffers) } override fun moveBuffer(bufferId: BufferId, pos: Int) { @@ -107,26 +116,38 @@ class BufferViewConfig constructor( _buffers.removeAt(currentPos) _buffers.add(bufferId, targetPos - 1) } + + live_buffers.onNext(_buffers) } override fun removeBuffer(bufferId: BufferId) { - if (_buffers.contains(bufferId)) + if (_buffers.contains(bufferId)) { _buffers.remove(bufferId) + live_buffers.onNext(_buffers) + } - if (_removedBuffers.contains(bufferId)) + if (_removedBuffers.contains(bufferId)) { _removedBuffers.remove(bufferId) + live_removedBuffers.onNext(_removedBuffers) + } _temporarilyRemovedBuffers.add(bufferId) + live_temporarilyRemovedBuffers.onNext(_temporarilyRemovedBuffers) } override fun removeBufferPermanently(bufferId: BufferId) { - if (_buffers.contains(bufferId)) + if (_buffers.contains(bufferId)) { _buffers.remove(bufferId) + live_buffers.onNext(_buffers) + } - if (_temporarilyRemovedBuffers.contains(bufferId)) + if (_temporarilyRemovedBuffers.contains(bufferId)) { _temporarilyRemovedBuffers.remove(bufferId) + live_temporarilyRemovedBuffers.onNext(_temporarilyRemovedBuffers) + } _removedBuffers.add(bufferId) + live_removedBuffers.onNext(_removedBuffers) } fun bufferViewId() = _bufferViewId @@ -206,4 +227,13 @@ class BufferViewConfig constructor( private var _buffers: MutableList<BufferId> = mutableListOf() private var _removedBuffers: MutableSet<BufferId> = mutableSetOf() private var _temporarilyRemovedBuffers: MutableSet<BufferId> = mutableSetOf() + + val live_buffers: BehaviorSubject<List<BufferId>> + = BehaviorSubject.createDefault<List<BufferId>>(emptyList()) + + val live_removedBuffers: BehaviorSubject<Set<BufferId>> + = BehaviorSubject.createDefault<Set<BufferId>>(emptySet()) + + val live_temporarilyRemovedBuffers: BehaviorSubject<Set<BufferId>> + = BehaviorSubject.createDefault<Set<BufferId>>(emptySet()) } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt index 101b215a48068c5443dc781699f283a6beaf2888..0de820eb5943b7459ef44a715b3502f21abcb848 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt @@ -36,7 +36,7 @@ class BufferViewManager constructor( proxy.synchronize(config, !initialized) _bufferViewConfigs[config.bufferViewId()] = config - bufferViewConfigIds.onNext(_bufferViewConfigs.keys) + live_bufferViewConfigs.onNext(_bufferViewConfigs.keys) } override fun addBufferViewConfig(bufferViewConfigId: Int) { @@ -51,12 +51,12 @@ class BufferViewManager constructor( return _bufferViewConfigs.remove(bufferViewConfigId) - bufferViewConfigIds.onNext(_bufferViewConfigs.keys) + live_bufferViewConfigs.onNext(_bufferViewConfigs.keys) } private val _bufferViewConfigs: MutableMap<Int, BufferViewConfig> = mutableMapOf() - val bufferViewConfigIds: BehaviorSubject<Set<Int>> + val live_bufferViewConfigs: BehaviorSubject<Set<Int>> = BehaviorSubject.createDefault<Set<Int>>(emptySet()) }