diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/adapter/RecentsAdapter.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/adapter/RecentsAdapter.kt index 3ad39f889..716e86306 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/adapter/RecentsAdapter.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/adapter/RecentsAdapter.kt @@ -4,10 +4,10 @@ import android.graphics.Color import android.net.Uri import android.provider.ContactsContract.CommonDataKinds.Phone import com.chooloo.www.chooloolib.interactor.animation.AnimationsInteractor +import com.chooloo.www.chooloolib.interactor.permission.PermissionsInteractor import com.chooloo.www.chooloolib.interactor.phoneaccounts.PhonesInteractor import com.chooloo.www.chooloolib.interactor.preferences.PreferencesInteractor import com.chooloo.www.chooloolib.interactor.recents.RecentsInteractor -import com.chooloo.www.chooloolib.interactor.string.StringsInteractor import com.chooloo.www.chooloolib.model.ListData import com.chooloo.www.chooloolib.model.RecentAccount import com.chooloo.www.chooloolib.ui.widgets.listitem.ListItem @@ -18,23 +18,35 @@ import javax.inject.Inject class RecentsAdapter @Inject constructor( animations: AnimationsInteractor, private val phones: PhonesInteractor, - private val strings: StringsInteractor, private val recents: RecentsInteractor, - private val preferences: PreferencesInteractor + private val preferences: PreferencesInteractor, + private val permissions: PermissionsInteractor ) : ListAdapter(animations) { + private var _groupSimilar: Boolean = false + + var groupSimilar: Boolean + get() = _groupSimilar + set(value) { + _groupSimilar = value + } + + override fun onBindListItem(listItem: ListItem, item: RecentAccount) { listItem.apply { + val date = context.getHoursString(item.date) + isCompact = preferences.isCompact - captionText = if (item.date != null) context.getHoursString(item.date) else null + captionText = if (item.groupCount > 1) "(${item.groupCount}) $date" else date + phones.lookupAccount(item.number) { titleText = it?.name ?: item.number it?.let { captionText = "$captionText · ${Phone.getTypeLabel(resources, it.type, it.label)} ·" setImageInitials(it.name?.initials()) - setImageUri(if (it.photoUri != null) Uri.parse(it.photoUri) else null) } + setImageUri(if (it?.photoUri != null) Uri.parse(it.photoUri) else null) } setImageBackgroundColor(Color.TRANSPARENT) @@ -42,5 +54,6 @@ class RecentsAdapter @Inject constructor( } } - override fun convertDataToListData(data: List) = ListData.fromRecents(data) + override fun convertDataToListData(data: List) = + ListData.fromRecents(data, groupSimilar) } \ No newline at end of file diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/di/factory/fragment/FragmentFactory.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/di/factory/fragment/FragmentFactory.kt index e3df046e4..560017134 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/di/factory/fragment/FragmentFactory.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/di/factory/fragment/FragmentFactory.kt @@ -12,7 +12,7 @@ import com.chooloo.www.chooloolib.ui.phones.PhonesFragment import com.chooloo.www.chooloolib.ui.prompt.PromptFragment import com.chooloo.www.chooloolib.ui.recent.RecentFragment import com.chooloo.www.chooloolib.ui.recents.RecentsFragment -import com.chooloo.www.chooloolib.ui.recents.RecentsHistoryFragment +import com.chooloo.www.chooloolib.ui.recentshistory.RecentsHistoryFragment import com.chooloo.www.chooloolib.ui.settings.SettingsFragment interface FragmentFactory { @@ -23,11 +23,11 @@ interface FragmentFactory { fun getRecentFragment(recentId: Long): RecentFragment fun getDialerFragment(text: String? = null): DialerFragment fun getPhonesFragment(contactId: Long? = null): PhonesFragment - fun getRecentsFragment(filter: String? = null): RecentsFragment fun getContactsSuggestionsFragment(): ContactsSuggestionsFragment fun getBriefContactFragment(contactId: Long): BriefContactFragment fun getPromptFragment(title: String, subtitle: String): PromptFragment fun getRecentsHistoryFragment(filter: String? = null): RecentsHistoryFragment + fun getRecentsFragment(filter: String? = null, isGrouped: Boolean? = null): RecentsFragment fun getChoicesFragment( @StringRes titleRes: Int, @StringRes subtitleRes: Int?, diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/di/factory/fragment/FragmentFactoryImpl.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/di/factory/fragment/FragmentFactoryImpl.kt index d2012eba6..0507a7acd 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/di/factory/fragment/FragmentFactoryImpl.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/di/factory/fragment/FragmentFactoryImpl.kt @@ -11,7 +11,7 @@ import com.chooloo.www.chooloolib.ui.phones.PhonesFragment import com.chooloo.www.chooloolib.ui.prompt.PromptFragment import com.chooloo.www.chooloolib.ui.recent.RecentFragment import com.chooloo.www.chooloolib.ui.recents.RecentsFragment -import com.chooloo.www.chooloolib.ui.recents.RecentsHistoryFragment +import com.chooloo.www.chooloolib.ui.recentshistory.RecentsHistoryFragment import com.chooloo.www.chooloolib.ui.settings.SettingsFragment import javax.inject.Inject import javax.inject.Singleton @@ -25,8 +25,8 @@ class FragmentFactoryImpl @Inject constructor() : FragmentFactory { override fun getContactsSuggestionsFragment() = ContactsSuggestionsFragment() override fun getDialerFragment(text: String?) = DialerFragment.newInstance(text) override fun getRecentFragment(recentId: Long) = RecentFragment.newInstance(recentId) - override fun getRecentsFragment(filter: String?) = RecentsFragment.newInstance(filter) override fun getPhonesFragment(contactId: Long?) = PhonesFragment.newInstance(contactId) + override fun getBriefContactFragment(contactId: Long) = BriefContactFragment.newInstance(contactId) @@ -36,6 +36,9 @@ class FragmentFactoryImpl @Inject constructor() : FragmentFactory { override fun getRecentsHistoryFragment(filter: String?) = RecentsHistoryFragment.newInstance(filter) + override fun getRecentsFragment(filter: String?, isGrouped: Boolean?): RecentsFragment = + RecentsFragment.newInstance(filter, isGrouped) + override fun getChoicesFragment( titleRes: Int, subtitleRes: Int?, diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/color/ColorsInteractor.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/color/ColorsInteractor.kt index ca93b41f3..5ccbb3edc 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/color/ColorsInteractor.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/color/ColorsInteractor.kt @@ -7,6 +7,8 @@ import com.chooloo.www.chooloolib.interactor.base.BaseInteractor interface ColorsInteractor : BaseInteractor { interface Listener + val windowColor: Int + fun getColor(@ColorRes colorRes: Int): Int fun getAttrColor(@AttrRes colorRes: Int): Int } \ No newline at end of file diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/color/ColorsInteractorImpl.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/color/ColorsInteractorImpl.kt index 093a62d43..11f668db0 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/color/ColorsInteractorImpl.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/color/ColorsInteractorImpl.kt @@ -25,6 +25,19 @@ class ColorsInteractorImpl @Inject constructor( return context.createConfigurationContext(configuration) } + override val windowColor: Int + get() { + val typedValue = TypedValue() + context.theme.resolveAttribute( + android.R.attr.windowBackground, + typedValue, + true + ) + return if (typedValue.type in TypedValue.TYPE_FIRST_COLOR_INT..TypedValue.TYPE_LAST_COLOR_INT) { + typedValue.data; + } else -1 + } + override fun getColor(colorRes: Int) = ContextCompat.getColor(getThemedContext(), colorRes) override fun getAttrColor(colorRes: Int) = diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/preferences/PreferencesInteractor.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/preferences/PreferencesInteractor.kt index 36a75c741..6828a09d0 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/preferences/PreferencesInteractor.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/preferences/PreferencesInteractor.kt @@ -10,6 +10,7 @@ interface PreferencesInteractor : BaseInteractor var isCompact: Boolean var isAnimations: Boolean var isShowBlocked: Boolean + var isGroupRecents: Boolean var isDialpadTones: Boolean var isDialpadVibrate: Boolean diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/preferences/PreferencesInteractorImpl.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/preferences/PreferencesInteractorImpl.kt index 322ad66b3..f7853d819 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/preferences/PreferencesInteractorImpl.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/interactor/preferences/PreferencesInteractorImpl.kt @@ -42,6 +42,15 @@ class PreferencesInteractorImpl @Inject constructor( preferencesManager.putBoolean(R.string.pref_key_show_blocked, value) } + override var isGroupRecents: Boolean + get() = preferencesManager.getBoolean( + R.string.pref_key_group_recents, + R.bool.pref_default_value_group_recents + ) + set(value) { + preferencesManager.putBoolean(R.string.pref_key_group_recents, value) + } + override var isDialpadTones: Boolean get() = preferencesManager.getBoolean( R.string.pref_key_dialpad_tones, diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/model/ListData.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/model/ListData.kt index ee875d9ea..f624f7d51 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/model/ListData.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/model/ListData.kt @@ -24,10 +24,41 @@ data class ListData( return ListData(contacts, headersToCounts) } - fun fromRecents(recents: List) = ListData( - recents, - recents.groupingBy { getRelativeDateString(it.date) }.eachCount() - ) + fun fromRecents(recents: List, isGrouped: Boolean) = + if (isGrouped && recents.size > 1) { + getGroupedRecents(recents) + } else { + ListData( + recents, + recents.groupingBy { getRelativeDateString(it.date) }.eachCount() + ) + } + + private fun getGroupedRecents(recents: List): ListData { + var prevItem: RecentAccount = recents[0] + val currentGroup = mutableListOf(prevItem) + var prevDate = getRelativeDateString(prevItem.date) + val groupedRecents = mutableListOf() + + recents.drop(1).forEach { curItem -> + val curDate = getRelativeDateString(curItem.date) + if (prevItem.number == curItem.number && prevDate == curDate) { + currentGroup.add(curItem) + } else { + groupedRecents.add(prevItem.copy(groupAccounts = currentGroup.map { it.copy() })) + currentGroup.clear() + currentGroup.add(curItem) + prevItem = curItem + } + prevDate = curDate + } + groupedRecents.add(prevItem.copy(groupAccounts = currentGroup)) + + return ListData( + groupedRecents, + groupedRecents.groupingBy { getRelativeDateString(it.date) }.eachCount() + ) + } fun fromPhones(phones: List): ListData { val phones = phones.toList().distinctBy { it.normalizedNumber } diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/model/RecentAccount.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/model/RecentAccount.kt index 7d780a926..5d5c739e7 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/model/RecentAccount.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/model/RecentAccount.kt @@ -7,12 +7,13 @@ import java.io.Serializable import java.util.* data class RecentAccount( + val date: Date, + val id: Long = 0, val number: String, @CallType val type: Int, - val id: Long = 0, val duration: Long = 0, - val date: Date? = null, val cachedName: String? = null, + val groupAccounts: List = listOf() ) : Serializable { @kotlin.annotation.Retention(AnnotationRetention.SOURCE) @@ -27,8 +28,9 @@ data class RecentAccount( ) annotation class CallType - val relativeTime: String? - get() = date?.time?.let { getTimeAgo(it) } + val groupCount get() = groupAccounts.size + + val relativeTime by lazy { getTimeAgo(date.time) } companion object { const val TYPE_UNKNOWN = 7 diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/base/BaseActivity.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/base/BaseActivity.kt index e1ee33c2b..b0739a294 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/base/BaseActivity.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/base/BaseActivity.kt @@ -1,36 +1,34 @@ package com.chooloo.www.chooloolib.ui.base -import android.graphics.Color import android.os.Bundle import android.view.View -import android.view.WindowManager import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate +import com.chooloo.www.chooloolib.interactor.color.ColorsInteractor import com.chooloo.www.chooloolib.interactor.preferences.PreferencesInteractor import com.chooloo.www.chooloolib.interactor.string.StringsInteractor import io.reactivex.disposables.CompositeDisposable import javax.inject.Inject abstract class BaseActivity : AppCompatActivity(), BaseView { + @Inject lateinit var colors: ColorsInteractor @Inject lateinit var strings: StringsInteractor @Inject lateinit var disposables: CompositeDisposable @Inject lateinit var preferences: PreferencesInteractor - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - window.apply { - clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) - addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) - decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - statusBarColor = Color.TRANSPARENT - } + applicationContext.setTheme(preferences.accentTheme.theme) setTheme(preferences.accentTheme.theme) + window.statusBarColor = colors.windowColor AppCompatDelegate.setDefaultNightMode(preferences.themeMode.mode) + contentView?.let { setContentView(it) } + onSetup() + viewState.apply { attach() diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/call/CallViewState.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/call/CallViewState.kt index 4fa1e8c78..7607ba594 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/call/CallViewState.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/call/CallViewState.kt @@ -100,7 +100,6 @@ class CallViewState @Inject constructor( callAudios.audioRoute?.let(this@CallViewState::onAudioRouteChanged) } - isManageEnabled.value = false } diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/contacts/ContactsViewState.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/contacts/ContactsViewState.kt index 84524c8a6..25567bf4e 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/contacts/ContactsViewState.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/contacts/ContactsViewState.kt @@ -1,5 +1,6 @@ package com.chooloo.www.chooloolib.ui.contacts +import android.Manifest.permission.READ_CONTACTS import androidx.lifecycle.LiveData import com.chooloo.www.chooloolib.R import com.chooloo.www.chooloolib.interactor.permission.PermissionsInteractor @@ -29,7 +30,10 @@ open class ContactsViewState @Inject constructor( override fun onFilterChanged(filter: String?) { super.onFilterChanged(filter) - contactsLiveData.filter = filter + if (permissions.hasSelfPermission(READ_CONTACTS)) { + onPermissionsChanged(true) + contactsLiveData.filter = filter + } } override fun onItemClick(item: ContactAccount) { diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/list/ListFragment.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/list/ListFragment.kt index a93c12f9d..3a0037c2c 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/list/ListFragment.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/list/ListFragment.kt @@ -6,7 +6,6 @@ import com.chooloo.www.chooloolib.databinding.ItemsBinding import com.chooloo.www.chooloolib.ui.base.BaseFragment abstract class ListFragment> : BaseFragment() { - override val contentView by lazy { binding.root } protected val binding by lazy { ItemsBinding.inflate(layoutInflater) } @@ -33,7 +32,9 @@ abstract class ListFragment> : BaseFragme ev.ifNew?.let { adapter.items = it } } - getItemsObservable { it.observe(this@ListFragment, viewState::onItemsChanged) } + if (args.getBoolean(ARG_OBSERVE, true)) { + getItemsObservable { it.observe(this@ListFragment, viewState::onItemsChanged) } + } } adapter.apply { @@ -61,5 +62,6 @@ abstract class ListFragment> : BaseFragme companion object { const val ARG_FILTER = "filter" + const val ARG_OBSERVE = "is_observe" } } \ No newline at end of file diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/list/ListViewState.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/list/ListViewState.kt index c4b6c2e50..b31d5e921 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/list/ListViewState.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/list/ListViewState.kt @@ -17,6 +17,10 @@ abstract class ListViewState : BaseViewState() { val itemLongClickedEvent = DataLiveEvent() val itemsChangedEvent = DataLiveEvent>() + protected open val noResultsIconRes: Int? = null + protected open val noResultsTextRes: Int? = null + protected open val noPermissionsTextRes: Int? = null + override fun attach() { emptyIcon.value = noResultsIconRes @@ -43,12 +47,11 @@ abstract class ListViewState : BaseViewState() { fun onPermissionsChanged(hasPermissions: Boolean) { emptyMessage.value = if (hasPermissions) noResultsTextRes else noPermissionsTextRes + isEmpty.value = !hasPermissions } + open fun onItemLeftClick(item: ItemType) {} open fun onItemRightClick(item: ItemType) {} - protected open val noResultsIconRes: Int? = null - protected open val noResultsTextRes: Int? = null - protected open val noPermissionsTextRes: Int? = null abstract fun getItemsObservable(callback: (LiveData>) -> Unit) } \ No newline at end of file diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recent/RecentFragment.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recent/RecentFragment.kt index d1f52e576..638e9dd60 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recent/RecentFragment.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recent/RecentFragment.kt @@ -10,7 +10,7 @@ import com.chooloo.www.chooloolib.interactor.call.CallNavigationsInteractor import com.chooloo.www.chooloolib.interactor.dialog.DialogsInteractor import com.chooloo.www.chooloolib.interactor.prompt.PromptsInteractor import com.chooloo.www.chooloolib.ui.base.BaseFragment -import com.chooloo.www.chooloolib.ui.recents.RecentsHistoryViewState +import com.chooloo.www.chooloolib.ui.recents.RecentsViewState import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -19,7 +19,7 @@ class RecentFragment @Inject constructor() : BaseFragment() { override val contentView by lazy { binding.root } override val viewState: RecentViewState by viewModels() - private val historyViewState: RecentsHistoryViewState by viewModels() + private val historyViewState: RecentsViewState by viewModels() private val binding by lazy { RecentBinding.inflate(layoutInflater) } @Inject lateinit var prompts: PromptsInteractor @@ -112,7 +112,7 @@ class RecentFragment @Inject constructor() : BaseFragment() { showHistoryEvent.observe(this@RecentFragment) { ev -> ev.ifNew?.let { - prompts.showFragment(fragmentFactory.getRecentsHistoryFragment(it)) + prompts.showFragment(fragmentFactory.getRecentsFragment(it)) } } diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsFragment.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsFragment.kt index 87dc9a6d6..92765e134 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsFragment.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsFragment.kt @@ -4,36 +4,53 @@ import android.os.Bundle import androidx.fragment.app.activityViewModels import com.chooloo.www.chooloolib.adapter.RecentsAdapter import com.chooloo.www.chooloolib.di.factory.fragment.FragmentFactory +import com.chooloo.www.chooloolib.interactor.preferences.PreferencesInteractor import com.chooloo.www.chooloolib.interactor.prompt.PromptsInteractor import com.chooloo.www.chooloolib.model.RecentAccount import com.chooloo.www.chooloolib.ui.list.ListFragment +import com.chooloo.www.chooloolib.ui.recentshistory.RecentsHistoryViewState import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @AndroidEntryPoint open class RecentsFragment @Inject constructor() : ListFragment() { - @Inject lateinit var prompts: PromptsInteractor @Inject override lateinit var adapter: RecentsAdapter - @Inject lateinit var fragmentFactory: FragmentFactory - override val viewState: RecentsViewState by activityViewModels() + @Inject lateinit var prompts: PromptsInteractor + @Inject lateinit var fragmentFactory: FragmentFactory + @Inject lateinit var preferences: PreferencesInteractor + private val recentsHistoryViewState: RecentsHistoryViewState by activityViewModels() + override fun onSetup() { super.onSetup() + adapter.groupSimilar = args.getBoolean(ARG_IS_GROUPED, preferences.isGroupRecents) viewState.showRecentEvent.observe(this@RecentsFragment) { ev -> ev.ifNew?.let { - prompts.showFragment(fragmentFactory.getRecentFragment(it)) + if (it.groupCount > 1) { + prompts.showFragment(fragmentFactory.getRecentsHistoryFragment().apply { + arguments = arguments ?: Bundle() + requireArguments().putBoolean(ARG_OBSERVE, false) + }) + recentsHistoryViewState.onItemsChanged(it.groupAccounts) + } else { + prompts.showFragment(fragmentFactory.getRecentFragment(it.id)) + } } } } companion object { - fun newInstance(filter: String? = null) = RecentsFragment().apply { - arguments = Bundle().apply { - putString(ARG_FILTER, filter) + private const val ARG_IS_GROUPED = "is_grouped" + + fun newInstance(filter: String? = null, isGrouped: Boolean? = null) = + RecentsFragment().apply { + arguments = Bundle().apply { + putString(ARG_FILTER, filter) + isGrouped?.let { putBoolean(ARG_IS_GROUPED, it) } + } } - } } } \ No newline at end of file diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsViewState.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsViewState.kt index 8d9d2ad23..461e2ee4a 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsViewState.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsViewState.kt @@ -1,10 +1,11 @@ package com.chooloo.www.chooloolib.ui.recents +import android.Manifest.permission.READ_CALL_LOG +import android.Manifest.permission.READ_CONTACTS import android.content.ClipData import android.content.ClipboardManager import androidx.lifecycle.LiveData import com.chooloo.www.chooloolib.R -import com.chooloo.www.chooloolib.interactor.navigation.NavigationsInteractor import com.chooloo.www.chooloolib.interactor.permission.PermissionsInteractor import com.chooloo.www.chooloolib.livedata.contentprovider.RecentsProviderLiveData import com.chooloo.www.chooloolib.model.RecentAccount @@ -24,21 +25,24 @@ open class RecentsViewState @Inject constructor( override val noResultsIconRes = R.drawable.round_history_24 override val noResultsTextRes = R.string.error_no_results_recents - override val noPermissionsTextRes = R.string.error_no_permissions_recents + override var noPermissionsTextRes = R.string.error_no_permissions_recents private val recentsLiveData = recentsRepository.getRecents() as RecentsProviderLiveData - val showRecentEvent = DataLiveEvent() + val showRecentEvent = DataLiveEvent() override fun onFilterChanged(filter: String?) { super.onFilterChanged(filter) - recentsLiveData.filter = filter + if (permissions.hasSelfPermissions(arrayOf(READ_CONTACTS, READ_CALL_LOG))) { + onPermissionsChanged(true) + recentsLiveData.filter = filter + } } override fun onItemClick(item: RecentAccount) { super.onItemClick(item) - showRecentEvent.call(item.id) + permissions.runWithReadCallLogPermissions { showRecentEvent.call(item) } } override fun onItemLongClick(item: RecentAccount) { @@ -48,9 +52,14 @@ open class RecentsViewState @Inject constructor( } override fun getItemsObservable(callback: (LiveData>) -> Unit) { - permissions.runWithReadCallLogPermissions { - onPermissionsChanged(it) - if (it) callback.invoke(recentsLiveData) + permissions.runWithReadCallLogPermissions { clp -> + if (!clp) noPermissionsTextRes = R.string.error_no_permissions_recents + onPermissionsChanged(clp) + permissions.runWithReadContactsPermissions { rcp -> + if (!rcp) noPermissionsTextRes = R.string.error_no_permissions_contacts + onPermissionsChanged(clp && rcp) + if (clp && rcp) callback.invoke(recentsLiveData) + } } } } \ No newline at end of file diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsHistoryFragment.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recentshistory/RecentsHistoryFragment.kt similarity index 69% rename from chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsHistoryFragment.kt rename to chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recentshistory/RecentsHistoryFragment.kt index b4c6e9d46..a66a58b3f 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsHistoryFragment.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recentshistory/RecentsHistoryFragment.kt @@ -1,13 +1,19 @@ -package com.chooloo.www.chooloolib.ui.recents +package com.chooloo.www.chooloolib.ui.recentshistory import android.os.Bundle import androidx.fragment.app.activityViewModels +import com.chooloo.www.chooloolib.ui.recents.RecentsFragment import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class RecentsHistoryFragment : RecentsFragment() { override val viewState: RecentsHistoryViewState by activityViewModels() + override fun onSetup() { + super.onSetup() + adapter.groupSimilar = false + } + companion object { fun newInstance(filter: String? = null) = RecentsHistoryFragment().apply { arguments = Bundle().apply { diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsHistoryViewState.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recentshistory/RecentsHistoryViewState.kt similarity index 80% rename from chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsHistoryViewState.kt rename to chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recentshistory/RecentsHistoryViewState.kt index 5ef532700..bfa389689 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recents/RecentsHistoryViewState.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/recentshistory/RecentsHistoryViewState.kt @@ -1,14 +1,16 @@ -package com.chooloo.www.chooloolib.ui.recents +package com.chooloo.www.chooloolib.ui.recentshistory import android.content.ClipboardManager import com.chooloo.www.chooloolib.interactor.permission.PermissionsInteractor import com.chooloo.www.chooloolib.repository.recents.RecentsRepository +import com.chooloo.www.chooloolib.ui.recents.RecentsViewState import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel class RecentsHistoryViewState @Inject constructor( - clipboardManager: ClipboardManager, permissions: PermissionsInteractor, + clipboardManager: ClipboardManager, recentsRepository: RecentsRepository -) : RecentsViewState(recentsRepository, permissions, clipboardManager) \ No newline at end of file +) : RecentsViewState(recentsRepository, permissions, clipboardManager) { +} \ No newline at end of file diff --git a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/settings/SettingsViewState.kt b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/settings/SettingsViewState.kt index 2f3f6a5e6..365bbb4f9 100644 --- a/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/settings/SettingsViewState.kt +++ b/chooloolib/src/main/java/com/chooloo/www/chooloolib/ui/settings/SettingsViewState.kt @@ -15,9 +15,9 @@ import javax.inject.Inject @HiltViewModel open class SettingsViewState @Inject constructor( - private val colorsInteractor: ColorsInteractor, - private val navigationsInteractor: NavigationsInteractor, - private val preferencesInteractor: PreferencesInteractor + protected val colors: ColorsInteractor, + protected val navigations: NavigationsInteractor, + protected val preferences: PreferencesInteractor ) : BaseViewState() { @@ -31,39 +31,39 @@ open class SettingsViewState @Inject constructor( open fun onMenuItemClick(menuItem: MenuItem) { when (menuItem.itemId) { - R.id.menu_chooloo_rate -> navigationsInteractor.rateApp() + R.id.menu_chooloo_rate -> navigations.rateApp() R.id.menu_chooloo_theme_mode -> askForThemeModeEvent.call() R.id.menu_chooloo_compact_mode -> askForCompactEvent.call() R.id.menu_chooloo_animations -> askForAnimationsEvent.call() - R.id.menu_chooloo_email -> navigationsInteractor.sendEmail() - R.id.menu_chooloo_report_bugs -> navigationsInteractor.reportBug() + R.id.menu_chooloo_email -> navigations.sendEmail() + R.id.menu_chooloo_report_bugs -> navigations.reportBug() R.id.menu_chooloo_accent_color -> askForColorEvent.call(R.array.accent_colors) } } fun onColorResponse(color: Int) { - preferencesInteractor.accentTheme = when (color) { - colorsInteractor.getColor(R.color.red_background) -> RED - colorsInteractor.getColor(R.color.blue_background) -> BLUE - colorsInteractor.getColor(R.color.green_background) -> GREEN - colorsInteractor.getColor(R.color.orange_background) -> ORANGE - colorsInteractor.getColor(R.color.purple_background) -> PURPLE + preferences.accentTheme = when (color) { + colors.getColor(R.color.red_background) -> RED + colors.getColor(R.color.blue_background) -> BLUE + colors.getColor(R.color.green_background) -> GREEN + colors.getColor(R.color.orange_background) -> ORANGE + colors.getColor(R.color.purple_background) -> PURPLE else -> DEFAULT } - navigationsInteractor.goToLauncherActivity() + navigations.goToLauncherActivity() } fun onCompactResponse(response: Boolean) { - preferencesInteractor.isCompact = response - navigationsInteractor.goToLauncherActivity() + preferences.isCompact = response + navigations.goToLauncherActivity() } fun onAnimationsResponse(response: Boolean) { - preferencesInteractor.isAnimations = response + preferences.isAnimations = response } fun onThemeModeResponse(response: ThemeMode) { - preferencesInteractor.themeMode = response - navigationsInteractor.goToLauncherActivity() + preferences.themeMode = response + navigations.goToLauncherActivity() } } \ No newline at end of file diff --git a/chooloolib/src/main/res/drawable-xhdpi/round_recent_actors_20.xml b/chooloolib/src/main/res/drawable-xhdpi/round_recent_actors_20.xml new file mode 100644 index 000000000..a05503856 --- /dev/null +++ b/chooloolib/src/main/res/drawable-xhdpi/round_recent_actors_20.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/chooloolib/src/main/res/drawable-xhdpi/round_recent_actors_24.xml b/chooloolib/src/main/res/drawable-xhdpi/round_recent_actors_24.xml new file mode 100644 index 000000000..f67890259 --- /dev/null +++ b/chooloolib/src/main/res/drawable-xhdpi/round_recent_actors_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/chooloolib/src/main/res/values-pt-rBR/strings.xml b/chooloolib/src/main/res/values-pt-rBR/strings.xml index 778fb78d5..f644e0b22 100644 --- a/chooloolib/src/main/res/values-pt-rBR/strings.xml +++ b/chooloolib/src/main/res/values-pt-rBR/strings.xml @@ -3,10 +3,21 @@ Recentes Contatos + Conferência + Padrão + Configurações + Escuro + Claro + Sistema + + + + Sim ou Não? + Tem certeza? - O número foi copiado ao Clipboard + O número foi copiado ao clipboard @@ -14,6 +25,11 @@ Desligar Sim Não + Mostrar histórico + Bloquear número + Desbloquear número + Escolha a via de áudio + Escolha uma conta de telefone @@ -23,6 +39,29 @@ Procurar + Tipo de tema + Conta de SIM + Página padrão + Mostrar animações + Buscar em recentes + Buscar em contatos + Mostrar lista de itens compatada + Buscar em contatos & recentes + Vibrar ao tocar no discador + Pergunte qual SIM usar antes de cada ligação + Ativar tons de discagem + Mostrar opção de bloqueio/desbloqueio de números (requer discador padrão). + + + + %s está em espera + Definir o tema no Koler, reinicie app para efetuar a mudança + Ligar via SIM diferente + Encaminhar áudio para uma fonte diferente + Continuar a ligação atual usando uma conta de telefone específica + Este contato será excluído permanentemente + Este registro de ligações será excluído permanentemente + Esta página sempre será mostrada ao reabrir o Koler @@ -35,24 +74,36 @@ Silenciar Não silenciar Alto-falante - Desligar alto-falante + Alto-falante desligado - Conectando… - Chamada em espera - Chamada recebida - Chamada concluída - Chamada em andamento + Nova ligação + Discando… + Ligação em espera + Conectando… + Ligação recebida + Ligação em andamento + Ligação concluída + Estabelecendo ligação… + Status da ligação desconhecido + A ligação está desconectando… + Selecione conta de telefone - avaliar - compacto - destacar_a_cor - animações - relatar_bugs - tela_padrão + rate + compact + ask_sim + accent_color + animations + theme_mode + report_bugs + show_blocked + default_page + dialpad_tones + manage_blocked + dialpad_vibrate @@ -60,33 +111,53 @@ Modo compacto Animações Destacar a cor + Tipo de tema Envie um e-mail - Relatar bugs + Relatar falhas + Gerenciar números bloqueados + + + - Nenhum resultado :/ - Nenhum número de telefone :/ - Nenhum contato :/ - Não há recentes :/ + Nenhum resultado + Nenhum recente + Nenhum contato + Nenhum número de telefone + Defina o Koler como discador padrão para poder ver números bloqueados + Defina o Koler como discador padrão para fazer ligações - Preciso de permissão para ler os contatos :) - Preciso de permissão para ler o registro de chamadas :) - Preciso de permissão para ler os contatos :) + Nenhuma permissão concedida para alterar o registro de ligações + Nenhuma permissão concedida para fazer ligação + Permissão exigida para ler os contatos + Permissão exigida para ler o registro de ligações + Permissão exigida para ler os contatos + Nenhum número foi encontrado + + Não é possível colocar ligação em espera + Não é possível trocar ligações + Não pode unir ligações + + Não foi possível encontrar o número + contatos + recentes + @string/pref_value_page_contacts + + Correio de voz ABC DEF GHI @@ -100,15 +171,28 @@ - Botão para atender + Botão de menu + Botão de atender Botão de recusar + Opções de ligação - Notificação de chamada - Notificação de chamada ativada, para uso mais confortável + Notificação de ligação + Notificação da ligação ativa para facilitar uso mais confortável + + Alto-falante + Fone de ouvido + Bluetooth + Fone de ouvido com fio + Fone de ouvido ou por cabo + + + + Estrela de favorito + Ícone de lista vazia diff --git a/chooloolib/src/main/res/values/bools.xml b/chooloolib/src/main/res/values/bools.xml index b454bba8f..2063e32c2 100644 --- a/chooloolib/src/main/res/values/bools.xml +++ b/chooloolib/src/main/res/values/bools.xml @@ -4,6 +4,7 @@ true false true + false true false \ No newline at end of file diff --git a/chooloolib/src/main/res/values/strings.xml b/chooloolib/src/main/res/values/strings.xml index aeb2de5f9..d67e63916 100644 --- a/chooloolib/src/main/res/values/strings.xml +++ b/chooloolib/src/main/res/values/strings.xml @@ -50,6 +50,7 @@ Vibrate on dialpad clicks Ask for a sim before each call Enabled sound tones on dialpad clicks + Group recents strikes from the same person Show block/unblock option for numbers (requires default dialer). @@ -101,6 +102,7 @@ report_bugs show_blocked default_page + group_recents dialpad_tones manage_blocked dialpad_vibrate @@ -136,6 +138,7 @@ I need permission to read contacts :) I need permission to read call log :) I need permission to read contacts :) + I need permission to read contacts and call log :) No number was found :( diff --git a/chooloolib/src/main/res/values/themes.xml b/chooloolib/src/main/res/values/themes.xml index 6a2f19c95..0ffc71279 100644 --- a/chooloolib/src/main/res/values/themes.xml +++ b/chooloolib/src/main/res/values/themes.xml @@ -12,7 +12,7 @@ ?colorLightBackground @color/color_on_background - false + true @font/google_sans_regular @style/Chooloo.AlertDialog diff --git a/koler/build.gradle b/koler/build.gradle index 91c5ddb3f..1d39e60b1 100644 --- a/koler/build.gradle +++ b/koler/build.gradle @@ -9,8 +9,8 @@ android { compileSdk 31 defaultConfig { - versionCode 76 - versionName "v1.4.4" + versionCode 77 + versionName "v1.4.5" minSdk 25 targetSdk 31 diff --git a/koler/src/main/java/com/chooloo/www/koler/ui/settings/SettingsFragment.kt b/koler/src/main/java/com/chooloo/www/koler/ui/settings/SettingsFragment.kt index 83163a9ae..a58668fdc 100644 --- a/koler/src/main/java/com/chooloo/www/koler/ui/settings/SettingsFragment.kt +++ b/koler/src/main/java/com/chooloo/www/koler/ui/settings/SettingsFragment.kt @@ -38,6 +38,15 @@ class SettingsFragment @Inject constructor() : ChoolooSettingsFragment() { ) } } + + askForGroupRecentsEvent.observe(this@SettingsFragment) { + it.ifNew?.let { + dialogs.askForBoolean( + R.string.hint_group_recents, + viewState::onGroupRecents + ) + } + } } } } \ No newline at end of file diff --git a/koler/src/main/java/com/chooloo/www/koler/ui/settings/SettingsViewState.kt b/koler/src/main/java/com/chooloo/www/koler/ui/settings/SettingsViewState.kt index 59f4acb29..b375c03be 100644 --- a/koler/src/main/java/com/chooloo/www/koler/ui/settings/SettingsViewState.kt +++ b/koler/src/main/java/com/chooloo/www/koler/ui/settings/SettingsViewState.kt @@ -14,31 +14,28 @@ import javax.inject.Inject @HiltViewModel class SettingsViewState @Inject constructor( - colorsInteractor: ColorsInteractor, - navigationsInteractor: NavigationsInteractor, - private val preferences: PreferencesInteractor, - private val permissionsInteractor: PermissionsInteractor, + colors: ColorsInteractor, + navigations: NavigationsInteractor, + preferences: PreferencesInteractor, + private val permissions: PermissionsInteractor ) : - SettingsViewState( - colorsInteractor, - navigationsInteractor, - preferences - ) { + SettingsViewState(colors, navigations, preferences) { override val menuResList = arrayListOf(R.menu.menu_koler) + super.menuResList val askForShowBlockedEvent = LiveEvent() val askForDefaultPageEvent = LiveEvent() + val askForGroupRecentsEvent = LiveEvent() val askForDialpadTonesEvent = LiveEvent() val askForDialpadVibrateEvent = LiveEvent() - override fun onMenuItemClick(menuItem: MenuItem) { when (menuItem.itemId) { R.id.menu_koler_default_page -> askForDefaultPageEvent.call() R.id.menu_koler_dialpad_tones -> askForDialpadTonesEvent.call() + R.id.menu_koler_group_recents -> askForGroupRecentsEvent.call() R.id.menu_koler_dialpad_vibrate -> askForDialpadVibrateEvent.call() - R.id.menu_koler_show_blocked -> permissionsInteractor.runWithDefaultDialer { + R.id.menu_koler_show_blocked -> permissions.runWithDefaultDialer { askForShowBlockedEvent.call() } else -> super.onMenuItemClick(menuItem) @@ -60,4 +57,9 @@ class SettingsViewState @Inject constructor( fun onDialpadVibrate(response: Boolean) { preferences.isDialpadVibrate = response } + + fun onGroupRecents(response: Boolean) { + preferences.isGroupRecents = response + navigations.goToLauncherActivity() + } } diff --git a/koler/src/main/res/menu/menu_koler.xml b/koler/src/main/res/menu/menu_koler.xml index 59ca23659..11e8da0d6 100644 --- a/koler/src/main/res/menu/menu_koler.xml +++ b/koler/src/main/res/menu/menu_koler.xml @@ -19,4 +19,9 @@ android:id="@+id/menu_koler_dialpad_vibrate" android:icon="@drawable/round_vibration_20" android:title="@string/pref_title_dialpad_vibrate" /> + + \ No newline at end of file diff --git a/koler/src/main/res/values/strings.xml b/koler/src/main/res/values/strings.xml index b61ccb6ba..ca341b6b8 100644 --- a/koler/src/main/res/values/strings.xml +++ b/koler/src/main/res/values/strings.xml @@ -3,6 +3,7 @@ Default Page Should ask for sim Dialpad click tones + Group similar recents Show block number option Vibrate on dialpad clicks \ No newline at end of file