blob: feb40ba45232549731b813ae0f582c8f292170a1 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.pandora
import android.bluetooth.BluetoothManager
import android.content.ContentProviderOperation
import android.content.ContentValues
import android.content.Context
import android.provider.CallLog
import android.provider.CallLog.Calls.*
import android.provider.ContactsContract
import android.provider.ContactsContract.*
import android.provider.ContactsContract.CommonDataKinds.*
import java.io.Closeable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import pandora.PBAPGrpc.PBAPImplBase
import pandora.PbapProto.*
@kotlinx.coroutines.ExperimentalCoroutinesApi
class Pbap(val context: Context) : PBAPImplBase(), Closeable {
private val TAG = "PandoraPbap"
private val scope: CoroutineScope
private val allowedDigits = ('0'..'9')
private val bluetoothManager = context.getSystemService(BluetoothManager::class.java)!!
private val bluetoothAdapter = bluetoothManager.adapter
init {
// Init the CoroutineScope
scope = CoroutineScope(Dispatchers.Default)
preparePBAPDatabase()
}
private fun preparePBAPDatabase() {
prepareContactList()
prepareCallLog()
}
private fun prepareContactList() {
var cursor =
context
.getContentResolver()
.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)
if (cursor.getCount() >= CONTACT_LIST_SIZE) return // return if contacts are present
for (item in cursor.getCount() + 1..CONTACT_LIST_SIZE) {
addContact(item)
}
}
private fun prepareCallLog() {
// Delete existing call log
context.getContentResolver().delete(CallLog.Calls.CONTENT_URI, null, null)
addCallLogItem(MISSED_TYPE)
addCallLogItem(OUTGOING_TYPE)
}
private fun addCallLogItem(callType: Int) {
var contentValues =
ContentValues().apply {
put(CallLog.Calls.NUMBER, generatePhoneNumber(PHONE_NUM_LENGTH))
put(CallLog.Calls.DATE, System.currentTimeMillis())
put(CallLog.Calls.DURATION, if (callType == MISSED_TYPE) 0 else 30)
put(CallLog.Calls.TYPE, callType)
put(CallLog.Calls.NEW, 1)
}
context.getContentResolver().insert(CallLog.Calls.CONTENT_URI, contentValues)
}
private fun addContact(contactIndex: Int) {
val operations = arrayListOf<ContentProviderOperation>()
val displayName = String.format(DEFAULT_DISPLAY_NAME, contactIndex)
val phoneNumber = generatePhoneNumber(PHONE_NUM_LENGTH)
val emailID = String.format(DEFAULT_EMAIL_ID, contactIndex)
val rawContactInsertIndex = operations.size
operations.add(
ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_TYPE, null)
.withValue(RawContacts.ACCOUNT_NAME, null)
.build()
)
operations.add(
ContentProviderOperation.newInsert(Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
.withValue(StructuredName.DISPLAY_NAME, displayName)
.build()
)
operations.add(
ContentProviderOperation.newInsert(Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
.withValue(Phone.NUMBER, phoneNumber)
.withValue(Phone.TYPE, Phone.TYPE_MOBILE)
.build()
)
operations.add(
ContentProviderOperation.newInsert(Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
.withValue(Email.DATA, emailID)
.withValue(Email.TYPE, Email.TYPE_MOBILE)
.build()
)
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations)
}
private fun generatePhoneNumber(length: Int): String {
return buildString { repeat(length) { append(allowedDigits.random()) } }
}
override fun close() {
// Deinit the CoroutineScope
scope.cancel()
}
companion object {
const val DEFAULT_DISPLAY_NAME = "Contact Name %d"
const val DEFAULT_EMAIL_ID = "user%d@example.com"
const val CONTACT_LIST_SIZE = 125
const val PHONE_NUM_LENGTH = 10
}
}