forked from upstream-mirrors/quik
Flatten hierarchy in presentation module
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QKSMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package presentation.common.base
|
||||
|
||||
import android.support.annotation.CallSuper
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
||||
/**
|
||||
* Base RecyclerView.Adapter that provides some convenience when creating a new Adapter, such as
|
||||
* data list handing and item animations
|
||||
*/
|
||||
abstract class FlowableAdapter<T> : QkAdapter<T>() {
|
||||
|
||||
var flowable: Flowable<List<T>>? = null
|
||||
set(value) {
|
||||
if (field == value) return
|
||||
|
||||
field = value
|
||||
|
||||
// Stop listening for updates on the old flowable
|
||||
dispose()
|
||||
|
||||
// If we're attached to any RecyclerViews, then subscribe to updates
|
||||
if (recyclerViews.isNotEmpty()) {
|
||||
subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
private val recyclerViews = ArrayList<RecyclerView>()
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
@CallSuper
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
|
||||
// If this is the first RecyclerView to be attached, then start listening to updates
|
||||
if (recyclerViews.isEmpty() || disposable == null) {
|
||||
subscribe()
|
||||
}
|
||||
|
||||
recyclerViews.add(recyclerView)
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
recyclerViews.remove(recyclerView)
|
||||
|
||||
// If no more RecyclerViews are attached, stop listening to updates
|
||||
if (recyclerViews.isEmpty()) {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribe() {
|
||||
disposable = flowable?.subscribe { data = it }
|
||||
}
|
||||
|
||||
private fun dispose() {
|
||||
disposable?.dispose()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QKSMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package presentation.common.base
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.arch.lifecycle.ViewModelProviders
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import presentation.common.Navigator
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
abstract class QkActivity<VM : QkViewModel<*, *>> : AppCompatActivity() {
|
||||
|
||||
protected abstract val viewModelClass: KClass<VM>
|
||||
protected val viewModel: VM by lazy {
|
||||
ViewModelProviders.of(this, Navigator.ViewModelFactory(intent))[viewModelClass.java]
|
||||
}
|
||||
|
||||
protected val menu: Subject<Menu> = BehaviorSubject.create()
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
onNewIntent(intent)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setContentView(layoutResID: Int) {
|
||||
super.setContentView(layoutResID)
|
||||
setSupportActionBar(toolbar)
|
||||
title = title // The title may have been set before layout inflation
|
||||
}
|
||||
|
||||
override fun setTitle(title: CharSequence?) {
|
||||
super.setTitle(title)
|
||||
toolbarTitle?.text = title
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
val result = super.onCreateOptionsMenu(menu)
|
||||
if (menu != null) {
|
||||
this.menu.onNext(menu)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QKSMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package presentation.common.base
|
||||
|
||||
import android.support.v7.util.DiffUtil
|
||||
import android.support.v7.widget.RecyclerView
|
||||
|
||||
/**
|
||||
* Base RecyclerView.Adapter that provides some convenience when creating a new Adapter, such as
|
||||
* data list handing and item animations
|
||||
*/
|
||||
abstract class QkAdapter<T> : RecyclerView.Adapter<QkViewHolder>() {
|
||||
|
||||
var data: List<T> = ArrayList()
|
||||
set(value) {
|
||||
val diff = DiffUtil.calculateDiff(getDiffUtilCallback(field, value))
|
||||
field = value
|
||||
diff.dispatchUpdatesTo(this)
|
||||
onDatasetChanged()
|
||||
}
|
||||
|
||||
fun getItem(position: Int): T {
|
||||
return data[position]
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return data.size
|
||||
}
|
||||
|
||||
open fun onDatasetChanged() {}
|
||||
|
||||
/**
|
||||
* Allows the adapter implementation to provide a custom DiffUtil.Callback
|
||||
* If not, then the abstract implementation will be used
|
||||
*/
|
||||
private fun getDiffUtilCallback(oldData: List<T>, newData: List<T>): DiffUtil.Callback {
|
||||
return object : DiffUtil.Callback() {
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
areItemsTheSame(oldData[oldItemPosition], newData[newItemPosition])
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
areContentsTheSame(oldData[oldItemPosition], newData[newItemPosition])
|
||||
|
||||
override fun getOldListSize() = oldData.size
|
||||
|
||||
override fun getNewListSize() = newData.size
|
||||
}
|
||||
}
|
||||
|
||||
open protected fun areItemsTheSame(old: T, new: T): Boolean {
|
||||
return old == new
|
||||
}
|
||||
|
||||
open protected fun areContentsTheSame(old: T, new: T): Boolean {
|
||||
return old == new
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QKSMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package presentation.common.base
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.arch.lifecycle.Lifecycle
|
||||
import android.os.Bundle
|
||||
import com.moez.QKSMS.R
|
||||
import com.uber.autodispose.android.lifecycle.scope
|
||||
import com.uber.autodispose.kotlin.autoDisposable
|
||||
import common.util.Colors
|
||||
import io.reactivex.rxkotlin.Observables
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Base activity that automatically applies any necessary theme theme settings and colors
|
||||
*
|
||||
* In most cases, this should be used instead of the base QkActivity, except for when
|
||||
* an activity does not depend on the theme
|
||||
*/
|
||||
abstract class QkThemedActivity<VM : QkViewModel<*, *>> : QkActivity<VM>() {
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
|
||||
/**
|
||||
* In case the activity should be themed for a specific conversation, the selected conversation
|
||||
* can be changed by pushing the threadId to this subject
|
||||
*/
|
||||
protected val threadId: Subject<Long> = BehaviorSubject.createDefault(0)
|
||||
|
||||
/**
|
||||
* Switch the theme if the threadId changes
|
||||
*/
|
||||
protected val theme = threadId
|
||||
.distinctUntilChanged()
|
||||
.switchMap { threadId -> colors.themeForConversation(threadId) }
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
colors.appThemeResources
|
||||
.autoDisposable(scope())
|
||||
.subscribe { res -> setTheme(res) }
|
||||
|
||||
colors.statusBarIcons
|
||||
.autoDisposable(scope())
|
||||
.subscribe { systemUiVisibility -> window.decorView.systemUiVisibility = systemUiVisibility }
|
||||
|
||||
colors.statusBar
|
||||
.autoDisposable(scope())
|
||||
.subscribe { color -> window.statusBarColor = color }
|
||||
}
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
|
||||
// Update the colours of the menu items
|
||||
Observables.combineLatest(menu, theme, colors.textTertiary, { menu, theme, textTertiary ->
|
||||
(0 until menu.size())
|
||||
.map { position -> menu.getItem(position) }
|
||||
.forEach { menuItem ->
|
||||
menuItem?.icon?.run {
|
||||
setTint(when (menuItem.itemId) {
|
||||
R.id.info -> textTertiary
|
||||
else -> theme
|
||||
})
|
||||
|
||||
menuItem.icon = this
|
||||
}
|
||||
}
|
||||
}).autoDisposable(scope(Lifecycle.Event.ON_DESTROY)).subscribe()
|
||||
|
||||
colors.textTertiary
|
||||
.doOnNext { color -> toolbar?.overflowIcon = toolbar?.overflowIcon?.apply { setTint(color) } }
|
||||
.doOnNext { color -> toolbar?.navigationIcon = toolbar?.navigationIcon?.apply { setTint(color) } }
|
||||
.autoDisposable(scope(Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe()
|
||||
|
||||
colors.popupThemeResource
|
||||
.autoDisposable(scope(Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe { res -> toolbar?.popupTheme = res }
|
||||
|
||||
colors.toolbarColor
|
||||
.autoDisposable(scope(Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe { color -> toolbar?.setBackgroundColor(color) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QKSMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package presentation.common.base
|
||||
|
||||
import android.arch.lifecycle.LifecycleOwner
|
||||
|
||||
interface QkView<in State> : LifecycleOwner {
|
||||
|
||||
fun render(state: State)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QKSMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package presentation.common.base
|
||||
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.View
|
||||
|
||||
class QkViewHolder(view: View) : RecyclerView.ViewHolder(view)
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QKSMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package presentation.common.base
|
||||
|
||||
import android.arch.lifecycle.ViewModel
|
||||
import android.support.annotation.CallSuper
|
||||
import com.uber.autodispose.android.lifecycle.scope
|
||||
import com.uber.autodispose.kotlin.autoDisposable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
|
||||
abstract class QkViewModel<in View : QkView<State>, State>(initialState: State) : ViewModel() {
|
||||
|
||||
protected val state: BehaviorSubject<State> = BehaviorSubject.createDefault(initialState)
|
||||
|
||||
protected val disposables = CompositeDisposable()
|
||||
|
||||
@CallSuper
|
||||
open fun bindView(view: View) {
|
||||
state
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe { view.render(it) }
|
||||
}
|
||||
|
||||
protected fun newState(reducer: (State) -> State) {
|
||||
state.value?.let { state.onNext(reducer(it)) }
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
disposables.dispose()
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user