forked from upstream-mirrors/quik
Flatten hierarchy in presentation module
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
|
||||
class GlideCompletionListener<T>(private val listener: () -> Unit) : RequestListener<T> {
|
||||
|
||||
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<T>?, isFirstResource: Boolean): Boolean {
|
||||
listener()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(resource: T, model: Any?, target: Target<T>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
|
||||
listener()
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.content.Context
|
||||
import android.support.annotation.ArrayRes
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
import com.moez.QKSMS.R
|
||||
import common.util.Colors
|
||||
import common.util.extensions.setTint
|
||||
import common.util.extensions.setVisible
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.plusAssign
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.menu_list_item.view.*
|
||||
import presentation.common.base.QkAdapter
|
||||
import presentation.common.base.QkViewHolder
|
||||
import javax.inject.Inject
|
||||
|
||||
data class MenuItem(val title: String, val actionId: Int)
|
||||
|
||||
class MenuItemAdapter @Inject constructor(private val context: Context, private val colors: Colors) : QkAdapter<MenuItem>() {
|
||||
|
||||
val menuItemClicks: Subject<Int> = PublishSubject.create()
|
||||
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
var selectedItem: Int? = null
|
||||
set(value) {
|
||||
val old = data.map { it.actionId }.indexOfFirst { it == field }
|
||||
val new = data.map { it.actionId }.indexOfFirst { it == value }
|
||||
|
||||
field = value
|
||||
|
||||
old.let { notifyItemChanged(it) }
|
||||
new.let { notifyItemChanged(it) }
|
||||
}
|
||||
|
||||
fun setData(@ArrayRes titles: Int, @ArrayRes values: Int = -1) {
|
||||
val valueInts = if (values != -1) context.resources.getIntArray(values) else null
|
||||
|
||||
data = context.resources.getStringArray(titles)
|
||||
.mapIndexed { index, title -> MenuItem(title, valueInts?.getOrNull(index) ?: index) }
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
val view = LayoutInflater.from(context).inflate(R.layout.menu_list_item, parent, false)
|
||||
|
||||
disposables += colors.theme
|
||||
.subscribe { view.check.setTint(it) }
|
||||
|
||||
return QkViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: QkViewHolder, position: Int) {
|
||||
val menuItem = getItem(position)
|
||||
val view = holder.itemView
|
||||
|
||||
view.clicks().subscribe { menuItemClicks.onNext(menuItem.actionId) }
|
||||
|
||||
view.title.text = menuItem.title
|
||||
view.check.setVisible(menuItem.actionId == selectedItem)
|
||||
}
|
||||
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onDetachedFromRecyclerView(recyclerView)
|
||||
disposables.clear()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ActivityOptions
|
||||
import android.arch.lifecycle.ViewModel
|
||||
import android.arch.lifecycle.ViewModelProvider
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.Settings
|
||||
import android.provider.Telephony
|
||||
import android.view.View
|
||||
import manager.NotificationManager
|
||||
import presentation.feature.blocked.BlockedActivity
|
||||
import presentation.feature.blocked.BlockedViewModel
|
||||
import presentation.feature.compose.ComposeActivity
|
||||
import presentation.feature.compose.ComposeViewModel
|
||||
import presentation.feature.conversationinfo.ConversationInfoActivity
|
||||
import presentation.feature.conversationinfo.ConversationInfoViewModel
|
||||
import presentation.feature.gallery.GalleryActivity
|
||||
import presentation.feature.gallery.GalleryViewModel
|
||||
import presentation.feature.main.MainViewModel
|
||||
import presentation.feature.notificationprefs.NotificationPrefsActivity
|
||||
import presentation.feature.notificationprefs.NotificationPrefsViewModel
|
||||
import presentation.feature.plus.PlusActivity
|
||||
import presentation.feature.plus.PlusViewModel
|
||||
import presentation.feature.settings.SettingsActivity
|
||||
import presentation.feature.settings.SettingsViewModel
|
||||
import presentation.feature.setup.SetupActivity
|
||||
import presentation.feature.setup.SetupViewModel
|
||||
import presentation.feature.themepicker.ThemePickerActivity
|
||||
import presentation.feature.themepicker.ThemePickerViewModel
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class Navigator @Inject constructor(private val context: Context, private val notificationManager: NotificationManager) {
|
||||
|
||||
private fun startActivity(intent: Intent) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
private fun startActivityExternal(intent: Intent) {
|
||||
if (intent.resolveActivity(context.packageManager) != null) {
|
||||
startActivity(intent)
|
||||
} else {
|
||||
startActivity(Intent.createChooser(intent, null))
|
||||
}
|
||||
}
|
||||
|
||||
fun showSetupActivity() {
|
||||
val intent = Intent(context, SetupActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun showQksmsPlusActivity() {
|
||||
val intent = Intent(context, PlusActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun showDefaultSmsDialog() {
|
||||
val intent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)
|
||||
if (Telephony.Sms.getDefaultSmsPackage(context) != context.packageName) {
|
||||
intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, context.packageName)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun showCompose(body: String? = null) {
|
||||
val intent = Intent(context, ComposeActivity::class.java)
|
||||
intent.putExtra(Intent.EXTRA_TEXT, body)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun showConversation(threadId: Long) {
|
||||
val intent = Intent(context, ComposeActivity::class.java)
|
||||
intent.putExtra("threadId", threadId)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun showConversationInfo(threadId: Long) {
|
||||
val intent = Intent(context, ConversationInfoActivity::class.java)
|
||||
intent.putExtra("threadId", threadId)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun showImage(partId: Long) {
|
||||
val intent = Intent(context, GalleryActivity::class.java)
|
||||
intent.putExtra("partId", partId)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the attachment full-screen
|
||||
* The transitionName for the view should be the id of the image being displayed
|
||||
*/
|
||||
fun showImageAnimated(from: Activity, view: View) {
|
||||
val intent = Intent(context, GalleryActivity::class.java)
|
||||
intent.putExtra("partId", view.transitionName.toLong())
|
||||
|
||||
val options = ActivityOptions.makeSceneTransitionAnimation(from, view, view.transitionName)
|
||||
from.startActivity(intent, options.toBundle())
|
||||
}
|
||||
|
||||
fun showSettings() {
|
||||
val intent = Intent(context, SettingsActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun showBlockedConversations() {
|
||||
val intent = Intent(context, BlockedActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun showThemePicker(threadId: Long = 0) {
|
||||
val intent = Intent(context, ThemePickerActivity::class.java)
|
||||
intent.putExtra("threadId", threadId)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun makePhoneCall(address: String) {
|
||||
val intent = Intent(Intent.ACTION_DIAL, Uri.parse("tel:$address"))
|
||||
startActivityExternal(intent)
|
||||
}
|
||||
|
||||
fun showSupport() {
|
||||
val intent = Intent(Intent.ACTION_SENDTO)
|
||||
intent.data = Uri.parse("mailto:")
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("team@qklabs.com"))
|
||||
startActivityExternal(intent)
|
||||
}
|
||||
|
||||
fun addContact(address: String) {
|
||||
val uri = Uri.parse("tel: $address")
|
||||
val intent = Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, uri)
|
||||
startActivityExternal(intent)
|
||||
}
|
||||
|
||||
fun showNotificationSettings(threadId: Long = 0) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (threadId != 0L) {
|
||||
notificationManager.createNotificationChannel(threadId)
|
||||
}
|
||||
|
||||
val channelId = notificationManager.buildNotificationChannelId(threadId)
|
||||
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId)
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
||||
startActivity(intent)
|
||||
} else {
|
||||
val intent = Intent(context, NotificationPrefsActivity::class.java)
|
||||
intent.putExtra("threadId", threadId)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
class ViewModelFactory(private val intent: Intent) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return when (modelClass) {
|
||||
MainViewModel::class.java -> MainViewModel()
|
||||
PlusViewModel::class.java -> PlusViewModel()
|
||||
SetupViewModel::class.java -> SetupViewModel()
|
||||
ComposeViewModel::class.java -> ComposeViewModel(intent)
|
||||
ConversationInfoViewModel::class.java -> ConversationInfoViewModel(intent)
|
||||
GalleryViewModel::class.java -> GalleryViewModel(intent)
|
||||
NotificationPrefsViewModel::class.java -> NotificationPrefsViewModel(intent)
|
||||
SettingsViewModel::class.java -> SettingsViewModel()
|
||||
BlockedViewModel::class.java -> BlockedViewModel()
|
||||
ThemePickerViewModel::class.java -> ThemePickerViewModel(intent)
|
||||
else -> throw IllegalArgumentException("Invalid ViewModel class. If this is a new ViewModel, please add it to Navigator.kt")
|
||||
} as T
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
package common/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
@@ -16,8 +16,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package common
|
||||
|
||||
import android.app.Application
|
||||
import android.support.text.emoji.EmojiCompat
|
||||
import android.support.text.emoji.FontRequestEmojiCompatConfig
|
||||
@@ -25,15 +23,14 @@ import android.support.v4.provider.FontRequest
|
||||
import com.bugsnag.android.Bugsnag
|
||||
import com.moez.QKSMS.BuildConfig
|
||||
import com.moez.QKSMS.R
|
||||
import common.di.AppComponentManager
|
||||
import common.di.appComponent
|
||||
import manager.AnalyticsManager
|
||||
import injection.AppComponentManager
|
||||
import injection.appComponent
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import manager.AnalyticsManager
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class QKApplication : Application() {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
}
|
||||
+4
-11
@@ -16,19 +16,12 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package common.di
|
||||
package presentation.common.base
|
||||
|
||||
import common.QKApplication
|
||||
import android.arch.lifecycle.LifecycleOwner
|
||||
|
||||
internal lateinit var appComponent: AppComponent
|
||||
private set
|
||||
interface QkView<in State> : LifecycleOwner {
|
||||
|
||||
internal object AppComponentManager {
|
||||
|
||||
fun init(application: QKApplication) {
|
||||
appComponent = DaggerAppComponent.builder()
|
||||
.appModule(AppModule(application))
|
||||
.build()
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* 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 common.di
|
||||
|
||||
import common.QKApplication
|
||||
import common.util.ContactImageLoader
|
||||
import dagger.Component
|
||||
import presentation.common.widget.AvatarView
|
||||
import presentation.common.widget.MmsPreviewView
|
||||
import presentation.common.widget.PagerTitleView
|
||||
import presentation.common.widget.PreferenceView
|
||||
import presentation.common.widget.QkEditText
|
||||
import presentation.common.widget.QkSwitch
|
||||
import presentation.common.widget.QkTextView
|
||||
import presentation.common.widget.Separator
|
||||
import presentation.feature.blocked.BlockedActivity
|
||||
import presentation.feature.blocked.BlockedViewModel
|
||||
import presentation.feature.compose.ComposeActivity
|
||||
import presentation.feature.compose.ComposeViewModel
|
||||
import presentation.feature.compose.DetailedChipView
|
||||
import presentation.feature.conversationinfo.ConversationInfoActivity
|
||||
import presentation.feature.conversationinfo.ConversationInfoViewModel
|
||||
import presentation.feature.gallery.GalleryActivity
|
||||
import presentation.feature.gallery.GalleryViewModel
|
||||
import presentation.feature.main.MainActivity
|
||||
import presentation.feature.main.MainViewModel
|
||||
import presentation.feature.notificationprefs.NotificationPrefsActivity
|
||||
import presentation.feature.notificationprefs.NotificationPrefsViewModel
|
||||
import presentation.feature.plus.PlusActivity
|
||||
import presentation.feature.plus.PlusViewModel
|
||||
import presentation.feature.settings.SettingsActivity
|
||||
import presentation.feature.settings.SettingsViewModel
|
||||
import presentation.feature.setup.SetupActivity
|
||||
import presentation.feature.setup.SetupViewModel
|
||||
import presentation.feature.themepicker.ThemePickerActivity
|
||||
import presentation.feature.themepicker.ThemePickerViewModel
|
||||
import presentation.receiver.DefaultSmsChangedReceiver
|
||||
import presentation.receiver.MarkReadReceiver
|
||||
import presentation.receiver.MarkSeenReceiver
|
||||
import presentation.receiver.MmsReceivedReceiver
|
||||
import presentation.receiver.MmsSentReceiver
|
||||
import presentation.receiver.MmsUpdatedReceiver
|
||||
import presentation.receiver.NightModeReceiver
|
||||
import presentation.receiver.RemoteMessagingReceiver
|
||||
import presentation.receiver.SmsDeliveredReceiver
|
||||
import presentation.receiver.SmsProviderChangedReceiver
|
||||
import presentation.receiver.SmsReceiver
|
||||
import presentation.receiver.SmsSentReceiver
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Component(modules = [(AppModule::class)])
|
||||
interface AppComponent {
|
||||
|
||||
fun inject(application: QKApplication)
|
||||
|
||||
fun inject(activity: MainActivity)
|
||||
fun inject(activity: SetupActivity)
|
||||
fun inject(activity: BlockedActivity)
|
||||
fun inject(activity: ComposeActivity)
|
||||
fun inject(activity: ConversationInfoActivity)
|
||||
fun inject(activity: GalleryActivity)
|
||||
fun inject(activity: NotificationPrefsActivity)
|
||||
fun inject(activity: PlusActivity)
|
||||
fun inject(activity: SettingsActivity)
|
||||
fun inject(activity: ThemePickerActivity)
|
||||
|
||||
fun inject(receiver: DefaultSmsChangedReceiver)
|
||||
fun inject(receiver: SmsDeliveredReceiver)
|
||||
fun inject(receiver: SmsSentReceiver)
|
||||
fun inject(receiver: MarkSeenReceiver)
|
||||
fun inject(receiver: MarkReadReceiver)
|
||||
fun inject(receiver: MmsReceivedReceiver)
|
||||
fun inject(receiver: MmsSentReceiver)
|
||||
fun inject(receiver: MmsUpdatedReceiver)
|
||||
fun inject(receiver: NightModeReceiver)
|
||||
fun inject(receiver: RemoteMessagingReceiver)
|
||||
fun inject(receiver: SmsProviderChangedReceiver)
|
||||
fun inject(receiver: SmsReceiver)
|
||||
|
||||
fun inject(view: AvatarView)
|
||||
fun inject(view: DetailedChipView)
|
||||
fun inject(view: MmsPreviewView)
|
||||
fun inject(view: PagerTitleView)
|
||||
fun inject(view: PreferenceView)
|
||||
fun inject(view: QkEditText)
|
||||
fun inject(view: QkSwitch)
|
||||
fun inject(view: QkTextView)
|
||||
fun inject(view: Separator)
|
||||
|
||||
fun inject(viewModel: MainViewModel)
|
||||
fun inject(viewModel: SetupViewModel)
|
||||
fun inject(viewModel: BlockedViewModel)
|
||||
fun inject(viewModel: ComposeViewModel)
|
||||
fun inject(viewModel: ConversationInfoViewModel)
|
||||
fun inject(viewModel: GalleryViewModel)
|
||||
fun inject(viewModel: NotificationPrefsViewModel)
|
||||
fun inject(viewModel: PlusViewModel)
|
||||
fun inject(viewModel: SettingsViewModel)
|
||||
fun inject(viewModel: ThemePickerViewModel)
|
||||
|
||||
fun inject(fetcher: ContactImageLoader.ContactImageFetcher)
|
||||
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* 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 common.di
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.preference.PreferenceManager
|
||||
import com.f2prateek.rx.preferences2.RxSharedPreferences
|
||||
import common.util.AnalyticsManagerImpl
|
||||
import common.util.NotificationManagerImpl
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import manager.AnalyticsManager
|
||||
import manager.KeyManager
|
||||
import manager.KeyManagerImpl
|
||||
import manager.NotificationManager
|
||||
import manager.PermissionManager
|
||||
import manager.PermissionManagerImpl
|
||||
import manager.WidgetManager
|
||||
import manager.WidgetManagerImpl
|
||||
import mapper.CursorToContact
|
||||
import mapper.CursorToContactImpl
|
||||
import mapper.CursorToConversation
|
||||
import mapper.CursorToConversationImpl
|
||||
import mapper.CursorToMessage
|
||||
import mapper.CursorToMessageImpl
|
||||
import mapper.CursorToPart
|
||||
import mapper.CursorToPartImpl
|
||||
import mapper.CursorToRecipienImpl
|
||||
import mapper.CursorToRecipient
|
||||
import repository.ContactRepository
|
||||
import repository.ContactRepositoryImpl
|
||||
import repository.MessageRepository
|
||||
import repository.MessageRepositoryImpl
|
||||
import repository.SyncRepository
|
||||
import repository.SyncRepositoryImpl
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
class AppModule(private var application: Application) {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideContext(): Context {
|
||||
return application
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRxPreferences(context: Context): RxSharedPreferences {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return RxSharedPreferences.create(preferences)
|
||||
}
|
||||
|
||||
// Manager
|
||||
|
||||
@Provides
|
||||
fun provideAnalyticsManager(manager: AnalyticsManagerImpl): AnalyticsManager = manager
|
||||
|
||||
@Provides
|
||||
fun provideKeyManager(manager: KeyManagerImpl): KeyManager = manager
|
||||
|
||||
@Provides
|
||||
fun provideNotificationsManager(manager: NotificationManagerImpl): NotificationManager = manager
|
||||
|
||||
@Provides
|
||||
fun providePermissionsManager(manager: PermissionManagerImpl): PermissionManager = manager
|
||||
|
||||
@Provides
|
||||
fun provideWidgetManager(manager: WidgetManagerImpl): WidgetManager = manager
|
||||
|
||||
|
||||
// Mapper
|
||||
|
||||
@Provides
|
||||
fun provideCursorToContact(mapper: CursorToContactImpl): CursorToContact = mapper
|
||||
|
||||
@Provides
|
||||
fun provideCursorToConversation(mapper: CursorToConversationImpl): CursorToConversation = mapper
|
||||
|
||||
@Provides
|
||||
fun provideCursorToMessage(mapper: CursorToMessageImpl): CursorToMessage = mapper
|
||||
|
||||
@Provides
|
||||
fun provideCursorToPart(mapper: CursorToPartImpl): CursorToPart = mapper
|
||||
|
||||
@Provides
|
||||
fun provideCursorToRecipient(mapper: CursorToRecipienImpl): CursorToRecipient = mapper
|
||||
|
||||
|
||||
// Repository
|
||||
|
||||
@Provides
|
||||
fun provideContactRepository(repository: ContactRepositoryImpl): ContactRepository = repository
|
||||
|
||||
@Provides
|
||||
fun provideMessageRepository(repository: MessageRepositoryImpl): MessageRepository = repository
|
||||
|
||||
@Provides
|
||||
fun provideSyncRepository(repository: SyncRepositoryImpl): SyncRepository = repository
|
||||
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import com.bumptech.glide.load.data.DataFetcher
|
||||
import com.bumptech.glide.load.model.ModelLoader
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory
|
||||
import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
||||
import common.di.appComponent
|
||||
import injection.appComponent
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import repository.ContactRepository
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.ContactsContract
|
||||
import android.telephony.PhoneNumberUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
import com.moez.QKSMS.R
|
||||
import com.uber.autodispose.android.scope
|
||||
import com.uber.autodispose.kotlin.autoDisposable
|
||||
import injection.appComponent
|
||||
import common.util.Colors
|
||||
import common.util.GlideApp
|
||||
import common.util.extensions.setTint
|
||||
import kotlinx.android.synthetic.main.avatar_view.view.*
|
||||
import model.Contact
|
||||
import presentation.common.Navigator
|
||||
import javax.inject.Inject
|
||||
|
||||
class AvatarView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
@Inject lateinit var navigator: Navigator
|
||||
|
||||
/**
|
||||
* This value can be changes if we should use the theme from a particular conversation
|
||||
*/
|
||||
var threadId: Long = 0
|
||||
|
||||
var contact: Contact? = null
|
||||
set(value) {
|
||||
field = value
|
||||
updateView()
|
||||
}
|
||||
|
||||
init {
|
||||
if (!isInEditMode) {
|
||||
appComponent.inject(this)
|
||||
}
|
||||
|
||||
View.inflate(context, R.layout.avatar_view, this)
|
||||
|
||||
setBackgroundResource(R.drawable.circle)
|
||||
clipToOutline = true
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
if (!isInEditMode) {
|
||||
colors.themeForConversation(threadId)
|
||||
.autoDisposable(scope())
|
||||
.subscribe { color -> background.setTint(color) }
|
||||
|
||||
colors.textPrimaryOnTheme
|
||||
.autoDisposable(scope())
|
||||
.subscribe { color -> icon.setTint(color) }
|
||||
|
||||
clicks()
|
||||
.autoDisposable(scope())
|
||||
.subscribe {
|
||||
if (contact?.lookupKey.isNullOrEmpty()) {
|
||||
contact?.numbers?.firstOrNull()?.let { number ->
|
||||
navigator.addContact(number.address)
|
||||
}
|
||||
} else {
|
||||
val key = contact?.lookupKey
|
||||
val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, key)
|
||||
ContactsContract.QuickContact.showQuickContact(context, this@AvatarView, uri,
|
||||
ContactsContract.QuickContact.MODE_MEDIUM, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
|
||||
if (!isInEditMode) {
|
||||
updateView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateView() {
|
||||
if (contact?.name.orEmpty().isNotEmpty()) {
|
||||
initial.text = contact?.name?.substring(0, 1)
|
||||
icon.visibility = GONE
|
||||
} else {
|
||||
initial.text = null
|
||||
icon.visibility = VISIBLE
|
||||
}
|
||||
|
||||
photo.setImageDrawable(null)
|
||||
contact?.numbers?.firstOrNull()?.address?.let { address ->
|
||||
GlideApp.with(photo).load(PhoneNumberUtils.stripSeparators(address)).into(photo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.support.constraint.ConstraintLayout
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import com.moez.QKSMS.R
|
||||
import kotlinx.android.synthetic.main.group_avatar_view.view.*
|
||||
import model.Contact
|
||||
|
||||
class GroupAvatarView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : ConstraintLayout(context, attrs) {
|
||||
|
||||
var contacts: List<Contact> = ArrayList()
|
||||
set(value) {
|
||||
field = value
|
||||
updateView()
|
||||
}
|
||||
|
||||
private val avatars by lazy { listOf(avatar1, avatar2, avatar3) }
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.group_avatar_view, this)
|
||||
setBackgroundResource(R.drawable.circle)
|
||||
clipToOutline = true
|
||||
}
|
||||
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
|
||||
avatars.forEach { avatar ->
|
||||
avatar.setBackgroundResource(R.drawable.rectangle)
|
||||
}
|
||||
|
||||
if (!isInEditMode) {
|
||||
updateView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateView() {
|
||||
avatars.forEachIndexed { index, avatar ->
|
||||
avatar.visibility = if (contacts.size > index) View.VISIBLE else View.GONE
|
||||
avatar.contact = if (contacts.size > index) contacts[index] else null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import injection.appComponent
|
||||
import common.util.GlideApp
|
||||
import common.util.extensions.setVisible
|
||||
import model.MmsPart
|
||||
import presentation.common.Navigator
|
||||
import javax.inject.Inject
|
||||
|
||||
class MmsPreviewView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) {
|
||||
|
||||
@Inject lateinit var navigator: Navigator
|
||||
|
||||
var parts: List<MmsPart> = ArrayList()
|
||||
set(value) {
|
||||
field = value
|
||||
updateView()
|
||||
}
|
||||
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
orientation = LinearLayout.VERTICAL
|
||||
}
|
||||
|
||||
private fun updateView() {
|
||||
val images = parts.filter { it.image != null }
|
||||
setVisible(images.isNotEmpty())
|
||||
|
||||
if (childCount > 0) {
|
||||
removeAllViews()
|
||||
}
|
||||
|
||||
images.forEach { image ->
|
||||
addView(ImageView(context).apply {
|
||||
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
|
||||
GlideApp.with(context).load(image.image).fitCenter().into(this)
|
||||
setOnClickListener { navigator.showImage(image.id) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.support.v4.view.ViewPager
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import com.moez.QKSMS.R
|
||||
import com.uber.autodispose.android.scope
|
||||
import com.uber.autodispose.kotlin.autoDisposable
|
||||
import injection.appComponent
|
||||
import common.util.Colors
|
||||
import common.util.extensions.forEach
|
||||
import io.reactivex.rxkotlin.Observables
|
||||
import kotlinx.android.synthetic.main.tab_view.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class PagerTitleView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) {
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
|
||||
var pager: ViewPager? = null
|
||||
set(value) {
|
||||
if (field !== value) {
|
||||
field = value
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if (!isInEditMode) appComponent.inject(this)
|
||||
}
|
||||
|
||||
private fun recreate() {
|
||||
removeAllViews()
|
||||
|
||||
pager?.adapter?.count?.forEach { position ->
|
||||
val view = LayoutInflater.from(context).inflate(R.layout.tab_view, this, false)
|
||||
view.label.text = pager?.adapter?.getPageTitle(position)
|
||||
view.setOnClickListener { pager?.currentItem = position }
|
||||
|
||||
addView(view)
|
||||
}
|
||||
|
||||
pager?.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
childCount.forEach { index ->
|
||||
getChildAt(index).isSelected = index == position
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
val states = arrayOf(
|
||||
intArrayOf(android.R.attr.state_selected),
|
||||
intArrayOf(-android.R.attr.state_selected))
|
||||
|
||||
Observables
|
||||
.combineLatest(colors.theme, colors.textSecondary, { theme, textSecondary ->
|
||||
ColorStateList(states, intArrayOf(theme, textSecondary))
|
||||
})
|
||||
.autoDisposable(scope())
|
||||
.subscribe { colorStateList ->
|
||||
childCount.forEach { index ->
|
||||
(getChildAt(index) as? TextView)?.setTextColor(colorStateList)
|
||||
}
|
||||
}
|
||||
|
||||
colors.toolbarColor
|
||||
.autoDisposable(scope())
|
||||
.subscribe { color -> setBackgroundColor(color) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v7.widget.LinearLayoutCompat
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.moez.QKSMS.R
|
||||
import com.uber.autodispose.android.scope
|
||||
import com.uber.autodispose.kotlin.autoDisposable
|
||||
import injection.appComponent
|
||||
import common.util.Colors
|
||||
import common.util.extensions.getColorCompat
|
||||
import common.util.extensions.setTint
|
||||
import common.util.extensions.setVisible
|
||||
import kotlinx.android.synthetic.main.preference_view.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class PreferenceView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayoutCompat(context, attrs) {
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
|
||||
var title: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
|
||||
if (isInEditMode) {
|
||||
findViewById<TextView>(R.id.titleView).text = value
|
||||
} else {
|
||||
titleView.text = value
|
||||
}
|
||||
}
|
||||
|
||||
var summary: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
|
||||
|
||||
if (isInEditMode) {
|
||||
findViewById<TextView>(R.id.summaryView).run {
|
||||
text = value
|
||||
setVisible(value?.isNotEmpty() == true)
|
||||
}
|
||||
} else {
|
||||
summaryView.text = value
|
||||
summaryView.setVisible(value?.isNotEmpty() == true)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if (!isInEditMode) {
|
||||
appComponent.inject(this)
|
||||
}
|
||||
|
||||
View.inflate(context, R.layout.preference_view, this)
|
||||
setBackgroundResource(R.drawable.ripple)
|
||||
orientation = HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
|
||||
context.obtainStyledAttributes(attrs, R.styleable.PreferenceView)?.run {
|
||||
title = getString(R.styleable.PreferenceView_title)
|
||||
summary = getString(R.styleable.PreferenceView_summary)
|
||||
|
||||
// If there's a custom view used for the preference's widget, inflate it
|
||||
getResourceId(R.styleable.PreferenceView_widget, -1).takeIf { it != -1 }?.let { id ->
|
||||
View.inflate(context, id, widgetFrame)
|
||||
}
|
||||
|
||||
// If an icon is being used, set up the icon view
|
||||
getResourceId(R.styleable.PreferenceView_icon, -1).takeIf { it != -1 }?.let { id ->
|
||||
icon.setVisible(true)
|
||||
icon.setImageResource(id)
|
||||
}
|
||||
|
||||
recycle()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
if (isInEditMode) {
|
||||
icon.setTint(context.getColorCompat(R.color.textSecondary))
|
||||
} else {
|
||||
colors.textSecondary
|
||||
.autoDisposable(scope())
|
||||
.subscribe { icon.setTint(it) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.util.AttributeSet
|
||||
import android.widget.EditText
|
||||
import com.moez.QKSMS.R
|
||||
import com.uber.autodispose.android.scope
|
||||
import com.uber.autodispose.kotlin.autoDisposable
|
||||
import injection.appComponent
|
||||
import common.util.Colors
|
||||
import common.util.FontProvider
|
||||
import io.reactivex.Observable
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Custom implementation of EditText to allow for dynamic text colors
|
||||
*
|
||||
* Beware of updating to extend AppCompatTextView, as this inexplicably breaks the view in
|
||||
* the contacts chip view
|
||||
*/
|
||||
class QkEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : EditText(context, attrs) {
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
@Inject lateinit var fontProvider: FontProvider
|
||||
|
||||
private var textColorObservable: Observable<Int>? = null
|
||||
private var textColorHintObservable: Observable<Int>? = null
|
||||
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
fontProvider.getLato { setTypeface(it, typeface?.style ?: Typeface.NORMAL) }
|
||||
|
||||
context.obtainStyledAttributes(attrs, R.styleable.QkEditText)?.run {
|
||||
textColorObservable = when (getInt(R.styleable.QkEditText_textColor, -1)) {
|
||||
0 -> colors.textPrimary
|
||||
1 -> colors.textSecondary
|
||||
2 -> colors.textTertiary
|
||||
3 -> colors.textPrimaryOnTheme
|
||||
4 -> colors.textSecondaryOnTheme
|
||||
5 -> colors.textTertiaryOnTheme
|
||||
else -> null
|
||||
}
|
||||
textColorHintObservable = when (getInt(R.styleable.QkEditText_textColorHint, -1)) {
|
||||
0 -> colors.textPrimary
|
||||
1 -> colors.textSecondary
|
||||
2 -> colors.textTertiary
|
||||
else -> null
|
||||
}
|
||||
recycle()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
textColorObservable
|
||||
?.autoDisposable(scope())
|
||||
?.subscribe { color -> setTextColor(color) }
|
||||
|
||||
textColorHintObservable
|
||||
?.autoDisposable(scope())
|
||||
?.subscribe { color -> setHintTextColor(color) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.support.v7.widget.SwitchCompat
|
||||
import android.util.AttributeSet
|
||||
import com.uber.autodispose.android.scope
|
||||
import com.uber.autodispose.kotlin.autoDisposable
|
||||
import injection.appComponent
|
||||
import common.util.Colors
|
||||
import common.util.extensions.withAlpha
|
||||
import io.reactivex.rxkotlin.Observables
|
||||
import javax.inject.Inject
|
||||
|
||||
class QkSwitch @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : SwitchCompat(context, attrs) {
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
|
||||
init {
|
||||
if (!isInEditMode) {
|
||||
appComponent.inject(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
if (!isInEditMode) {
|
||||
val states = arrayOf(
|
||||
intArrayOf(-android.R.attr.state_enabled),
|
||||
intArrayOf(android.R.attr.state_checked),
|
||||
intArrayOf())
|
||||
|
||||
Observables.combineLatest(colors.theme, colors.switchThumbEnabled, colors.switchThumbDisabled,
|
||||
{ color, enabled, disabled -> intArrayOf(disabled, color, enabled) })
|
||||
.map { values -> ColorStateList(states, values) }
|
||||
.autoDisposable(scope())
|
||||
.subscribe { tintList -> thumbTintList = tintList }
|
||||
|
||||
Observables.combineLatest(colors.theme, colors.switchTrackEnabled, colors.switchTrackDisabled,
|
||||
{ color, enabled, disabled -> intArrayOf(disabled, color.withAlpha(0x4D), enabled) })
|
||||
.map { values -> ColorStateList(states, values) }
|
||||
.autoDisposable(scope())
|
||||
.subscribe { tintList -> trackTintList = tintList }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.support.text.emoji.widget.EmojiAppCompatTextView
|
||||
import android.util.AttributeSet
|
||||
import com.moez.QKSMS.R
|
||||
import injection.appComponent
|
||||
import common.util.Colors
|
||||
import common.util.FontProvider
|
||||
import common.util.extensions.getColorCompat
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import javax.inject.Inject
|
||||
|
||||
open class QkTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null)
|
||||
: EmojiAppCompatTextView(context, attrs) {
|
||||
|
||||
companion object {
|
||||
const val COLOR_PRIMARY = 0
|
||||
const val COLOR_SECONDARY = 1
|
||||
const val COLOR_TERTIARY = 2
|
||||
const val COLOR_PRIMARY_ON_THEME = 3
|
||||
const val COLOR_SECONDARY_ON_THEME = 4
|
||||
const val COLOR_TERTIARY_ON_THEME = 5
|
||||
}
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
@Inject lateinit var fontProvider: FontProvider
|
||||
|
||||
var textColorObservable: Observable<Int>? = null
|
||||
set(value) {
|
||||
field = value
|
||||
updateSubscription()
|
||||
}
|
||||
|
||||
private var textColorDisposable: Disposable? = null
|
||||
set(value) {
|
||||
field?.dispose()
|
||||
field = value
|
||||
}
|
||||
|
||||
init {
|
||||
if (!isInEditMode) {
|
||||
appComponent.inject(this)
|
||||
fontProvider.getLato { setTypeface(it, typeface?.style ?: Typeface.NORMAL) }
|
||||
}
|
||||
|
||||
context.obtainStyledAttributes(attrs, R.styleable.QkTextView)?.run {
|
||||
val colorAttr = getInt(R.styleable.QkTextView_textColor, -1)
|
||||
if (isInEditMode) {
|
||||
setTextColor(context.getColorCompat(when (colorAttr) {
|
||||
COLOR_PRIMARY -> R.color.textPrimary
|
||||
COLOR_SECONDARY -> R.color.textSecondary
|
||||
COLOR_TERTIARY -> R.color.textTertiary
|
||||
COLOR_PRIMARY_ON_THEME -> R.color.textPrimaryDark
|
||||
COLOR_SECONDARY_ON_THEME -> R.color.textSecondaryDark
|
||||
COLOR_TERTIARY_ON_THEME -> R.color.textTertiaryDark
|
||||
else -> R.color.textPrimary
|
||||
}))
|
||||
} else {
|
||||
textColorObservable = when (colorAttr) {
|
||||
COLOR_PRIMARY -> colors.textPrimary
|
||||
COLOR_SECONDARY -> colors.textSecondary
|
||||
COLOR_TERTIARY -> colors.textTertiary
|
||||
COLOR_PRIMARY_ON_THEME -> colors.textPrimaryOnTheme
|
||||
COLOR_SECONDARY_ON_THEME -> colors.textSecondaryOnTheme
|
||||
COLOR_TERTIARY_ON_THEME -> colors.textTertiaryOnTheme
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
recycle()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSubscription() {
|
||||
if (isAttachedToWindow) {
|
||||
textColorDisposable = textColorObservable?.subscribe { color ->
|
||||
setTextColor(color)
|
||||
setLinkTextColor(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
updateSubscription()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
textColorDisposable?.dispose()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import com.moez.QKSMS.R
|
||||
import com.uber.autodispose.android.scope
|
||||
import com.uber.autodispose.kotlin.autoDisposable
|
||||
import injection.appComponent
|
||||
import common.util.Colors
|
||||
import common.util.extensions.getColorCompat
|
||||
import javax.inject.Inject
|
||||
|
||||
class Separator @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
|
||||
|
||||
@Inject lateinit var colors: Colors
|
||||
|
||||
init {
|
||||
if (!isInEditMode) {
|
||||
appComponent.inject(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
if (isInEditMode) {
|
||||
setBackgroundColor(context.getColorCompat(R.color.separatorLight))
|
||||
} else {
|
||||
colors.separator
|
||||
.autoDisposable(scope())
|
||||
.subscribe { color -> setBackgroundColor(color) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageView
|
||||
|
||||
class SquareImageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : ImageView(context, attrs) {
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, widthMeasureSpec)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
|
||||
class TightTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : QkTextView(context, attrs) {
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
|
||||
layout?.let {
|
||||
val maxLineWidth = (0 until layout.lineCount)
|
||||
.asSequence()
|
||||
.map { layout.getLineMax(it) }
|
||||
.max() ?: 0f
|
||||
|
||||
val width = (Math.ceil(maxLineWidth.toDouble()).toInt() + compoundPaddingLeft + compoundPaddingRight)
|
||||
val height = measuredHeight
|
||||
setMeasuredDimension(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user