Collecting logs from deleting contacts in Android

I’m trying to save informations about deleting contacts in .txt log, but when i’m deleting any contact, nothing happens. If i Block or Unblock Wipe Data, there is info in logs, but no while i’m deleting or moving contacts to the bin.

My MainActivity.kt:

package com.example.test2

import android.annotation.SuppressLint
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.database.ContentObserver
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.os.UserManager
import android.provider.ContactsContract
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.example.test2.databinding.ActivityMainBinding
import java.io.File
import java.io.FileWriter
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*

class MainActivity : AppCompatActivity() {

    private lateinit var devicePolicyManager: DevicePolicyManager
    private lateinit var componentName: ComponentName
    private val disallowFactoryReset = "no_factory_reset"

    private lateinit var binding: ActivityMainBinding

    private lateinit var contactsObserver: ContentObserver

    @SuppressLint("WrongConstant")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        devicePolicyManager = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
        componentName = ComponentName(this, MyDeviceAdminReceiver::class.java)

        // Ustawienie tekstu przycisków na podstawie zasobów ciągów
        binding.activateButton.setText(R.string.activate_button_text)
        binding.deactivateButton.setText(R.string.deactivate_button_text)
        binding.blockWipeDataButton.setText(R.string.block_wipe_data_button_text)
        binding.unblockWipeDataButton.setText(R.string.unblock_wipe_data_button_text)

        binding.activateButton.setOnClickListener {
            if (!devicePolicyManager.isAdminActive(componentName)) {
                val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
                intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName)
                startActivity(intent) // Uruchamianie intencji
            }
        }

        binding.deactivateButton.setOnClickListener {
            if (devicePolicyManager.isAdminActive(componentName)) {
                devicePolicyManager.removeActiveAdmin(componentName)
            }
        }

        binding.blockWipeDataButton.setOnClickListener {
            // Blokowanie możliwości resetowania do ustawień fabrycznych
            devicePolicyManager.addUserRestriction(this.componentName, UserManager.DISALLOW_FACTORY_RESET)
            devicePolicyManager.addUserRestriction(this.componentName, UserManager.DISALLOW_NETWORK_RESET)
            saveLog("Zablokowano możliwość resetowania do ustawień fabrycznych: ${getCurrentDateTime()}")
        }

        binding.unblockWipeDataButton.setOnClickListener {
            if (devicePolicyManager.isAdminActive(componentName)) {
                // Odblokowywanie możliwości resetowania do ustawień fabrycznych
                devicePolicyManager.clearUserRestriction(this.componentName, disallowFactoryReset)
                devicePolicyManager.clearUserRestriction(this.componentName, UserManager.DISALLOW_NETWORK_RESET)
                saveLog("Odblokowano możliwość resetowania do ustawień fabrycznych: ${getCurrentDateTime()}")
            }
        }

        // Utwórz instancję obserwatora kontaktów
        contactsObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
            override fun onChange(selfChange: Boolean, uri: Uri?) {
                super.onChange(selfChange, uri)
                // Sprawdź, czy zmiana dotyczy usunięcia kontaktu
                if (uri != null && uri.toString().contains("contacts")) {
                    saveLog("Usunięto kontakt: ${getContactDetails(uri)}")
                }
            }
        }
        // Zarejestruj obserwatora kontaktów
        contentResolver.registerContentObserver(
            ContactsContract.Contacts.CONTENT_URI,
            true,
            contactsObserver
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        // Odrejestruj obserwatora kontaktów
        contentResolver.unregisterContentObserver(contactsObserver)
    }

    private fun saveLog(logMessage: String) {
        val logsDir = getLogsDirectory()
        val logFile = File(logsDir, "app_logs.txt")
        try {
            val writer = FileWriter(logFile, true)
            writer.append("$logMessage\n")
            writer.flush()
            writer.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    private fun getLogsDirectory(): File {
        val logsDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "logs")
        if (!logsDir.exists()) {
            logsDir.mkdirs()
            Log.d("Path", logsDir.absolutePath)
        }
        return logsDir
    }

    private fun getCurrentDateTime(): String {
        val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
        val date = Date()
        return dateFormat.format(date)
    }

    @SuppressLint("Range")
    private fun getContactDetails(contactUri: Uri): String {
        val resolver: ContentResolver = contentResolver
        val cursor: Cursor? = resolver.query(contactUri, null, null, null, null)
        cursor?.use {
            if (it.moveToFirst()) {
                val nameIndex = it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
                val numberIndex = it.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)
                if (nameIndex != -1 && numberIndex != -1) {
                    val name = it.getString(nameIndex)
                    val number = it.getString(numberIndex)
                    return "Nazwa: $name, Numer: $number, Data: ${getCurrentDateTime()}"
                }
            } else {
                // Kontakt nie istnieje, został prawdopodobnie przeniesiony do kosza
                return "Kontakt przeniesiony do kosza: $contactUri, Data: ${getCurrentDateTime()}"
            }
        }
        return ""
    }
}

Also add permissions to AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />

I tried to retrieve contact information using ContentResolver.query() and Cursor. Initially, I attempted to access contact details through ContactsContract.Contacts.CONTENT_URI, and then checked if the cursor contains the relevant columns (e.g., DISPLAY_NAME and HAS_PHONE_NUMBER) and retrieved the data accordingly. Additionally, I tried to handle the situation when a contact does not exist (moved to trash), which was part of my approach to solving the problem.

Leave a Comment