diff --git a/azure-communication-ui/build.gradle b/azure-communication-ui/build.gradle index 341c6d5e1c..121c8b84f4 100644 --- a/azure-communication-ui/build.gradle +++ b/azure-communication-ui/build.gradle @@ -17,7 +17,7 @@ buildscript { androidx_activity_ktx_version = '1.4.0' androidx_appcompat_version = '1.4.1' androidx_constraint_layout_version = '2.1.4' - androidx_core_ktx_version = '1.7.0' + androidx_core_ktx_version = '1.16.0' androidx_core_testing_version = '2.1.0' androidx_espresso_contrib_version = '3.4.0' androidx_espresso_core_version = '3.4.0' diff --git a/azure-communication-ui/calling/build.gradle b/azure-communication-ui/calling/build.gradle index 6c2911dd32..7b413482fd 100644 --- a/azure-communication-ui/calling/build.gradle +++ b/azure-communication-ui/calling/build.gradle @@ -9,11 +9,11 @@ apply from: file('jacoco.gradle') android { resourcePrefix 'azure_communication_ui_calling_' - compileSdk 34 + compileSdk 35 defaultConfig { minSdkVersion 26 - targetSdkVersion 34 + targetSdkVersion 35 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" buildConfigField "String", "UI_SDK_VERSION", "\"" + call_library_version_name + "\"" @@ -102,6 +102,7 @@ dependencies { implementation "com.microsoft.fluentui:fluentui_drawer:$microsoft_fluent_ui_version" implementation "com.microsoft.fluentui:fluentui_persona:$microsoft_fluent_ui_version" implementation "com.microsoft.fluentui:fluentui_transients:$microsoft_fluent_ui_version" + implementation "androidx.activity:activity-ktx:$androidx_activity_ktx_version" api 'com.jakewharton.threetenabp:threetenabp:1.4.4' diff --git a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/CallCompositeActivity.kt b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/CallCompositeActivity.kt index c6cba997f0..eae4b81a1c 100644 --- a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/CallCompositeActivity.kt +++ b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/CallCompositeActivity.kt @@ -15,6 +15,7 @@ import android.util.LayoutDirection import android.util.Rational import android.view.MenuItem import android.view.View +import android.view.ViewGroup import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS import android.view.WindowManager import androidx.activity.result.ActivityResultLauncher @@ -22,6 +23,10 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.fragment.app.FragmentTransaction import androidx.lifecycle.lifecycleScope import com.azure.android.communication.ui.calling.CallCompositeException @@ -94,6 +99,10 @@ internal open class CallCompositeActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { // Before super, we'll set up the DI injector and check the PiP state + if (Build.VERSION.SDK_INT >= 35) { + // Turn OFF edge-to-edge behavior + WindowCompat.setDecorFitsSystemWindows(window, true) + } try { diContainerHolder.instanceId = instanceId diContainerHolder.container.callCompositeActivityWeakReference = WeakReference(this) @@ -132,7 +141,18 @@ internal open class CallCompositeActivity : AppCompatActivity() { } updatableOptionsManager.start() setContentView(R.layout.azure_communication_ui_calling_activity_call_composite) + if (Build.VERSION.SDK_INT >= 35) { + val rootView = findViewById(R.id.azure_communication_ui_fragment_container_view) + + ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets -> + val statusBarHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top + val navBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom + + view.updatePadding(top = statusBarHeight, bottom = navBarHeight) + insets + } + } permissionManager.start( this, getAudioPermissionLauncher(), diff --git a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/hangup/LeaveConfirmView.kt b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/hangup/LeaveConfirmView.kt index d20e95c76c..5c4ebf3073 100644 --- a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/hangup/LeaveConfirmView.kt +++ b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/hangup/LeaveConfirmView.kt @@ -5,10 +5,15 @@ package com.azure.android.communication.ui.calling.presentation.fragment.calling import android.annotation.SuppressLint import android.content.Context +import android.content.res.Configuration +import android.os.Build import android.view.View import android.widget.RelativeLayout import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat +import androidx.core.view.updatePadding import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager @@ -18,9 +23,9 @@ import com.azure.android.communication.ui.calling.utilities.BottomCellAdapter import com.azure.android.communication.ui.calling.utilities.BottomCellItem import com.azure.android.communication.ui.calling.utilities.BottomCellItemType import com.microsoft.fluentui.drawer.DrawerDialog -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlin.math.max +import kotlinx.coroutines.flow.collect @SuppressLint("ViewConstructor") internal class LeaveConfirmView( @@ -74,6 +79,21 @@ internal class LeaveConfirmView( private fun initializeLeaveConfirmMenuDrawer() { leaveConfirmMenuDrawer = DrawerDialog(context, DrawerDialog.BehaviorType.BOTTOM) leaveConfirmMenuDrawer.setContentView(this) + if (context.applicationInfo.targetSdkVersion >= 35) { + ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets -> + val orientation = resources.configuration.orientation + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()) + + // Apply padding only in portrait orientation + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + view.updatePadding(0, 0, 0, insets.bottom + 76) + } else { + view.updatePadding(0, 0, 0, 0) + } + WindowInsetsCompat.CONSUMED + } + } + leaveConfirmMenuDrawer.setOnDismissListener { viewModel.cancel() } diff --git a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/participantlist/ParticipantListView.kt b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/participantlist/ParticipantListView.kt index bfee08d1d9..6ac4a1de21 100644 --- a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/participantlist/ParticipantListView.kt +++ b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/participantlist/ParticipantListView.kt @@ -5,9 +5,13 @@ package com.azure.android.communication.ui.calling.presentation.fragment.calling import android.app.AlertDialog import android.content.Context +import android.os.Build import android.view.accessibility.AccessibilityManager import android.widget.RelativeLayout import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager @@ -78,6 +82,14 @@ internal class ParticipantListView( } } } + if (context.applicationInfo.targetSdkVersion >= 35) { + ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()) + view.updatePadding(0, 0, 0, insets.bottom + 106) + + WindowInsetsCompat.CONSUMED + } + } } fun stop() { @@ -131,22 +143,37 @@ internal class ParticipantListView( } private fun updateTableHeight(listSize: Int) { - - // title for in call participants var titles = 1 - - // title for in lobby participants if (viewModel.participantListContentStateFlow.value.remoteParticipantList.any { it.status == ParticipantStatus.IN_LOBBY }) { titles += 1 } - // set the height of the list to be half of the screen height or 50dp per item, whichever is smaller - participantTable.layoutParams.height = - (((listSize - titles) * 50 * context.resources.displayMetrics.density + titles * 30 * context.resources.displayMetrics.density).toInt()).coerceAtMost( - context.resources.displayMetrics.heightPixels / 2 - ) - } + val density = context.resources.displayMetrics.density + + if (context.applicationInfo.targetSdkVersion >= 35) { + // On Android 15+, calculate available height using WindowInsets + participantTable.post { + val windowInsets = ViewCompat.getRootWindowInsets(this) + val insets = windowInsets?.getInsets(WindowInsetsCompat.Type.systemBars()) + val availableHeight = context.resources.displayMetrics.heightPixels - (insets?.top ?: 0) - (insets?.bottom ?: 0) + val desiredHeight = (((listSize - titles) * 50 * density + titles * 30 * density).toInt()) + val finalHeight = desiredHeight.coerceAtMost(availableHeight / 2) + + participantTable.layoutParams = participantTable.layoutParams.apply { + height = finalHeight + } + } + } else { + // For Android 13 and below: keep existing logic + val desiredHeight = (((listSize - titles) * 50 * density + titles * 30 * density).toInt()) + val finalHeight = desiredHeight.coerceAtMost(context.resources.displayMetrics.heightPixels / 2) + + participantTable.layoutParams = participantTable.layoutParams.apply { + height = finalHeight + } + } + } private fun generateBottomCellItems( participantListContent: ParticipantListContent, ): MutableList { diff --git a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/support/SupportView.kt b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/support/SupportView.kt index ce9275b77f..c9ed7c5755 100644 --- a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/support/SupportView.kt +++ b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/presentation/fragment/calling/support/SupportView.kt @@ -8,10 +8,14 @@ import android.content.Context import android.content.Intent import android.content.Intent.ACTION_VIEW import android.net.Uri +import android.os.Build import android.util.AttributeSet import android.widget.EditText import androidx.appcompat.widget.AppCompatTextView import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.core.widget.addTextChangedListener import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope @@ -20,8 +24,8 @@ import com.azure.android.communication.ui.calling.utilities.implementation.Compo import com.microsoft.fluentui.drawer.DrawerDialog import com.microsoft.fluentui.widget.Button import kotlinx.coroutines.InternalCoroutinesApi -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.collect /** * SupportView is a custom view that is used to display the support form. @@ -61,7 +65,14 @@ internal class SupportView : ConstraintLayout { fun start(viewModel: SupportViewModel, viewLifecycleOwner: LifecycleOwner) { // Text Changed, Submit, Cancel Buttons bindViewInputs(viewModel) + if (context.applicationInfo.targetSdkVersion >= 35) { + ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()) + view.updatePadding(0, 0, 0, insets.bottom + 106) + WindowInsetsCompat.CONSUMED + } + } // Send Button stat bindViewOutputs(viewLifecycleOwner, viewModel) } diff --git a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/utilities/implementation/CompositeDrawerDialog.java b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/utilities/implementation/CompositeDrawerDialog.java index c823c0c6f8..b4f6190f84 100644 --- a/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/utilities/implementation/CompositeDrawerDialog.java +++ b/azure-communication-ui/calling/src/main/java/com/azure/android/communication/ui/calling/utilities/implementation/CompositeDrawerDialog.java @@ -4,10 +4,15 @@ package com.azure.android.communication.ui.calling.utilities.implementation; import android.content.Context; +import android.content.res.Configuration; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.recyclerview.widget.RecyclerView; import com.azure.android.communication.ui.calling.implementation.R; import com.microsoft.fluentui.drawer.DrawerDialog; @@ -39,12 +44,33 @@ private void initOnShow() { } private void onShow() { - // Temporary using the drawer container, it's the only way to set the content description at the moment. - // The issue is posted to the FluentUI library: https://github.com/microsoft/fluentui-android/issues/758 - final View view = findViewById(R.id.drawer_container); + final View drawer = findViewById(R.id.drawer_container); + if (drawer.getContext().getApplicationInfo().targetSdkVersion >= 35) { + final RecyclerView recyclerView = findViewById(R.id.bottom_drawer_table); + if (recyclerView == null) { + return; + } + + ViewCompat.setOnApplyWindowInsetsListener(drawer, (view, windowInsets) -> { + final Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + + final int orientation = view.getResources().getConfiguration().orientation; + final boolean isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT; - if (view != null) { - view.setContentDescription(getContext().getString(contentDescription)); + recyclerView.setPadding( + insets.left, + insets.top, + insets.right, + isPortrait ? insets.bottom + 150 : insets.bottom + ); + + return WindowInsetsCompat.CONSUMED; + }); } + + + // Temporary using the drawer container, it's the only way to set the content description at the moment. + // The issue is posted to the FluentUI library: https://github.com/microsoft/fluentui-android/issues/758 + drawer.setContentDescription(getContext().getString(contentDescription)); } } diff --git a/azure-communication-ui/calling/src/main/res/layout/azure_communication_ui_calling_listview.xml b/azure-communication-ui/calling/src/main/res/layout/azure_communication_ui_calling_listview.xml index d06507a3f4..ee83892623 100644 --- a/azure-communication-ui/calling/src/main/res/layout/azure_communication_ui_calling_listview.xml +++ b/azure-communication-ui/calling/src/main/res/layout/azure_communication_ui_calling_listview.xml @@ -3,11 +3,18 @@ Licensed under the MIT License. --> - + android:backgroundTint="@color/azure_communication_ui_calling_color_bottom_drawer_background"> + + diff --git a/azure-communication-ui/calling/src/main/res/values/azure_communication_ui_calling_themes.xml b/azure-communication-ui/calling/src/main/res/values/azure_communication_ui_calling_themes.xml index b67dc0361f..16c6795495 100644 --- a/azure-communication-ui/calling/src/main/res/values/azure_communication_ui_calling_themes.xml +++ b/azure-communication-ui/calling/src/main/res/values/azure_communication_ui_calling_themes.xml @@ -19,6 +19,9 @@ @color/fluentui_communication_blue + + @android:color/transparent + @color/azure_communication_ui_calling_color_on_primary diff --git a/azure-communication-ui/demo-app/build.gradle b/azure-communication-ui/demo-app/build.gradle index f3c1a69f95..094fe309aa 100644 --- a/azure-communication-ui/demo-app/build.gradle +++ b/azure-communication-ui/demo-app/build.gradle @@ -13,11 +13,11 @@ if (properties.containsKey("ENABLE_GOOGLE_SERVICES") && properties.getProperty(" } android { - compileSdk 34 + compileSdk 35 defaultConfig { applicationId "com.azure.android.communication.ui.callingcompositedemoapp" - targetSdkVersion 34 + targetSdkVersion 35 versionCode ui_library_version_code testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/azure-communication-ui/demo-app/src/calling/java/com/azure/android/communication/ui/callingcompositedemoapp/CallLauncherActivity.kt b/azure-communication-ui/demo-app/src/calling/java/com/azure/android/communication/ui/callingcompositedemoapp/CallLauncherActivity.kt index e3eb8e6702..eeb533e349 100644 --- a/azure-communication-ui/demo-app/src/calling/java/com/azure/android/communication/ui/callingcompositedemoapp/CallLauncherActivity.kt +++ b/azure-communication-ui/demo-app/src/calling/java/com/azure/android/communication/ui/callingcompositedemoapp/CallLauncherActivity.kt @@ -25,6 +25,10 @@ import android.view.View import android.widget.LinearLayout import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.lifecycleScope import com.azure.android.communication.ui.callingcompositedemoapp.databinding.ActivityCallLauncherBinding @@ -73,6 +77,11 @@ class CallLauncherActivity : AppCompatActivity() { finish() return } + + if (Build.VERSION.SDK_INT >= 35) { + // Turn OFF edge-to-edge behavior + WindowCompat.setDecorFitsSystemWindows(window, true) + } isActivityRunning = true createNotificationChannels() initCallCompositeManager() @@ -284,6 +293,14 @@ class CallLauncherActivity : AppCompatActivity() { IntentFilter(CALL_LAUNCHER_BROADCAST_ACTION) ) } + if (Build.VERSION.SDK_INT >= 35) { + ViewCompat.setOnApplyWindowInsetsListener(binding.launchActivity) { view, windowInsets -> + + view.updatePadding(0, 150, 0, 0) + + WindowInsetsCompat.CONSUMED + } + } autoRegisterPushIfNeeded() } diff --git a/azure-communication-ui/demo-app/src/calling/res/layout/activity_call_launcher.xml b/azure-communication-ui/demo-app/src/calling/res/layout/activity_call_launcher.xml index e65582ea8e..10d574573e 100644 --- a/azure-communication-ui/demo-app/src/calling/res/layout/activity_call_launcher.xml +++ b/azure-communication-ui/demo-app/src/calling/res/layout/activity_call_launcher.xml @@ -8,9 +8,11 @@