package com.secspace.sms.activities

import android.annotation.SuppressLint
import android.app.Activity
import android.app.role.RoleManager
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.database.Cursor
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.provider.Telephony
import android.text.TextUtils
import androidx.annotation.RequiresApi
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.lifecycle.lifecycleScope
import com.google.gson.Gson
import com.secspace.log.Log
import com.secspace.sms.R
import com.secspace.sms.adapters.ConversationsAdapter
import com.secspace.sms.adapters.SearchResultsAdapter
import com.secspace.sms.databinding.ActivityMainBinding
import com.secspace.sms.extensions.*
import com.secspace.sms.helpers.*
import com.secspace.sms.models.*
import com.secspace.sms.shortcut.ShortCutActivity
import com.secspace.sms.util.PhoneFromUtil
import com.secspace.sms.util.PhoneUtils
import com.secspace.sms.util.SmsCountUtil
import com.simplemobiletools.commons.dialogs.PermissionRequiredDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.util.GsonUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.Collections
import java.util.WeakHashMap


class MainActivity : SimpleActivity() {
    private val TAG = "MainActivity"
    private val MAKE_DEFAULT_APP_REQUEST = 1

    private var storedTextColor = 0
    private var storedFontSize = 0
    private var lastSearchedText = ""
    private var bus: EventBus? = null
    private var wasProtectionHandled = false
//    private val listMessageId = arrayListOf<Long>()
    private val listMessageId = Collections.newSetFromMap(WeakHashMap<Long, Boolean>())

    private val binding by viewBinding(ActivityMainBinding::inflate)

    @SuppressLint("InlinedApi")
    override fun onCreate(savedInstanceState: Bundle?) {
        isMaterialActivity = true
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        setupOptionsMenu()
        refreshMenuItems()
        initConfig()
//        contentResolver.registerContentObserver(Uri.parse("content://sms"), true, SmsSendObserver(this, Handler()))

        updateMaterialActivityViews(
            mainCoordinatorLayout = binding.mainCoordinator,
            nestedView = binding.conversationsList,
            useTransparentNavigation = true,
            useTopSearchMenu = true
        )

        if (savedInstanceState == null) {
            checkAndDeleteOldRecycleBinMessages()
            handleAppPasswordProtection {
                Log.d(TAG, "onCreate: handleAppPasswordProtection = $it")
                wasProtectionHandled = it
                if (it) {
                    clearAllMessagesIfNeeded {
                        Log.d(TAG, "onCreate: clearAllMessagesIfNeeded")
                        loadMessages()
                    }
                } else {
                    finish()
                }
            }
        }

        if (checkAppSideloading()) {
            return
        }
    }

    /**
     * 初始化配置项
     */
    private fun initConfig() {
        config.startNameWithSurname = true//姓氏在前
    }

    private fun isNotificationAccessEnabled(): Boolean {
        val enabledListeners: String = Settings.Secure.getString(
            contentResolver,
            "enabled_notification_listeners"
        )
        return enabledListeners != null && enabledListeners.contains(packageName)
    }

    override fun onResume() {
        super.onResume()

        //是否拥有通知使用权
        /*val result = isNotificationAccessEnabled()
        if (!result) {
            val intent = Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            startActivity(intent)
        }*/


        updateMenuColors()
        refreshMenuItems()

        getOrCreateConversationsAdapter().apply {
            if (storedTextColor != getProperTextColor()) {
                updateTextColor(getProperTextColor())
            }

            if (storedFontSize != config.fontSize) {
                updateFontSize()
            }

            updateDrafts()
        }

        updateTextColors(binding.mainCoordinator)
        binding.searchHolder.setBackgroundColor(getProperBackgroundColor())

        val properPrimaryColor = getProperPrimaryColor()
        binding.noConversationsPlaceholder2.setTextColor(properPrimaryColor)
        binding.noConversationsPlaceholder2.underlineText()
        binding.conversationsFastscroller.updateColors(properPrimaryColor)
        binding.conversationsProgressBar.setIndicatorColor(properPrimaryColor)
        binding.conversationsProgressBar.trackColor = properPrimaryColor.adjustAlpha(LOWER_ALPHA)
        (binding.conversationsFab.layoutParams as? CoordinatorLayout.LayoutParams)?.bottomMargin =
            navigationBarHeight + resources.getDimension(com.simplemobiletools.commons.R.dimen.activity_margin).toInt()

        checkShortcuts()
    }

    override fun onPause() {
        super.onPause()
        storeStateVariables()
    }

    override fun onDestroy() {
        super.onDestroy()
        bus?.unregister(this)
//        contentResolver.unregisterContentObserver(SmsSendObserver(this, Handler()))
    }

    override fun onBackPressed() {
        if (binding.mainMenu.isSearchOpen) {
            binding.mainMenu.closeSearch()
        } else {
            super.onBackPressed()
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putBoolean(WAS_PROTECTION_HANDLED, wasProtectionHandled)
    }

    @RequiresApi(Build.VERSION_CODES.Q)
    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        wasProtectionHandled = savedInstanceState.getBoolean(WAS_PROTECTION_HANDLED, false)
        Log.d(TAG, "onRestoreInstanceState: wasProtectionHandled = $wasProtectionHandled ")

        if (!wasProtectionHandled) {
            handleAppPasswordProtection {
                wasProtectionHandled = it
                if (it) {
                    loadMessages()
                } else {
                    finish()
                }
            }
        } else {
            loadMessages()
        }
    }

    private fun setupOptionsMenu() {
        binding.mainMenu.getToolbar().inflateMenu(R.menu.menu_main)
        binding.mainMenu.toggleHideOnScroll(true)
        binding.mainMenu.setupMenu()

        binding.mainMenu.onSearchClosedListener = {
            fadeOutSearch()
        }

        binding.mainMenu.onSearchTextChangedListener = { text ->
            if (text.isNotEmpty()) {
                if (binding.searchHolder.alpha < 1f) {
                    binding.searchHolder.fadeIn()
                }
            } else {
                fadeOutSearch()
            }
            searchTextChanged(text)
        }

        binding.mainMenu.getToolbar().setOnMenuItemClickListener { menuItem ->
            when (menuItem.itemId) {
                /* R.id.more_apps_from_us -> launchMoreAppsFromUsIntent()
                 R.id.show_recycle_bin -> launchRecycleBin()
                 R.id.show_archived -> launchArchivedConversations()
                 R.id.about -> launchAbout()*/
                R.id.settings -> launchSettings()
                else -> return@setOnMenuItemClickListener false
            }
            return@setOnMenuItemClickListener true
        }
    }

    private fun refreshMenuItems() {
        binding.mainMenu.getToolbar().menu.apply {
            findItem(R.id.more_apps_from_us).isVisible = /*!resources.getBoolean(com.simplemobiletools.commons.R.bool.hide_google_relations)*/false
            findItem(R.id.show_recycle_bin).isVisible = /*config.useRecycleBin*/ false
            findItem(R.id.show_archived).isVisible = /*config.isArchiveAvailable*/ false
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
        super.onActivityResult(requestCode, resultCode, resultData)
        if (requestCode == MAKE_DEFAULT_APP_REQUEST) {
            if (resultCode == Activity.RESULT_OK) {
                askPermissions()
            } else {
                finish()
            }
        }
    }

    private fun storeStateVariables() {
        storedTextColor = getProperTextColor()
        storedFontSize = config.fontSize
    }

    private fun updateMenuColors() {
        updateStatusbarColor(getProperBackgroundColor())
        binding.mainMenu.updateColors()
    }

    @RequiresApi(Build.VERSION_CODES.Q)
    private fun loadMessages() {
        Log.d(TAG, "loadMessages: isQPlus = ${isQPlus()}")
        if (isQPlus()) {
            val roleManager = getSystemService(RoleManager::class.java)
            if (roleManager!!.isRoleAvailable(RoleManager.ROLE_SMS)) {
                if (roleManager.isRoleHeld(RoleManager.ROLE_SMS)) {
                    askPermissions()
                } else {
                    val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS)
                    startActivityForResult(intent, MAKE_DEFAULT_APP_REQUEST)
                }
            } else {
                toast(com.simplemobiletools.commons.R.string.unknown_error_occurred)
                finish()
            }
        } else {
            if (Telephony.Sms.getDefaultSmsPackage(this) == packageName) {
                askPermissions()
            } else {
                val intent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)
                intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName)
                startActivityForResult(intent, MAKE_DEFAULT_APP_REQUEST)
            }
        }
    }

    // while SEND_SMS and READ_SMS permissions are mandatory, READ_CONTACTS is optional. If we don't have it, we just won't be able to show the contact name in some cases
    private fun askPermissions() {
        handlePermission(PERMISSION_READ_SMS) {
            if (it) {
                handlePermission(PERMISSION_SEND_SMS) {
                    if (it) {
                        handlePermission(PERMISSION_READ_CONTACTS) {
                            handleNotificationPermission { granted ->
                                if (!granted) {
                                    PermissionRequiredDialog(
                                        activity = this,
                                        textId = com.simplemobiletools.commons.R.string.allow_notifications_incoming_messages,
                                        positiveActionCallback = { openNotificationSettings() })
                                }
                            }

                            initMessenger()
                            bus = EventBus.getDefault()
                            try {
                                bus!!.register(this)
                            } catch (ignored: Exception) {
                            }
                        }
                    } else {
                        finish()
                    }
                }
            } else {
                finish()
            }
        }
    }

    private fun initMessenger() {
        storeStateVariables()
        getCachedConversations()

        binding.noConversationsPlaceholder2.setOnClickListener {
            launchNewConversation()
        }

        binding.conversationsFab.setOnClickListener {
            launchNewConversation()
        }
    }

    private fun getCachedConversations() {
        lifecycleScope.launch(Dispatchers.IO) {
            val conversations = try {
                conversationsDB.getNonArchived().toMutableList() as ArrayList<Conversation>
            } catch (e: Exception) {
                Log.d(TAG, "getCachedConversations: catch 出问题了！ 返回空数据  info = ${e.message}")
                ArrayList()
            }
            Log.d(TAG, "getCachedConversations: 列表数据长度 = ${conversations.size}   toJson conversations = ${Gson().toJson(conversations)}")
            if (conversations.isEmpty()) {
                Log.e(TAG, "getCachedConversations: 当前短信列表数据为空 ")
            }
            val archived = try {
                conversationsDB.getAllArchived()
            } catch (e: Exception) {
                listOf()
            }
            if (archived.isNotEmpty())
                Log.d(TAG, "getCachedConversations: archived = ${Gson().toJson(archived)}")

            setupConversations(conversations, cached = true)
            getNewConversations((conversations + archived).toMutableList() as ArrayList<Conversation>)
            conversations.forEach {
                clearExpiredScheduledMessages(it.threadId)
            }
        }
    }

    private fun getNewConversations(cachedConversations: ArrayList<Conversation>) {
        var privateCursor: Cursor? = null
        lifecycleScope.launch(Dispatchers.Main) {
            privateCursor = getMyContactsCursor(favoritesOnly = false, withPhoneNumbersOnly = true)
        }

        val privateContacts = MyContactsContentProvider.getSimpleContacts(this@MainActivity, privateCursor)
        Log.d(TAG, " getNewConversations  privateContacts:${GsonUtil.parseListToJson(privateContacts)}")
        //获取系统SMS DB短信列表，根据手机号检测通讯录备注名称
        val conversations: java.util.ArrayList<Conversation> = getConversations(privateContacts = privateContacts)
        Log.d(TAG, " getNewConversations  获取系统SMS DB短信列表(脏数据) conversations:${GsonUtil.parseListToJson(conversations)}")

        //通过DB数据与cache对比threadID
        val threadIdSet = cachedConversations.map { it.threadId }.toSet()
        conversations.forEach { clonedConversation ->
            if (!threadIdSet.contains(clonedConversation.threadId)) { // O(1) 查找
                conversationsDB.insertOrUpdate(clonedConversation)
                cachedConversations.add(clonedConversation)
            }
        }


        cachedConversations.forEach { cachedConversation ->
            val threadId = cachedConversation.threadId

            val isTemporaryThread = cachedConversation.isScheduled
            val isConversationDeleted = !conversations.map { it.threadId }.contains(threadId)
            if (isConversationDeleted && !isTemporaryThread) {
                conversationsDB.deleteThreadId(threadId)
            }

            val newConversation: Conversation? = conversations.find { it.phoneNumber == cachedConversation.phoneNumber }
            if (isTemporaryThread && newConversation != null) {
                // delete the original temporary thread and move any scheduled messages to the new thread
                conversationsDB.deleteThreadId(threadId)
                messagesDB.getScheduledThreadMessages(threadId)
                    .forEach { message ->
                        messagesDB.insertOrUpdate(message.copy(threadId = newConversation.threadId))
                    }
                insertOrUpdateConversation(newConversation, cachedConversation)
            }
        }

        cachedConversations.forEach { cachedConv ->
            val conv = conversations.find {
                it.threadId == cachedConv.threadId && !Conversation.areContentsTheSame(cachedConv, it)
            }
//                Log.d(TAG, " getNewConversations 对比后的结果(新数据)222 find conv:${GsonUtil.parseBeanToJson(conv)}")
            if (conv != null && conv.isSFNumber) {
//                    Log.d(TAG, " conversations find it conv:${GsonUtil.parseBeanToJson(conv)}")
                val lastModified = maxOf(cachedConv.date, conv.date)
                val conversation = conv.copy(date = lastModified)
//                    Log.d(
//                        TAG,
//                        " getNewConversations 对比后的结果(新数据) isSFNumber=${conv.isSFNumber} find conversation:${GsonUtil.parseBeanToJson(conversation)}"
//                    )
                insertOrUpdateConversation(conversation)
            }
        }

        val allConversations = conversationsDB.getNonArchived() as ArrayList<Conversation>
        Log.d(TAG, "getNewConversations   获取所有列表长度 = ${allConversations.size}  toJson allConversations = ${GsonUtil.parseListToJson(allConversations)} ")
        setupConversations(allConversations)

        //未读消息 桌面Icon提示
        val listMessage = messagesDB.getAll()
        val readResult = listMessage.count { !it.read }
        SmsCountUtil.setBadgeNum(this@MainActivity, readResult)
        config.badgeTotalNumber = readResult

        if (config.appRunCount == 1) {
            conversations.map { it.threadId }.forEach { threadId ->
                val messages = getMessages(threadId, getImageResolutions = false, includeScheduledMessages = false)
                messages.chunked(30).forEach { currentMessages: List<Message> ->
                    messagesDB.insertMessages(*currentMessages.toTypedArray())
                }
            }
        }
    }

    private fun getOrCreateConversationsAdapter(): ConversationsAdapter {
        var currAdapter = binding.conversationsList.adapter
        if (currAdapter == null) {
            hideKeyboard()
            currAdapter = ConversationsAdapter(
                activity = this,
                recyclerView = binding.conversationsList,
                onRefresh = { notifyDatasetChanged() },
                itemClick = { handleConversationClick(it) }
            )

            binding.conversationsList.adapter = currAdapter
            if (areSystemAnimationsEnabled) {
                binding.conversationsList.scheduleLayoutAnimation()
            }
        }
        return currAdapter as ConversationsAdapter
    }

    /**
     * 1.获取最新短信 先于自身message列表查找,检测对话列表(message)是否包含[零宽度字符]
     * 2.message列表找不到的情况下，通话记录中对比数据进行掩码操作
     * 3.落库生成记录
     */
    private fun setFirstConversationAndUpdateData(sortedConversations: ArrayList<Conversation>) {
        val hasPerMissionResult = this@MainActivity.hasPermission(PERMISSION_READ_CALL_LOG)
        if (hasPerMissionResult && sortedConversations.isNotEmpty()) {
            val firstConversation: Conversation = sortedConversations.firstOrNull() ?: return//获取第一条未掩码会话
            Log.d(TAG, " setFirstConversationAndUpdateData:  获取第一条会话  =: ${GsonUtil.parseBeanToJson(firstConversation)}")

            //根据最新的短信threadId 获取所有短信列表 取最新一条记录
            val messages: java.util.ArrayList<Message> = getMessages(firstConversation.threadId, true)
            Log.d(TAG, " setFirstConversationAndUpdateData:  执行查询会话 messages 数据库  =:${GsonUtil.parseListToJson(messages)}  ")

            if(messages.isEmpty()) return
            val lastMsg: Message = messages.last()
            Log.d(TAG, "setFirstConversationAndUpdateData: lastMsg = ${Gson().toJson(lastMsg)}")
            firstConversation.snippet = lastMsg.body
            firstConversation.date = lastMsg.date
            firstConversation.userName = lastMsg.senderName

            val listContains: List<String> = messages.map { it.body }.filter { it.contains("\u200B") }
            Log.d(TAG, "setFirstConversationAndUpdateData:  该对话是否有包含零宽字符 = ${listContains.isNotEmpty()}")
            if (listContains.isNotEmpty()) {
                firstConversation.isSFNumber = true
                if (lastMsg.senderPhoneNumber.isNotEmpty() && PhoneFromUtil.isNumeric(lastMsg.senderPhoneNumber))
                    PhoneUtils.phoneNumberFormat(lastMsg.senderPhoneNumber) {
                        firstConversation.phoneNumber = it
//                        firstConversation.title = it
                    }

                val listMsg = messagesDB.getThreadMessages(lastMsg.threadId)
                listMsg.forEach { itemBean ->
                    PhoneUtils.phoneNumberFormat(lastMsg.senderName) {
                        itemBean.senderName = it
                    }
                    messagesDB.insertOrUpdate(itemBean)
                }
                Log.d(TAG, " setFirstConversationAndUpdateData:  lastMsg 数据库 threadId = ${lastMsg.threadId}  =:${GsonUtil.parseListToJson(listMsg)}  ")
            } else {
                val phoneNumber = firstConversation.title.replace(" ", "")
                Log.d(TAG, "setFirstConversationAndUpdateData: phoneNumber = $phoneNumber")
                //只针对手机号
                if (phoneNumber.length < 11) return
                if (!firstConversation.isSFNumber) {
                    Log.d(TAG, "setFirstConversationAndUpdateData: 获取第一条会话手机号 phoneNumber:$phoneNumber")
                    RecentsHelper(context = this@MainActivity).getRecentCalls(false, Int.MAX_VALUE) { recents: List<RecentCall> ->
                        val listCallRecent: List<RecentCall> = recents.distinctBy { it.phoneNumber }
                        Log.d(TAG, "setFirstConversationAndUpdateData: 最近通话记录列表 :${GsonUtil.parseListToJson(listCallRecent)}")
                        val findResult: RecentCall? = listCallRecent.firstOrNull { it.phoneNumber.replace(" ", "") == phoneNumber && it.isShunFeng }
                        Log.d(TAG, "setFirstConversationAndUpdateData: 是否从通话记录中查找到电话号Result:${findResult != null}")
                        //从最近通话记录中找到了号码 进行掩码操作
                        if (findResult != null) {
                            PhoneUtils.phoneNumberFormat(phoneNumber) { itNumber ->
                                firstConversation.phoneNumber = itNumber
                                firstConversation.isSFNumber = true
                            }
                        }
                    }
                }
            }
            conversationsDB.insertOrUpdate(firstConversation)
            Log.d(TAG, "setFirstConversationAndUpdateData:   firstConversation  = ${Gson().toJson(firstConversation)}")
        }
    }

    private fun setupConversations(conversations: ArrayList<Conversation>, cached: Boolean = false) {

        val sortedConversations = conversations.sortedWith(
            compareByDescending<Conversation> { config.pinnedConversations.contains(it.threadId.toString()) }
                .thenByDescending { it.date }
        ).toMutableList() as ArrayList<Conversation>
        Log.d(TAG, "setupConversations: sortedConversations = ${sortedConversations.size}  toJson = ${Gson().toJson(sortedConversations)}")

        //加载缓存数据不执行此处
        if (!cached) {
            //根据最新的短信 是否需要对手机号掩码及db bean修改
            setFirstConversationAndUpdateData(sortedConversations)

            //根据记录被删除的会话手机号，从会话列表中判断手机号是否一致，满足就脱敏手机号(为了解决荣耀云备份后恢复，手机号未脱敏的情况)
            val listMsgIdAll: String? = Config.newInstance(this@MainActivity).listDeleteMsgIdAllData
            Log.d(TAG, "setupConversations 云备份 获取被删除的会话集合  listMsgIdAll  不等于空 = ${!listMsgIdAll.isNullOrEmpty()}")
            if (!listMsgIdAll.isNullOrEmpty()) {
                val listData: ArrayList<String> = listMsgIdAll.split(",").toArrayList()
                Log.d(TAG, "setupConversations 云备份  split  listData = ${listData}")
                if (listData.isNotEmpty()) {
                    val listConversationData: List<Conversation> = conversations.filter { listData.contains(it.phoneNumber) }
                    Log.d(TAG, "setupConversations 云备份 找到被删除的 ListConversation 会话  = ${Gson().toJson(listConversationData)}")
                    if (listConversationData.isNotEmpty()) {
                        listConversationData.forEach { conversation ->
                            conversation.isSFNumber = true
                            conversationsDB.insertOrUpdate(conversation)
                        }
                    }
                }
            }
        }

        lifecycleScope.launch(Dispatchers.Main) {
            if (cached && config.appRunCount == 1) {
                // there are no cached conversations on the first run so we show the loading placeholder and progress until we are done loading from telephony
                showOrHideProgress(conversations.isEmpty())
            } else {
                showOrHideProgress(false)
                showOrHidePlaceholder(conversations.isEmpty())
            }

            try {
                getOrCreateConversationsAdapter().apply {
                    Log.d(TAG, "setupConversations: 短信列表数量 = ${sortedConversations.size}  toJson = ${Gson().toJson(sortedConversations)}")
                    updateConversations(sortedConversations) {
                        Log.d(TAG, "setupConversations: cached = ${!cached}")
                        if (!cached) {
                            showOrHidePlaceholder(currentList.isEmpty())
                        }
                    }
                }
            } catch (ignored: Exception) {
            }
        }
    }

    private fun showOrHideProgress(show: Boolean) {
        if (show) {
            binding.conversationsProgressBar.show()
            binding.noConversationsPlaceholder.beVisible()
            binding.noConversationsPlaceholder.text = getString(R.string.loading_messages)
        } else {
            binding.conversationsProgressBar.hide()
            binding.noConversationsPlaceholder.beGone()
        }
    }

    private fun showOrHidePlaceholder(show: Boolean) {
        binding.conversationsFastscroller.beGoneIf(show)
        binding.noConversationsPlaceholder.beVisibleIf(show)
        binding.noConversationsPlaceholder.text = getString(R.string.no_conversations_found)
        binding.noConversationsPlaceholder2.beVisibleIf(show)
    }

    private fun fadeOutSearch() {
        binding.searchHolder.animate().alpha(0f).setDuration(SHORT_ANIMATION_DURATION).withEndAction {
            binding.searchHolder.beGone()
            searchTextChanged("", true)
        }.start()
    }

    @SuppressLint("NotifyDataSetChanged")
    private fun notifyDatasetChanged() {
        getOrCreateConversationsAdapter().notifyDataSetChanged()
    }

    private fun handleConversationClick(any: Any) {
        Intent(this, ThreadActivity::class.java).apply {
            val conversation = any as Conversation
            Log.d(TAG, "handleConversationClick: conversation = ${Gson().toJson(conversation)}")
            val userName = conversation.userName
            Log.d(TAG, "handleConversationClick:  通讯录备注名称 = $userName  短信内容 = ${conversation.snippet}  threadId  = ${conversation.threadId}")
            ensureBackgroundThread {
                val listMessageData = getMessages(conversation.threadId, false)
                Log.d(TAG, "handleConversationClick: listMessageData是否为空 = ${listMessageData.isNotEmpty()}")
                if (listMessageData.isNotEmpty()) {
                    val listContains: List<Message> = listMessageData.filter { msg -> msg.body.contains("\u200B") }
                    Log.d(TAG, "handleConversationClick: 是否包含零宽度字符 = ${listContains.isNotEmpty()}")
                    if (listContains.isNotEmpty() || conversation.isSFNumber) {
                        if (userName.isNotBlank() && !PhoneFromUtil.isNumeric(userName)) {
                            putExtra(THREAD_TITLE, userName)
                        } else {
                            val phoneNumber = conversation.title
                            if (phoneNumber.contains("+86")) {
                                phoneNumber.replace("+86", "")
                            }
                            PhoneUtils.phoneNumberFormat(phoneNumber) { itNumber ->
                                putExtra(THREAD_TITLE, itNumber)
                            }
                        }
                    } else {
                        if (userName.isNotBlank()) {
                            putExtra(THREAD_TITLE, userName)
                        } else {
                            putExtra(THREAD_TITLE, conversation.title)
                        }
                    }
                } else {
                    if (userName.isNotBlank()) {
                        putExtra(THREAD_TITLE, userName)
                    } else {
                        putExtra(THREAD_TITLE, conversation.title)
                    }
                }

                putExtra(THREAD_ID, conversation.threadId)
                putExtra(WAS_PROTECTION_HANDLED, wasProtectionHandled)
                startActivity(this)
            }
        }
    }

    private fun launchNewConversation() {
        hideKeyboard()
        Intent(this, NewConversationActivity::class.java).apply {
            startActivity(this)
        }
    }

    /*@SuppressLint("NewApi")
    private fun checkShortcut() {
        val appIconColor = config.appIconColor
        if (isNougatMR1Plus() && config.lastHandledShortcutColor != appIconColor) {
            val newConversation = getCreateNewContactShortcut(appIconColor)

            val manager = getSystemService(ShortcutManager::class.java)
            try {
                manager.dynamicShortcuts = listOf(newConversation)
                config.lastHandledShortcutColor = appIconColor
            } catch (ignored: Exception) {
            }
        }
    }*/

    /* @SuppressLint("NewApi")
     private fun getCreateNewContactShortcut(appIconColor: Int): ShortcutInfo {
         val newEvent = getString(R.string.new_conversation)
         val drawable = resources.getDrawable(com.simplemobiletools.commons.R.drawable.shortcut_plus)
         (drawable as LayerDrawable).findDrawableByLayerId(com.simplemobiletools.commons.R.id.shortcut_plus_background).applyColorFilter(appIconColor)
         val bmp = drawable.convertToBitmap()

         val intent = Intent(this, NewConversationActivity::class.java)
         intent.action = Intent.ACTION_VIEW
         return ShortcutInfo.Builder(this, "new_conversation")
             .setShortLabel(newEvent)
             .setLongLabel(newEvent)
             .setIcon(Icon.createWithBitmap(bmp))
             .setIntent(intent)
             .build()
     }*/

    private fun searchTextChanged(text: String, forceUpdate: Boolean = false) {
        if (!binding.mainMenu.isSearchOpen && !forceUpdate) {
            return
        }

        lastSearchedText = text
        binding.searchPlaceholder2.beGoneIf(text.length >= 2)
        if (text.length >= 2) {
            ensureBackgroundThread {
                val searchQuery = "%$text%"
                val messages: List<Message> = messagesDB.getMessagesWithText(searchQuery)
                Log.d(TAG, "setupOptionsMenu  搜索消息 messages = ${GsonUtil.parseListToJson(messages)} ")
                val conversations: List<Conversation> = conversationsDB.getConversationsWithText(searchQuery)
                Log.d(TAG, "setupOptionsMenu  搜索消息 conversations = ${GsonUtil.parseListToJson(conversations)} ")
                if (text == lastSearchedText) {
                    showSearchResults(messages, conversations, text)
                }
            }
        } else {
            binding.searchPlaceholder.beVisible()
            binding.searchResultsList.beGone()
        }
    }

    private fun showSearchResults(messages: List<Message>, conversations: List<Conversation>, searchedText: String) {
        val searchResults = ArrayList<SearchResult>()
        conversations.forEach { conversation ->
            val date = conversation.date.formatDateOrTime(this, true, true)
            val searchResult =
                SearchResult(-1, conversation.title, conversation.phoneNumber, date, conversation.threadId, conversation.photoUri, conversation.isSFNumber)
            searchResults.add(searchResult)
        }
        Log.d(TAG, "setupOptionsMenu  搜索消息 conversations  searchResults = ${GsonUtil.parseListToJson(searchResults)} ")

        if (messages.isNotEmpty()) {
            val listConversationData: List<String> = conversationsDB.getNonArchived().filter { it.isSFNumber }.map { it.title }
            messages.forEach {
                if (listConversationData.contains(it.senderPhoneNumber)) {
                    Log.d(TAG, "showSearchResults: 找到了符合掩码的手机号  ${it.senderPhoneNumber}")
                    listMessageId.add(it.id)
                }
            }

            messages.sortedByDescending { it.id }.forEach { message ->
                var recipient = message.senderName
                if (recipient.isEmpty() && message.participants.isNotEmpty()) {
                    val participantNames = message.participants.map { it.name }
                    recipient = TextUtils.join(", ", participantNames)
                }
                val date = message.date.formatDateOrTime(this, true, true)

                if (listMessageId.contains(message.id)) {
                    val searchResult = SearchResult(message.id, recipient, message.body, date, message.threadId, message.senderPhotoUri, isSfNumber = true)
                    searchResults.add(searchResult)
                } else {
                    val searchResult = SearchResult(message.id, recipient, message.body, date, message.threadId, message.senderPhotoUri, isSfNumber = false)
                    searchResults.add(searchResult)
                }
            }
        }

        runOnUiThread {
            binding.searchResultsList.beVisibleIf(searchResults.isNotEmpty())
            binding.searchPlaceholder.beVisibleIf(searchResults.isEmpty())

            val currAdapter = binding.searchResultsList.adapter
            if (currAdapter == null) {
                SearchResultsAdapter(this, searchResults, binding.searchResultsList, searchedText) {
                    hideKeyboard()
                    Intent(this, ThreadActivity::class.java).apply {
                        putExtra(THREAD_ID, (it as SearchResult).threadId)
                        if (it.isSfNumber) {
                            PhoneUtils.phoneNumberFormat(it.title)
                        } else {
                            putExtra(THREAD_TITLE, it.title)
                        }
                        putExtra(SEARCHED_MESSAGE_ID, it.messageId)
                        startActivity(this)
                    }
                }.apply {
                    binding.searchResultsList.adapter = this
                }
            } else {
                (currAdapter as SearchResultsAdapter).updateItems(searchResults, searchedText)
            }
        }
    }

    private fun launchRecycleBin() {
        hideKeyboard()
        startActivity(Intent(applicationContext, RecycleBinConversationsActivity::class.java))
    }

    private fun launchArchivedConversations() {
        hideKeyboard()
        startActivity(Intent(applicationContext, ArchivedConversationsActivity::class.java))
    }

    private fun launchSettings() {
        hideKeyboard()
        startActivity(Intent(applicationContext, SettingsActivity::class.java))
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun refreshMessages(event: Events.RefreshMessages) {
        Log.d(TAG, "refreshMessages: ")
        initMessenger()
    }


    @SuppressLint("NewApi")
    private fun checkShortcuts() {
        val appIconColor = config.appIconColor
        if (isNougatMR1Plus()) {
            Log.d(TAG, "checkShortcuts: ")
            val launchDialerUpdate = getLaunchExportShortcut(appIconColor)
            try {
                shortcutManager.dynamicShortcuts = listOf(launchDialerUpdate)
                config.lastHandledShortcutColor = appIconColor
                shortcutManager.updateShortcuts(shortcutManager.dynamicShortcuts)
            } catch (ignored: Exception) {
                ignored.printStackTrace()
            }
        }
    }


    @SuppressLint("NewApi")
    private fun getLaunchExportShortcut(appIconColor: Int): ShortcutInfo {
        val newEvent = getString(R.string.shortcuts_back_short_label)
        val drawable = resources.getDrawable(R.drawable.shortcut_dialpad_update)
        (drawable as LayerDrawable).findDrawableByLayerId(R.id.shortcut_update_background).applyColorFilter(appIconColor)
        val bmp = drawable.convertToBitmap()

        val intent = Intent(this, ShortCutActivity::class.java)
        intent.action = Constant.EXPORT_LOG

        return ShortcutInfo.Builder(this, Constant.EXPORT_LOG)
            .setShortLabel(newEvent)
            .setLongLabel(newEvent)
            .setIcon(Icon.createWithBitmap(bmp))
            .setIntent(intent)
            .build()
    }

}
