Skip to content

Commit d1ed709

Browse files
author
sm-deployer
authored
Merge pull request #496 from salemove/feature/MOB-1929_show_global_bubble_for_screensharing
2 parents f42cd79 + d11e875 commit d1ed709

20 files changed

+259
-72
lines changed

app/src/main/java/com/glia/exampleapp/MainFragment.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
8080
view.findViewById(R.id.end_engagement_button).setOnClickListener(v ->
8181
GliaWidgets.endEngagement());
8282
view.findViewById(R.id.initGliaWidgetsButton).setOnClickListener(v ->
83-
initGliaWidgets());
83+
new Thread(this::initGliaWidgets).start()
84+
);
8485
view.findViewById(R.id.authenticationButton).setOnClickListener(v ->
8586
showAuthenticationDialog());
8687
view.findViewById(R.id.deauthenticationButton).setOnClickListener(v ->
@@ -147,10 +148,13 @@ private void setupAuthButtonsVisibility() {
147148
containerView.findViewById(R.id.initGliaWidgetsButton).setVisibility(View.VISIBLE);
148149
containerView.findViewById(R.id.authenticationButton).setVisibility(View.GONE);
149150
containerView.findViewById(R.id.deauthenticationButton).setVisibility(View.GONE);
151+
containerView.findViewById(R.id.visitor_code_button).setVisibility(View.GONE);
150152
});
151153
return;
152154
}
153-
155+
getActivity().runOnUiThread(() -> {
156+
containerView.findViewById(R.id.visitor_code_button).setVisibility(View.VISIBLE);
157+
});
154158
if (authentication == null) return;
155159

156160
if (authentication.isAuthenticated()) {

app/src/main/res/layout/main_fragment.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<androidx.constraintlayout.widget.ConstraintLayout
1212
android:id="@+id/constraint_layout"
1313
android:layout_width="match_parent"
14+
android:animateLayoutChanges="true"
1415
android:layout_height="wrap_content">
1516

1617
<com.google.android.material.appbar.AppBarLayout

widgetssdk/src/main/java/com/glia/widgets/callvisualizer/ActivityWatcherForDialogs.kt renamed to widgetssdk/src/main/java/com/glia/widgets/callvisualizer/ActivityWatcherForCallVisualizer.kt

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import android.media.projection.MediaProjectionManager
1010
import android.net.Uri
1111
import android.os.Bundle
1212
import android.provider.Settings
13+
import android.view.View
1314
import android.widget.Toast
1415
import androidx.activity.ComponentActivity
1516
import androidx.activity.result.ActivityResultLauncher
@@ -25,35 +26,39 @@ import com.glia.widgets.UiTheme
2526
import com.glia.widgets.call.CallActivity
2627
import com.glia.widgets.call.Configuration
2728
import com.glia.widgets.callvisualizer.controller.CallVisualizerController
29+
import com.glia.widgets.chat.ChatView
2830
import com.glia.widgets.core.dialog.Dialog
2931
import com.glia.widgets.core.dialog.DialogController
3032
import com.glia.widgets.core.dialog.model.DialogState
3133
import com.glia.widgets.core.notification.device.NotificationManager
3234
import com.glia.widgets.core.screensharing.ScreenSharingController
3335
import com.glia.widgets.core.screensharing.data.GliaScreenSharingRepository.SKIP_ASKING_SCREEN_SHARING_PERMISSION_RESULT_CODE
3436
import com.glia.widgets.di.Dependencies
37+
import com.glia.widgets.filepreview.ui.FilePreviewView
3538
import com.glia.widgets.helper.Logger
3639
import com.glia.widgets.helper.Utils
3740
import com.glia.widgets.view.Dialogs
41+
import com.glia.widgets.view.head.controller.ServiceChatHeadController
3842
import com.google.android.material.theme.overlay.MaterialThemeOverlay
3943
import java.lang.ref.WeakReference
4044

41-
class ActivityWatcherForDialogs(
45+
class ActivityWatcherForCallVisualizer(
4246
private val callVisualizerController: CallVisualizerController,
4347
private val screenSharingController: ScreenSharingController,
4448
private val dialogController: DialogController,
49+
private var serviceChatHeadController: ServiceChatHeadController,
4550
) : Application.ActivityLifecycleCallbacks {
4651

4752
companion object {
48-
private val TAG = ActivityWatcherForDialogs::class.java.simpleName
53+
private val TAG = ActivityWatcherForCallVisualizer::class.java.simpleName
4954
}
5055

5156
@VisibleForTesting
5257
var dialogCallback: DialogController.Callback? = null
5358
private var screenSharingViewCallback: ScreenSharingController.ViewCallback? = null
5459

5560
@VisibleForTesting
56-
var startMediaProjection: ActivityResultLauncher<Intent>? = null
61+
val startMediaProjectionLaunchers = mutableMapOf<String, ActivityResultLauncher<Intent>?>()
5762

5863
@VisibleForTesting
5964
var alertDialog: AlertDialog? = null
@@ -68,39 +73,51 @@ class ActivityWatcherForDialogs(
6873
override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) {
6974
if (callVisualizerController.isCallOrChatScreenActiveUseCase(activity)) {
7075
// Call and Chat screens process screen sharing requests on their own
71-
startMediaProjection = null
76+
startMediaProjectionLaunchers.remove(activity::class.simpleName)
7277
return
7378
}
7479
registerForMediaProjectionPermissionResult(activity)
7580
super.onActivityPreCreated(activity, savedInstanceState)
7681
}
7782

7883
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
79-
override fun onActivityDestroyed(activity: Activity) {}
8084

85+
override fun onActivityDestroyed(activity: Activity) {
86+
if (activity.isFinishing) serviceChatHeadController.onDestroy()
87+
}
8188

8289
override fun onActivityResumed(activity: Activity) {
8390
resumedActivity = WeakReference(activity)
8491
addDialogCallback(resumedActivity)
8592
addScreenSharingCallback(resumedActivity)
93+
val gliaOrRootView: View? = getGliaViewOrRootView(activity)
94+
serviceChatHeadController.onResume(gliaOrRootView)
95+
}
96+
97+
@VisibleForTesting
98+
fun getGliaViewOrRootView(activity: Activity): View? {
99+
return activity.findViewById(R.id.call_view)
100+
?: activity.findViewById<FilePreviewView>(R.id.file_preview_view)
101+
?: activity.findViewById<ChatView>(R.id.chat_view)
102+
?: activity.findViewById<EndScreenSharingView>(R.id.screen_sharing_screen_view)
103+
?: activity.findViewById(android.R.id.content)
104+
?: activity.window.decorView.findViewById(android.R.id.content)
86105
}
87106

88107
override fun onActivityPaused(activity: Activity) {
89108
resumedActivity.clear()
90109
removeDialogCallback()
91110
removeScreenSharingCallback()
111+
val gliaOrRootView: View? = getGliaViewOrRootView(activity)
112+
serviceChatHeadController.onPause(gliaOrRootView)
92113
}
93114

94115

95116
override fun onActivityStarted(activity: Activity) {}
96117
override fun onActivityStopped(activity: Activity) {}
97118
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
98119

99-
override fun onActivityPreDestroyed(activity: Activity) {
100-
super.onActivityPreDestroyed(activity)
101-
startMediaProjection = null
102-
}
103-
120+
@Suppress("RedundantNullableReturnType")
104121
@VisibleForTesting
105122
fun registerForMediaProjectionPermissionResult(activity: Activity) {
106123
// Request a token that grants the app the ability to capture the display contents
@@ -111,11 +128,11 @@ class ActivityWatcherForDialogs(
111128
TAG, "Activity does not support ActivityResultRegistry APIs, " +
112129
"legacy onActivityResult() will be used to acquire a media projection token"
113130
)
114-
startMediaProjection = null
131+
startMediaProjectionLaunchers.remove(activity::class.simpleName)
115132
return
116133
}
117134

118-
startMediaProjection = componentActivity.registerForActivityResult(
135+
val launcher : ActivityResultLauncher<Intent>? = componentActivity.registerForActivityResult(
119136
ActivityResultContracts.StartActivityForResult()
120137
) { result ->
121138
Logger.d(TAG, "Acquire a media projection token: result received")
@@ -133,6 +150,7 @@ class ActivityWatcherForDialogs(
133150
}
134151
}
135152
}
153+
activity::class.simpleName?.let { startMediaProjectionLaunchers.put(it, launcher) }
136154
}
137155

138156
private fun addDialogCallback(resumedActivity: WeakReference<Activity?>) {
@@ -169,7 +187,11 @@ class ActivityWatcherForDialogs(
169187
}
170188

171189
override fun onScreenSharingStarted() {
172-
// TODO: 15.02.2023 show bubble
190+
if (Glia.isInitialized()) {
191+
serviceChatHeadController.init()
192+
}
193+
val gliaOrRootView: View? = getGliaViewOrRootView(it)
194+
serviceChatHeadController.onResume(gliaOrRootView)
173195
}
174196
}
175197
}
@@ -353,10 +375,10 @@ class ActivityWatcherForDialogs(
353375
}
354376

355377
private fun acquireMediaProjectionToken(activity: Activity) {
356-
startMediaProjection?.let { startMediaProjection ->
378+
startMediaProjectionLaunchers[activity::class.simpleName].let { startMediaProjection ->
357379
activity.getSystemService(MediaProjectionManager::class.java)
358380
?.let { mediaProjectionManager ->
359-
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())
381+
startMediaProjection?.launch(mediaProjectionManager.createScreenCaptureIntent())
360382
Logger.d(
361383
TAG,
362384
"Acquire a media projection token: launching permission request"

widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/ToggleChatHeadServiceUseCase.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import android.view.View;
44

55
import com.glia.widgets.call.CallView;
6+
import com.glia.widgets.callvisualizer.EndScreenSharingView;
67
import com.glia.widgets.chat.ChatView;
78
import com.glia.widgets.core.chathead.ChatHeadManager;
89
import com.glia.widgets.core.configuration.GliaSdkConfigurationManager;
@@ -11,11 +12,13 @@
1112
import com.glia.widgets.core.permissions.PermissionManager;
1213
import com.glia.widgets.core.queue.GliaQueueRepository;
1314
import com.glia.widgets.core.queue.model.GliaQueueingState;
15+
import com.glia.widgets.core.screensharing.data.GliaScreenSharingRepository;
1416
import com.glia.widgets.filepreview.ui.FilePreviewView;
1517

1618
public class ToggleChatHeadServiceUseCase {
1719
private final GliaEngagementRepository engagementRepository;
1820
private final GliaQueueRepository queueRepository;
21+
private final GliaScreenSharingRepository screenSharingRepository;
1922
private final ChatHeadManager chatHeadManager;
2023
private final PermissionManager permissionManager;
2124
private final GliaSdkConfigurationManager configurationManager;
@@ -24,27 +27,39 @@ public class ToggleChatHeadServiceUseCase {
2427
public ToggleChatHeadServiceUseCase(
2528
GliaEngagementRepository engagementRepository,
2629
GliaQueueRepository queueRepository,
30+
GliaScreenSharingRepository screenSharingRepository,
2731
ChatHeadManager chatHeadManager,
2832
PermissionManager permissionManager,
2933
GliaSdkConfigurationManager configurationManager,
3034
GliaEngagementTypeRepository engagementTypeRepository
3135
) {
3236
this.engagementRepository = engagementRepository;
3337
this.queueRepository = queueRepository;
38+
this.screenSharingRepository = screenSharingRepository;
3439
this.chatHeadManager = chatHeadManager;
3540
this.permissionManager = permissionManager;
3641
this.configurationManager = configurationManager;
3742
this.engagementTypeRepository = engagementTypeRepository;
3843
}
3944

4045
public void execute(View view) {
41-
if (isBubbleEnabled() && hasOverlayPermission() && (isShowForMediaEngagement(view) || isShowForChatEngagement(view))) {
46+
if (isBubbleEnabled() && hasOverlayPermission() && (
47+
isShowForMediaEngagement(view) || isShowForChatEngagement(view) || isCallVisualizerScreenSharing(view))) {
4248
chatHeadManager.startChatHeadService();
4349
} else {
4450
chatHeadManager.stopChatHeadService();
4551
}
4652
}
4753

54+
private boolean isCallVisualizerScreenSharing(View view) {
55+
return engagementRepository.isCallVisualizerEngagement() &&
56+
screenSharingRepository.isSharingScreen() &&
57+
isNotInChatView(view) &&
58+
isNotInCallView(view) &&
59+
isNotInFilePreviewView(view) &&
60+
isNotInEndScreenSharingView(view);
61+
}
62+
4863
private boolean isBubbleEnabled() {
4964
return configurationManager.isUseOverlay();
5065
}
@@ -58,7 +73,11 @@ private boolean isShowForMediaEngagement(View view) {
5873
}
5974

6075
private boolean isShowForChatEngagement(View view) {
61-
return isChatEngagementOrQueueingOngoing() && isNotInChatView(view) && isNotInCallView(view) && isNotInFilePreviewView(view);
76+
return isChatEngagementOrQueueingOngoing() &&
77+
isNotInChatView(view) &&
78+
isNotInCallView(view) &&
79+
isNotInFilePreviewView(view) &&
80+
isNotInEndScreenSharingView(view);
6281
}
6382

6483
private boolean isChatEngagementOrQueueingOngoing() {
@@ -77,6 +96,10 @@ private boolean isNotInChatView(View view) {
7796
return !(view instanceof ChatView);
7897
}
7998

99+
private boolean isNotInEndScreenSharingView(View view) {
100+
return !(view instanceof EndScreenSharingView);
101+
}
102+
80103
private boolean isNotInFilePreviewView(View view) {
81104
return !(view instanceof FilePreviewView);
82105
}

widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaEngagementRepository.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,18 @@ public void endEngagement() {
4040
}));
4141
}
4242

43-
public void listenForEngagement(Consumer<OmnicoreEngagement> engagementConsumer) {
43+
public void listenForOmnicoreEngagement(Consumer<OmnicoreEngagement> engagementConsumer) {
4444
gliaCore.on(Glia.Events.ENGAGEMENT, engagementConsumer);
4545
}
4646

4747
public void listenForCallVisualizerEngagement(Consumer<OmnibrowseEngagement> engagementConsumer) {
4848
gliaCore.getCallVisualizer().on(Omnibrowse.Events.ENGAGEMENT, engagementConsumer);
4949
}
5050

51+
public void listenForOmnibrowseEngagement(Consumer<OmnibrowseEngagement> engagementConsumer) {
52+
gliaCore.getCallVisualizer().on(Omnibrowse.Events.ENGAGEMENT, engagementConsumer);
53+
}
54+
5155
public void unregisterEngagementListener(Consumer<OmnicoreEngagement> engagementConsumer) {
5256
gliaCore.off(Glia.Events.ENGAGEMENT, engagementConsumer);
5357
}
@@ -60,6 +64,12 @@ public boolean hasOngoingEngagement() {
6064
return gliaCore.getCurrentEngagement().isPresent();
6165
}
6266

67+
public boolean isCallVisualizerEngagement() {
68+
return gliaCore.getCurrentEngagement()
69+
.filter(engagement -> engagement instanceof OmnibrowseEngagement)
70+
.isPresent();
71+
}
72+
6373
public void getSiteInfo(@NonNull RequestCallback<SiteInfo> callback) {
6474
gliaCore.getSiteInfo(callback);
6575
}

widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaEngagementTypeRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public boolean isMediaEngagement() {
2929

3030
public boolean isChatEngagement() {
3131
return engagementRepository.hasOngoingEngagement() &&
32+
!engagementRepository.isCallVisualizerEngagement() &&
3233
engagementStateRepository.isOperatorPresent() &&
3334
!hasAnyMedia();
3435
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.glia.widgets.core.engagement.domain
2+
3+
import com.glia.androidsdk.omnibrowse.OmnibrowseEngagement
4+
import com.glia.widgets.core.engagement.GliaEngagementRepository
5+
import com.glia.widgets.core.engagement.GliaEngagementStateRepository
6+
import com.glia.widgets.core.operator.GliaOperatorMediaRepository
7+
import com.glia.widgets.core.queue.GliaQueueRepository
8+
import com.glia.widgets.core.visitor.GliaVisitorMediaRepository
9+
import java.util.function.Consumer
10+
11+
class GliaOnCallVisualizerUseCase(
12+
private val gliaRepository: GliaEngagementRepository,
13+
private val operatorMediaRepository: GliaOperatorMediaRepository,
14+
private val gliaQueueRepository: GliaQueueRepository,
15+
private val gliaVisitorMediaRepository: GliaVisitorMediaRepository,
16+
private val gliaEngagementStateRepository: GliaEngagementStateRepository
17+
) : Consumer<OmnibrowseEngagement> {
18+
interface Listener {
19+
fun newEngagementLoaded(engagement: OmnibrowseEngagement)
20+
}
21+
22+
private var listener: Listener? = null
23+
fun execute(listener: Listener) {
24+
if (this.listener === listener) {
25+
// Already listening
26+
return
27+
}
28+
this.listener = listener
29+
gliaRepository.listenForOmnibrowseEngagement(this)
30+
}
31+
32+
override fun accept(engagement: OmnibrowseEngagement) {
33+
operatorMediaRepository.onEngagementStarted(engagement)
34+
gliaVisitorMediaRepository.onEngagementStarted(engagement)
35+
gliaEngagementStateRepository.onEngagementStarted(engagement)
36+
gliaQueueRepository.onEngagementStarted()
37+
if (listener != null) {
38+
listener!!.newEngagementLoaded(engagement)
39+
}
40+
}
41+
42+
fun unregisterListener(listener: Listener) {
43+
if (this.listener === listener) {
44+
gliaRepository.unregisterCallVisualizerEngagementListener(this)
45+
this.listener = null
46+
}
47+
}
48+
}

widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/GliaOnEngagementUseCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void execute(Listener listener) {
4242
return;
4343
}
4444
this.listener = listener;
45-
gliaRepository.listenForEngagement(this);
45+
gliaRepository.listenForOmnicoreEngagement(this);
4646
}
4747

4848
@Override
Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package com.glia.widgets.core.engagement.domain
22

3-
import com.glia.androidsdk.Engagement
4-
import com.glia.androidsdk.Glia
5-
import com.glia.androidsdk.omnibrowse.OmnibrowseEngagement
3+
import com.glia.widgets.core.engagement.GliaEngagementRepository
64

75

8-
class IsCallVisualizerUseCase {
6+
class IsCallVisualizerUseCase(
7+
private val gliaEngagementRepository: GliaEngagementRepository
8+
) {
99
fun execute(): Boolean {
10-
return Glia.getCurrentEngagement()
11-
.filter { engagement: Engagement -> engagement is OmnibrowseEngagement }
12-
.isPresent
10+
return gliaEngagementRepository.isCallVisualizerEngagement
1311
}
1412
}

0 commit comments

Comments
 (0)