Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature][Calling] CSS call history #710

Merged
merged 30 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8ab9f09
Add call history
pavelprystinka Jan 19, 2023
42f6fed
init AndroidThreeTen
pavelprystinka Jan 19, 2023
1e78e00
Do not store call locator
pavelprystinka Jan 19, 2023
8b64418
Remove old records
pavelprystinka Jan 20, 2023
637657f
Remove launcher selection from UI
pavelprystinka Jan 20, 2023
c45f7d0
Update CallHistoryRepository
pavelprystinka Jan 23, 2023
1d179b5
Change debugInfo prop to fun
pavelprystinka Jan 23, 2023
7174f85
add javadocs
pavelprystinka Jan 23, 2023
e08955b
Rename data layer CallHistoryRecord to CallHistoryRecordData
pavelprystinka Jan 24, 2023
b7d14c4
Rename callIds to callIdList
pavelprystinka Jan 25, 2023
cebd954
rename list to plurals
pavelprystinka Jan 26, 2023
f8e8216
Call History unit tests
pavelprystinka Jan 30, 2023
563b4dc
Move column indexes
pavelprystinka Jan 30, 2023
2904d71
TOKEN_FUNCTION_URL is in use in Android tests
pavelprystinka Jan 30, 2023
db25b7b
Make CallHistoryRepository suspend functions
pavelprystinka Feb 6, 2023
f4c3f80
format
pavelprystinka Feb 6, 2023
1d16960
use getColumnIndexOrThrow
pavelprystinka Feb 6, 2023
eb4a912
Rename CallHistory.date to callStartedOn
pavelprystinka Feb 6, 2023
c188f07
Refactor CallHistoryRepository
pavelprystinka Feb 7, 2023
cc54555
lean old history records inside repository
pavelprystinka Feb 8, 2023
35bb239
format
pavelprystinka Feb 8, 2023
752f91d
Use weak ref to hold di container
pavelprystinka Feb 8, 2023
c2a700a
fix android tests
pavelprystinka Feb 9, 2023
16ec677
Add index to the history table
pavelprystinka Feb 9, 2023
585e889
code format
pavelprystinka Feb 9, 2023
5a885aa
Updated to call history repository
pavelprystinka Feb 10, 2023
fe65e5f
Store call history time in milliseconds plus tests
pavelprystinka Feb 15, 2023
5824d68
Update javadoc
pavelprystinka Feb 19, 2023
fc67da0
Update version
pavelprystinka Feb 24, 2023
a453cc3
Make cleanupOldRecords synchronized
pavelprystinka Feb 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion azure-communication-ui/calling/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,16 @@ dependencies {
implementation "com.microsoft.fluentui:fluentui_persona:$microsoft_fluent_ui_version"
implementation "com.microsoft.fluentui:fluentui_transients:$microsoft_fluent_ui_version"

api 'com.jakewharton.threetenabp:threetenabp:1.4.4'
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved

testImplementation "androidx.arch.core:core-testing:$androidx_core_testing_version"
testImplementation "junit:junit:$junit_version"
testImplementation "org.mockito:mockito-inline:$mockito_inline_version"
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$jetbrains_kotlinx_coroutines_test_version"

testImplementation('org.threeten:threetenbp:1.6.5') {
exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}
androidTestImplementation "androidx.test.ext:junit:$androidx_junit_version"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidx_espresso_core_version"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$androidx_espresso_contrib_version"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
import com.azure.android.communication.ui.calling.models.CallCompositeTeamsMeetingLinkLocator;
import com.azure.android.communication.ui.calling.presentation.CallCompositeActivity;
import com.azure.android.communication.ui.calling.presentation.manager.DebugInfoManager;
import com.jakewharton.threetenabp.AndroidThreeTen;

import static com.azure.android.communication.ui.calling.models.CallCompositeDebugInfoExtensionsKt.buildCallCompositeDebugInfo;
import static com.azure.android.communication.ui.calling.CallCompositeExtentionsKt.createDebugInfoManager;
import static com.azure.android.communication.ui.calling.service.sdk.TypeConversionsKt.into;

import java.lang.ref.WeakReference;
Expand Down Expand Up @@ -213,25 +214,32 @@ public CallCompositeSetParticipantViewDataResult setRemoteParticipantViewData(
*
* @return {@link CallCompositeDebugInfo}
*/
public CallCompositeDebugInfo getDebugInfo() {
final DebugInfoManager debugInfoManager = getDebugInfoManager();
return debugInfoManager != null
? debugInfoManager.getDebugInfo()
: buildCallCompositeDebugInfo();
public CallCompositeDebugInfo getDebugInfo(final Context context) {
AndroidThreeTen.init(context.getApplicationContext());
final DebugInfoManager debugInfoManager = getDebugInfoManager(context.getApplicationContext());
return debugInfoManager.getDebugInfo();
}

void setDependencyInjectionContainer(final DependencyInjectionContainer diContainer) {
this.diContainer = new WeakReference<DependencyInjectionContainer>(diContainer);
this.diContainer = new WeakReference<>(diContainer);
}

private DebugInfoManager getDebugInfoManager() {
return diContainer != null ? diContainer.get().getDebugInfoManager() : null;
private DebugInfoManager getDebugInfoManager(final Context context) {
if (diContainer != null) {
final DependencyInjectionContainer container = diContainer.get();
if (container != null) {
return container.getDebugInfoManager();
}
}

return createDebugInfoManager(context.getApplicationContext());
}

private void launchComposite(final Context context,
final CallCompositeRemoteOptions remoteOptions,
final CallCompositeLocalOptions localOptions,
final boolean isTest) {
AndroidThreeTen.init(context.getApplicationContext());

UUID groupId = null;
String meetingLink = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.android.communication.ui.calling

import android.content.Context
import com.azure.android.communication.ui.calling.data.CallHistoryRepositoryImpl
import com.azure.android.communication.ui.calling.presentation.manager.DebugInfoManager
import com.azure.android.communication.ui.calling.presentation.manager.DebugInfoManagerImpl

internal fun createDebugInfoManager(context: Context): DebugInfoManager {
return DebugInfoManagerImpl(CallHistoryRepositoryImpl(context))
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.android.communication.ui.calling.data

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import com.azure.android.communication.ui.calling.data.model.CallHistoryRecordData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import org.threeten.bp.Instant
import org.threeten.bp.OffsetDateTime
import org.threeten.bp.ZoneId
import java.util.concurrent.Executors

internal interface CallHistoryRepository {
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
suspend fun insert(callId: String, callDateTime: OffsetDateTime)
suspend fun getAll(): List<CallHistoryRecordData>
}

internal class CallHistoryRepositoryImpl(
private val context: Context
) : CallHistoryRepository {
private val insetLock = Any()

override suspend fun insert(callId: String, callDateTime: OffsetDateTime) {
return withContext(Executors.newSingleThreadExecutor().asCoroutineDispatcher()) {
synchronized(insetLock) {
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
DbHelper(context).writableDatabase
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
.use { db ->
val values = ContentValues().apply {
put(CallHistoryContract.COLUMN_NAME_CALL_ID, callId)
put(CallHistoryContract.COLUMN_NAME_CALL_DATE, callDateTime.toInstant().epochSecond)
}

db.insert(CallHistoryContract.TABLE_NAME, null, values)
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
cleanUpOldRecords(db)
db.close()
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

override suspend fun getAll(): List<CallHistoryRecordData> {
return withContext(Dispatchers.IO) {
DbHelper(context).writableDatabase.use { db ->
val items = mutableListOf<CallHistoryRecordData>()
db.rawQuery(
"SELECT * FROM ${CallHistoryContract.TABLE_NAME} ORDER BY ${CallHistoryContract.COLUMN_NAME_CALL_DATE} ASC",
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
null
).use {
if (it.moveToFirst()) {
val idColumnIndex = it.getColumnIndexOrThrow(CallHistoryContract.COLUMN_NAME_ID)
val nameColumnIndex = it.getColumnIndexOrThrow(CallHistoryContract.COLUMN_NAME_CALL_ID)
val dateColumnIndex = it.getColumnIndexOrThrow(CallHistoryContract.COLUMN_NAME_CALL_DATE)
do {
items.add(
CallHistoryRecordData(
id = it.getInt(idColumnIndex),
callId = it.getString(nameColumnIndex),
callStartedOn = OffsetDateTime.ofInstant(
Instant.ofEpochSecond(it.getLong(dateColumnIndex)), ZoneId.systemDefault()
),
)
)
} while (it.moveToNext())
}
}
return@withContext items
}
}
}

private fun cleanUpOldRecords(db: SQLiteDatabase) {
val threshold = OffsetDateTime.now().minusDays(31).toInstant().epochSecond
val sql = "DELETE FROM ${CallHistoryContract.TABLE_NAME} " +
"WHERE ${CallHistoryContract.COLUMN_NAME_CALL_DATE} < $threshold"
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
db.execSQL(sql)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.android.communication.ui.calling.data

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.provider.BaseColumns

internal class DbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(CallHistoryContract.SQL_CREATE_CALL_HISTORY)
db.execSQL(CallHistoryContract.SQL_CREATE_CALL_HISTORY_INDEX)
}

override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
}

override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
}

companion object {
// If you change the database schema, you must increment the database version.
const val DATABASE_VERSION = 1
const val DATABASE_NAME = "com.azure.android.communication.ui.calling.CallHistoryReader.db"
}
}

internal object CallHistoryContract {

const val COLUMN_NAME_ID = BaseColumns._ID
const val TABLE_NAME = "call_history"

const val COLUMN_NAME_CALL_ID = "call_id"
const val COLUMN_NAME_CALL_DATE = "call_date"

const val SQL_CREATE_CALL_HISTORY =
"CREATE TABLE IF NOT EXISTS $TABLE_NAME (" +
"$COLUMN_NAME_ID INTEGER PRIMARY KEY AUTOINCREMENT," +
"$COLUMN_NAME_CALL_ID TEXT NOT NULL," +
"$COLUMN_NAME_CALL_DATE INTEGER NOT NULL)"

const val SQL_CREATE_CALL_HISTORY_INDEX =
"CREATE INDEX IF NOT EXISTS call_dateindex ON $TABLE_NAME($COLUMN_NAME_CALL_DATE);"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.android.communication.ui.calling.data.model

import org.threeten.bp.OffsetDateTime

internal data class CallHistoryRecordData(
val id: Int,
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
val callId: String,
val callStartedOn: OffsetDateTime,
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package com.azure.android.communication.ui.calling.di

import com.azure.android.communication.ui.calling.CallComposite
import com.azure.android.communication.ui.calling.configuration.CallCompositeConfiguration
import com.azure.android.communication.ui.calling.data.CallHistoryRepository
import com.azure.android.communication.ui.calling.error.ErrorHandler
import com.azure.android.communication.ui.calling.handlers.RemoteParticipantHandler
import com.azure.android.communication.ui.calling.logger.Logger
Expand All @@ -21,6 +22,7 @@ import com.azure.android.communication.ui.calling.redux.Store
import com.azure.android.communication.ui.calling.redux.middleware.handler.CallingMiddlewareActionHandler
import com.azure.android.communication.ui.calling.redux.state.ReduxState
import com.azure.android.communication.ui.calling.presentation.manager.DebugInfoManager
import com.azure.android.communication.ui.calling.service.CallHistoryService
import com.azure.android.communication.ui.calling.service.NotificationService

// Dependency Container for the Call Composite Activity
Expand Down Expand Up @@ -51,7 +53,11 @@ internal interface DependencyInjectionContainer {
val audioFocusManager: AudioFocusManager
val networkManager: NetworkManager
val debugInfoManager: DebugInfoManager
val callHistoryService: CallHistoryService

// UI
val videoViewManager: VideoViewManager

// Data
val callHistoryRepository: CallHistoryRepository
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package com.azure.android.communication.ui.calling.di

import android.content.Context
import com.azure.android.communication.ui.calling.CallComposite
import com.azure.android.communication.ui.calling.data.CallHistoryRepositoryImpl
import com.azure.android.communication.ui.calling.error.ErrorHandler
import com.azure.android.communication.ui.calling.getConfig
import com.azure.android.communication.ui.calling.handlers.RemoteParticipantHandler
Expand Down Expand Up @@ -45,6 +46,8 @@ import com.azure.android.communication.ui.calling.redux.state.ReduxState
import com.azure.android.communication.ui.calling.service.CallingService
import com.azure.android.communication.ui.calling.presentation.manager.DebugInfoManager
import com.azure.android.communication.ui.calling.presentation.manager.DebugInfoManagerImpl
import com.azure.android.communication.ui.calling.service.CallHistoryService
import com.azure.android.communication.ui.calling.service.CallHistoryServiceImpl
import com.azure.android.communication.ui.calling.service.NotificationService
import com.azure.android.communication.ui.calling.service.sdk.CallingSDK
import com.azure.android.communication.ui.calling.service.sdk.CallingSDKEventHandler
Expand Down Expand Up @@ -111,7 +114,14 @@ internal class DependencyInjectionContainerImpl(
}
override val debugInfoManager: DebugInfoManager by lazy {
DebugInfoManagerImpl(
callHistoryRepository,
)
}

override val callHistoryService: CallHistoryService by lazy {
CallHistoryServiceImpl(
appStore,
callHistoryRepository
)
}

Expand Down Expand Up @@ -158,6 +168,10 @@ internal class DependencyInjectionContainerImpl(
RemoteParticipantHandler(configuration, appStore, callingSDKWrapper)
}

override val callHistoryRepository by lazy {
CallHistoryRepositoryImpl(applicationContext)
}

//region Redux
// Initial State
private val initialState by lazy { AppReduxState(configuration.callConfig?.displayName) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.android.communication.ui.calling.models;

import java.util.List;
import org.threeten.bp.OffsetDateTime;

/**
* Call history.
*/
public class CallCompositeCallHistoryRecord {
private final OffsetDateTime callStartedOn;
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
private final List<String> callIds;

CallCompositeCallHistoryRecord(final OffsetDateTime callStartedOn, final List<String> callIds) {
this.callStartedOn = callStartedOn;
this.callIds = callIds;
}

/**
* Get offset date call started on.
* @return
*/
public OffsetDateTime getCallStartedOn() {
return callStartedOn;
}

/**
* Call Id list associated with particular call.
* @return
*/
public List<String> getCallIds() {
return callIds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,24 @@

package com.azure.android.communication.ui.calling.models;

import java.util.List;

/**
* A Call Composite Debug information.
*/
public final class CallCompositeDebugInfo {

private String lastCallId;

CallCompositeDebugInfo() { }
private final List<CallCompositeCallHistoryRecord> callHistoryRecord;

/**
* Set last call id.
* @param lastCallId last call id.
* @return {@link CallCompositeDebugInfo}
*/
CallCompositeDebugInfo setLastCallId(final String lastCallId) {
this.lastCallId = lastCallId;
return this;
CallCompositeDebugInfo(final List<CallCompositeCallHistoryRecord> callHistoryRecord) {
this.callHistoryRecord = callHistoryRecord;
}

/**
* Get last call id.
* @return {@link String}
* Call history.
* @return
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
*/
public String getLastCallId() {
return lastCallId;
public List<CallCompositeCallHistoryRecord> getCallHistoryRecords() {
return callHistoryRecord;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

package com.azure.android.communication.ui.calling.models

internal fun CallCompositeDebugInfo.setCallId(lastKnownCallId: String?) {
this.lastCallId = lastKnownCallId
}
import org.threeten.bp.OffsetDateTime

internal fun buildCallCompositeDebugInfo(callHistoryRecordList: List<CallCompositeCallHistoryRecord>) =
CallCompositeDebugInfo(callHistoryRecordList)

internal fun buildCallCompositeDebugInfo(): CallCompositeDebugInfo = CallCompositeDebugInfo()
internal fun buildCallHistoryRecord(callStartedOn: OffsetDateTime, callIds: List<String>): CallCompositeCallHistoryRecord {
return CallCompositeCallHistoryRecord(callStartedOn, callIds)
}
Loading