2023-02-17 02:19:59 +01:00

531 lines
19 KiB
Kotlin

package com.simplemobiletools.dialer.activities
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.os.Bundle
import android.os.Handler
import android.provider.Settings
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.viewpager.widget.ViewPager
import com.google.android.material.snackbar.Snackbar
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.FAQItem
import com.simplemobiletools.commons.models.contacts.Contact
import com.simplemobiletools.dialer.BuildConfig
import com.simplemobiletools.dialer.R
import com.simplemobiletools.dialer.adapters.ViewPagerAdapter
import com.simplemobiletools.dialer.dialogs.ChangeSortingDialog
import com.simplemobiletools.dialer.extensions.config
import com.simplemobiletools.dialer.extensions.launchCreateNewContactIntent
import com.simplemobiletools.dialer.fragments.FavoritesFragment
import com.simplemobiletools.dialer.fragments.MyViewPagerFragment
import com.simplemobiletools.dialer.helpers.OPEN_DIAL_PAD_AT_LAUNCH
import com.simplemobiletools.dialer.helpers.RecentsHelper
import com.simplemobiletools.dialer.helpers.tabsList
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_contacts.*
import kotlinx.android.synthetic.main.fragment_favorites.*
import kotlinx.android.synthetic.main.fragment_recents.*
import me.grantland.widget.AutofitHelper
class MainActivity : SimpleActivity() {
private var launchedDialer = false
private var storedShowTabs = 0
var cachedContacts = ArrayList<Contact>()
override fun onCreate(savedInstanceState: Bundle?) {
isMaterialActivity = true
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
appLaunched(BuildConfig.APPLICATION_ID)
setupOptionsMenu()
refreshMenuItems()
updateMaterialActivityViews(main_coordinator, main_holder, useTransparentNavigation = false, useTopSearchMenu = true)
launchedDialer = savedInstanceState?.getBoolean(OPEN_DIAL_PAD_AT_LAUNCH) ?: false
if (isDefaultDialer()) {
checkContactPermissions()
if (!config.wasOverlaySnackbarConfirmed && !Settings.canDrawOverlays(this)) {
val snackbar = Snackbar.make(main_holder, R.string.allow_displaying_over_other_apps, Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok) {
config.wasOverlaySnackbarConfirmed = true
startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION))
}
snackbar.setBackgroundTint(getProperBackgroundColor().darkenColor())
snackbar.setTextColor(getProperTextColor())
snackbar.setActionTextColor(getProperTextColor())
snackbar.show()
}
handleNotificationPermission { granted ->
if (!granted) {
toast(R.string.no_post_notifications_permissions)
}
}
} else {
launchSetDefaultDialerIntent()
}
if (isQPlus() && config.blockUnknownNumbers) {
setDefaultCallerIdApp()
}
setupTabs()
Contact.sorting = config.sorting
}
override fun onResume() {
super.onResume()
if (storedShowTabs != config.showTabs) {
config.lastUsedViewPagerPage = 0
System.exit(0)
return
}
updateMenuColors()
val properPrimaryColor = getProperPrimaryColor()
val dialpadIcon = resources.getColoredDrawableWithColor(R.drawable.ic_dialpad_vector, properPrimaryColor.getContrastColor())
main_dialpad_button.setImageDrawable(dialpadIcon)
updateTextColors(main_holder)
setupTabColors()
getAllFragments().forEach {
it?.setupColors(getProperTextColor(), getProperPrimaryColor(), getProperPrimaryColor())
}
if (!main_menu.isSearchOpen) {
refreshItems(true)
}
checkShortcuts()
Handler().postDelayed({
recents_fragment?.refreshItems()
}, 2000)
}
override fun onPause() {
super.onPause()
storedShowTabs = config.showTabs
config.lastUsedViewPagerPage = view_pager.currentItem
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
super.onActivityResult(requestCode, resultCode, resultData)
// we don't really care about the result, the app can work without being the default Dialer too
if (requestCode == REQUEST_CODE_SET_DEFAULT_DIALER) {
checkContactPermissions()
} else if (requestCode == REQUEST_CODE_SET_DEFAULT_CALLER_ID && resultCode != Activity.RESULT_OK) {
toast(R.string.must_make_default_caller_id_app, length = Toast.LENGTH_LONG)
baseConfig.blockUnknownNumbers = false
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(OPEN_DIAL_PAD_AT_LAUNCH, launchedDialer)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
refreshItems()
}
override fun onBackPressed() {
if (main_menu.isSearchOpen) {
main_menu.closeSearch()
} else {
super.onBackPressed()
}
}
private fun refreshMenuItems() {
val currentFragment = getCurrentFragment()
main_menu.getToolbar().menu.apply {
findItem(R.id.clear_call_history).isVisible = currentFragment == recents_fragment
findItem(R.id.sort).isVisible = currentFragment != recents_fragment
findItem(R.id.create_new_contact).isVisible = currentFragment == contacts_fragment
findItem(R.id.more_apps_from_us).isVisible = !resources.getBoolean(R.bool.hide_google_relations)
}
}
private fun setupOptionsMenu() {
main_menu.getToolbar().inflateMenu(R.menu.menu)
main_menu.toggleHideOnScroll(false)
main_menu.setupMenu()
main_menu.onSearchClosedListener = {
getAllFragments().forEach {
it?.onSearchQueryChanged("")
}
}
main_menu.onSearchTextChangedListener = { text ->
getCurrentFragment()?.onSearchQueryChanged(text)
}
main_menu.getToolbar().setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.clear_call_history -> clearCallHistory()
R.id.create_new_contact -> launchCreateNewContactIntent()
R.id.sort -> showSortingDialog(showCustomSorting = getCurrentFragment() is FavoritesFragment)
R.id.more_apps_from_us -> launchMoreAppsFromUsIntent()
R.id.settings -> launchSettings()
R.id.about -> launchAbout()
else -> return@setOnMenuItemClickListener false
}
return@setOnMenuItemClickListener true
}
}
private fun updateMenuColors() {
updateStatusbarColor(getProperBackgroundColor())
main_menu.updateColors()
}
private fun checkContactPermissions() {
handlePermission(PERMISSION_READ_CONTACTS) {
initFragments()
}
}
private fun clearCallHistory() {
val confirmationText = "${getString(R.string.remove_confirmation)}\n\n${getString(R.string.cannot_be_undone)}"
ConfirmationDialog(this, confirmationText) {
RecentsHelper(this).removeAllRecentCalls(this) {
runOnUiThread {
recents_fragment?.refreshItems()
}
}
}
}
@SuppressLint("NewApi")
private fun checkShortcuts() {
val appIconColor = config.appIconColor
if (isNougatMR1Plus() && config.lastHandledShortcutColor != appIconColor) {
val launchDialpad = getLaunchDialpadShortcut(appIconColor)
try {
shortcutManager.dynamicShortcuts = listOf(launchDialpad)
config.lastHandledShortcutColor = appIconColor
} catch (ignored: Exception) {
}
}
}
@SuppressLint("NewApi")
private fun getLaunchDialpadShortcut(appIconColor: Int): ShortcutInfo {
val newEvent = getString(R.string.dialpad)
val drawable = resources.getDrawable(R.drawable.shortcut_dialpad)
(drawable as LayerDrawable).findDrawableByLayerId(R.id.shortcut_dialpad_background).applyColorFilter(appIconColor)
val bmp = drawable.convertToBitmap()
val intent = Intent(this, DialpadActivity::class.java)
intent.action = Intent.ACTION_VIEW
return ShortcutInfo.Builder(this, "launch_dialpad")
.setShortLabel(newEvent)
.setLongLabel(newEvent)
.setIcon(Icon.createWithBitmap(bmp))
.setIntent(intent)
.build()
}
private fun setupTabColors() {
val activeView = main_tabs_holder.getTabAt(view_pager.currentItem)?.customView
updateBottomTabItemColors(activeView, true, getSelectedTabDrawableIds()[view_pager.currentItem])
getInactiveTabIndexes(view_pager.currentItem).forEach { index ->
val inactiveView = main_tabs_holder.getTabAt(index)?.customView
updateBottomTabItemColors(inactiveView, false, getDeselectedTabDrawableIds()[index])
}
val bottomBarColor = getBottomNavigationBackgroundColor()
main_tabs_holder.setBackgroundColor(bottomBarColor)
updateNavigationBarColor(bottomBarColor)
}
private fun getInactiveTabIndexes(activeIndex: Int) = (0 until main_tabs_holder.tabCount).filter { it != activeIndex }
private fun getSelectedTabDrawableIds(): ArrayList<Int> {
val showTabs = config.showTabs
val icons = ArrayList<Int>()
if (showTabs and TAB_CONTACTS != 0) {
icons.add(R.drawable.ic_person_vector)
}
if (showTabs and TAB_FAVORITES != 0) {
icons.add(R.drawable.ic_star_vector)
}
if (showTabs and TAB_CALL_HISTORY != 0) {
icons.add(R.drawable.ic_clock_filled_vector)
}
return icons
}
private fun getDeselectedTabDrawableIds(): ArrayList<Int> {
val showTabs = config.showTabs
val icons = ArrayList<Int>()
if (showTabs and TAB_CONTACTS != 0) {
icons.add(R.drawable.ic_person_outline_vector)
}
if (showTabs and TAB_FAVORITES != 0) {
icons.add(R.drawable.ic_star_outline_vector)
}
if (showTabs and TAB_CALL_HISTORY != 0) {
icons.add(R.drawable.ic_clock_vector)
}
return icons
}
private fun initFragments() {
view_pager.offscreenPageLimit = 2
view_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
main_tabs_holder.getTabAt(position)?.select()
getAllFragments().forEach {
it?.finishActMode()
}
refreshMenuItems()
}
})
// selecting the proper tab sometimes glitches, add an extra selector to make sure we have it right
main_tabs_holder.onGlobalLayout {
Handler().postDelayed({
var wantedTab = getDefaultTab()
// open the Recents tab if we got here by clicking a missed call notification
if (intent.action == Intent.ACTION_VIEW && config.showTabs and TAB_CALL_HISTORY > 0) {
wantedTab = main_tabs_holder.tabCount - 1
ensureBackgroundThread {
clearMissedCalls()
}
}
main_tabs_holder.getTabAt(wantedTab)?.select()
refreshMenuItems()
}, 100L)
}
main_dialpad_button.setOnClickListener {
launchDialpad()
}
view_pager.onGlobalLayout {
refreshMenuItems()
}
if (config.openDialPadAtLaunch && !launchedDialer) {
launchDialpad()
launchedDialer = true
}
}
private fun setupTabs() {
view_pager.adapter = null
main_tabs_holder.removeAllTabs()
tabsList.forEachIndexed { index, value ->
if (config.showTabs and value != 0) {
main_tabs_holder.newTab().setCustomView(R.layout.bottom_tablayout_item).apply {
customView?.findViewById<ImageView>(R.id.tab_item_icon)?.setImageDrawable(getTabIcon(index))
customView?.findViewById<TextView>(R.id.tab_item_label)?.text = getTabLabel(index)
AutofitHelper.create(customView?.findViewById(R.id.tab_item_label))
main_tabs_holder.addTab(this)
}
}
}
main_tabs_holder.onTabSelectionChanged(
tabUnselectedAction = {
updateBottomTabItemColors(it.customView, false, getDeselectedTabDrawableIds()[it.position])
},
tabSelectedAction = {
main_menu.closeSearch()
view_pager.currentItem = it.position
updateBottomTabItemColors(it.customView, true, getSelectedTabDrawableIds()[it.position])
}
)
main_tabs_holder.beGoneIf(main_tabs_holder.tabCount == 1)
storedShowTabs = config.showTabs
}
private fun getTabIcon(position: Int): Drawable {
val drawableId = when (position) {
0 -> R.drawable.ic_person_vector
1 -> R.drawable.ic_star_vector
else -> R.drawable.ic_clock_vector
}
return resources.getColoredDrawableWithColor(drawableId, getProperTextColor())
}
private fun getTabLabel(position: Int): String {
val stringId = when (position) {
0 -> R.string.contacts_tab
1 -> R.string.favorites_tab
else -> R.string.call_history_tab
}
return resources.getString(stringId)
}
private fun refreshItems(openLastTab: Boolean = false) {
if (isDestroyed || isFinishing) {
return
}
if (view_pager.adapter == null) {
view_pager.adapter = ViewPagerAdapter(this)
view_pager.currentItem = if (openLastTab) config.lastUsedViewPagerPage else getDefaultTab()
view_pager.onGlobalLayout {
refreshFragments()
}
} else {
refreshFragments()
}
}
private fun launchDialpad() {
Intent(applicationContext, DialpadActivity::class.java).apply {
startActivity(this)
}
}
private fun refreshFragments() {
contacts_fragment?.refreshItems()
favorites_fragment?.refreshItems()
recents_fragment?.refreshItems()
}
private fun getAllFragments(): ArrayList<MyViewPagerFragment?> {
val showTabs = config.showTabs
val fragments = arrayListOf<MyViewPagerFragment?>()
if (showTabs and TAB_CONTACTS > 0) {
fragments.add(contacts_fragment)
}
if (showTabs and TAB_FAVORITES > 0) {
fragments.add(favorites_fragment)
}
if (showTabs and TAB_CALL_HISTORY > 0) {
fragments.add(recents_fragment)
}
return fragments
}
private fun getCurrentFragment(): MyViewPagerFragment? = getAllFragments().getOrNull(view_pager.currentItem)
private fun getDefaultTab(): Int {
val showTabsMask = config.showTabs
return when (config.defaultTab) {
TAB_LAST_USED -> if (config.lastUsedViewPagerPage < main_tabs_holder.tabCount) config.lastUsedViewPagerPage else 0
TAB_CONTACTS -> 0
TAB_FAVORITES -> if (showTabsMask and TAB_CONTACTS > 0) 1 else 0
else -> {
if (showTabsMask and TAB_CALL_HISTORY > 0) {
if (showTabsMask and TAB_CONTACTS > 0) {
if (showTabsMask and TAB_FAVORITES > 0) {
2
} else {
1
}
} else {
if (showTabsMask and TAB_FAVORITES > 0) {
1
} else {
0
}
}
} else {
0
}
}
}
}
@SuppressLint("MissingPermission")
private fun clearMissedCalls() {
try {
// notification cancellation triggers MissedCallNotifier.clearMissedCalls() which, in turn,
// should update the database and reset the cached missed call count in MissedCallNotifier.java
// https://android.googlesource.com/platform/packages/services/Telecomm/+/master/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java#170
telecomManager.cancelMissedCallsNotification()
} catch (ignored: Exception) {
}
}
private fun launchSettings() {
hideKeyboard()
startActivity(Intent(applicationContext, SettingsActivity::class.java))
}
private fun launchAbout() {
val licenses = LICENSE_GLIDE or LICENSE_INDICATOR_FAST_SCROLL or LICENSE_AUTOFITTEXTVIEW
val faqItems = arrayListOf(
FAQItem(R.string.faq_1_title, R.string.faq_1_text),
FAQItem(R.string.faq_9_title_commons, R.string.faq_9_text_commons)
)
if (!resources.getBoolean(R.bool.hide_google_relations)) {
faqItems.add(FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons))
faqItems.add(FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons))
}
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
}
private fun showSortingDialog(showCustomSorting: Boolean) {
ChangeSortingDialog(this, showCustomSorting) {
favorites_fragment?.refreshItems {
if (main_menu.isSearchOpen) {
getCurrentFragment()?.onSearchQueryChanged(main_menu.getCurrentQuery())
}
}
contacts_fragment?.refreshItems {
if (main_menu.isSearchOpen) {
getCurrentFragment()?.onSearchQueryChanged(main_menu.getCurrentQuery())
}
}
}
}
fun cacheContacts(contacts: List<Contact>) {
try {
cachedContacts.clear()
cachedContacts.addAll(contacts)
} catch (e: Exception) {
}
}
}